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