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