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