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