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, ¤t_target_memkb,
230 ¤t_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