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 "libxl_osdeps.h"
16 
17 #include "libxl_internal.h"
18 #include "libxl_arch.h"
19 
20 /*
21  * Set the maximum memory size of the domain in the hypervisor. There is no
22  * change of the current memory size involved. The specified memory size can
23  * even be above the configured maxmem size of the domain, but the related
24  * Xenstore entry memory/static-max isn't modified!
25  */
libxl_domain_setmaxmem(libxl_ctx * ctx,uint32_t domid,uint64_t max_memkb)26 int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t max_memkb)
27 {
28     GC_INIT(ctx);
29     char *mem, *endptr;
30     uint64_t memorykb, size;
31     char *dompath = libxl__xs_get_dompath(gc, domid);
32     int rc = 1;
33     libxl__flock *lock = NULL;
34     libxl_domain_config d_config;
35 
36     libxl_domain_config_init(&d_config);
37 
38     CTX_LOCK;
39 
40     lock = libxl__lock_domain_userdata(gc, domid);
41     if (!lock) {
42         rc = ERROR_LOCK_FAIL;
43         goto out;
44     }
45 
46     mem = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target", dompath));
47     if (!mem) {
48         LOGED(ERROR, domid, "Cannot get memory info from %s/memory/target",
49               dompath);
50         goto out;
51     }
52     memorykb = strtoull(mem, &endptr, 10);
53     if (*endptr != '\0') {
54         LOGED(ERROR, domid, "Invalid memory %s from %s/memory/target\n",
55               mem, dompath);
56         goto out;
57     }
58 
59     if (max_memkb < memorykb) {
60         LOGED(ERROR, domid,
61               "memory_static_max must be greater than or or equal to memory_dynamic_max");
62         goto out;
63     }
64 
65     rc = libxl__get_domain_configuration(gc, domid, &d_config);
66     if (rc < 0) {
67         LOGE(ERROR, "unable to retrieve domain configuration");
68         goto out;
69     }
70 
71     rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
72     if (rc < 0) {
73         LOGE(ERROR, "Couldn't get arch extra constant memory size");
74         goto out;
75     }
76 
77     rc = xc_domain_setmaxmem(ctx->xch, domid, max_memkb + size);
78     if (rc != 0) {
79         LOGED(ERROR, domid,
80               "xc_domain_setmaxmem domid=%d memkb=%"PRIu64" failed ""rc=%d\n",
81               domid, max_memkb + size, rc);
82         goto out;
83     }
84 
85     rc = 0;
86 out:
87     libxl_domain_config_dispose(&d_config);
88     if (lock) libxl__unlock_file(lock);
89     CTX_UNLOCK;
90     GC_FREE;
91     return rc;
92 }
93 
libxl__fill_dom0_memory_info(libxl__gc * gc,uint64_t * target_memkb,uint64_t * max_memkb)94 static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint64_t *target_memkb,
95                                         uint64_t *max_memkb)
96 {
97     int rc;
98     libxl_dominfo info;
99     libxl_physinfo physinfo;
100     char *target = NULL, *staticmax = NULL, *endptr = NULL;
101     char *target_path = "/local/domain/0/memory/target";
102     char *max_path = "/local/domain/0/memory/static-max";
103     xs_transaction_t t;
104     libxl_ctx *ctx = libxl__gc_owner(gc);
105 
106     libxl_dominfo_init(&info);
107 
108 retry_transaction:
109     t = xs_transaction_start(ctx->xsh);
110 
111     target = libxl__xs_read(gc, t, target_path);
112     staticmax = libxl__xs_read(gc, t, max_path);
113     if (target && staticmax) {
114         rc = 0;
115         goto out;
116     }
117 
118     if (target) {
119         *target_memkb = strtoull(target, &endptr, 10);
120         if (*endptr != '\0') {
121             LOGED(ERROR, 0, "Invalid memory target %s from %s\n", target,
122                  target_path);
123             rc = ERROR_FAIL;
124             goto out;
125         }
126     }
127 
128     if (staticmax) {
129         *max_memkb = strtoull(staticmax, &endptr, 10);
130         if (*endptr != '\0') {
131             LOGED(ERROR, 0, "Invalid memory static-max %s from %s\n",
132                  staticmax,
133                  max_path);
134             rc = ERROR_FAIL;
135             goto out;
136         }
137     }
138 
139     libxl_dominfo_dispose(&info);
140     libxl_dominfo_init(&info);
141     rc = libxl_domain_info(ctx, &info, 0);
142     if (rc < 0)
143         goto out;
144 
145     rc = libxl_get_physinfo(ctx, &physinfo);
146     if (rc < 0)
147         goto out;
148 
149     if (target == NULL) {
150         libxl__xs_printf(gc, t, target_path, "%"PRIu64, info.current_memkb);
151         *target_memkb = info.current_memkb;
152     }
153     if (staticmax == NULL) {
154         libxl__xs_printf(gc, t, max_path, "%"PRIu64, info.max_memkb);
155         *max_memkb = info.max_memkb;
156     }
157 
158     rc = 0;
159 
160 out:
161     if (!xs_transaction_end(ctx->xsh, t, 0)) {
162         if (errno == EAGAIN)
163             goto retry_transaction;
164         else
165             rc = ERROR_FAIL;
166     }
167 
168     libxl_dominfo_dispose(&info);
169     return rc;
170 }
171 
libxl_set_memory_target(libxl_ctx * ctx,uint32_t domid,int64_t target_memkb,int relative,int enforce)172 int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid,
173         int64_t target_memkb, int relative, int enforce)
174 {
175     GC_INIT(ctx);
176     int rc, r, lrc, abort_transaction = 0;
177     uint64_t memorykb, size;
178     uint64_t videoram = 0;
179     uint64_t current_target_memkb = 0, new_target_memkb = 0;
180     uint64_t current_max_memkb = 0;
181     char *memmax, *endptr, *videoram_s = NULL, *target = NULL;
182     char *dompath = libxl__xs_get_dompath(gc, domid);
183     xc_domaininfo_t info;
184     libxl_dominfo ptr;
185     char *uuid;
186     xs_transaction_t t;
187     libxl__flock *lock;
188     libxl_domain_config d_config;
189 
190     libxl_domain_config_init(&d_config);
191 
192     CTX_LOCK;
193 
194     lock = libxl__lock_domain_userdata(gc, domid);
195     if (!lock) {
196         rc = ERROR_LOCK_FAIL;
197         goto out_no_transaction;
198     }
199 
200     rc = libxl__get_domain_configuration(gc, domid, &d_config);
201     if (rc < 0) {
202         LOGE(ERROR, "unable to retrieve domain configuration");
203         goto out_no_transaction;
204     }
205 
206     rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
207     if (rc < 0) {
208         LOGE(ERROR, "Couldn't get arch extra constant memory size");
209         goto out_no_transaction;
210     }
211 
212 retry_transaction:
213     t = xs_transaction_start(ctx->xsh);
214 
215     target = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/target", dompath));
216     if (!target && !domid) {
217         if (!xs_transaction_end(ctx->xsh, t, 1)) {
218             rc = ERROR_FAIL;
219             goto out_no_transaction;
220         }
221         lrc = libxl__fill_dom0_memory_info(gc, &current_target_memkb,
222                                            &current_max_memkb);
223         if (lrc < 0) { rc = ERROR_FAIL; goto out_no_transaction; }
224         goto retry_transaction;
225     } else if (!target) {
226         LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
227               dompath);
228         abort_transaction = 1;
229         rc = ERROR_FAIL;
230         goto out;
231     } else {
232         current_target_memkb = strtoull(target, &endptr, 10);
233         if (*endptr != '\0') {
234             LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
235                   target, dompath);
236             abort_transaction = 1;
237             rc = ERROR_FAIL;
238             goto out;
239         }
240     }
241     memmax = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/static-max", dompath));
242     if (!memmax) {
243         LOGED(ERROR, domid, "Cannot get memory info from %s/memory/static-max",
244               dompath);
245         abort_transaction = 1;
246         rc = ERROR_FAIL;
247         goto out;
248     }
249     memorykb = strtoull(memmax, &endptr, 10);
250     if (*endptr != '\0') {
251         LOGED(ERROR, domid, "Invalid max memory %s from %s/memory/static-max\n",
252              memmax, dompath);
253         abort_transaction = 1;
254         rc = ERROR_FAIL;
255         goto out;
256     }
257 
258     videoram_s = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/videoram",
259                                                  dompath));
260     videoram = videoram_s ? atoi(videoram_s) : 0;
261 
262     if (relative) {
263         if (target_memkb < 0 && llabs(target_memkb) > current_target_memkb)
264             new_target_memkb = 0;
265         else
266             new_target_memkb = current_target_memkb + target_memkb;
267     } else
268         new_target_memkb = target_memkb - videoram;
269     if (new_target_memkb > memorykb) {
270         LOGD(ERROR, domid,
271              "memory_dynamic_max must be less than or equal to"
272              " memory_static_max\n");
273         abort_transaction = 1;
274         rc = ERROR_INVAL;
275         goto out;
276     }
277 
278     if (!domid && new_target_memkb < LIBXL_MIN_DOM0_MEM) {
279         LOGD(ERROR, domid,
280              "New target %"PRIu64" for dom0 is below the minimum threshold",
281              new_target_memkb);
282         abort_transaction = 1;
283         rc = ERROR_INVAL;
284         goto out;
285     }
286 
287     if (enforce) {
288         memorykb = new_target_memkb + videoram;
289         r = xc_domain_setmaxmem(ctx->xch, domid, memorykb + size);
290         if (r != 0) {
291             LOGED(ERROR, domid,
292                   "xc_domain_setmaxmem memkb=%"PRIu64" failed ""rc=%d\n",
293                   memorykb + size,
294                   r);
295             abort_transaction = 1;
296             rc = ERROR_FAIL;
297             goto out;
298         }
299     }
300 
301     if (d_config.c_info.type != LIBXL_DOMAIN_TYPE_PV) {
302         r = xc_domain_set_pod_target(ctx->xch, domid,
303                 (new_target_memkb + size) / 4, NULL, NULL, NULL);
304         if (r != 0) {
305             LOGED(ERROR, domid,
306                   "xc_domain_set_pod_target memkb=%"PRIu64" failed rc=%d\n",
307                   (new_target_memkb + size) / 4,
308                   r);
309             abort_transaction = 1;
310             rc = ERROR_FAIL;
311             goto out;
312         }
313     }
314 
315     libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath),
316                      "%"PRIu64, new_target_memkb);
317 
318     r = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
319     if (r != 1 || info.domain != domid) {
320         abort_transaction = 1;
321         rc = ERROR_FAIL;
322         goto out;
323     }
324 
325     libxl_dominfo_init(&ptr);
326     libxl__xcinfo2xlinfo(ctx, &info, &ptr);
327     uuid = libxl__uuid2string(gc, ptr.uuid);
328     libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid),
329                      "%"PRIu64, new_target_memkb / 1024);
330     libxl_dominfo_dispose(&ptr);
331 
332     rc = 0;
333 out:
334     if (!xs_transaction_end(ctx->xsh, t, abort_transaction)
335         && !abort_transaction)
336         if (errno == EAGAIN)
337             goto retry_transaction;
338 
339 out_no_transaction:
340     libxl_domain_config_dispose(&d_config);
341     if (lock) libxl__unlock_file(lock);
342     CTX_UNLOCK;
343     GC_FREE;
344     return rc;
345 }
346 
347 /* out_target_memkb and out_max_memkb can be NULL */
libxl__get_memory_target(libxl__gc * gc,uint32_t domid,uint64_t * out_target_memkb,uint64_t * out_max_memkb)348 int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
349                              uint64_t *out_target_memkb,
350                              uint64_t *out_max_memkb)
351 {
352     int rc;
353     char *target = NULL, *static_max = NULL, *endptr = NULL;
354     char *dompath = libxl__xs_get_dompath(gc, domid);
355     uint64_t target_memkb, max_memkb;
356 
357     target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target",
358                                                     dompath));
359     static_max = libxl__xs_read(gc, XBT_NULL,
360                     GCSPRINTF("%s/memory/static-max", dompath));
361 
362     rc = ERROR_FAIL;
363     if ((!target || !static_max) && !domid) {
364         rc = libxl__fill_dom0_memory_info(gc, &target_memkb,
365                                           &max_memkb);
366         if (rc < 0)
367             goto out;
368     } else if (!target) {
369         LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
370               dompath);
371         goto out;
372     } else if (!static_max) {
373         LOGED(ERROR, domid,
374               "Cannot get target memory info from %s/memory/static-max",
375                dompath);
376         goto out;
377     } else {
378         target_memkb = strtoull(target, &endptr, 10);
379         if (*endptr != '\0') {
380             LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
381                   target, dompath);
382             goto out;
383         }
384         max_memkb = strtoull(static_max, &endptr, 10);
385         if (*endptr != '\0') {
386             LOGED(ERROR, domid,
387                   "Invalid memory target %s from %s/memory/static-max\n",
388                   static_max,
389                   dompath);
390             goto out;
391         }
392 
393     }
394 
395     if (out_target_memkb)
396         *out_target_memkb = target_memkb;
397 
398     if (out_max_memkb)
399         *out_max_memkb = max_memkb;
400 
401     rc = 0;
402 
403 out:
404     return rc;
405 }
406 
libxl__memkb_64to32(libxl_ctx * ctx,int rc,uint64_t val64,uint32_t * ptr32)407 static int libxl__memkb_64to32(libxl_ctx *ctx, int rc,
408                                uint64_t val64, uint32_t *ptr32)
409 {
410     GC_INIT(ctx);
411 
412     if (rc)
413         goto out;
414 
415     *ptr32 = val64;
416     if (*ptr32 == val64)
417         goto out;
418 
419     LOGE(ERROR, "memory size %"PRIu64" too large for 32 bit value\n", val64);
420     rc = ERROR_FAIL;
421 
422 out:
423     GC_FREE;
424     return rc;
425 }
426 
libxl_get_memory_target(libxl_ctx * ctx,uint32_t domid,uint64_t * out_target)427 int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
428                             uint64_t *out_target)
429 {
430     GC_INIT(ctx);
431     int rc;
432 
433     rc = libxl__get_memory_target(gc, domid, out_target, NULL);
434 
435     GC_FREE;
436     return rc;
437 }
438 
libxl_get_memory_target_0x040700(libxl_ctx * ctx,uint32_t domid,uint32_t * out_target)439 int libxl_get_memory_target_0x040700(
440     libxl_ctx *ctx, uint32_t domid, uint32_t *out_target)
441 {
442     uint64_t my_out_target;
443     int rc;
444 
445     rc = libxl_get_memory_target(ctx, domid, &my_out_target);
446     return libxl__memkb_64to32(ctx, rc, my_out_target, out_target);
447 }
448 
libxl__domain_need_memory_calculate(libxl__gc * gc,libxl_domain_build_info * b_info,uint64_t * need_memkb)449 int libxl__domain_need_memory_calculate(libxl__gc *gc,
450                               libxl_domain_build_info *b_info,
451                               uint64_t *need_memkb)
452 {
453     int rc;
454 
455     *need_memkb = b_info->target_memkb;
456     *need_memkb += b_info->shadow_memkb + b_info->iommu_memkb;
457 
458     switch (b_info->type) {
459     case LIBXL_DOMAIN_TYPE_PVH:
460     case LIBXL_DOMAIN_TYPE_HVM:
461         *need_memkb += LIBXL_HVM_EXTRA_MEMORY;
462         if (libxl_defbool_val(b_info->device_model_stubdomain)) {
463             *need_memkb += b_info->stubdomain_memkb;
464             *need_memkb += b_info->video_memkb;
465         }
466         break;
467     case LIBXL_DOMAIN_TYPE_PV:
468         *need_memkb += LIBXL_PV_EXTRA_MEMORY;
469         break;
470     default:
471         rc = ERROR_INVAL;
472         goto out;
473     }
474     if (*need_memkb % (2 * 1024))
475         *need_memkb += (2 * 1024) - (*need_memkb % (2 * 1024));
476     rc = 0;
477 out:
478     return rc;
479 }
480 
libxl_domain_need_memory(libxl_ctx * ctx,libxl_domain_config * d_config,uint32_t domid_for_logging,uint64_t * need_memkb)481 int libxl_domain_need_memory(libxl_ctx *ctx,
482                              libxl_domain_config *d_config,
483                              uint32_t domid_for_logging,
484                              uint64_t *need_memkb)
485 {
486     GC_INIT(ctx);
487     int rc;
488 
489     ctx->libxl_domain_need_memory_called = 1;
490 
491     rc = libxl__domain_config_setdefault(gc,
492                                          d_config,
493                                          domid_for_logging);
494     if (rc) goto out;
495 
496     rc = libxl__domain_need_memory_calculate(gc,
497                                    &d_config->b_info,
498                                    need_memkb);
499     if (rc) goto out;
500 
501     rc = 0;
502  out:
503     GC_FREE;
504     return rc;
505 }
506 
libxl_domain_need_memory_0x041200(libxl_ctx * ctx,const libxl_domain_build_info * b_info_in,uint64_t * need_memkb)507 int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
508                                       const libxl_domain_build_info *b_info_in,
509                                       uint64_t *need_memkb)
510 {
511     GC_INIT(ctx);
512     int rc;
513 
514     ctx->libxl_domain_need_memory_0x041200_called = 1;
515 
516     libxl_domain_build_info b_info[1];
517     libxl_domain_build_info_init(b_info);
518     libxl_domain_build_info_copy(ctx, b_info, b_info_in);
519 
520     rc = libxl__domain_build_info_setdefault(gc, b_info);
521     if (rc) goto out;
522 
523     rc = libxl__domain_need_memory_calculate(gc,
524                                    b_info,
525                                    need_memkb);
526     if (rc) goto out;
527 
528     rc = 0;
529  out:
530     libxl_domain_build_info_dispose(b_info);
531     GC_FREE;
532     return rc;
533 }
534 
libxl_domain_need_memory_0x040700(libxl_ctx * ctx,const libxl_domain_build_info * b_info_in,uint32_t * need_memkb)535 int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
536                                       const libxl_domain_build_info *b_info_in,
537                                       uint32_t *need_memkb)
538 {
539     uint64_t my_need_memkb;
540     int rc;
541 
542     rc = libxl_domain_need_memory_0x041200(ctx, b_info_in, &my_need_memkb);
543     return libxl__memkb_64to32(ctx, rc, my_need_memkb, need_memkb);
544 }
545 
libxl_get_free_memory(libxl_ctx * ctx,uint64_t * memkb)546 int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb)
547 {
548     int rc = 0;
549     libxl_physinfo info;
550     GC_INIT(ctx);
551 
552     rc = libxl_get_physinfo(ctx, &info);
553     if (rc < 0)
554         goto out;
555 
556     *memkb = (info.free_pages + info.scrub_pages) * 4;
557 
558 out:
559     GC_FREE;
560     return rc;
561 }
562 
libxl_get_free_memory_0x040700(libxl_ctx * ctx,uint32_t * memkb)563 int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
564 {
565     uint64_t my_memkb = 0;
566     int rc;
567 
568     rc = libxl_get_free_memory(ctx, &my_memkb);
569     return libxl__memkb_64to32(ctx, rc, my_memkb, memkb);
570 }
571 
libxl_wait_for_free_memory(libxl_ctx * ctx,uint32_t domid,uint64_t memory_kb,int wait_secs)572 int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid,
573                                uint64_t memory_kb, int wait_secs)
574 {
575     int rc = 0;
576     libxl_physinfo info;
577     GC_INIT(ctx);
578 
579     while (wait_secs > 0) {
580         rc = libxl_get_physinfo(ctx, &info);
581         if (rc < 0)
582             goto out;
583         if (info.free_pages * 4 >= memory_kb) {
584             rc = 0;
585             goto out;
586         }
587         wait_secs--;
588         sleep(1);
589     }
590     rc = ERROR_NOMEM;
591 
592 out:
593     GC_FREE;
594     return rc;
595 }
596 
libxl_wait_for_memory_target(libxl_ctx * ctx,uint32_t domid,int wait_secs)597 int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs)
598 {
599     int rc = 0;
600     uint64_t target_memkb = 0;
601     uint64_t current_memkb, prev_memkb;
602     libxl_dominfo info;
603 
604     rc = libxl_get_memory_target(ctx, domid, &target_memkb);
605     if (rc < 0)
606         return rc;
607 
608     libxl_dominfo_init(&info);
609     prev_memkb = UINT64_MAX;
610 
611     do {
612         sleep(2);
613 
614         libxl_dominfo_dispose(&info);
615         libxl_dominfo_init(&info);
616         rc = libxl_domain_info(ctx, &info, domid);
617         if (rc < 0)
618             goto out;
619 
620         current_memkb = info.current_memkb + info.outstanding_memkb;
621 
622         if (current_memkb > prev_memkb)
623         {
624             rc = ERROR_FAIL;
625             goto out;
626         }
627         else if (current_memkb == prev_memkb)
628             wait_secs -= 2;
629         /* if current_memkb < prev_memkb loop for free as progress has
630          * been made */
631 
632         prev_memkb = current_memkb;
633     } while (wait_secs > 0 && current_memkb > target_memkb);
634 
635     if (current_memkb <= target_memkb)
636         rc = 0;
637     else
638         rc = ERROR_FAIL;
639 
640 out:
641     libxl_dominfo_dispose(&info);
642     return rc;
643 }
644 
645 /*
646  * Local variables:
647  * mode: C
648  * c-basic-offset: 4
649  * indent-tabs-mode: nil
650  * End:
651  */
652