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