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