1 /* 2 * memtoy: commands.c - command line interface 3 * 4 * A brute force/ad hoc command interpreter: 5 * + parse commands [interactive or batch] 6 * + convert/validate arguments 7 * + some general/administrative commands herein 8 * + actual segment management routines in segment.c 9 */ 10 /* 11 * Copyright (c) 2005 Hewlett-Packard, Inc 12 * All rights reserved. 13 */ 14 15 /* 16 * This program is free software; you can redistribute it and/or modify 17 * it under the terms of the GNU General Public License as published by 18 * the Free Software Foundation; either version 2 of the License, or 19 * (at your option) any later version. 20 * 21 * This program is distributed in the hope that it will be useful, 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * GNU General Public License for more details. 25 * 26 * You should have received a copy of the GNU General Public License 27 * along with this program; if not, write to the Free Software 28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 29 */ 30 31 #include "config.h" 32 33 #ifdef HAVE_NUMA_V2 34 #include <sys/types.h> 35 #include <sys/time.h> 36 #include <sys/mman.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <numa.h> 40 #include <numaif.h> 41 #include <stdarg.h> 42 #include <stdlib.h> 43 #include <stdio.h> 44 #include <string.h> 45 #include <unistd.h> 46 #include <sys/syscall.h> 47 48 #include "memtoy.h" 49 #include "test.h" 50 51 #define CMD_SUCCESS 0 52 #define CMD_ERROR 1 53 54 #ifndef __NR_migrate_pages 55 #define __NR_migrate_pages 0 56 #endif 57 58 #ifndef MPOL_MF_WAIT 59 #define MPOL_MF_WAIT (1<<2) /* Wait for existing pages to migrate */ 60 #endif 61 62 static inline int nodemask_isset(nodemask_t * mask, int node) 63 { 64 if ((unsigned)node >= NUMA_NUM_NODES) 65 return 0; 66 if (mask->n[node / (8 * sizeof(unsigned long))] & 67 (1UL << (node % (8 * sizeof(unsigned long))))) 68 return 1; 69 return 0; 70 } 71 72 static inline void nodemask_set(nodemask_t * mask, int node) 73 { 74 mask->n[node / (8 * sizeof(unsigned long))] |= 75 (1UL << (node % (8 * sizeof(unsigned long)))); 76 } 77 78 static char *whitespace = " \t"; 79 80 /* 81 * ========================================================================= 82 */ 83 static int help_me(char *); /* forward reference */ 84 85 /* 86 * required_arg -- check for a required argument; issue message if not there 87 * 88 * return true if arg [something] exists; else return false 89 */ 90 static bool required_arg(char *arg, char *arg_name) 91 { 92 glctx_t *gcp = &glctx; 93 94 if (*arg != '\0') 95 return true; 96 97 fprintf(stderr, "%s: command '%s' missing required argument: %s\n\n", 98 gcp->program_name, gcp->cmd_name, arg_name); 99 help_me(gcp->cmd_name); 100 101 return false; 102 } 103 104 /* 105 * size_kmgp() -- convert ascii arg to numeric and scale as requested 106 */ 107 #define KILO_SHIFT 10 108 static size_t size_kmgp(char *arg) 109 { 110 size_t argval; 111 char *next; 112 113 argval = strtoul(arg, &next, 0); 114 if (*next == '\0') 115 return argval; 116 117 switch (tolower(*next)) { 118 case 'p': /* pages */ 119 argval *= glctx.pagesize; 120 break; 121 122 case 'k': 123 argval <<= KILO_SHIFT; 124 break; 125 126 case 'm': 127 argval <<= KILO_SHIFT * 2; 128 break; 129 130 case 'g': 131 argval <<= KILO_SHIFT * 3; 132 break; 133 134 default: 135 return BOGUS_SIZE; /* bogus chars after number */ 136 } 137 138 return argval; 139 } 140 141 static size_t get_scaled_value(char *args, char *what) 142 { 143 glctx_t *gcp = &glctx; 144 size_t size = size_kmgp(args); 145 146 if (size == BOGUS_SIZE) { 147 fprintf(stderr, "%s: segment %s must be numeric value" 148 " followed by optional k, m, g or p [pages] scale factor.\n", 149 gcp->program_name, what); 150 } 151 152 return size; 153 } 154 155 static int get_range(char *args, range_t * range, char **nextarg) 156 { 157 158 if (isdigit(*args)) { 159 char *nextarg; 160 161 args = strtok_r(args, whitespace, &nextarg); 162 range->offset = get_scaled_value(args, "offset"); 163 if (range->offset == BOGUS_SIZE) 164 return CMD_ERROR; 165 args = nextarg + strspn(nextarg, whitespace); 166 167 /* 168 * <length> ... only if offset specified 169 */ 170 if (*args != '\0') { 171 args = strtok_r(args, whitespace, &nextarg); 172 if (*args != '*') { 173 range->length = 174 get_scaled_value(args, "length"); 175 if (range->length == BOGUS_SIZE) 176 return CMD_ERROR; 177 } else 178 range->length = 0; /* map to end of file */ 179 args = nextarg + strspn(nextarg, whitespace); 180 } 181 } 182 183 *nextarg = args; 184 return CMD_SUCCESS; 185 } 186 187 static int get_shared(char *args) 188 { 189 glctx_t *gcp = &glctx; 190 int segflag = MAP_PRIVATE; 191 192 if (!strcmp(args, "shared")) 193 segflag = MAP_SHARED; 194 else if (*args != '\0' && strcmp(args, "private")) { 195 fprintf(stderr, "%s: anon seg access type must be one of: " 196 "'private' or 'shared'\n", gcp->program_name); 197 return -1; 198 } 199 return segflag; 200 } 201 202 /* 203 * get_access() - check args for 'read'\'write' 204 * return: 205 * 1 = read 206 * 2 = write 207 * 0 = neither [error] 208 */ 209 static int get_access(char *args) 210 { 211 glctx_t *gcp = &glctx; 212 int axcs = 1; 213 int len = strlen(args); 214 215 if (tolower(*args) == 'w') 216 axcs = 2; 217 else if (len != 0 && tolower(*args) != 'r') { 218 fprintf(stderr, 219 "%s: segment access must be 'r[ead]' or 'w[rite]'\n", 220 gcp->program_name); 221 return 0; 222 } 223 224 return axcs; 225 } 226 227 static bool numa_supported(void) 228 { 229 glctx_t *gcp = &glctx; 230 231 if (gcp->numa_max_node <= 0) { 232 fprintf(stderr, "%s: no NUMA support on this platform\n", 233 gcp->program_name); 234 return false; 235 } 236 return true; 237 } 238 239 static struct policies { 240 char *pol_name; 241 int pol_flag; 242 } policies[] = { 243 { 244 "default", MPOL_DEFAULT}, { 245 "preferred", MPOL_PREFERRED}, { 246 "bind", MPOL_BIND}, { 247 "interleaved", MPOL_INTERLEAVE}, { 248 NULL, -1} 249 }; 250 251 /* 252 * get_mbind_policy() - parse <policy> argument to mbind command 253 * 254 * format: <mpol>[+<flags>] 255 * <mpol> is one of the policies[] above. 256 * '+<flags>' = modifiers to mbind() call. parsed by get_mbind_flags() 257 */ 258 static int get_mbind_policy(char *args, char **nextarg) 259 { 260 glctx_t *gcp = &glctx; 261 struct policies *polp; 262 char *pol; 263 264 pol = args; 265 args += strcspn(args, " +"); 266 267 for (polp = policies; polp->pol_name != NULL; ++polp) { 268 size_t plen = args - pol; 269 270 if (strncmp(pol, polp->pol_name, plen)) 271 continue; 272 273 *nextarg = args; 274 return polp->pol_flag; 275 } 276 277 fprintf(stderr, "%s: unrecognized policy %s\n", 278 gcp->program_name, pol); 279 return CMD_ERROR; 280 } 281 282 /* 283 * get_mbind_flags() - parse mbind(2) modifier flags 284 * 285 * format: +move[+wait] 286 * 'move' specifies that currently allocated pages should be migrated. 287 * => MPOL_MF_MOVE 288 * 'wait' [only if 'move' specified] specifies that mbind(2) should not 289 * return until all pages that can be migrated have been. 290 * => MPOL_MF_WAIT 291 * 292 * returns flags on success; -1 on error 293 */ 294 static int get_mbind_flags(char *args, char **nextarg) 295 { 296 glctx_t *gcp = &glctx; 297 char *arg; 298 int flags = 0; 299 300 arg = args; 301 args += strcspn(args, " +"); 302 303 if (strncmp(arg, "move", args - arg)) 304 goto flags_err; 305 306 flags = MPOL_MF_MOVE; 307 308 if (*args == '+') { 309 ++args; 310 if (*args == '\0') { 311 fprintf(stderr, "%s: expected 'wait' after '+'\n", 312 gcp->program_name); 313 return -1; 314 } 315 arg = strtok_r(args, " ", &args); 316 if (strncmp(arg, "wait", strlen(arg))) 317 goto flags_err; 318 319 flags |= MPOL_MF_WAIT; 320 } 321 322 *nextarg = args; 323 return flags; 324 325 flags_err: 326 fprintf(stderr, "%s: unrecognized mbind flag: %s\n", 327 gcp->program_name, arg); 328 return -1; 329 330 } 331 332 /* 333 * get_nodemask() -- get nodemask from comma-separated list of node ids. 334 * 335 * N.B., caller must free returned nodemask 336 */ 337 static nodemask_t *get_nodemask(char *args) 338 { 339 glctx_t *gcp = &glctx; 340 nodemask_t *nmp = (nodemask_t *) calloc(1, sizeof(nodemask_t)); 341 char *next; 342 int node; 343 while (*args != '\0') { 344 if (!isdigit(*args)) { 345 fprintf(stderr, "%s: expected digit for <node/list>\n", 346 gcp->program_name); 347 goto out_err; 348 } 349 350 node = strtoul(args, &next, 10); 351 352 if (node > gcp->numa_max_node) { 353 fprintf(stderr, "%s: node ids must be <= %d\n", 354 gcp->program_name, gcp->numa_max_node); 355 goto out_err; 356 } 357 358 nodemask_set(nmp, node); 359 360 if (*next == '\0') 361 return nmp; 362 if (*next != ',') { 363 break; 364 } 365 args = next + 1; 366 } 367 368 out_err: 369 free(nmp); 370 return NULL; 371 } 372 373 /* 374 * get_arg_nodeid_list() -- get list [array] of node ids from comma-separated list. 375 * 376 * on success, returns count of id's in list; on error -1 377 */ 378 static int get_arg_nodeid_list(char *args, unsigned int *list) 379 { 380 glctx_t *gcp; 381 char *next; 382 nodemask_t my_allowed_nodes; 383 int node, count = 0; 384 385 gcp = &glctx; 386 my_allowed_nodes = numa_get_membind_compat(); 387 while (*args != '\0') { 388 if (!isdigit(*args)) { 389 fprintf(stderr, "%s: expected digit for <node/list>\n", 390 gcp->program_name); 391 return -1; 392 } 393 394 node = strtoul(args, &next, 10); 395 396 if (node > gcp->numa_max_node) { 397 fprintf(stderr, "%s: node ids must be <= %d\n", 398 gcp->program_name, gcp->numa_max_node); 399 return -1; 400 } 401 402 if (!nodemask_isset(&my_allowed_nodes, node)) { 403 fprintf(stderr, 404 "%s: node %d is not in my allowed node mask\n", 405 gcp->program_name, node); 406 return -1; 407 } 408 409 *(list + count++) = node; 410 411 if (*next == '\0') 412 return count; 413 if (*next != ',') { 414 break; 415 } 416 417 if (count >= gcp->numa_max_node) { 418 fprintf(stderr, "%s: too many node ids in list\n", 419 gcp->program_name); 420 } 421 args = next + 1; 422 } 423 424 return -1; 425 } 426 427 /* 428 * get_current_nodeid_list() - fill arg array with nodes from 429 * current thread's allowed node mask. return # of nodes in 430 * mask. 431 */ 432 static int get_current_nodeid_list(unsigned int *fromids) 433 { 434 /* 435 * FIXME (garrcoop): gcp is uninitialized and shortly hereafter used in 436 * an initialization statement..... UHHHHHHH... test writer fail? 437 */ 438 glctx_t *gcp; 439 nodemask_t my_allowed_nodes; 440 int nr_nodes = 0, max_node = gcp->numa_max_node; 441 int node; 442 443 gcp = &glctx; 444 my_allowed_nodes = numa_get_membind_compat(); 445 for (node = 0; node <= max_node; ++node) { 446 if (nodemask_isset(&my_allowed_nodes, node)) 447 *(fromids + nr_nodes++) = node; 448 } 449 450 /* 451 * shouldn't happen, but let 'em know if it does 452 */ 453 if (nr_nodes == 0) 454 fprintf(stderr, "%s: my allowed node mask is empty !!???\n", 455 gcp->program_name); 456 return nr_nodes; 457 } 458 459 /* 460 * NOTE (garrcoop): Get rid of an -Wunused warning. This wasn't deleted because 461 * I don't know what the original intent was for this code. 462 */ 463 #if 0 464 static void not_implemented() 465 { 466 glctx_t *gcp = &glctx; 467 468 fprintf(stderr, "%s: %s not implemented yet\n", 469 gcp->program_name, gcp->cmd_name); 470 } 471 #endif 472 473 /* 474 * ========================================================================= 475 */ 476 static int quit(char *args) 477 { 478 exit(0); /* let cleanup() do its thing */ 479 } 480 481 static int show_pid(char *args) 482 { 483 glctx_t *gcp = &glctx; 484 485 printf("%s: pid = %d\n", gcp->program_name, getpid()); 486 487 return CMD_SUCCESS; 488 } 489 490 static int pause_me(char *args) 491 { 492 // glctx_t *gcp = &glctx; 493 494 pause(); 495 reset_signal(); 496 497 return CMD_SUCCESS; 498 } 499 500 static char *numa_header = " Node Total Mem[MB] Free Mem[MB]\n"; 501 static int numa_info(char *args) 502 { 503 glctx_t *gcp = &glctx; 504 unsigned int *nodeids; 505 int nr_nodes, i; 506 bool do_header = true; 507 508 if (!numa_supported()) 509 return CMD_ERROR; 510 511 nodeids = calloc(gcp->numa_max_node, sizeof(*nodeids)); 512 nr_nodes = get_current_nodeid_list(nodeids); 513 if (nr_nodes < 0) 514 return CMD_ERROR; 515 516 for (i = 0; i < nr_nodes; ++i) { 517 int node = nodeids[i]; 518 long node_size, node_free; 519 520 node_size = numa_node_size(node, &node_free); 521 if (node_size < 0) { 522 fprintf(stderr, 523 "%s: numa_node_size() failed for node %d\n", 524 gcp->program_name, node); 525 return CMD_ERROR; 526 } 527 528 if (do_header) { 529 do_header = false; 530 puts(numa_header); 531 } 532 printf(" %3d %9ld %8ld\n", node, 533 node_size / (1024 * 1024), node_free / (1024 * 1024)); 534 } 535 536 return CMD_SUCCESS; 537 } 538 539 /* 540 * migrate <to-node-id[s]> [<from-node-id[s]>] 541 * 542 * Node id[s] - single node id or comma-separated list 543 * <to-node-id[s]> - 1-for-1 with <from-node-id[s]>, OR 544 * if <from-node-id[s]> omitted, <to-node-id[s]> must be 545 * a single node id. 546 */ 547 static int migrate_process(char *args) 548 { 549 glctx_t *gcp = &glctx; 550 unsigned int *fromids, *toids; 551 char *idlist, *nextarg; 552 struct timeval t_start, t_end; 553 int nr_to, nr_from; 554 int nr_migrated; 555 int ret = CMD_ERROR; 556 557 if (!numa_supported()) 558 return CMD_ERROR; 559 560 toids = calloc(gcp->numa_max_node, sizeof(*toids)); 561 fromids = calloc(gcp->numa_max_node, sizeof(*fromids)); 562 563 /* 564 * <to-node-id[s]> 565 */ 566 if (!required_arg(args, "<to-node-id[s]>")) 567 return CMD_ERROR; 568 idlist = strtok_r(args, whitespace, &nextarg); 569 nr_to = get_arg_nodeid_list(idlist, toids); 570 if (nr_to <= 0) 571 goto out_free; 572 args = nextarg + strspn(nextarg, whitespace); 573 574 if (*args != '\0') { 575 /* 576 * apparently, <from-node-id[s]> present 577 */ 578 idlist = strtok_r(args, whitespace, &nextarg); 579 nr_from = get_arg_nodeid_list(idlist, fromids); 580 if (nr_from <= 0) 581 goto out_free; 582 if (nr_from != nr_to) { 583 fprintf(stderr, 584 "%s: # of 'from' ids must = # of 'to' ids\n", 585 gcp->program_name); 586 goto out_free; 587 } 588 } else { 589 int i; 590 591 /* 592 * no <from-node-id[s]>, nr_to must == 1, 593 * get fromids from memory policy. 594 */ 595 if (nr_to > 1) { 596 fprintf(stderr, "%s: # to ids must = 1" 597 " when no 'from' ids specified\n", 598 gcp->program_name); 599 goto out_free; 600 } 601 nr_from = get_current_nodeid_list(fromids); 602 if (nr_from <= 0) 603 goto out_free; 604 605 /* 606 * remove 'to' node from 'from' list. to and from 607 * lists can't intersect. 608 */ 609 for (i = nr_from - 1; i >= 0; --i) { 610 if (*toids == *(fromids + i)) { 611 while (i <= nr_from) { 612 *(fromids + i) = *(fromids + i + 1); 613 ++i; 614 } 615 --nr_from; 616 break; 617 } 618 } 619 620 /* 621 * fill out nr_from toids with the single 'to' node 622 */ 623 for (; nr_to < nr_from; ++nr_to) 624 *(toids + nr_to) = *toids; /* toids[0] */ 625 } 626 627 gettimeofday(&t_start, NULL); 628 nr_migrated = 629 syscall(__NR_migrate_pages, getpid(), nr_from, fromids, toids); 630 if (nr_migrated < 0) { 631 int err = errno; 632 fprintf(stderr, "%s: migrate_pages failed - %s\n", 633 gcp->program_name, strerror(err)); 634 goto out_free; 635 } 636 gettimeofday(&t_end, NULL); 637 printf("%s: migrated %d pages in %6.3fsecs\n", 638 gcp->program_name, nr_migrated, 639 (float)(tv_diff_usec(&t_start, &t_end)) / 1000000.0); 640 ret = CMD_SUCCESS; 641 642 out_free: 643 free(toids); 644 free(fromids); 645 return ret; 646 } 647 648 static int show_seg(char *args) 649 { 650 glctx_t *gcp = &glctx; 651 652 char *segname = NULL, *nextarg; 653 654 args += strspn(args, whitespace); 655 if (*args != '\0') 656 segname = strtok_r(args, whitespace, &nextarg); 657 658 if (!segment_show(segname)) 659 return CMD_ERROR; 660 661 return CMD_SUCCESS; 662 } 663 664 /* 665 * anon_seg: <seg-name> <size>[kmgp] [private|shared] 666 */ 667 static int anon_seg(char *args) 668 { 669 glctx_t *gcp = &glctx; 670 671 char *segname, *nextarg; 672 range_t range = { 0L, 0L }; 673 int segflag = 0; 674 675 args += strspn(args, whitespace); 676 677 if (!required_arg(args, "<seg-name>")) 678 return CMD_ERROR; 679 segname = strtok_r(args, whitespace, &nextarg); 680 args = nextarg + strspn(nextarg, whitespace); 681 682 if (!required_arg(args, "<size>")) 683 return CMD_ERROR; 684 args = strtok_r(args, whitespace, &nextarg); 685 range.length = get_scaled_value(args, "size"); 686 if (range.length == BOGUS_SIZE) 687 return CMD_ERROR; 688 args = nextarg + strspn(nextarg, whitespace); 689 690 if (*args != '\0') { 691 segflag = get_shared(args); 692 if (segflag == -1) 693 return CMD_ERROR; 694 } 695 696 if (!segment_register(SEGT_ANON, segname, &range, segflag)) 697 return CMD_ERROR; 698 699 return CMD_SUCCESS; 700 } 701 702 /* 703 * file_seg: <path-name> [<offset>[kmgp] <length>[kmgp] [private|shared]] 704 */ 705 static int file_seg(char *args) 706 { 707 glctx_t *gcp = &glctx; 708 709 char *pathname, *nextarg; 710 range_t range = { 0L, 0L }; 711 int segflag = MAP_PRIVATE; 712 713 args += strspn(args, whitespace); 714 715 if (!required_arg(args, "<path-name>")) 716 return CMD_ERROR; 717 pathname = strtok_r(args, whitespace, &nextarg); 718 args = nextarg + strspn(nextarg, whitespace); 719 720 /* 721 * offset, length are optional 722 */ 723 if (get_range(args, &range, &nextarg) == CMD_ERROR) 724 return CMD_ERROR; 725 args = nextarg; 726 727 if (*args != '\0') { 728 segflag = get_shared(args); 729 if (segflag == -1) 730 return CMD_ERROR; 731 } 732 733 if (!segment_register(SEGT_FILE, pathname, &range, segflag)) 734 return CMD_ERROR; 735 736 return CMD_SUCCESS; 737 } 738 739 /* 740 * remove_seg: <seg-name> [<seg-name> ...] 741 */ 742 static int remove_seg(char *args) 743 { 744 glctx_t *gcp = &glctx; 745 746 args += strspn(args, whitespace); 747 if (!required_arg(args, "<seg-name>")) 748 return CMD_ERROR; 749 750 while (*args != '\0') { 751 char *segname, *nextarg; 752 753 segname = strtok_r(args, whitespace, &nextarg); 754 args = nextarg + strspn(nextarg, whitespace); 755 756 segment_remove(segname); 757 } 758 return 0; 759 } 760 761 /* 762 * touch_seg: <seg-name> [<offset> <length>] [read|write] 763 */ 764 static int touch_seg(char *args) 765 { 766 glctx_t *gcp = &glctx; 767 768 char *segname, *nextarg; 769 range_t range = { 0L, 0L }; 770 int axcs; 771 772 args += strspn(args, whitespace); 773 if (!required_arg(args, "<seg-name>")) 774 return CMD_ERROR; 775 segname = strtok_r(args, whitespace, &nextarg); 776 args = nextarg + strspn(nextarg, whitespace); 777 778 /* 779 * offset, length are optional 780 */ 781 if (get_range(args, &range, &nextarg) == CMD_ERROR) 782 return CMD_ERROR; 783 args = nextarg; 784 785 axcs = get_access(args); 786 if (axcs == 0) 787 return CMD_ERROR; 788 789 if (!segment_touch(segname, &range, axcs - 1)) 790 return CMD_ERROR; 791 792 return CMD_SUCCESS; 793 } 794 795 /* 796 * unmap <seg-name> - unmap specified segment, but remember name/size/... 797 */ 798 static int unmap_seg(char *args) 799 { 800 glctx_t *gcp = &glctx; 801 char *segname, *nextarg; 802 803 args += strspn(args, whitespace); 804 if (!required_arg(args, "<seg-name>")) 805 return CMD_ERROR; 806 segname = strtok_r(args, whitespace, &nextarg); 807 args = nextarg + strspn(nextarg, whitespace); 808 809 if (!segment_unmap(segname)) 810 return CMD_ERROR; 811 812 return CMD_SUCCESS; 813 } 814 815 /* 816 * map <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]] [<seg-share>] 817 */ 818 static int map_seg(char *args) 819 { 820 glctx_t *gcp = &glctx; 821 822 char *segname, *nextarg; 823 range_t range = { 0L, 0L }; 824 range_t *rangep = NULL; 825 int segflag = MAP_PRIVATE; 826 827 args += strspn(args, whitespace); 828 if (!required_arg(args, "<seg-name>")) 829 return CMD_ERROR; 830 segname = strtok_r(args, whitespace, &nextarg); 831 args = nextarg + strspn(nextarg, whitespace); 832 833 /* 834 * offset, length are optional 835 */ 836 if (get_range(args, &range, &nextarg) == CMD_ERROR) 837 return CMD_ERROR; 838 if (args != nextarg) { 839 rangep = ⦥ /* override any registered range */ 840 args = nextarg; 841 } 842 843 if (*args != '\0') { 844 segflag = get_shared(args); 845 if (segflag == -1) 846 return CMD_ERROR; 847 } 848 849 if (!segment_map(segname, rangep, segflag)) 850 return CMD_ERROR; 851 852 return CMD_SUCCESS; 853 } 854 855 /* 856 * mbind <seg-name> [<offset>[kmgp] <length>[kmgp]] <policy> <node-list> 857 */ 858 static int mbind_seg(char *args) 859 { 860 glctx_t *gcp = &glctx; 861 862 char *segname, *nextarg; 863 range_t range = { 0L, 0L }; 864 nodemask_t *nodemask = NULL; 865 int policy, flags = 0; 866 int ret; 867 868 if (!numa_supported()) 869 return CMD_ERROR; 870 871 args += strspn(args, whitespace); 872 if (!required_arg(args, "<seg-name>")) 873 return CMD_ERROR; 874 segname = strtok_r(args, whitespace, &nextarg); 875 args = nextarg + strspn(nextarg, whitespace); 876 877 /* 878 * offset, length are optional 879 */ 880 if (get_range(args, &range, &nextarg) == CMD_ERROR) 881 return CMD_ERROR; 882 args = nextarg; 883 884 if (!required_arg(args, "<policy>")) 885 return CMD_ERROR; 886 policy = get_mbind_policy(args, &nextarg); 887 if (policy < 0) 888 return CMD_ERROR; 889 890 args = nextarg + strspn(nextarg, whitespace); 891 if (*args == '+') { 892 flags = get_mbind_flags(++args, &nextarg); 893 if (flags == -1) 894 return CMD_ERROR; 895 } 896 args = nextarg + strspn(nextarg, whitespace); 897 898 if (policy != MPOL_DEFAULT) { 899 if (!required_arg(args, "<node/list>")) 900 return CMD_ERROR; 901 nodemask = get_nodemask(args); 902 if (nodemask == NULL) 903 return CMD_ERROR; 904 } 905 906 ret = CMD_SUCCESS; 907 #if 1 // for testing 908 if (!segment_mbind(segname, &range, policy, nodemask, flags)) 909 ret = CMD_ERROR; 910 #endif 911 912 if (nodemask != NULL) 913 free(nodemask); 914 return ret; 915 } 916 917 /* 918 * shmem_seg - create [shmget] and register a SysV shared memory segment 919 * of specified size 920 */ 921 static int shmem_seg(char *args) 922 { 923 glctx_t *gcp = &glctx; 924 925 char *segname, *nextarg; 926 range_t range = { 0L, 0L }; 927 928 args += strspn(args, whitespace); 929 930 if (!required_arg(args, "<seg-name>")) 931 return CMD_ERROR; 932 segname = strtok_r(args, whitespace, &nextarg); 933 args = nextarg + strspn(nextarg, whitespace); 934 935 if (!required_arg(args, "<size>")) 936 return CMD_ERROR; 937 args = strtok_r(args, whitespace, &nextarg); 938 range.length = get_scaled_value(args, "size"); 939 if (range.length == BOGUS_SIZE) 940 return CMD_ERROR; 941 args = nextarg + strspn(nextarg, whitespace); 942 943 if (!segment_register(SEGT_SHM, segname, &range, MAP_SHARED)) 944 return CMD_ERROR; 945 946 return CMD_SUCCESS; 947 } 948 949 /* 950 * where <seg-name> [<offset>[kmgp] <length>[kmgp]] - show node location 951 * of specified range of segment. 952 * 953 * NOTE: if neither <offset> nor <length> specified, <offset> defaults 954 * to 0 [start of segment], as usual, and length defaults to 64 pages 955 * rather than the entire segment. Suitable for a "quick look" at where 956 * segment resides. 957 */ 958 static int where_seg(char *args) 959 { 960 glctx_t *gcp = &glctx; 961 962 char *segname, *nextarg; 963 range_t range = { 0L, 0L }; 964 int ret; 965 966 if (!numa_supported()) 967 return CMD_ERROR; 968 969 args += strspn(args, whitespace); 970 if (!required_arg(args, "<seg-name>")) 971 return CMD_ERROR; 972 segname = strtok_r(args, whitespace, &nextarg); 973 args = nextarg + strspn(nextarg, whitespace); 974 975 /* 976 * offset, length are optional 977 */ 978 if (get_range(args, &range, &nextarg) == CMD_ERROR) 979 return CMD_ERROR; 980 if (args == nextarg) 981 range.length = 64 * gcp->pagesize; /* default length */ 982 983 if (!segment_location(segname, &range)) 984 return CMD_ERROR; 985 986 return CMD_SUCCESS; 987 } 988 989 #if 0 990 static int command(char *args) 991 { 992 glctx_t *gcp = &glctx; 993 994 return CMD_SUCCESS; 995 } 996 997 #endif 998 /* 999 * ========================================================================= 1000 */ 1001 typedef int (*cmd_func_t) (char *); 1002 1003 struct command { 1004 char *cmd_name; 1005 cmd_func_t cmd_func; /* */ 1006 char *cmd_help; 1007 1008 } cmd_table[] = { 1009 { 1010 .cmd_name = "quit",.cmd_func = quit,.cmd_help = 1011 "quit - just what you think\n" 1012 "\tEOF on stdin has the same effect\n"}, { 1013 .cmd_name = "help",.cmd_func = help_me,.cmd_help = 1014 "help - show this help\n" 1015 "help <command> - display help for just <command>\n"}, { 1016 .cmd_name = "pid",.cmd_func = show_pid,.cmd_help = 1017 "pid - show process id of this session\n"}, { 1018 .cmd_name = "pause",.cmd_func = pause_me,.cmd_help = 1019 "pause - pause program until signal" 1020 " -- e.g., INT, USR1\n"}, { 1021 .cmd_name = "numa",.cmd_func = numa_info,.cmd_help = 1022 "numa - display numa info as seen by this program.\n" 1023 "\tshows nodes from which program may allocate memory\n" 1024 "\twith total and free memory.\n"}, { 1025 .cmd_name = "migrate",.cmd_func = migrate_process,.cmd_help = 1026 "migrate <to-node-id[s]> [<from-node-id[s]>] - \n" 1027 "\tmigrate this process' memory from <from-node-id[s]>\n" 1028 "\tto <to-node-id[s]>. Specify multiple node ids as a\n" 1029 "\tcomma-separated list. TODO - more info\n"}, { 1030 .cmd_name = "show",.cmd_func = show_seg,.cmd_help = 1031 "show [<name>] - show info for segment[s]; default all\n"}, 1032 { 1033 .cmd_name = "anon",.cmd_func = anon_seg,.cmd_help = 1034 "anon <seg-name> <seg-size>[k|m|g|p] [<seg-share>] -\n" 1035 "\tdefine a MAP_ANONYMOUS segment of specified size\n" 1036 "\t<seg-share> := private|shared - default = private\n"}, { 1037 .cmd_name = "file",.cmd_func = file_seg,.cmd_help = 1038 "file <pathname> [<offset>[k|m|g|p] <length>[k|m|g|p]] [<seg-share>] -\n" 1039 "\tdefine a mapped file segment of specified length starting at the\n" 1040 "\tspecified offset into the file. <offset> and <length> may be\n" 1041 "\tomitted and specified on the map command.\n" 1042 "\t<seg-share> := private|shared - default = private\n"}, { 1043 .cmd_name = "shm",.cmd_func = shmem_seg,.cmd_help = 1044 "shm <seg-name> <seg-size>[k|m|g|p] - \n" 1045 "\tdefine a shared memory segment of specified size.\n" 1046 "\tYou may need to increase limits [/proc/sys/kernel/shmmax].\n" 1047 "\tUse map/unmap to attach/detach\n"}, { 1048 .cmd_name = "remove",.cmd_func = remove_seg,.cmd_help = 1049 "remove <seg-name> [<seg-name> ...] - remove the named segment[s]\n"}, 1050 { 1051 .cmd_name = "map",.cmd_func = map_seg,.cmd_help = 1052 "map <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]] [<seg-share>] - \n" 1053 "\tmmap()/shmat() a previously defined, currently unmapped() segment.\n" 1054 "\t<offset> and <length> apply only to mapped files.\n" 1055 "\tUse <length> of '*' or '0' to map to the end of the file.\n"}, 1056 { 1057 .cmd_name = "unmap",.cmd_func = unmap_seg,.cmd_help = 1058 "unmap <seg-name> - unmap specified segment, but remember name/size/...\n"}, 1059 { 1060 .cmd_name = "touch",.cmd_func = touch_seg,.cmd_help = 1061 "touch <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]] [read|write] - \n" 1062 "\tread [default] or write the named segment from <offset> through\n" 1063 "\t<offset>+<length>. If <offset> and <length> omitted, touches all\n" 1064 "\t of mapped segment.\n"}, { 1065 .cmd_name = "mbind",.cmd_func = mbind_seg,.cmd_help = 1066 "mbind <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]]\n" 1067 " <policy>[+move[+wait]] [<node/list>] - \n" 1068 "\tset the numa policy for the specified range of the name segment\n" 1069 "\tto policy -- one of {default, bind, preferred, interleaved}.\n" 1070 "\t<node/list> specifies a node id or a comma separated list of\n" 1071 "\tnode ids. <node> is ignored for 'default' policy, and only\n" 1072 "\tthe first node is used for 'preferred' policy.\n" 1073 "\t'+move' specifies that currently allocated pages be prepared\n" 1074 "\t for migration on next touch\n" 1075 "\t'+wait' [valid only with +move] specifies that pages mbind()\n" 1076 " touch the pages and wait for migration before returning.\n"}, 1077 { 1078 .cmd_name = "where",.cmd_func = where_seg,.cmd_help = 1079 "where <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]] - \n" 1080 "\tshow the node location of pages in the specified range\n" 1081 "\tof the specified segment. <offset> defaults to start of\n" 1082 "\tsegment; <length> defaults to 64 pages.\n"}, 1083 #if 0 /* template for new commands */ 1084 { 1085 .cmd_name = "",.cmd_func =,.cmd_help =}, 1086 #endif 1087 { 1088 .cmd_name = NULL} 1089 }; 1090 1091 static int help_me(char *args) 1092 { 1093 struct command *cmdp = cmd_table; 1094 char *cmd, *nextarg; 1095 int cmdlen; 1096 bool match = false; 1097 1098 args += strspn(args, whitespace); 1099 if (*args != '\0') { 1100 cmd = strtok_r(args, whitespace, &nextarg); 1101 cmdlen = strlen(cmd); 1102 } else { 1103 cmd = NULL; 1104 cmdlen = 0; 1105 } 1106 1107 for (cmdp = cmd_table; cmdp->cmd_name != NULL; ++cmdp) { 1108 if (cmd == NULL || !strncmp(cmd, cmdp->cmd_name, cmdlen)) { 1109 printf("%s\n", cmdp->cmd_help); 1110 match = true; 1111 } 1112 } 1113 1114 if (!match) { 1115 printf("unrecognized command: %s\n", cmd); 1116 printf("\tuse 'help' for a complete list of commands\n"); 1117 return CMD_ERROR; 1118 } 1119 1120 return CMD_SUCCESS; 1121 } 1122 1123 /* 1124 * ========================================================================= 1125 */ 1126 #define CMDBUFSZ 256 1127 1128 static bool unique_abbrev(char *cmd, size_t clen, struct command *cmdp) 1129 { 1130 for (; cmdp->cmd_name != NULL; ++cmdp) { 1131 if (!strncmp(cmd, cmdp->cmd_name, clen)) 1132 return false; /* match: not unique */ 1133 } 1134 return true; 1135 } 1136 1137 static int parse_command(char *cmdline) 1138 { 1139 glctx_t *gcp = &glctx; 1140 char *cmd, *args; 1141 struct command *cmdp; 1142 1143 cmdline += strspn(cmdline, whitespace); /* possibly redundant */ 1144 1145 cmd = strtok_r(cmdline, whitespace, &args); 1146 1147 for (cmdp = cmd_table; cmdp->cmd_name != NULL; ++cmdp) { 1148 size_t clen = strlen(cmd); 1149 int ret; 1150 1151 if (strncmp(cmd, cmdp->cmd_name, clen)) 1152 continue; 1153 if (!unique_abbrev(cmd, clen, cmdp + 1)) { 1154 fprintf(stderr, "%s: ambiguous command: %s\n", 1155 gcp->program_name, cmd); 1156 return CMD_ERROR; 1157 } 1158 gcp->cmd_name = cmdp->cmd_name; 1159 ret = cmdp->cmd_func(args); 1160 gcp->cmd_name = NULL; 1161 return ret; 1162 } 1163 1164 fprintf(stderr, "%s: unrecognized command %s\n", __FUNCTION__, cmd); 1165 return CMD_ERROR; 1166 } 1167 1168 void process_commands() 1169 { 1170 glctx_t *gcp = &glctx; 1171 1172 char cmdbuf[CMDBUFSZ]; 1173 1174 do { 1175 char *cmdline; 1176 size_t cmdlen; 1177 1178 if (is_option(INTERACTIVE)) 1179 printf("%s>", gcp->program_name); 1180 1181 cmdline = fgets(cmdbuf, CMDBUFSZ, stdin); 1182 if (cmdline == NULL) { 1183 printf("%s\n", 1184 is_option(INTERACTIVE) ? "" : "EOF on stdin"); 1185 exit(0); /* EOF */ 1186 } 1187 if (cmdline[0] == '\n') 1188 continue; 1189 1190 /* 1191 * trim trailing newline, if any 1192 */ 1193 cmdlen = strlen(cmdline); 1194 if (cmdline[cmdlen - 1] == '\n') 1195 cmdline[--cmdlen] = '\0'; 1196 1197 cmdline += strspn(cmdline, whitespace); 1198 cmdlen -= (cmdline - cmdbuf); 1199 1200 if (cmdlen == 0) { 1201 //TODO: interactive help? 1202 continue; /* ignore blank lines */ 1203 } 1204 1205 if (*cmdline == '#') 1206 continue; /* comments */ 1207 1208 /* 1209 * trim trailing whitespace for ease of parsing 1210 */ 1211 while (strchr(whitespace, cmdline[cmdlen - 1])) 1212 cmdline[--cmdlen] = '\0'; 1213 1214 if (cmdlen == 0) 1215 continue; 1216 1217 /* 1218 * reset signals just before parsing a command. 1219 * non-interactive: exit on SIGQUIT 1220 */ 1221 if (signalled(gcp)) { 1222 if (!is_option(INTERACTIVE) && 1223 gcp->siginfo->si_signo == SIGQUIT) 1224 exit(0); 1225 reset_signal(); 1226 } 1227 1228 /* 1229 * non-interactive: errors are fatal 1230 */ 1231 if (!is_option(INTERACTIVE)) { 1232 vprint("%s>%s\n", gcp->program_name, cmdline); 1233 if (parse_command(cmdline) == CMD_ERROR) { 1234 fprintf(stderr, "%s: command error\n", 1235 gcp->program_name); 1236 exit(4); 1237 } 1238 } else 1239 parse_command(cmdline); 1240 1241 } while (1); 1242 } 1243 #endif 1244