• 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 #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 
nodemask_isset(nodemask_t * mask,int node)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 
nodemask_set(nodemask_t * mask,int node)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  */
required_arg(char * arg,char * arg_name)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
size_kmgp(char * arg)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 
get_scaled_value(char * args,char * what)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 
get_range(char * args,range_t * range,char ** nextarg)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 
get_shared(char * args)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  */
get_access(char * args)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 
numa_supported(void)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  */
get_mbind_policy(char * args,char ** nextarg)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  */
get_mbind_flags(char * args,char ** nextarg)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  */
get_nodemask(char * args)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  */
get_arg_nodeid_list(char * args,unsigned int * list)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  */
get_current_nodeid_list(unsigned int * fromids)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  */
quit(char * args)479 static int quit(char *args)
480 {
481 	exit(0);		/* let cleanup() do its thing */
482 }
483 
show_pid(char * args)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 
pause_me(char * args)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";
numa_info(char * args)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  */
migrate_process(char * args)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 
show_seg(char * args)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  */
anon_seg(char * args)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  */
file_seg(char * args)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  */
remove_seg(char * args)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  */
touch_seg(char * args)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  */
unmap_seg(char * args)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  */
map_seg(char * args)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  */
mbind_seg(char * args)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  */
shmem_seg(char * args)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  */
where_seg(char * args)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 
help_me(char * args)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 
unique_abbrev(char * cmd,size_t clen,struct command * cmdp)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 
parse_command(char * cmdline)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 
process_commands()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