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