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