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