commands.c revision bbc20fd61f00535be685e588d46f881542424952
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#if defined(LIBNUMA_API_VERSION) && LIBNUMA_API_VERSION == 2 398 my_allowed_nodes = numa_get_membind_compat(); 399#else 400 my_allowed_nodes = numa_get_membind(); 401#endif 402 while (*args != '\0') { 403 if (!isdigit(*args)) { 404 fprintf(stderr, "%s: expected digit for <node/list>\n", 405 gcp->program_name); 406 return -1; 407 } 408 409 node = strtoul(args, &next, 10); 410 411 if (node > gcp->numa_max_node) { 412 fprintf(stderr, "%s: node ids must be <= %d\n", 413 gcp->program_name, gcp->numa_max_node); 414 return -1; 415 } 416 417 if (!nodemask_isset(&my_allowed_nodes, node)) { 418 fprintf(stderr, "%s: node %d is not in my allowed node mask\n", 419 gcp->program_name, node); 420 return -1; 421 } 422 423 *(list + count++) = node; 424 425 if (*next == '\0') 426 return count; 427 if (*next != ',') { 428 break; 429 } 430 431 if (count >= gcp->numa_max_node) { 432 fprintf(stderr, "%s: too many node ids in list\n", 433 gcp->program_name); 434 } 435 args = next+1; 436 } 437 438 return -1; 439} 440 441/* 442 * get_current_nodeid_list() - fill arg array with nodes from 443 * current thread's allowed node mask. return # of nodes in 444 * mask. 445 */ 446static int 447get_current_nodeid_list(unsigned int *fromids) 448{ 449 /* 450 * FIXME (garrcoop): gcp is unitialized and shortly hereafter used in 451 * an initialization statement..... UHHHHHHH... test writer fail? 452 */ 453 glctx_t *gcp; 454 nodemask_t my_allowed_nodes; 455 int nr_nodes = 0, max_node = gcp->numa_max_node; 456 int node; 457 458 gcp = &glctx; 459#if defined(LIBNUMA_API_VERSION) && LIBNUMA_API_VERSION == 2 460 my_allowed_nodes = numa_get_membind_compat(); 461#else 462 my_allowed_nodes = numa_get_membind(); 463#endif 464 for (node=0; node <= max_node; ++node) { 465 if (nodemask_isset(&my_allowed_nodes, node)) 466 *(fromids + nr_nodes++) = node; 467 } 468 469 /* 470 * shouldn't happen, but let 'em know if it does 471 */ 472 if (nr_nodes == 0) 473 fprintf(stderr, "%s: my allowed node mask is empty !!???\n", 474 gcp->program_name); 475 return nr_nodes; 476} 477 478/* 479 * NOTE (garrcoop): Get rid of an -Wunused warning. This wasn't deleted because 480 * I don't know what the original intent was for this code. 481 */ 482#if 0 483static void 484not_implemented() 485{ 486 glctx_t *gcp = &glctx; 487 488 fprintf(stderr, "%s: %s not implemented yet\n", 489 gcp->program_name, gcp->cmd_name); 490} 491#endif 492 493/* 494 * ========================================================================= 495 */ 496static int 497quit(char *args) 498{ 499 exit(0); /* let cleanup() do its thing */ 500} 501 502static int 503show_pid(char *args) 504{ 505 glctx_t *gcp = &glctx; 506 507 printf("%s: pid = %d\n", gcp->program_name, getpid()); 508 509 return CMD_SUCCESS; 510} 511 512static int 513pause_me(char *args) 514{ 515 // glctx_t *gcp = &glctx; 516 517 pause(); 518 reset_signal(); 519 520 return CMD_SUCCESS; 521} 522 523static char *numa_header = 524" Node Total Mem[MB] Free Mem[MB]\n"; 525static int 526numa_info(char *args) 527{ 528 glctx_t *gcp = &glctx; 529 unsigned int *nodeids; 530 int nr_nodes, i; 531 bool do_header = true; 532 533 if (!numa_supported()) 534 return CMD_ERROR; 535 536 nodeids = calloc(gcp->numa_max_node, sizeof(*nodeids)); 537 nr_nodes = get_current_nodeid_list(nodeids); 538 if (nr_nodes < 0) 539 return CMD_ERROR; 540 541 for (i=0; i < nr_nodes; ++i) { 542 int node = nodeids[i]; 543 long node_size, node_free; 544 545 node_size = numa_node_size(node, &node_free); 546 if (node_size < 0) { 547 fprintf(stderr, "%s: numa_node_size() failed for node %d\n", 548 gcp->program_name, node); 549 return CMD_ERROR; 550 } 551 552 if (do_header) { 553 do_header = false; 554 puts(numa_header); 555 } 556 printf(" %3d %9ld %8ld\n", node, 557 node_size/(1024*1024), node_free/(1024*1024)); 558 } 559 560 return CMD_SUCCESS; 561} 562 563/* 564 * migrate <to-node-id[s]> [<from-node-id[s]>] 565 * 566 * Node id[s] - single node id or comma-separated list 567 * <to-node-id[s]> - 1-for-1 with <from-node-id[s]>, OR 568 * if <from-node-id[s]> omitted, <to-node-id[s]> must be 569 * a single node id. 570 */ 571static int 572migrate_process(char *args) 573{ 574 glctx_t *gcp = &glctx; 575 unsigned int *fromids, *toids; 576 char *idlist, *nextarg; 577 struct timeval t_start, t_end; 578 int nr_to, nr_from; 579 int nr_migrated; 580 int ret = CMD_ERROR; 581 582 if (!numa_supported()) 583 return CMD_ERROR; 584 585 toids = calloc(gcp->numa_max_node, sizeof(*toids)); 586 fromids = calloc(gcp->numa_max_node, sizeof(*fromids)); 587 588 /* 589 * <to-node-id[s]> 590 */ 591 if (!required_arg(args, "<to-node-id[s]>")) 592 return CMD_ERROR; 593 idlist = strtok_r(args, whitespace, &nextarg); 594 nr_to = get_arg_nodeid_list(idlist, toids); 595 if (nr_to <= 0) 596 goto out_free; 597 args = nextarg + strspn(nextarg, whitespace); 598 599 if (*args != '\0') { 600 /* 601 * apparently, <from-node-id[s]> present 602 */ 603 idlist = strtok_r(args, whitespace, &nextarg); 604 nr_from = get_arg_nodeid_list(idlist, fromids); 605 if (nr_from <= 0) 606 goto out_free; 607 if (nr_from != nr_to) { 608 fprintf(stderr, "%s: # of 'from' ids must = # of 'to' ids\n", 609 gcp->program_name); 610 goto out_free; 611 } 612 } else { 613 int i; 614 615 /* 616 * no <from-node-id[s]>, nr_to must == 1, 617 * get fromids from memory policy. 618 */ 619 if (nr_to > 1) { 620 fprintf(stderr, "%s: # to ids must = 1" 621 " when no 'from' ids specified\n", 622 gcp->program_name); 623 goto out_free; 624 } 625 nr_from = get_current_nodeid_list(fromids); 626 if (nr_from <= 0) 627 goto out_free; 628 629 /* 630 * remove 'to' node from 'from' list. to and from 631 * lists can't intersect. 632 */ 633 for (i = nr_from-1; i >= 0; --i) { 634 if (*toids == *(fromids + i)) { 635 while (i <= nr_from) { 636 *(fromids + i) = *(fromids + i+1); 637 ++i; 638 } 639 --nr_from; 640 break; 641 } 642 } 643 644 /* 645 * fill out nr_from toids with the single 'to' node 646 */ 647 for (; nr_to < nr_from; ++nr_to) 648 *(toids + nr_to) = *toids; /* toids[0] */ 649 } 650 651 gettimeofday(&t_start, NULL); 652 nr_migrated = syscall(__NR_migrate_pages, getpid(), nr_from, fromids, toids); 653 if (nr_migrated < 0) { 654 int err = errno; 655 fprintf(stderr, "%s: migrate_pages failed - %s\n", 656 gcp->program_name, strerror(err)); 657 goto out_free; 658 } 659 gettimeofday(&t_end, NULL); 660 printf("%s: migrated %d pages in %6.3fsecs\n", 661 gcp->program_name, nr_migrated, 662 (float)(tv_diff_usec(&t_start, &t_end))/1000000.0); 663 ret = CMD_SUCCESS; 664 665out_free: 666 free(toids); 667 free(fromids); 668 return ret; 669} 670 671static int 672show_seg(char *args) 673{ 674 glctx_t *gcp = &glctx; 675 676 char *segname = NULL, *nextarg; 677 678 args += strspn(args, whitespace); 679 if (*args != '\0') 680 segname = strtok_r(args, whitespace, &nextarg); 681 682 if (!segment_show(segname)) 683 return CMD_ERROR; 684 685 return CMD_SUCCESS; 686} 687 688/* 689 * anon_seg: <seg-name> <size>[kmgp] [private|shared] 690 */ 691static int 692anon_seg(char *args) 693{ 694 glctx_t *gcp = &glctx; 695 696 char *segname, *nextarg; 697 range_t range = { 0L, 0L }; 698 int segflag = 0; 699 700 args += strspn(args, whitespace); 701 702 if (!required_arg(args, "<seg-name>")) 703 return CMD_ERROR; 704 segname = strtok_r(args, whitespace, &nextarg); 705 args = nextarg + strspn(nextarg, whitespace); 706 707 if (!required_arg(args, "<size>")) 708 return CMD_ERROR; 709 args = strtok_r(args, whitespace, &nextarg); 710 range.length = get_scaled_value(args, "size"); 711 if (range.length == BOGUS_SIZE) 712 return CMD_ERROR; 713 args = nextarg + strspn(nextarg, whitespace); 714 715 if (*args != '\0') { 716 segflag = get_shared(args); 717 if (segflag == -1) 718 return CMD_ERROR; 719 } 720 721 if (!segment_register(SEGT_ANON, segname, &range, segflag)) 722 return CMD_ERROR; 723 724 return CMD_SUCCESS; 725} 726 727/* 728 * file_seg: <path-name> [<offset>[kmgp] <length>[kmgp] [private|shared]] 729 */ 730static int 731file_seg(char *args) 732{ 733 glctx_t *gcp = &glctx; 734 735 char *pathname, *nextarg; 736 range_t range = { 0L, 0L }; 737 int segflag = MAP_PRIVATE; 738 739 args += strspn(args, whitespace); 740 741 if (!required_arg(args, "<path-name>")) 742 return CMD_ERROR; 743 pathname = strtok_r(args, whitespace, &nextarg); 744 args = nextarg + strspn(nextarg, whitespace); 745 746 /* 747 * offset, length are optional 748 */ 749 if (get_range(args, &range, &nextarg) == CMD_ERROR) 750 return CMD_ERROR; 751 args = nextarg; 752 753 if (*args != '\0') { 754 segflag = get_shared(args); 755 if (segflag == -1) 756 return CMD_ERROR; 757 } 758 759 if (!segment_register(SEGT_FILE, pathname, &range, segflag)) 760 return CMD_ERROR; 761 762 return CMD_SUCCESS; 763} 764 765/* 766 * remove_seg: <seg-name> [<seg-name> ...] 767 */ 768static int 769remove_seg(char *args) 770{ 771 glctx_t *gcp = &glctx; 772 773 args += strspn(args, whitespace); 774 if (!required_arg(args, "<seg-name>")) 775 return CMD_ERROR; 776 777 while (*args != '\0') { 778 char *segname, *nextarg; 779 780 segname = strtok_r(args, whitespace, &nextarg); 781 args = nextarg + strspn(nextarg, whitespace); 782 783 segment_remove(segname); 784 } 785 return 0; 786} 787 788/* 789 * touch_seg: <seg-name> [<offset> <length>] [read|write] 790 */ 791static int 792touch_seg(char *args) 793{ 794 glctx_t *gcp = &glctx; 795 796 char *segname, *nextarg; 797 range_t range = { 0L, 0L }; 798 int axcs; 799 800 args += strspn(args, whitespace); 801 if (!required_arg(args, "<seg-name>")) 802 return CMD_ERROR; 803 segname = strtok_r(args, whitespace, &nextarg); 804 args = nextarg + strspn(nextarg, whitespace); 805 806 /* 807 * offset, length are optional 808 */ 809 if (get_range(args, &range, &nextarg) == CMD_ERROR) 810 return CMD_ERROR; 811 args = nextarg; 812 813 axcs = get_access(args); 814 if (axcs == 0) 815 return CMD_ERROR; 816 817 if (!segment_touch(segname, &range, axcs-1)) 818 return CMD_ERROR; 819 820 return CMD_SUCCESS; 821} 822 823/* 824 * unmap <seg-name> - unmap specified segment, but remember name/size/... 825 */ 826static int 827unmap_seg(char *args) 828{ 829 glctx_t *gcp = &glctx; 830 char *segname, *nextarg; 831 832 args += strspn(args, whitespace); 833 if (!required_arg(args, "<seg-name>")) 834 return CMD_ERROR; 835 segname = strtok_r(args, whitespace, &nextarg); 836 args = nextarg + strspn(nextarg, whitespace); 837 838 if (!segment_unmap(segname)) 839 return CMD_ERROR; 840 841 return CMD_SUCCESS; 842} 843 844/* 845 * map <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]] [<seg-share>] 846 */ 847static int 848map_seg(char *args) 849{ 850 glctx_t *gcp = &glctx; 851 852 char *segname, *nextarg; 853 range_t range = { 0L, 0L }; 854 range_t *rangep = NULL; 855 int segflag = MAP_PRIVATE; 856 857 args += strspn(args, whitespace); 858 if (!required_arg(args, "<seg-name>")) 859 return CMD_ERROR; 860 segname = strtok_r(args, whitespace, &nextarg); 861 args = nextarg + strspn(nextarg, whitespace); 862 863 /* 864 * offset, length are optional 865 */ 866 if (get_range(args, &range, &nextarg) == CMD_ERROR) 867 return CMD_ERROR; 868 if (args != nextarg) { 869 rangep = ⦥ /* override any registered range */ 870 args = nextarg; 871 } 872 873 if (*args != '\0') { 874 segflag = get_shared(args); 875 if (segflag == -1) 876 return CMD_ERROR; 877 } 878 879 if (!segment_map(segname, rangep, segflag)) 880 return CMD_ERROR; 881 882 return CMD_SUCCESS; 883} 884 885/* 886 * mbind <seg-name> [<offset>[kmgp] <length>[kmgp]] <policy> <node-list> 887 */ 888static int 889mbind_seg(char *args) 890{ 891 glctx_t *gcp = &glctx; 892 893 char *segname, *nextarg; 894 range_t range = { 0L, 0L }; 895 nodemask_t *nodemask = NULL; 896 int policy, flags = 0; 897 int ret; 898 899 if (!numa_supported()) 900 return CMD_ERROR; 901 902 args += strspn(args, whitespace); 903 if (!required_arg(args, "<seg-name>")) 904 return CMD_ERROR; 905 segname = strtok_r(args, whitespace, &nextarg); 906 args = nextarg + strspn(nextarg, whitespace); 907 908 /* 909 * offset, length are optional 910 */ 911 if (get_range(args, &range, &nextarg) == CMD_ERROR) 912 return CMD_ERROR; 913 args = nextarg; 914 915 if (!required_arg(args, "<policy>")) 916 return CMD_ERROR; 917 policy = get_mbind_policy(args, &nextarg); 918 if (policy < 0) 919 return CMD_ERROR; 920 921 args = nextarg + strspn(nextarg, whitespace); 922 if (*args == '+') { 923 flags = get_mbind_flags(++args, &nextarg); 924 if (flags == -1) 925 return CMD_ERROR; 926 } 927 args = nextarg + strspn(nextarg, whitespace); 928 929 if (policy != MPOL_DEFAULT) { 930 if (!required_arg(args, "<node/list>")) 931 return CMD_ERROR; 932 nodemask = get_nodemask(args); 933 if (nodemask == NULL) 934 return CMD_ERROR; 935 } 936 937 ret = CMD_SUCCESS; 938#if 1 // for testing 939 if (!segment_mbind(segname, &range, policy, nodemask, flags)) 940 ret = CMD_ERROR; 941#endif 942 943 if (nodemask != NULL) 944 free(nodemask); 945 return ret; 946} 947 948/* 949 * shmem_seg - create [shmget] and register a SysV shared memory segment 950 * of specified size 951 */ 952static int 953shmem_seg(char *args) 954{ 955 glctx_t *gcp = &glctx; 956 957 char *segname, *nextarg; 958 range_t range = { 0L, 0L }; 959 960 args += strspn(args, whitespace); 961 962 if (!required_arg(args, "<seg-name>")) 963 return CMD_ERROR; 964 segname = strtok_r(args, whitespace, &nextarg); 965 args = nextarg + strspn(nextarg, whitespace); 966 967 if (!required_arg(args, "<size>")) 968 return CMD_ERROR; 969 args = strtok_r(args, whitespace, &nextarg); 970 range.length = get_scaled_value(args, "size"); 971 if (range.length == BOGUS_SIZE) 972 return CMD_ERROR; 973 args = nextarg + strspn(nextarg, whitespace); 974 975 if (!segment_register(SEGT_SHM, segname, &range, MAP_SHARED)) 976 return CMD_ERROR; 977 978 return CMD_SUCCESS; 979} 980 981/* 982 * where <seg-name> [<offset>[kmgp] <length>[kmgp]] - show node location 983 * of specified range of segment. 984 * 985 * NOTE: if neither <offset> nor <length> specified, <offset> defaults 986 * to 0 [start of segment], as usual, and length defaults to 64 pages 987 * rather than the entire segment. Suitable for a "quick look" at where 988 * segment resides. 989 */ 990static int 991where_seg(char *args) 992{ 993 glctx_t *gcp = &glctx; 994 995 char *segname, *nextarg; 996 range_t range = { 0L, 0L }; 997 int ret; 998 999 if (!numa_supported()) 1000 return CMD_ERROR; 1001 1002 args += strspn(args, whitespace); 1003 if (!required_arg(args, "<seg-name>")) 1004 return CMD_ERROR; 1005 segname = strtok_r(args, whitespace, &nextarg); 1006 args = nextarg + strspn(nextarg, whitespace); 1007 1008 /* 1009 * offset, length are optional 1010 */ 1011 if (get_range(args, &range, &nextarg) == CMD_ERROR) 1012 return CMD_ERROR; 1013 if (args == nextarg) 1014 range.length = 64 * gcp->pagesize; /* default length */ 1015 1016 if (!segment_location(segname, &range)) 1017 return CMD_ERROR; 1018 1019 return CMD_SUCCESS; 1020} 1021 1022#if 0 1023static int 1024command(char *args) 1025{ 1026 glctx_t *gcp = &glctx; 1027 1028 return CMD_SUCCESS; 1029} 1030 1031#endif 1032/* 1033 * ========================================================================= 1034 */ 1035typedef int (*cmd_func_t)(char *); 1036 1037struct command { 1038 char *cmd_name; 1039 cmd_func_t cmd_func; /* */ 1040 char *cmd_help; 1041 1042} cmd_table[] = { 1043 { 1044 .cmd_name="quit", 1045 .cmd_func=quit, 1046 .cmd_help= 1047 "quit - just what you think\n" 1048 "\tEOF on stdin has the same effect\n" 1049 }, 1050 { 1051 .cmd_name="help", 1052 .cmd_func=help_me, 1053 .cmd_help= 1054 "help - show this help\n" 1055 "help <command> - display help for just <command>\n" 1056 }, 1057 { 1058 .cmd_name="pid", 1059 .cmd_func=show_pid, 1060 .cmd_help= 1061 "pid - show process id of this session\n" 1062 }, 1063 { 1064 .cmd_name="pause", 1065 .cmd_func=pause_me, 1066 .cmd_help= 1067 "pause - pause program until signal" 1068 " -- e.g., INT, USR1\n" 1069 }, 1070 { 1071 .cmd_name="numa", 1072 .cmd_func=numa_info, 1073 .cmd_help= 1074 "numa - display numa info as seen by this program.\n" 1075 "\tshows nodes from which program may allocate memory\n" 1076 "\twith total and free memory.\n" 1077 }, 1078 { 1079 .cmd_name="migrate", 1080 .cmd_func=migrate_process, 1081 .cmd_help= 1082 "migrate <to-node-id[s]> [<from-node-id[s]>] - \n" 1083 "\tmigrate this process' memory from <from-node-id[s]>\n" 1084 "\tto <to-node-id[s]>. Specify multiple node ids as a\n" 1085 "\tcomma-separated list. TODO - more info\n" 1086 }, 1087 1088 { 1089 .cmd_name="show", 1090 .cmd_func=show_seg, 1091 .cmd_help= 1092 "show [<name>] - show info for segment[s]; default all\n" 1093 }, 1094 { 1095 .cmd_name="anon", 1096 .cmd_func=anon_seg, 1097 .cmd_help= 1098 "anon <seg-name> <seg-size>[k|m|g|p] [<seg-share>] -\n" 1099 "\tdefine a MAP_ANONYMOUS segment of specified size\n" 1100 "\t<seg-share> := private|shared - default = private\n" 1101 }, 1102 { 1103 .cmd_name="file", 1104 .cmd_func=file_seg, 1105 .cmd_help= 1106 "file <pathname> [<offset>[k|m|g|p] <length>[k|m|g|p]] [<seg-share>] -\n" 1107 "\tdefine a mapped file segment of specified length starting at the\n" 1108 "\tspecified offset into the file. <offset> and <length> may be\n" 1109 "\tomitted and specified on the map command.\n" 1110 "\t<seg-share> := private|shared - default = private\n" 1111 }, 1112 { 1113 .cmd_name="shm", 1114 .cmd_func=shmem_seg, 1115 .cmd_help= 1116 "shm <seg-name> <seg-size>[k|m|g|p] - \n" 1117 "\tdefine a shared memory segment of specified size.\n" 1118 "\tYou may need to increase limits [/proc/sys/kernel/shmmax].\n" 1119 "\tUse map/unmap to attach/detach\n" 1120 }, 1121 { 1122 .cmd_name="remove", 1123 .cmd_func=remove_seg, 1124 .cmd_help= 1125 "remove <seg-name> [<seg-name> ...] - remove the named segment[s]\n" 1126 1127 }, 1128 1129 { 1130 .cmd_name="map", 1131 .cmd_func=map_seg, 1132 .cmd_help= 1133 "map <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]] [<seg-share>] - \n" 1134 "\tmmap()/shmat() a previously defined, currently unmapped() segment.\n" 1135 "\t<offset> and <length> apply only to mapped files.\n" 1136 "\tUse <length> of '*' or '0' to map to the end of the file.\n" 1137 }, 1138 { 1139 .cmd_name="unmap", 1140 .cmd_func=unmap_seg, 1141 .cmd_help= 1142 "unmap <seg-name> - unmap specified segment, but remember name/size/...\n" 1143 }, 1144 { 1145 .cmd_name="touch", 1146 .cmd_func=touch_seg, 1147 .cmd_help= 1148 "touch <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]] [read|write] - \n" 1149 "\tread [default] or write the named segment from <offset> through\n" 1150 "\t<offset>+<length>. If <offset> and <length> omitted, touches all\n" 1151 "\t of mapped segment.\n" 1152 }, 1153 { 1154 .cmd_name="mbind", 1155 .cmd_func=mbind_seg, 1156 .cmd_help= 1157 "mbind <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]]\n" 1158 " <policy>[+move[+wait]] [<node/list>] - \n" 1159 "\tset the numa policy for the specified range of the name segment\n" 1160 "\tto policy -- one of {default, bind, preferred, interleaved}.\n" 1161 "\t<node/list> specifies a node id or a comma separated list of\n" 1162 "\tnode ids. <node> is ignored for 'default' policy, and only\n" 1163 "\tthe first node is used for 'preferred' policy.\n" 1164 "\t'+move' specifies that currently allocated pages be prepared\n" 1165 "\t for migration on next touch\n" 1166 "\t'+wait' [valid only with +move] specifies that pages mbind()\n" 1167 " touch the pages and wait for migration before returning.\n" 1168 }, 1169 { 1170 .cmd_name="where", 1171 .cmd_func=where_seg, 1172 .cmd_help= 1173 "where <seg-name> [<offset>[k|m|g|p] <length>[k|m|g|p]] - \n" 1174 "\tshow the node location of pages in the specified range\n" 1175 "\tof the specified segment. <offset> defaults to start of\n" 1176 "\tsegment; <length> defaults to 64 pages.\n" 1177 }, 1178 1179#if 0 /* template for new commands */ 1180 { 1181 .cmd_name="", 1182 .cmd_func= , 1183 .cmd_help= 1184 }, 1185#endif 1186 { 1187 .cmd_name=NULL 1188 } 1189}; 1190 1191static int 1192help_me(char *args) 1193{ 1194 struct command *cmdp = cmd_table; 1195 char *cmd, *nextarg; 1196 int cmdlen; 1197 bool match = false; 1198 1199 args += strspn(args, whitespace); 1200 if (*args != '\0') { 1201 cmd = strtok_r(args, whitespace, &nextarg); 1202 cmdlen = strlen(cmd); 1203 } else { 1204 cmd = NULL; 1205 cmdlen = 0; 1206 } 1207 1208 for (cmdp = cmd_table; cmdp->cmd_name != NULL; ++cmdp) { 1209 if (cmd == NULL || 1210 !strncmp(cmd, cmdp->cmd_name, cmdlen)) { 1211 printf("%s\n", cmdp->cmd_help); 1212 match = true; 1213 } 1214 } 1215 1216 if (!match) { 1217 printf("unrecognized command: %s\n", cmd); 1218 printf("\tuse 'help' for a complete list of commands\n"); 1219 return CMD_ERROR; 1220 } 1221 1222 return CMD_SUCCESS; 1223} 1224 1225/* 1226 * ========================================================================= 1227 */ 1228#define CMDBUFSZ 256 1229 1230static bool 1231unique_abbrev(char *cmd, size_t clen, struct command *cmdp) 1232{ 1233 for (; cmdp->cmd_name != NULL; ++cmdp) { 1234 if (!strncmp(cmd, cmdp->cmd_name, clen)) 1235 return false; /* match: not unique */ 1236 } 1237 return true; 1238} 1239 1240static int 1241parse_command(char *cmdline) 1242{ 1243 glctx_t *gcp = &glctx; 1244 char *cmd, *args; 1245 struct command *cmdp; 1246 1247 cmdline += strspn(cmdline, whitespace); /* possibly redundant */ 1248 1249 cmd = strtok_r(cmdline, whitespace, &args); 1250 1251 for (cmdp = cmd_table; cmdp->cmd_name != NULL; ++cmdp) { 1252 size_t clen = strlen(cmd); 1253 int ret; 1254 1255 if (strncmp(cmd, cmdp->cmd_name, clen)) 1256 continue; 1257 if (!unique_abbrev(cmd, clen, cmdp+1)) { 1258 fprintf(stderr, "%s: ambiguous command: %s\n", 1259 gcp->program_name, cmd); 1260 return CMD_ERROR; 1261 } 1262 gcp->cmd_name = cmdp->cmd_name; 1263 ret = cmdp->cmd_func(args); 1264 gcp->cmd_name = NULL; 1265 return ret; 1266 } 1267 1268 fprintf(stderr, "%s: unrecognized command %s\n", 1269 __FUNCTION__, cmd); 1270 return CMD_ERROR; 1271} 1272 1273void 1274process_commands() 1275{ 1276 glctx_t *gcp = &glctx; 1277 1278 char cmdbuf[CMDBUFSZ]; 1279 1280 do { 1281 char *cmdline; 1282 size_t cmdlen; 1283 1284 if (is_option(INTERACTIVE)) 1285 printf("%s>", gcp->program_name); 1286 1287 cmdline = fgets(cmdbuf, CMDBUFSZ, stdin); 1288 if (cmdline == NULL) { 1289 printf("%s\n", 1290 is_option(INTERACTIVE) ? "" : "EOF on stdin"); 1291 exit(0); /* EOF */ 1292 } 1293 if (cmdline[0] == '\n') 1294 continue; 1295 1296 /* 1297 * trim trailing newline, if any 1298 */ 1299 cmdlen = strlen(cmdline); 1300 if (cmdline[cmdlen-1] == '\n') 1301 cmdline[--cmdlen] = '\0'; 1302 1303 cmdline += strspn(cmdline, whitespace); 1304 cmdlen -= (cmdline - cmdbuf); 1305 1306 if (cmdlen == 0) { 1307 //TODO: interactive help? 1308 continue; /* ignore blank lines */ 1309 } 1310 1311 if (*cmdline == '#') 1312 continue; /* comments */ 1313 1314 /* 1315 * trim trailing whitespace for ease of parsing 1316 */ 1317 while (strchr(whitespace, cmdline[cmdlen-1])) 1318 cmdline[--cmdlen] = '\0'; 1319 1320 if (cmdlen == 0) 1321 continue; 1322 1323 /* 1324 * reset signals just before parsing a command. 1325 * non-interactive: exit on SIGQUIT 1326 */ 1327 if (signalled(gcp)) { 1328 if (!is_option(INTERACTIVE) && 1329 gcp->siginfo->si_signo == SIGQUIT) 1330 exit(0); 1331 reset_signal(); 1332 } 1333 1334 /* 1335 * non-interactive: errors are fatal 1336 */ 1337 if (!is_option(INTERACTIVE)) { 1338 vprint("%s>%s\n", gcp->program_name, cmdline); 1339 if (parse_command(cmdline) == CMD_ERROR) { 1340 fprintf(stderr, "%s: command error\n", 1341 gcp->program_name); 1342 exit(4); 1343 } 1344 } else 1345 parse_command(cmdline); 1346 1347 } while (1); 1348} 1349#endif 1350