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