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, ¤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 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