Files
xen/tools/libs/light/libxl_mem.c
T
Alejandro Vallejo cb860a95a9 tools: Modify single-domid callers of xc_domain_getinfolist()
xc_domain_getinfolist() internally relies on a sysctl that performs
a linear search for the domids. Many callers of xc_domain_getinfolist()
who require information about a precise domid are much better off calling
xc_domain_getinfo_single() instead, that will use the getdomaininfo domctl
instead and ensure the returned domid matches the requested one. The domtctl
will find the domid faster too, because that uses hashed lists.

Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Christian Lindig <christian.lindig@cloud.com>
Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>
2023-05-10 19:59:27 +01:00

660 lines
18 KiB
C

/*
* Copyright 2009-2017 Citrix Ltd and other contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; version 2.1 only. with the special
* exception on linking described in file LICENSE.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*/
#include "libxl_osdeps.h"
#include "libxl_internal.h"
#include "libxl_arch.h"
/*
* Set the maximum memory size of the domain in the hypervisor. There is no
* change of the current memory size involved. The specified memory size can
* even be above the configured maxmem size of the domain.
*/
int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t max_memkb)
{
GC_INIT(ctx);
char *mem, *endptr;
uint64_t memorykb, size;
char *dompath = libxl__xs_get_dompath(gc, domid);
int rc = 1;
libxl__flock *lock = NULL;
libxl_domain_config d_config;
libxl_domain_config_init(&d_config);
CTX_LOCK;
lock = libxl__lock_domain_userdata(gc, domid);
if (!lock) {
rc = ERROR_LOCK_FAIL;
goto out;
}
mem = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target", dompath));
if (!mem) {
LOGED(ERROR, domid, "Cannot get memory info from %s/memory/target",
dompath);
goto out;
}
memorykb = strtoull(mem, &endptr, 10);
if (*endptr != '\0') {
LOGED(ERROR, domid, "Invalid memory %s from %s/memory/target\n",
mem, dompath);
goto out;
}
if (max_memkb < memorykb) {
LOGED(ERROR, domid,
"memory_static_max must be greater than or or equal to memory_dynamic_max");
goto out;
}
rc = libxl__get_domain_configuration(gc, domid, &d_config);
if (rc < 0) {
LOGE(ERROR, "unable to retrieve domain configuration");
goto out;
}
rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
if (rc < 0) {
LOGE(ERROR, "Couldn't get arch extra constant memory size");
goto out;
}
rc = xc_domain_setmaxmem(ctx->xch, domid, max_memkb + size);
if (rc != 0) {
LOGED(ERROR, domid,
"xc_domain_setmaxmem domid=%d memkb=%"PRIu64" failed ""rc=%d\n",
domid, max_memkb + size, rc);
goto out;
}
rc = libxl__xs_printf(gc, XBT_NULL,
GCSPRINTF("%s/memory/static-max", dompath),
"%"PRIu64, max_memkb);
if (rc != 0) {
LOGED(ERROR, domid, "Couldn't set %s/memory/static-max, rc=%d\n",
dompath, rc);
goto out;
}
rc = 0;
out:
libxl_domain_config_dispose(&d_config);
if (lock) libxl__unlock_file(lock);
CTX_UNLOCK;
GC_FREE;
return rc;
}
static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint64_t *target_memkb,
uint64_t *max_memkb)
{
int rc;
libxl_dominfo info;
libxl_physinfo physinfo;
char *target = NULL, *staticmax = NULL, *endptr = NULL;
char *target_path = "/local/domain/0/memory/target";
char *max_path = "/local/domain/0/memory/static-max";
xs_transaction_t t;
libxl_ctx *ctx = libxl__gc_owner(gc);
libxl_dominfo_init(&info);
retry_transaction:
t = xs_transaction_start(ctx->xsh);
target = libxl__xs_read(gc, t, target_path);
staticmax = libxl__xs_read(gc, t, max_path);
if (target && staticmax) {
rc = 0;
goto out;
}
if (target) {
*target_memkb = strtoull(target, &endptr, 10);
if (*endptr != '\0') {
LOGED(ERROR, 0, "Invalid memory target %s from %s\n", target,
target_path);
rc = ERROR_FAIL;
goto out;
}
}
if (staticmax) {
*max_memkb = strtoull(staticmax, &endptr, 10);
if (*endptr != '\0') {
LOGED(ERROR, 0, "Invalid memory static-max %s from %s\n",
staticmax,
max_path);
rc = ERROR_FAIL;
goto out;
}
}
libxl_dominfo_dispose(&info);
libxl_dominfo_init(&info);
rc = libxl_domain_info(ctx, &info, 0);
if (rc < 0)
goto out;
rc = libxl_get_physinfo(ctx, &physinfo);
if (rc < 0)
goto out;
if (target == NULL) {
libxl__xs_printf(gc, t, target_path, "%"PRIu64, info.current_memkb);
*target_memkb = info.current_memkb;
}
if (staticmax == NULL) {
libxl__xs_printf(gc, t, max_path, "%"PRIu64, info.max_memkb);
*max_memkb = info.max_memkb;
}
rc = 0;
out:
if (!xs_transaction_end(ctx->xsh, t, 0)) {
if (errno == EAGAIN)
goto retry_transaction;
else
rc = ERROR_FAIL;
}
libxl_dominfo_dispose(&info);
return rc;
}
int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid,
int64_t target_memkb, int relative, int enforce)
{
GC_INIT(ctx);
int rc, r, lrc, abort_transaction = 0;
uint64_t memorykb, size;
uint64_t videoram = 0;
uint64_t current_target_memkb = 0, new_target_memkb = 0;
uint64_t current_max_memkb = 0;
char *memmax, *endptr, *videoram_s = NULL, *target = NULL;
char *dompath = libxl__xs_get_dompath(gc, domid);
xc_domaininfo_t info;
libxl_dominfo ptr;
char *uuid;
xs_transaction_t t;
libxl__flock *lock;
libxl_domain_config d_config;
libxl_domain_config_init(&d_config);
CTX_LOCK;
lock = libxl__lock_domain_userdata(gc, domid);
if (!lock) {
rc = ERROR_LOCK_FAIL;
goto out_no_transaction;
}
rc = libxl__get_domain_configuration(gc, domid, &d_config);
if (rc < 0) {
LOGE(ERROR, "unable to retrieve domain configuration");
goto out_no_transaction;
}
rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size);
if (rc < 0) {
LOGE(ERROR, "Couldn't get arch extra constant memory size");
goto out_no_transaction;
}
retry_transaction:
t = xs_transaction_start(ctx->xsh);
target = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/target", dompath));
if (!target && !domid) {
if (!xs_transaction_end(ctx->xsh, t, 1)) {
rc = ERROR_FAIL;
goto out_no_transaction;
}
lrc = libxl__fill_dom0_memory_info(gc, &current_target_memkb,
&current_max_memkb);
if (lrc < 0) { rc = ERROR_FAIL; goto out_no_transaction; }
goto retry_transaction;
} else if (!target) {
LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
dompath);
abort_transaction = 1;
rc = ERROR_FAIL;
goto out;
} else {
current_target_memkb = strtoull(target, &endptr, 10);
if (*endptr != '\0') {
LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
target, dompath);
abort_transaction = 1;
rc = ERROR_FAIL;
goto out;
}
}
memmax = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/static-max", dompath));
if (!memmax) {
LOGED(ERROR, domid, "Cannot get memory info from %s/memory/static-max",
dompath);
abort_transaction = 1;
rc = ERROR_FAIL;
goto out;
}
memorykb = strtoull(memmax, &endptr, 10);
if (*endptr != '\0') {
LOGED(ERROR, domid, "Invalid max memory %s from %s/memory/static-max\n",
memmax, dompath);
abort_transaction = 1;
rc = ERROR_FAIL;
goto out;
}
videoram_s = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/videoram",
dompath));
videoram = videoram_s ? atoi(videoram_s) : 0;
if (relative) {
if (target_memkb < 0 && llabs(target_memkb) > current_target_memkb)
new_target_memkb = 0;
else
new_target_memkb = current_target_memkb + target_memkb;
} else
new_target_memkb = target_memkb - videoram;
if (new_target_memkb > memorykb) {
LOGD(ERROR, domid,
"memory_dynamic_max must be less than or equal to"
" memory_static_max\n");
abort_transaction = 1;
rc = ERROR_INVAL;
goto out;
}
if (!domid && new_target_memkb < LIBXL_MIN_DOM0_MEM) {
LOGD(ERROR, domid,
"New target %"PRIu64" for dom0 is below the minimum threshold",
new_target_memkb);
abort_transaction = 1;
rc = ERROR_INVAL;
goto out;
}
if (enforce) {
memorykb = new_target_memkb + videoram;
r = xc_domain_setmaxmem(ctx->xch, domid, memorykb + size);
if (r != 0) {
LOGED(ERROR, domid,
"xc_domain_setmaxmem memkb=%"PRIu64" failed ""rc=%d\n",
memorykb + size,
r);
abort_transaction = 1;
rc = ERROR_FAIL;
goto out;
}
}
if (d_config.c_info.type != LIBXL_DOMAIN_TYPE_PV) {
r = xc_domain_set_pod_target(ctx->xch, domid,
(new_target_memkb + size) / 4, NULL, NULL, NULL);
if (r != 0) {
LOGED(ERROR, domid,
"xc_domain_set_pod_target memkb=%"PRIu64" failed rc=%d\n",
(new_target_memkb + size) / 4,
r);
abort_transaction = 1;
rc = ERROR_FAIL;
goto out;
}
}
libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath),
"%"PRIu64, new_target_memkb);
r = xc_domain_getinfo_single(ctx->xch, domid, &info);
if (r < 0) {
abort_transaction = 1;
rc = ERROR_FAIL;
goto out;
}
libxl_dominfo_init(&ptr);
libxl__xcinfo2xlinfo(ctx, &info, &ptr);
uuid = libxl__uuid2string(gc, ptr.uuid);
libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid),
"%"PRIu64, new_target_memkb / 1024);
libxl_dominfo_dispose(&ptr);
rc = 0;
out:
if (!xs_transaction_end(ctx->xsh, t, abort_transaction)
&& !abort_transaction)
if (errno == EAGAIN)
goto retry_transaction;
out_no_transaction:
libxl_domain_config_dispose(&d_config);
if (lock) libxl__unlock_file(lock);
CTX_UNLOCK;
GC_FREE;
return rc;
}
/* out_target_memkb and out_max_memkb can be NULL */
int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
uint64_t *out_target_memkb,
uint64_t *out_max_memkb)
{
int rc;
char *target = NULL, *static_max = NULL, *endptr = NULL;
char *dompath = libxl__xs_get_dompath(gc, domid);
uint64_t target_memkb, max_memkb;
target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target",
dompath));
static_max = libxl__xs_read(gc, XBT_NULL,
GCSPRINTF("%s/memory/static-max", dompath));
rc = ERROR_FAIL;
if ((!target || !static_max) && !domid) {
rc = libxl__fill_dom0_memory_info(gc, &target_memkb,
&max_memkb);
if (rc < 0)
goto out;
} else if (!target) {
LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target",
dompath);
goto out;
} else if (!static_max) {
LOGED(ERROR, domid,
"Cannot get target memory info from %s/memory/static-max",
dompath);
goto out;
} else {
target_memkb = strtoull(target, &endptr, 10);
if (*endptr != '\0') {
LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n",
target, dompath);
goto out;
}
max_memkb = strtoull(static_max, &endptr, 10);
if (*endptr != '\0') {
LOGED(ERROR, domid,
"Invalid memory target %s from %s/memory/static-max\n",
static_max,
dompath);
goto out;
}
}
if (out_target_memkb)
*out_target_memkb = target_memkb;
if (out_max_memkb)
*out_max_memkb = max_memkb;
rc = 0;
out:
return rc;
}
static int libxl__memkb_64to32(libxl_ctx *ctx, int rc,
uint64_t val64, uint32_t *ptr32)
{
GC_INIT(ctx);
if (rc)
goto out;
*ptr32 = val64;
if (*ptr32 == val64)
goto out;
LOGE(ERROR, "memory size %"PRIu64" too large for 32 bit value\n", val64);
rc = ERROR_FAIL;
out:
GC_FREE;
return rc;
}
int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
uint64_t *out_target)
{
GC_INIT(ctx);
int rc;
rc = libxl__get_memory_target(gc, domid, out_target, NULL);
GC_FREE;
return rc;
}
int libxl_get_memory_target_0x040700(
libxl_ctx *ctx, uint32_t domid, uint32_t *out_target)
{
uint64_t my_out_target;
int rc;
rc = libxl_get_memory_target(ctx, domid, &my_out_target);
return libxl__memkb_64to32(ctx, rc, my_out_target, out_target);
}
int libxl__domain_need_memory_calculate(libxl__gc *gc,
libxl_domain_build_info *b_info,
uint64_t *need_memkb)
{
int rc;
*need_memkb = b_info->target_memkb;
*need_memkb += b_info->shadow_memkb + b_info->iommu_memkb;
switch (b_info->type) {
case LIBXL_DOMAIN_TYPE_PVH:
case LIBXL_DOMAIN_TYPE_HVM:
*need_memkb += LIBXL_HVM_EXTRA_MEMORY;
if (libxl_defbool_val(b_info->device_model_stubdomain)) {
*need_memkb += b_info->stubdomain_memkb;
*need_memkb += b_info->video_memkb;
}
break;
case LIBXL_DOMAIN_TYPE_PV:
*need_memkb += LIBXL_PV_EXTRA_MEMORY;
break;
default:
rc = ERROR_INVAL;
goto out;
}
if (*need_memkb % (2 * 1024))
*need_memkb += (2 * 1024) - (*need_memkb % (2 * 1024));
rc = 0;
out:
return rc;
}
int libxl_domain_need_memory(libxl_ctx *ctx,
libxl_domain_config *d_config,
uint32_t domid_for_logging,
uint64_t *need_memkb)
{
GC_INIT(ctx);
int rc;
ctx->libxl_domain_need_memory_called = 1;
rc = libxl__domain_config_setdefault(gc,
d_config,
domid_for_logging);
if (rc) goto out;
rc = libxl__domain_need_memory_calculate(gc,
&d_config->b_info,
need_memkb);
if (rc) goto out;
rc = 0;
out:
GC_FREE;
return rc;
}
int libxl_domain_need_memory_0x041200(libxl_ctx *ctx,
const libxl_domain_build_info *b_info_in,
uint64_t *need_memkb)
{
GC_INIT(ctx);
int rc;
ctx->libxl_domain_need_memory_0x041200_called = 1;
libxl_domain_build_info b_info[1];
libxl_domain_build_info_init(b_info);
libxl_domain_build_info_copy(ctx, b_info, b_info_in);
rc = libxl__domain_build_info_setdefault(gc, b_info);
if (rc) goto out;
rc = libxl__domain_need_memory_calculate(gc,
b_info,
need_memkb);
if (rc) goto out;
rc = 0;
out:
libxl_domain_build_info_dispose(b_info);
GC_FREE;
return rc;
}
int libxl_domain_need_memory_0x040700(libxl_ctx *ctx,
const libxl_domain_build_info *b_info_in,
uint32_t *need_memkb)
{
uint64_t my_need_memkb;
int rc;
rc = libxl_domain_need_memory_0x041200(ctx, b_info_in, &my_need_memkb);
return libxl__memkb_64to32(ctx, rc, my_need_memkb, need_memkb);
}
int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb)
{
int rc = 0;
libxl_physinfo info;
GC_INIT(ctx);
rc = libxl_get_physinfo(ctx, &info);
if (rc < 0)
goto out;
*memkb = (info.free_pages + info.scrub_pages) * 4;
out:
GC_FREE;
return rc;
}
int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb)
{
uint64_t my_memkb = 0;
int rc;
rc = libxl_get_free_memory(ctx, &my_memkb);
return libxl__memkb_64to32(ctx, rc, my_memkb, memkb);
}
int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid,
uint64_t memory_kb, int wait_secs)
{
int rc = 0;
libxl_physinfo info;
GC_INIT(ctx);
while (wait_secs > 0) {
rc = libxl_get_physinfo(ctx, &info);
if (rc < 0)
goto out;
if (info.free_pages * 4 >= memory_kb) {
rc = 0;
goto out;
}
wait_secs--;
sleep(1);
}
rc = ERROR_NOMEM;
out:
GC_FREE;
return rc;
}
int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs)
{
int rc = 0;
uint64_t target_memkb = 0;
uint64_t current_memkb, prev_memkb;
libxl_dominfo info;
rc = libxl_get_memory_target(ctx, domid, &target_memkb);
if (rc < 0)
return rc;
libxl_dominfo_init(&info);
prev_memkb = UINT64_MAX;
do {
sleep(2);
libxl_dominfo_dispose(&info);
libxl_dominfo_init(&info);
rc = libxl_domain_info(ctx, &info, domid);
if (rc < 0)
goto out;
current_memkb = info.current_memkb + info.outstanding_memkb;
if (current_memkb > prev_memkb)
{
rc = ERROR_FAIL;
goto out;
}
else if (current_memkb == prev_memkb)
wait_secs -= 2;
/* if current_memkb < prev_memkb loop for free as progress has
* been made */
prev_memkb = current_memkb;
} while (wait_secs > 0 && current_memkb > target_memkb);
if (current_memkb <= target_memkb)
rc = 0;
else
rc = ERROR_FAIL;
out:
libxl_dominfo_dispose(&info);
return rc;
}
/*
* Local variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/