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 <stdlib.h>
16 #include <limits.h>
17 
18 #include <libxl.h>
19 #include <libxl_utils.h>
20 #include <libxlutil.h>
21 
22 #include "xl.h"
23 #include "xl_utils.h"
24 #include "xl_parse.h"
25 
print_vcpuinfo(uint32_t tdomid,const libxl_vcpuinfo * vcpuinfo,uint32_t nr_cpus)26 static void print_vcpuinfo(uint32_t tdomid,
27                            const libxl_vcpuinfo *vcpuinfo,
28                            uint32_t nr_cpus)
29 {
30     char *domname;
31 
32     /*      NAME  ID  VCPU */
33     domname = libxl_domid_to_name(ctx, tdomid);
34     printf("%-32s %5u %5u",
35            domname, tdomid, vcpuinfo->vcpuid);
36     free(domname);
37     if (!vcpuinfo->online) {
38         /*      CPU STA */
39         printf("%5c %3c%cp ", '-', '-', '-');
40     } else {
41         /*      CPU STA */
42         printf("%5u %3c%c- ", vcpuinfo->cpu,
43                vcpuinfo->running ? 'r' : '-',
44                vcpuinfo->blocked ? 'b' : '-');
45     }
46     /*      TIM */
47     printf("%9.1f  ", ((float)vcpuinfo->vcpu_time / 1e9));
48     /* CPU HARD AND SOFT AFFINITY */
49     print_bitmap(vcpuinfo->cpumap.map, nr_cpus, stdout);
50     printf(" / ");
51     print_bitmap(vcpuinfo->cpumap_soft.map, nr_cpus, stdout);
52     printf("\n");
53 }
54 
print_domain_vcpuinfo(uint32_t domid,uint32_t nr_cpus)55 static void print_domain_vcpuinfo(uint32_t domid, uint32_t nr_cpus)
56 {
57     libxl_vcpuinfo *vcpuinfo;
58     int i, nb_vcpu, nrcpus;
59 
60     vcpuinfo = libxl_list_vcpu(ctx, domid, &nb_vcpu, &nrcpus);
61 
62     if (!vcpuinfo)
63         return;
64 
65     for (i = 0; i < nb_vcpu; i++) {
66         print_vcpuinfo(domid, &vcpuinfo[i], nr_cpus);
67     }
68 
69     libxl_vcpuinfo_list_free(vcpuinfo, nb_vcpu);
70 }
71 
apply_global_affinity_masks(libxl_domain_type type,libxl_bitmap * vcpu_affinity_array,unsigned int size)72 void apply_global_affinity_masks(libxl_domain_type type,
73                                  libxl_bitmap *vcpu_affinity_array,
74                                  unsigned int size)
75 {
76     libxl_bitmap *mask = &global_vm_affinity_mask;
77     libxl_bitmap *type_mask;
78     unsigned int i;
79 
80     switch (type) {
81     case LIBXL_DOMAIN_TYPE_HVM:
82     case LIBXL_DOMAIN_TYPE_PVH:
83         type_mask = &global_hvm_affinity_mask;
84         break;
85     case LIBXL_DOMAIN_TYPE_PV:
86         type_mask = &global_pv_affinity_mask;
87         break;
88     default:
89         fprintf(stderr, "Unknown guest type\n");
90         exit(EXIT_FAILURE);
91     }
92 
93     for (i = 0; i < size; i++) {
94         int rc;
95         libxl_bitmap *t = &vcpu_affinity_array[i];
96         libxl_bitmap b1, b2;
97 
98         libxl_bitmap_init(&b1);
99         libxl_bitmap_init(&b2);
100 
101         rc = libxl_bitmap_and(ctx, &b1, t, mask);
102         if (rc) {
103             fprintf(stderr, "libxl_bitmap_and errored\n");
104             exit(EXIT_FAILURE);
105         }
106         rc = libxl_bitmap_and(ctx, &b2, &b1, type_mask);
107         if (rc) {
108             fprintf(stderr, "libxl_bitmap_and errored\n");
109             exit(EXIT_FAILURE);
110         }
111 
112         if (libxl_bitmap_is_empty(&b2)) {
113             fprintf(stderr, "vcpu hard affinity map is empty\n");
114             exit(EXIT_FAILURE);
115         }
116 
117         /* Replace target bitmap with the result */
118         libxl_bitmap_dispose(t);
119         libxl_bitmap_init(t);
120         libxl_bitmap_copy_alloc(ctx, t, &b2);
121 
122         libxl_bitmap_dispose(&b1);
123         libxl_bitmap_dispose(&b2);
124     }
125 }
126 
vcpulist(int argc,char ** argv)127 static void vcpulist(int argc, char **argv)
128 {
129     libxl_dominfo *dominfo;
130     libxl_physinfo physinfo;
131     int i, nb_domain;
132 
133     if (libxl_get_physinfo(ctx, &physinfo) != 0) {
134         fprintf(stderr, "libxl_physinfo failed.\n");
135         goto vcpulist_out;
136     }
137 
138     printf("%-32s %5s %5s %5s %5s %9s %s\n",
139            "Name", "ID", "VCPU", "CPU", "State", "Time(s)",
140            "Affinity (Hard / Soft)");
141     if (!argc) {
142         if (!(dominfo = libxl_list_domain(ctx, &nb_domain))) {
143             fprintf(stderr, "libxl_list_domain failed.\n");
144             goto vcpulist_out;
145         }
146 
147         for (i = 0; i<nb_domain; i++)
148             print_domain_vcpuinfo(dominfo[i].domid, physinfo.max_cpu_id + 1);
149 
150         libxl_dominfo_list_free(dominfo, nb_domain);
151     } else {
152         for (; argc > 0; ++argv, --argc) {
153             uint32_t domid = find_domain(*argv);
154             print_domain_vcpuinfo(domid, physinfo.max_cpu_id + 1);
155         }
156     }
157   vcpulist_out:
158     libxl_physinfo_dispose(&physinfo);
159 }
160 
main_vcpulist(int argc,char ** argv)161 int main_vcpulist(int argc, char **argv)
162 {
163     int opt;
164 
165     SWITCH_FOREACH_OPT(opt, "", NULL, "vcpu-list", 0) {
166         /* No options */
167     }
168 
169     vcpulist(argc - optind, argv + optind);
170     return EXIT_SUCCESS;
171 }
172 
main_vcpupin(int argc,char ** argv)173 int main_vcpupin(int argc, char **argv)
174 {
175     static struct option opts[] = {
176         {"force", 0, 0, 'f'},
177         {"ignore-global-affinity-masks", 0, 0, 'i'},
178         COMMON_LONG_OPTS
179     };
180     libxl_vcpuinfo *vcpuinfo;
181     libxl_bitmap cpumap_hard, cpumap_soft;;
182     libxl_bitmap *soft = &cpumap_soft, *hard = &cpumap_hard;
183     uint32_t domid;
184     /*
185      * int would be enough for vcpuid, but we don't want to
186      * mess aroung range checking the return value of strtol().
187      */
188     long vcpuid;
189     const char *vcpu, *hard_str, *soft_str;
190     char *endptr;
191     int opt, nb_cpu, nb_vcpu, rc = EXIT_FAILURE;
192     bool force = false, ignore_masks = false;
193 
194     libxl_bitmap_init(&cpumap_hard);
195     libxl_bitmap_init(&cpumap_soft);
196 
197     SWITCH_FOREACH_OPT(opt, "fi", opts, "vcpu-pin", 3) {
198     case 'f':
199         force = true;
200         break;
201     case 'i':
202         ignore_masks = true;
203         break;
204     default:
205         break;
206     }
207 
208     domid = find_domain(argv[optind]);
209     vcpu = argv[optind+1];
210     hard_str = argv[optind+2];
211     soft_str = (argc > optind+3) ? argv[optind+3] : NULL;
212 
213     /* Figure out with which vCPU we are dealing with */
214     vcpuid = strtol(vcpu, &endptr, 10);
215     if (vcpu == endptr || vcpuid < 0) {
216         if (strcmp(vcpu, "all")) {
217             fprintf(stderr, "Error: Invalid argument %s as VCPU.\n", vcpu);
218             goto out;
219         }
220         if (force) {
221             fprintf(stderr, "Error: --force and 'all' as VCPU not allowed.\n");
222             goto out;
223         }
224         vcpuid = -1;
225     }
226 
227     if (libxl_cpu_bitmap_alloc(ctx, &cpumap_hard, 0) ||
228         libxl_cpu_bitmap_alloc(ctx, &cpumap_soft, 0))
229         goto out;
230 
231     /*
232      * Syntax is: xl vcpu-pin <domid> <vcpu> <hard> <soft>
233      * We want to handle all the following cases ('-' means
234      * "leave it alone"):
235      *  xl vcpu-pin 0 3 3,4
236      *  xl vcpu-pin 0 3 3,4 -
237      *  xl vcpu-pin 0 3 - 6-9
238      *  xl vcpu-pin 0 3 3,4 6-9
239      */
240 
241     /*
242      * Hard affinity is always present. However, if it's "-", all we need
243      * is passing a NULL pointer to the libxl_set_vcpuaffinity() call below.
244      */
245     if (!strcmp(hard_str, "-"))
246         hard = NULL;
247     else if (parse_cpurange(hard_str, hard))
248         goto out;
249     /*
250      * Soft affinity is handled similarly. Only difference: we also want
251      * to pass NULL to libxl_set_vcpuaffinity() if it is not specified.
252      */
253     if (argc <= optind+3 || !strcmp(soft_str, "-"))
254         soft = NULL;
255     else if (parse_cpurange(soft_str, soft))
256         goto out;
257 
258     if (dryrun_only) {
259         nb_cpu = libxl_get_max_cpus(ctx);
260         if (nb_cpu < 0) {
261             fprintf(stderr, "libxl_get_max_cpus failed.\n");
262             goto out;
263         }
264 
265         fprintf(stdout, "cpumap: ");
266         if (hard)
267             print_bitmap(hard->map, nb_cpu, stdout);
268         else
269             fprintf(stdout, "-");
270         if (soft) {
271             fprintf(stdout, " ");
272             print_bitmap(soft->map, nb_cpu, stdout);
273         }
274         fprintf(stdout, "\n");
275 
276         if (ferror(stdout) || fflush(stdout)) {
277             perror("stdout");
278             exit(EXIT_FAILURE);
279         }
280 
281         rc = EXIT_SUCCESS;
282         goto out;
283     }
284 
285     /* Only hard affinity matters here */
286     if (!ignore_masks && hard) {
287         libxl_dominfo dominfo;
288 
289         if (libxl_domain_info(ctx, &dominfo, domid)) {
290             fprintf(stderr, "Could not get domain info\n");
291             goto out;
292         }
293 
294         /* HVM and PVH domains use the same global affinity mask */
295         apply_global_affinity_masks(dominfo.domain_type, hard, 1);
296     }
297 
298     if (force) {
299         if (libxl_set_vcpuaffinity_force(ctx, domid, vcpuid, hard, soft)) {
300             fprintf(stderr, "Could not set affinity for vcpu `%ld'.\n",
301                     vcpuid);
302             goto out;
303         }
304     }
305     else if (vcpuid != -1) {
306         if (libxl_set_vcpuaffinity(ctx, domid, vcpuid, hard, soft)) {
307             fprintf(stderr, "Could not set affinity for vcpu `%ld'.\n",
308                     vcpuid);
309             goto out;
310         }
311     } else {
312         if (!(vcpuinfo = libxl_list_vcpu(ctx, domid, &nb_vcpu, &nb_cpu))) {
313             fprintf(stderr, "libxl_list_vcpu failed.\n");
314             goto out;
315         }
316         if (libxl_set_vcpuaffinity_all(ctx, domid, nb_vcpu, hard, soft))
317             fprintf(stderr, "Could not set affinity.\n");
318         libxl_vcpuinfo_list_free(vcpuinfo, nb_vcpu);
319     }
320 
321     rc = EXIT_SUCCESS;
322  out:
323     libxl_bitmap_dispose(&cpumap_soft);
324     libxl_bitmap_dispose(&cpumap_hard);
325     return rc;
326 }
327 
vcpuset(uint32_t domid,const char * nr_vcpus,int check_host)328 static int vcpuset(uint32_t domid, const char* nr_vcpus, int check_host)
329 {
330     char *endptr;
331     unsigned int i;
332     unsigned long max_vcpus;
333     libxl_bitmap cpumap;
334     int rc;
335 
336     libxl_bitmap_init(&cpumap);
337     max_vcpus = strtoul(nr_vcpus, &endptr, 10);
338     if (nr_vcpus == endptr || max_vcpus > INT_MAX) {
339         fprintf(stderr, "Error: Invalid argument.\n");
340         return 1;
341     }
342 
343     /*
344      * Maximum amount of vCPUS the guest is allowed to set is limited
345      * by the host's amount of pCPUs.
346      */
347     if (check_host) {
348         unsigned int online_vcpus, host_cpu = libxl_get_max_cpus(ctx);
349         libxl_dominfo dominfo;
350 
351         if (libxl_domain_info(ctx, &dominfo, domid))
352             return 1;
353 
354         online_vcpus = dominfo.vcpu_online;
355         libxl_dominfo_dispose(&dominfo);
356 
357         if (max_vcpus > online_vcpus && max_vcpus > host_cpu) {
358             fprintf(stderr, "You are overcommmitting! You have %d physical" \
359                     " CPUs and want %ld vCPUs! Aborting, use --ignore-host to" \
360                     " continue\n", host_cpu, max_vcpus);
361             return 1;
362         }
363     }
364     rc = libxl_cpu_bitmap_alloc(ctx, &cpumap, max_vcpus);
365     if (rc) {
366         fprintf(stderr, "libxl_cpu_bitmap_alloc failed, rc: %d\n", rc);
367         return 1;
368     }
369     for (i = 0; i < max_vcpus; i++)
370         libxl_bitmap_set(&cpumap, i);
371 
372     rc = libxl_set_vcpuonline(ctx, domid, &cpumap, NULL);
373     if (rc == ERROR_DOMAIN_NOTFOUND)
374         fprintf(stderr, "Domain %u does not exist.\n", domid);
375     else if (rc)
376         fprintf(stderr, "libxl_set_vcpuonline failed domid=%u max_vcpus=%ld," \
377                 " rc: %d\n", domid, max_vcpus, rc);
378 
379     libxl_bitmap_dispose(&cpumap);
380     return rc ? 1 : 0;
381 }
382 
main_vcpuset(int argc,char ** argv)383 int main_vcpuset(int argc, char **argv)
384 {
385     static struct option opts[] = {
386         {"ignore-host", 0, 0, 'i'},
387         COMMON_LONG_OPTS
388     };
389     int opt, check_host = 1;
390 
391     SWITCH_FOREACH_OPT(opt, "i", opts, "vcpu-set", 2) {
392     case 'i':
393         check_host = 0;
394         break;
395     default:
396         break;
397     }
398 
399     if (vcpuset(find_domain(argv[optind]), argv[optind + 1], check_host))
400         return EXIT_FAILURE;
401 
402     return EXIT_SUCCESS;
403 }
404 
405 /*
406  * Local variables:
407  * mode: C
408  * c-basic-offset: 4
409  * indent-tabs-mode: nil
410  * End:
411  */
412