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