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