1 /*
2  * Copyright 2009-2017 Citrix Ltd and other contributors
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation; version 2.1 only. with the special
7  * exception on linking described in file LICENSE.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  */
14 
15 #include <limits.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 
19 #include <libxl.h>
20 #include <libxl_utils.h>
21 #include <libxlutil.h>
22 
23 #include "xl.h"
24 #include "xl_utils.h"
25 #include "xl_parse.h"
26 
main_cpupoolcreate(int argc,char ** argv)27 int main_cpupoolcreate(int argc, char **argv)
28 {
29     const char *filename = NULL, *config_src=NULL;
30     const char *p;
31     char *extra_config = NULL;
32     int opt;
33     static struct option opts[] = {
34         {"defconfig", 1, 0, 'f'},
35         {"dryrun", 0, 0, 'n'},
36         COMMON_LONG_OPTS
37     };
38     int ret;
39     char *config_data = 0;
40     int config_len = 0;
41     XLU_Config *config;
42     const char *buf;
43     char *name = NULL;
44     uint32_t poolid;
45     libxl_scheduler sched = 0;
46     XLU_ConfigList *cpus;
47     XLU_ConfigList *nodes;
48     int n_cpus, n_nodes, i, n;
49     libxl_bitmap freemap;
50     libxl_bitmap cpumap;
51     libxl_uuid uuid;
52     libxl_cputopology *topology;
53     int rc = EXIT_FAILURE;
54 
55     SWITCH_FOREACH_OPT(opt, "nf:", opts, "cpupool-create", 0) {
56     case 'f':
57         filename = optarg;
58         break;
59     case 'n':
60         dryrun_only = 1;
61         break;
62     }
63 
64     libxl_bitmap_init(&freemap);
65     libxl_bitmap_init(&cpumap);
66 
67     while (optind < argc) {
68         if ((p = strchr(argv[optind], '='))) {
69             string_realloc_append(&extra_config, "\n");
70             string_realloc_append(&extra_config, argv[optind]);
71         } else if (!filename) {
72             filename = argv[optind];
73         } else {
74             help("cpupool-create");
75             goto out;
76         }
77         optind++;
78     }
79 
80     if (filename)
81     {
82         if (libxl_read_file_contents(ctx, filename, (void **)&config_data,
83                                      &config_len)) {
84             fprintf(stderr, "Failed to read config file: %s: %s\n",
85                     filename, strerror(errno));
86             goto out;
87         }
88         config_src=filename;
89     }
90     else
91         config_src="command line";
92 
93     if (extra_config && strlen(extra_config)) {
94         if (config_len > INT_MAX - (strlen(extra_config) + 2)) {
95             fprintf(stderr, "Failed to attach extra configuration\n");
96             goto out;
97         }
98         config_data = xrealloc(config_data,
99                                config_len + strlen(extra_config) + 2);
100         if (!config_data) {
101             fprintf(stderr, "Failed to realloc config_data\n");
102             goto out;
103         }
104         config_data[config_len] = 0;
105         strcat(config_data, extra_config);
106         strcat(config_data, "\n");
107         config_len += strlen(extra_config) + 1;
108     }
109 
110     config = xlu_cfg_init(stderr, config_src);
111     if (!config) {
112         fprintf(stderr, "Failed to allocate for configuration\n");
113         goto out;
114     }
115 
116     ret = xlu_cfg_readdata(config, config_data, config_len);
117     if (ret) {
118         fprintf(stderr, "Failed to parse config file: %s\n", strerror(ret));
119         goto out_cfg;
120     }
121 
122     if (!xlu_cfg_get_string (config, "name", &buf, 0))
123         name = strdup(buf);
124     else if (filename)
125         name = libxl_basename(filename);
126     else {
127         fprintf(stderr, "Missing cpupool name!\n");
128         goto out_cfg;
129     }
130     if (!libxl_name_to_cpupoolid(ctx, name, &poolid)) {
131         fprintf(stderr, "Pool name \"%s\" already exists\n", name);
132         goto out_cfg;
133     }
134 
135     if (!xlu_cfg_get_string (config, "sched", &buf, 0)) {
136         if ((libxl_scheduler_from_string(buf, &sched)) < 0) {
137             fprintf(stderr, "Unknown scheduler\n");
138             goto out_cfg;
139         }
140     } else {
141         rc = libxl_get_scheduler(ctx);
142         if (rc < 0) {
143             fprintf(stderr, "get_scheduler sysctl failed.\n");
144             goto out_cfg;
145         }
146         sched = rc;
147     }
148 
149     if (libxl_get_freecpus(ctx, &freemap)) {
150         fprintf(stderr, "libxl_get_freecpus failed\n");
151         goto out_cfg;
152     }
153     if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) {
154         fprintf(stderr, "Failed to allocate cpumap\n");
155         goto out_cfg;
156     }
157     if (!xlu_cfg_get_list(config, "nodes", &nodes, 0, 0)) {
158         int nr;
159         n_cpus = 0;
160         n_nodes = 0;
161         topology = libxl_get_cpu_topology(ctx, &nr);
162         if (topology == NULL) {
163             fprintf(stderr, "libxl_get_topologyinfo failed\n");
164             goto out_cfg;
165         }
166         while ((buf = xlu_cfg_get_listitem(nodes, n_nodes)) != NULL) {
167             n = atoi(buf);
168             for (i = 0; i < nr; i++) {
169                 if ((topology[i].node == n) &&
170                     libxl_bitmap_test(&freemap, i)) {
171                     libxl_bitmap_set(&cpumap, i);
172                     n_cpus++;
173                 }
174             }
175             n_nodes++;
176         }
177 
178         libxl_cputopology_list_free(topology, nr);
179 
180         if (n_cpus == 0) {
181             fprintf(stderr, "no free cpu found\n");
182             goto out_cfg;
183         }
184     } else if (!xlu_cfg_get_list(config, "cpus", &cpus, 0, 1)) {
185         n_cpus = 0;
186         while ((buf = xlu_cfg_get_listitem(cpus, n_cpus)) != NULL) {
187             i = atoi(buf);
188             if ((i < 0) || !libxl_bitmap_test(&freemap, i)) {
189                 fprintf(stderr, "cpu %d illegal or not free\n", i);
190                 goto out_cfg;
191             }
192             libxl_bitmap_set(&cpumap, i);
193             n_cpus++;
194         }
195     } else if (!xlu_cfg_get_string(config, "cpus", &buf, 0)) {
196         if (parse_cpurange(buf, &cpumap))
197             goto out_cfg;
198 
199         n_cpus = 0;
200         libxl_for_each_set_bit(i, cpumap) {
201             if (!libxl_bitmap_test(&freemap, i)) {
202                 fprintf(stderr, "cpu %d illegal or not free\n", i);
203                 goto out_cfg;
204             }
205             n_cpus++;
206         }
207     } else
208         n_cpus = 0;
209 
210     libxl_uuid_generate(&uuid);
211 
212     printf("Using config file \"%s\"\n", config_src);
213     printf("cpupool name:   %s\n", name);
214     printf("scheduler:      %s\n", libxl_scheduler_to_string(sched));
215     printf("number of cpus: %d\n", n_cpus);
216 
217     if (!dryrun_only) {
218         poolid = LIBXL_CPUPOOL_POOLID_ANY;
219         if (libxl_cpupool_create(ctx, name, sched, cpumap, &uuid, &poolid)) {
220             fprintf(stderr, "error on creating cpupool\n");
221             goto out_cfg;
222         }
223     }
224     /* We made it! */
225     rc = EXIT_SUCCESS;
226 
227 out_cfg:
228     xlu_cfg_destroy(config);
229 out:
230     libxl_bitmap_dispose(&freemap);
231     libxl_bitmap_dispose(&cpumap);
232     free(name);
233     free(config_data);
234     free(extra_config);
235     return rc;
236 }
237 
main_cpupoollist(int argc,char ** argv)238 int main_cpupoollist(int argc, char **argv)
239 {
240     int opt;
241     static struct option opts[] = {
242         {"cpus", 0, 0, 'c'},
243         COMMON_LONG_OPTS
244     };
245     int opt_cpus = 0;
246     const char *pool = NULL;
247     libxl_cpupoolinfo *poolinfo;
248     int n_pools, p, c, n;
249     uint32_t poolid;
250     char *name;
251 
252     SWITCH_FOREACH_OPT(opt, "c", opts, "cpupool-list", 0) {
253     case 'c':
254         opt_cpus = 1;
255         break;
256     }
257 
258     if (optind < argc) {
259         pool = argv[optind];
260         if (libxl_name_to_cpupoolid(ctx, pool, &poolid)) {
261             fprintf(stderr, "Pool \'%s\' does not exist\n", pool);
262             return EXIT_FAILURE;
263         }
264     }
265 
266     poolinfo = libxl_list_cpupool(ctx, &n_pools);
267     if (!poolinfo) {
268         fprintf(stderr, "error getting cpupool info\n");
269         return EXIT_FAILURE;
270     }
271 
272     printf("%-19s", "Name");
273     if (opt_cpus)
274         printf("CPU list\n");
275     else
276         printf("CPUs   Sched     Active   Domain count\n");
277 
278     for (p = 0; p < n_pools; p++) {
279         if (!pool || (poolinfo[p].poolid == poolid)) {
280             name = poolinfo[p].pool_name;
281             printf("%-19s", name);
282             n = 0;
283             libxl_for_each_bit(c, poolinfo[p].cpumap)
284                 if (libxl_bitmap_test(&poolinfo[p].cpumap, c)) {
285                     if (n && opt_cpus) printf(",");
286                     if (opt_cpus) printf("%d", c);
287                     n++;
288                 }
289             if (!opt_cpus) {
290                 printf("%3d %9s       y       %4d", n,
291                        libxl_scheduler_to_string(poolinfo[p].sched),
292                        poolinfo[p].n_dom);
293             }
294             printf("\n");
295         }
296     }
297 
298     libxl_cpupoolinfo_list_free(poolinfo, n_pools);
299 
300     return EXIT_SUCCESS;
301 }
302 
main_cpupooldestroy(int argc,char ** argv)303 int main_cpupooldestroy(int argc, char **argv)
304 {
305     int opt;
306     const char *pool;
307     uint32_t poolid;
308 
309     SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-destroy", 1) {
310         /* No options */
311     }
312 
313     pool = argv[optind];
314 
315     if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) ||
316         !libxl_cpupoolid_is_valid(ctx, poolid)) {
317         fprintf(stderr, "unknown cpupool '%s'\n", pool);
318         return EXIT_FAILURE;
319     }
320 
321     if (libxl_cpupool_destroy(ctx, poolid)) {
322         fprintf(stderr, "Can't destroy cpupool '%s'\n", pool);
323         return EXIT_FAILURE;
324     }
325 
326     return EXIT_SUCCESS;
327 }
328 
main_cpupoolrename(int argc,char ** argv)329 int main_cpupoolrename(int argc, char **argv)
330 {
331     int opt;
332     const char *pool;
333     const char *new_name;
334     uint32_t poolid;
335 
336     SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-rename", 2) {
337         /* No options */
338     }
339 
340     pool = argv[optind++];
341 
342     if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) ||
343         !libxl_cpupoolid_is_valid(ctx, poolid)) {
344         fprintf(stderr, "unknown cpupool '%s'\n", pool);
345         return EXIT_FAILURE;
346     }
347 
348     new_name = argv[optind];
349 
350     if (libxl_cpupool_rename(ctx, new_name, poolid)) {
351         fprintf(stderr, "Can't rename cpupool '%s'\n", pool);
352         return EXIT_FAILURE;
353     }
354 
355     return EXIT_SUCCESS;
356 }
357 
main_cpupoolcpuadd(int argc,char ** argv)358 int main_cpupoolcpuadd(int argc, char **argv)
359 {
360     int opt;
361     const char *pool;
362     uint32_t poolid;
363     libxl_bitmap cpumap;
364     int rc = EXIT_FAILURE;
365 
366     SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-cpu-add", 2) {
367         /* No options */
368     }
369 
370     libxl_bitmap_init(&cpumap);
371     if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) {
372         fprintf(stderr, "Unable to allocate cpumap");
373         return EXIT_FAILURE;
374     }
375 
376     pool = argv[optind++];
377     if (parse_cpurange(argv[optind], &cpumap))
378         goto out;
379 
380     if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) ||
381         !libxl_cpupoolid_is_valid(ctx, poolid)) {
382         fprintf(stderr, "unknown cpupool \'%s\'\n", pool);
383         goto out;
384     }
385 
386     if (libxl_cpupool_cpuadd_cpumap(ctx, poolid, &cpumap))
387         fprintf(stderr, "some cpus may not have been added to %s\n", pool);
388 
389     rc = EXIT_SUCCESS;
390 
391 out:
392     libxl_bitmap_dispose(&cpumap);
393     return rc;
394 }
395 
main_cpupoolcpuremove(int argc,char ** argv)396 int main_cpupoolcpuremove(int argc, char **argv)
397 {
398     int opt;
399     const char *pool;
400     uint32_t poolid;
401     libxl_bitmap cpumap;
402     int rc = EXIT_FAILURE;
403 
404     libxl_bitmap_init(&cpumap);
405     if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) {
406         fprintf(stderr, "Unable to allocate cpumap");
407         return EXIT_FAILURE;
408     }
409 
410     SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-cpu-remove", 2) {
411         /* No options */
412     }
413 
414     pool = argv[optind++];
415     if (parse_cpurange(argv[optind], &cpumap))
416         goto out;
417 
418     if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) ||
419         !libxl_cpupoolid_is_valid(ctx, poolid)) {
420         fprintf(stderr, "unknown cpupool \'%s\'\n", pool);
421         goto out;
422     }
423 
424     if (libxl_cpupool_cpuremove_cpumap(ctx, poolid, &cpumap)) {
425         fprintf(stderr, "Some cpus may have not or only partially been removed from '%s'.\n", pool);
426         fprintf(stderr, "If a cpu can't be added to another cpupool, add it to '%s' again and retry.\n", pool);
427     }
428 
429     rc = EXIT_SUCCESS;
430 
431 out:
432     libxl_bitmap_dispose(&cpumap);
433     return rc;
434 }
435 
main_cpupoolmigrate(int argc,char ** argv)436 int main_cpupoolmigrate(int argc, char **argv)
437 {
438     int opt;
439     const char *pool;
440     uint32_t poolid;
441     const char *dom;
442     uint32_t domid;
443 
444     SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-migrate", 2) {
445         /* No options */
446     }
447 
448     dom = argv[optind++];
449     pool = argv[optind];
450 
451     if (libxl_domain_qualifier_to_domid(ctx, dom, &domid) ||
452         !libxl_domid_to_name(ctx, domid)) {
453         fprintf(stderr, "unknown domain '%s'\n", dom);
454         return EXIT_FAILURE;
455     }
456 
457     if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) ||
458         !libxl_cpupoolid_is_valid(ctx, poolid)) {
459         fprintf(stderr, "unknown cpupool '%s'\n", pool);
460         return EXIT_FAILURE;
461     }
462 
463     if (libxl_cpupool_movedomain(ctx, poolid, domid))
464         return EXIT_FAILURE;
465 
466     return EXIT_SUCCESS;
467 }
468 
main_cpupoolnumasplit(int argc,char ** argv)469 int main_cpupoolnumasplit(int argc, char **argv)
470 {
471     int rc;
472     int opt;
473     int p;
474     int c;
475     int n;
476     uint32_t poolid;
477     libxl_scheduler sched;
478     int n_pools;
479     int node;
480     int n_cpus;
481     char *name = NULL;
482     libxl_uuid uuid;
483     libxl_bitmap cpumap;
484     libxl_cpupoolinfo *poolinfo;
485     libxl_cputopology *topology;
486     libxl_dominfo info;
487 
488     SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-numa-split", 0) {
489         /* No options */
490     }
491 
492     libxl_dominfo_init(&info);
493 
494     rc = EXIT_FAILURE;
495 
496     libxl_bitmap_init(&cpumap);
497     poolinfo = libxl_list_cpupool(ctx, &n_pools);
498     if (!poolinfo) {
499         fprintf(stderr, "error getting cpupool info\n");
500         return EXIT_FAILURE;
501     }
502     poolid = poolinfo[0].poolid;
503     sched = poolinfo[0].sched;
504     libxl_cpupoolinfo_list_free(poolinfo, n_pools);
505 
506     if (n_pools > 1) {
507         fprintf(stderr, "splitting not possible, already cpupools in use\n");
508         return EXIT_FAILURE;
509     }
510 
511     topology = libxl_get_cpu_topology(ctx, &n_cpus);
512     if (topology == NULL) {
513         fprintf(stderr, "libxl_get_topologyinfo failed\n");
514         return EXIT_FAILURE;
515     }
516 
517     if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) {
518         fprintf(stderr, "Failed to allocate cpumap\n");
519         goto out;
520     }
521 
522     /* Reset Pool-0 to 1st node: first add cpus, then remove cpus to avoid
523        a cpupool without cpus in between */
524 
525     node = topology[0].node;
526     if (libxl_cpupool_cpuadd_node(ctx, 0, node, &n)) {
527         fprintf(stderr, "error on adding cpu to Pool 0\n");
528         goto out;
529     }
530 
531     xasprintf(&name, "Pool-node%d", node);
532     if (libxl_cpupool_rename(ctx, name, 0)) {
533         fprintf(stderr, "error on renaming Pool 0\n");
534         goto out;
535     }
536 
537     n = 0;
538     for (c = 0; c < n_cpus; c++) {
539         if (topology[c].node == node) {
540             topology[c].node = LIBXL_CPUTOPOLOGY_INVALID_ENTRY;
541             libxl_bitmap_set(&cpumap, n);
542             n++;
543         }
544     }
545     if (libxl_domain_info(ctx, &info, 0)) {
546         fprintf(stderr, "error on getting info for Domain-0\n");
547         goto out;
548     }
549     if (info.vcpu_online > n && libxl_set_vcpuonline(ctx, 0, &cpumap, NULL)) {
550         fprintf(stderr, "error on removing vcpus for Domain-0\n");
551         goto out;
552     }
553     for (c = 0; c < 10; c++) {
554         /* We've called libxl_dominfo_init before the loop and will
555          * call libxl_dominfo_dispose after the loop when we're done
556          * with info.
557          */
558         libxl_dominfo_dispose(&info);
559         libxl_dominfo_init(&info);
560         if (libxl_domain_info(ctx, &info, 0)) {
561             fprintf(stderr, "error on getting info for Domain-0\n");
562             goto out;
563         }
564         if (info.vcpu_online <= n) {
565             break;
566         }
567         sleep(1);
568     }
569     if (info.vcpu_online > n) {
570         fprintf(stderr, "failed to offline vcpus\n");
571         goto out;
572     }
573     libxl_bitmap_set_none(&cpumap);
574 
575     for (c = 0; c < n_cpus; c++) {
576         if (topology[c].node == LIBXL_CPUTOPOLOGY_INVALID_ENTRY) {
577             continue;
578         }
579 
580         node = topology[c].node;
581         if (libxl_cpupool_cpuremove_node(ctx, 0, node, &n)) {
582             fprintf(stderr, "error on removing cpu from Pool 0\n");
583             goto out;
584         }
585 
586         free(name);
587         xasprintf(&name, "Pool-node%d", node);
588         libxl_uuid_generate(&uuid);
589         poolid = 0;
590         if (libxl_cpupool_create(ctx, name, sched, cpumap, &uuid, &poolid)) {
591             fprintf(stderr, "error on creating cpupool\n");
592             goto out;
593         }
594 
595         if (libxl_cpupool_cpuadd_node(ctx, poolid, node, &n)) {
596             fprintf(stderr, "error on adding cpus to cpupool\n");
597             goto out;
598         }
599 
600         for (p = c; p < n_cpus; p++) {
601             if (topology[p].node == node) {
602                 topology[p].node = LIBXL_CPUTOPOLOGY_INVALID_ENTRY;
603             }
604         }
605     }
606 
607     rc = EXIT_SUCCESS;
608 
609 out:
610     libxl_cputopology_list_free(topology, n_cpus);
611     libxl_bitmap_dispose(&cpumap);
612     libxl_dominfo_dispose(&info);
613     free(name);
614 
615     return rc;
616 }
617 
618 /*
619  * Local variables:
620  * mode: C
621  * c-basic-offset: 4
622  * indent-tabs-mode: nil
623  * End:
624  */
625