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__domain_userdata_lock *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_domain_userdata(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__domain_userdata_lock *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     r = xc_domain_set_pod_target(ctx->xch, domid,
302             (new_target_memkb + size) / 4, NULL, NULL, NULL);
303     if (r != 0) {
304         LOGED(ERROR, domid,
305               "xc_domain_set_pod_target memkb=%"PRIu64" failed rc=%d\n",
306               (new_target_memkb + size) / 4,
307               r);
308         abort_transaction = 1;
309         rc = ERROR_FAIL;
310         goto out;
311     }
312 
313     libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath),
314                      "%"PRIu64, new_target_memkb);
315 
316     r = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
317     if (r != 1 || info.domain != domid) {
318         abort_transaction = 1;
319         rc = ERROR_FAIL;
320         goto out;
321     }
322 
323     libxl_dominfo_init(&ptr);
324     libxl__xcinfo2xlinfo(ctx, &info, &ptr);
325     uuid = libxl__uuid2string(gc, ptr.uuid);
326     libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid),
327                      "%"PRIu64, new_target_memkb / 1024);
328     libxl_dominfo_dispose(&ptr);
329 
330     rc = 0;
331 out:
332     if (!xs_transaction_end(ctx->xsh, t, abort_transaction)
333         && !abort_transaction)
334         if (errno == EAGAIN)
335             goto retry_transaction;
336 
337 out_no_transaction:
338     libxl_domain_config_dispose(&d_config);
339     if (lock) libxl__unlock_domain_userdata(lock);
340     CTX_UNLOCK;
341     GC_FREE;
342     return rc;
343 }
344 
345 /* 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)346 int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
347                              uint64_t *out_target_memkb,
348                              uint64_t *out_max_memkb)
349 {
350     int rc;
351     char *target = NULL, *static_max = NULL, *endptr = NULL;
352     char *dompath = libxl__xs_get_dompath(gc, domid);
353     uint64_t target_memkb, max_memkb;
354 
355     target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target",
356                                                     dompath));
357     static_max = libxl__xs_read(gc, XBT_NULL,
358                     GCSPRINTF("%s/memory/static-max", dompath));
359 
360     rc = ERROR_FAIL;
361     if ((!target || !static_max) && !domid) {
362         rc = libxl__fill_dom0_memory_info(gc, &target_memkb,
363                                           &max_memkb);
364         if (rc < 0)
365             goto out;
366     } else if (!target) {
367         LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
368               dompath);
369         goto out;
370     } else if (!static_max) {
371         LOGED(ERROR, domid,
372               "Cannot get target memory info from %s/memory/static-max",
373                dompath);
374         goto out;
375     } else {
376         target_memkb = strtoull(target, &endptr, 10);
377         if (*endptr != '\0') {
378             LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
379                   target, dompath);
380             goto out;
381         }
382         max_memkb = strtoull(static_max, &endptr, 10);
383         if (*endptr != '\0') {
384             LOGED(ERROR, domid,
385                   "Invalid memory target %s from %s/memory/static-max\n",
386                   static_max,
387                   dompath);
388             goto out;
389         }
390 
391     }
392 
393     if (out_target_memkb)
394         *out_target_memkb = target_memkb;
395 
396     if (out_max_memkb)
397         *out_max_memkb = max_memkb;
398 
399     rc = 0;
400 
401 out:
402     return rc;
403 }
404 
libxl__memkb_64to32(libxl_ctx * ctx,int rc,uint64_t val64,uint32_t * ptr32)405 static int libxl__memkb_64to32(libxl_ctx *ctx, int rc,
406                                uint64_t val64, uint32_t *ptr32)
407 {
408     GC_INIT(ctx);
409 
410     if (rc)
411         goto out;
412 
413     *ptr32 = val64;
414     if (*ptr32 == val64)
415         goto out;
416 
417     LOGE(ERROR, "memory size %"PRIu64" too large for 32 bit value\n", val64);
418     rc = ERROR_FAIL;
419 
420 out:
421     GC_FREE;
422     return rc;
423 }
424 
libxl_get_memory_target(libxl_ctx * ctx,uint32_t domid,uint64_t * out_target)425 int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
426                             uint64_t *out_target)
427 {
428     GC_INIT(ctx);
429     int rc;
430 
431     rc = libxl__get_memory_target(gc, domid, out_target, NULL);
432 
433     GC_FREE;
434     return rc;
435 }
436 
libxl_get_memory_target_0x040700(libxl_ctx * ctx,uint32_t domid,uint32_t * out_target)437 int libxl_get_memory_target_0x040700(
438     libxl_ctx *ctx, uint32_t domid, uint32_t *out_target)
439 {
440     uint64_t my_out_target;
441     int rc;
442 
443     rc = libxl_get_memory_target(ctx, domid, &my_out_target);
444     return libxl__memkb_64to32(ctx, rc, my_out_target, out_target);
445 }
446 
libxl_domain_need_memory(libxl_ctx * ctx,const libxl_domain_build_info * b_info_in,uint64_t * need_memkb)447 int libxl_domain_need_memory(libxl_ctx *ctx,
448                              const libxl_domain_build_info *b_info_in,
449                              uint64_t *need_memkb)
450 {
451     GC_INIT(ctx);
452     libxl_domain_build_info b_info[1];
453     int rc;
454 
455     libxl_domain_build_info_init(b_info);
456     libxl_domain_build_info_copy(ctx, b_info, b_info_in);
457 
458     rc = libxl__domain_build_info_setdefault(gc, b_info);
459     if (rc) goto out;
460 
461     *need_memkb = b_info->target_memkb;
462     switch (b_info->type) {
463     case LIBXL_DOMAIN_TYPE_PVH:
464     case LIBXL_DOMAIN_TYPE_HVM:
465         *need_memkb += b_info->shadow_memkb + LIBXL_HVM_EXTRA_MEMORY;
466         if (libxl_defbool_val(b_info->device_model_stubdomain))
467             *need_memkb += 32 * 1024;
468         break;
469     case LIBXL_DOMAIN_TYPE_PV:
470         *need_memkb += b_info->shadow_memkb + LIBXL_PV_EXTRA_MEMORY;
471         break;
472     default:
473         rc = ERROR_INVAL;
474         goto out;
475     }
476     if (*need_memkb % (2 * 1024))
477         *need_memkb += (2 * 1024) - (*need_memkb % (2 * 1024));
478     rc = 0;
479 out:
480     GC_FREE;
481     libxl_domain_build_info_dispose(b_info);
482     return rc;
483 
484 }
485 
libxl_domain_need_memory_0x040700(libxl_ctx * ctx,const libxl_domain_build_info * b_info_in,uint32_t * need_memkb)486 int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
487                                       const libxl_domain_build_info *b_info_in,
488                                       uint32_t *need_memkb)
489 {
490     uint64_t my_need_memkb;
491     int rc;
492 
493     rc = libxl_domain_need_memory(ctx, b_info_in, &my_need_memkb);
494     return libxl__memkb_64to32(ctx, rc, my_need_memkb, need_memkb);
495 }
496 
libxl_get_free_memory(libxl_ctx * ctx,uint64_t * memkb)497 int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb)
498 {
499     int rc = 0;
500     libxl_physinfo info;
501     GC_INIT(ctx);
502 
503     rc = libxl_get_physinfo(ctx, &info);
504     if (rc < 0)
505         goto out;
506 
507     *memkb = (info.free_pages + info.scrub_pages) * 4;
508 
509 out:
510     GC_FREE;
511     return rc;
512 }
513 
libxl_get_free_memory_0x040700(libxl_ctx * ctx,uint32_t * memkb)514 int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
515 {
516     uint64_t my_memkb;
517     int rc;
518 
519     rc = libxl_get_free_memory(ctx, &my_memkb);
520     return libxl__memkb_64to32(ctx, rc, my_memkb, memkb);
521 }
522 
libxl_wait_for_free_memory(libxl_ctx * ctx,uint32_t domid,uint64_t memory_kb,int wait_secs)523 int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid,
524                                uint64_t memory_kb, int wait_secs)
525 {
526     int rc = 0;
527     libxl_physinfo info;
528     GC_INIT(ctx);
529 
530     while (wait_secs > 0) {
531         rc = libxl_get_physinfo(ctx, &info);
532         if (rc < 0)
533             goto out;
534         if (info.free_pages * 4 >= memory_kb) {
535             rc = 0;
536             goto out;
537         }
538         wait_secs--;
539         sleep(1);
540     }
541     rc = ERROR_NOMEM;
542 
543 out:
544     GC_FREE;
545     return rc;
546 }
547 
libxl_wait_for_memory_target(libxl_ctx * ctx,uint32_t domid,int wait_secs)548 int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs)
549 {
550     int rc = 0;
551     uint64_t target_memkb = 0;
552     uint64_t current_memkb, prev_memkb;
553     libxl_dominfo info;
554 
555     rc = libxl_get_memory_target(ctx, domid, &target_memkb);
556     if (rc < 0)
557         return rc;
558 
559     libxl_dominfo_init(&info);
560     prev_memkb = UINT64_MAX;
561 
562     do {
563         sleep(2);
564 
565         libxl_dominfo_dispose(&info);
566         libxl_dominfo_init(&info);
567         rc = libxl_domain_info(ctx, &info, domid);
568         if (rc < 0)
569             goto out;
570 
571         current_memkb = info.current_memkb + info.outstanding_memkb;
572 
573         if (current_memkb > prev_memkb)
574         {
575             rc = ERROR_FAIL;
576             goto out;
577         }
578         else if (current_memkb == prev_memkb)
579             wait_secs -= 2;
580         /* if current_memkb < prev_memkb loop for free as progress has
581          * been made */
582 
583         prev_memkb = current_memkb;
584     } while (wait_secs > 0 && current_memkb > target_memkb);
585 
586     if (current_memkb <= target_memkb)
587         rc = 0;
588     else
589         rc = ERROR_FAIL;
590 
591 out:
592     libxl_dominfo_dispose(&info);
593     return rc;
594 }
595 
596 /*
597  * Local variables:
598  * mode: C
599  * c-basic-offset: 4
600  * indent-tabs-mode: nil
601  * End:
602  */
603