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