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