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