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