Files
xen/tools/libs/light/libxl_utils.c
T
Oleksandr Tyshchenko 66dd1c62b2 libxl: Add support for Virtio disk configuration
This patch adds basic support for configuring and assisting virtio-mmio
based virtio-disk backend (emulator) which is intended to run out of
Qemu and could be run in any domain.
Although the Virtio block device is quite different from traditional
Xen PV block device (vbd) from the toolstack's point of view:
 - as the frontend is virtio-blk which is not a Xenbus driver, nothing
   written to Xenstore are fetched by the frontend currently ("vdev"
   is not passed to the frontend). But this might need to be revised
   in future, so frontend data might be written to Xenstore in order to
   support hotplugging virtio devices or passing the backend domain id
   on arch where the device-tree is not available.
 - the ring-ref/event-channel are not used for the backend<->frontend
   communication, the proposed IPC for Virtio is IOREQ/DM
it is still a "block device" and ought to be integrated in existing
"disk" handling. So, re-use (and adapt) "disk" parsing/configuration
logic to deal with Virtio devices as well.

For the immediate purpose and an ability to extend that support for
other use-cases in future (Qemu, virtio-pci, etc) perform the following
actions:
- Add new disk backend type (LIBXL_DISK_BACKEND_STANDALONE) and reflect
  that in the configuration
- Introduce new disk "specification" and "transport" fields to struct
  libxl_device_disk. Both are written to the Xenstore. The transport
  field is only used for the specification "virtio" and it assumes
  only "mmio" value for now.
- Introduce new "specification" option with "xen" communication
  protocol being default value.
- Add new device kind (LIBXL__DEVICE_KIND_VIRTIO_DISK) as current
  one (LIBXL__DEVICE_KIND_VBD) doesn't fit into Virtio disk model

An example of domain configuration for Virtio disk:
disk = [ 'phy:/dev/mmcblk0p3, xvda1, backendtype=standalone, specification=virtio']

Nothing has changed for default Xen disk configuration.

Please note, this patch is not enough for virtio-disk to work
on Xen (Arm), as for every Virtio device (including disk) we need
to allocate Virtio MMIO params (IRQ and memory region) and pass
them to the backend, also update Guest device-tree. The subsequent
patch will add these missing bits. For the current patch,
the default "irq" and "base" are just written to the Xenstore.
This is not an ideal splitting, but this way we avoid breaking
the bisectability.

Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>
Acked-by: George Dunlap <george.dunlap@citrix.com>
Tested-by: Jiamei Xie <jiamei.xie@arm.com>
2022-07-28 19:25:43 +01:00

1268 lines
32 KiB
C

/*
* Copyright (C) 2009 Citrix Ltd.
* Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
*
* 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" /* must come before any other headers */
#include <ctype.h>
#include "libxl_internal.h"
#ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE
const
#endif
char *libxl_basename(const char *name)
{
const char *filename;
if (name == NULL)
return strdup(".");
if (name[0] == '\0')
return strdup(".");
filename = strrchr(name, '/');
if (filename)
return strdup(filename+1);
return strdup(name);
}
unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus)
{
return libxl__get_required_paging_memory(maxmem_kb, smp_cpus,
LIBXL_DOMAIN_TYPE_INVALID, false);
}
char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid)
{
unsigned int len;
char path[strlen("/local/domain") + 12];
char *s;
snprintf(path, sizeof(path), "/local/domain/%d/name", domid);
s = xs_read(ctx->xsh, XBT_NULL, path, &len);
return s;
}
char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid)
{
char *s = libxl_domid_to_name(CTX, domid);
libxl__ptr_add(gc, s);
return s;
}
int libxl_name_to_domid(libxl_ctx *ctx, const char *name,
uint32_t *domid)
{
int i, nb_domains;
char *domname;
libxl_dominfo *dominfo;
int ret = ERROR_INVAL;
dominfo = libxl_list_domain(ctx, &nb_domains);
if (!dominfo)
return ERROR_NOMEM;
for (i = 0; i < nb_domains; i++) {
domname = libxl_domid_to_name(ctx, dominfo[i].domid);
if (!domname)
continue;
if (strcmp(domname, name) == 0) {
*domid = dominfo[i].domid;
ret = 0;
free(domname);
break;
}
free(domname);
}
libxl_dominfo_list_free(dominfo, nb_domains);
return ret;
}
int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name,
uint32_t *domid)
{
int i, rv;
for (i=0; name[i]; i++) {
if (!CTYPE(isdigit, name[i])) {
goto nondigit_found;
}
}
*domid = strtoul(name, NULL, 10);
return 0;
nondigit_found:
/* this could also check for uuids */
rv = libxl_name_to_domid(ctx, name, domid);
return rv;
}
static int qualifier_to_id(const char *p, uint32_t *id_r)
{
int i, alldigit;
alldigit = 1;
for (i = 0; p[i]; i++) {
if (!isdigit((uint8_t)p[i])) {
alldigit = 0;
break;
}
}
if (i > 0 && alldigit) {
*id_r = strtoul(p, NULL, 10);
return 0;
} else {
/* check here if it's a uuid and do proper conversion */
}
return 1;
}
int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p,
uint32_t *poolid_r,
int *was_name_r)
{
int was_name;
was_name = qualifier_to_id(p, poolid_r);
if (was_name_r) *was_name_r = was_name;
return was_name ? libxl_name_to_cpupoolid(ctx, p, poolid_r) : 0;
}
char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid)
{
unsigned int len;
char path[strlen("/local/pool") + 12];
char *s;
snprintf(path, sizeof(path), "/local/pool/%d/name", poolid);
s = xs_read(ctx->xsh, XBT_NULL, path, &len);
return s;
}
/* This is a bit horrid but without xs_exists it seems like the only way. */
int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid)
{
int ret;
char *s = libxl_cpupoolid_to_name(ctx, poolid);
ret = (s != NULL);
free(s);
return ret;
}
char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid)
{
char *s = libxl_cpupoolid_to_name(CTX, poolid);
libxl__ptr_add(gc, s);
return s;
}
int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name,
uint32_t *poolid)
{
int i, nb_pools;
char *poolname;
libxl_cpupoolinfo *poolinfo;
int ret = ERROR_INVAL;
poolinfo = libxl_list_cpupool(ctx, &nb_pools);
if (!poolinfo)
return ERROR_NOMEM;
for (i = 0; i < nb_pools; i++) {
if (ret && ((poolname = libxl_cpupoolid_to_name(ctx,
poolinfo[i].poolid)) != NULL)) {
if (strcmp(poolname, name) == 0) {
*poolid = poolinfo[i].poolid;
ret = 0;
}
free(poolname);
}
}
libxl_cpupoolinfo_list_free(poolinfo, nb_pools);
return ret;
}
int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid)
{
GC_INIT(ctx);
char * stubdom_id_s;
int ret;
stubdom_id_s = libxl__xs_read(gc, XBT_NULL,
GCSPRINTF("%s/image/device-model-domid",
libxl__xs_get_dompath(gc, guest_domid)));
if (stubdom_id_s)
ret = atoi(stubdom_id_s);
else
ret = 0;
GC_FREE;
return ret;
}
int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid)
{
GC_INIT(ctx);
char *target, *endptr;
uint32_t value;
int ret = 0;
target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/target",
libxl__xs_get_dompath(gc, domid)));
if (!target)
goto out;
value = strtol(target, &endptr, 10);
if (*endptr != '\0')
goto out;
if (target_domid)
*target_domid = value;
ret = 1;
out:
GC_FREE;
return ret;
}
static int logrename(libxl__gc *gc, const char *old, const char *new)
{
int r;
r = rename(old, new);
if (r) {
if (errno == ENOENT) return 0; /* ok */
LOGE(ERROR, "failed to rotate logfile - "
"could not rename %s to %s", old, new);
return ERROR_FAIL;
}
return 0;
}
int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name)
{
GC_INIT(ctx);
struct stat stat_buf;
char *logfile, *logfile_new;
int i, rc;
logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
if (stat(logfile, &stat_buf) == 0) {
/* file exists, rotate */
logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.10", name);
unlink(logfile);
for (i = 9; i > 0; i--) {
logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i);
logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i + 1);
rc = logrename(gc, logfile, logfile_new);
if (rc)
goto out;
}
logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name);
logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.1", name);
rc = logrename(gc, logfile, logfile_new);
if (rc)
goto out;
} else {
if (errno != ENOENT)
LOGE(WARN, "problem checking existence of logfile %s, "
"which might have needed to be rotated",
name);
}
*full_name = strdup(logfile);
rc = 0;
out:
GC_FREE;
return rc;
}
int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend)
{
char *p;
int rc = 0;
if (!strcmp(s, "phy")) {
*backend = LIBXL_DISK_BACKEND_PHY;
} else if (!strcmp(s, "file")) {
*backend = LIBXL_DISK_BACKEND_TAP;
} else if (!strcmp(s, "qdisk")) {
*backend = LIBXL_DISK_BACKEND_QDISK;
} else if (!strcmp(s, "standalone")) {
*backend = LIBXL_DISK_BACKEND_STANDALONE;
} else if (!strcmp(s, "tap")) {
p = strchr(s, ':');
if (!p) {
rc = ERROR_INVAL;
goto out;
}
p++;
if (!strcmp(p, "vhd")) {
*backend = LIBXL_DISK_BACKEND_TAP;
} else if (!strcmp(p, "qcow")) {
*backend = LIBXL_DISK_BACKEND_QDISK;
} else if (!strcmp(p, "qcow2")) {
*backend = LIBXL_DISK_BACKEND_QDISK;
} else if (!strcmp(p, "qed")) {
*backend = LIBXL_DISK_BACKEND_QDISK;
}
}
out:
return rc;
}
int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
void **data_r, int *datalen_r) {
GC_INIT(ctx);
FILE *f = 0;
uint8_t *data = 0;
int datalen = 0;
int e;
struct stat stab;
ssize_t rs;
f = fopen(filename, "r");
if (!f) {
if (errno == ENOENT) return ENOENT;
LOGE(ERROR, "failed to open %s", filename);
goto xe;
}
if (fstat(fileno(f), &stab)) {
LOGE(ERROR, "failed to fstat %s", filename);
goto xe;
}
if (!S_ISREG(stab.st_mode)) {
LOGE(ERROR, "%s is not a plain file", filename);
errno = ENOTTY;
goto xe;
}
if (stab.st_size > INT_MAX) {
LOG(ERROR, "file %s is far too large", filename);
errno = EFBIG;
goto xe;
}
datalen = stab.st_size;
if (stab.st_size && data_r) {
data = malloc(datalen);
if (!data) goto xe;
rs = fread(data, 1, datalen, f);
if (rs != datalen) {
if (ferror(f))
LOGE(ERROR, "failed to read %s", filename);
else if (feof(f))
LOG(ERROR, "%s changed size while we were reading it",
filename);
else
abort();
goto xe;
}
}
if (fclose(f)) {
f = 0;
LOGE(ERROR, "failed to close %s", filename);
goto xe;
}
if (data_r) *data_r = data;
if (datalen_r) *datalen_r = datalen;
GC_FREE;
return 0;
xe:
GC_FREE;
e = errno;
assert(e != ENOENT);
if (f) fclose(f);
free(data);
return e;
}
int libxl__read_sysfs_file_contents(libxl__gc *gc, const char *filename,
void **data_r, int *datalen_r)
{
FILE *f = 0;
uint8_t *data = 0;
int datalen = 0;
int e;
struct stat stab;
ssize_t rs;
f = fopen(filename, "r");
if (!f) {
if (errno == ENOENT) return ENOENT;
LOGE(ERROR, "failed to open %s", filename);
goto xe;
}
if (fstat(fileno(f), &stab)) {
LOGE(ERROR, "failed to fstat %s", filename);
goto xe;
}
if (!S_ISREG(stab.st_mode)) {
LOGE(ERROR, "%s is not a plain file", filename);
errno = ENOTTY;
goto xe;
}
if (stab.st_size > INT_MAX) {
LOG(ERROR, "file %s is far too large", filename);
errno = EFBIG;
goto xe;
}
datalen = stab.st_size;
if (stab.st_size && data_r) {
data = libxl__malloc(gc, datalen);
/* For sysfs file, datalen is always PAGE_SIZE. 'read'
* will return the number of bytes of the actual content,
* rs <= datalen is expected.
*/
rs = fread(data, 1, datalen, f);
if (rs < datalen) {
if (ferror(f)) {
LOGE(ERROR, "failed to read %s", filename);
goto xe;
}
datalen = rs;
data = libxl__realloc(gc, data, datalen);
}
}
if (fclose(f)) {
f = 0;
LOGE(ERROR, "failed to close %s", filename);
goto xe;
}
if (data_r) *data_r = data;
if (datalen_r) *datalen_r = datalen;
return 0;
xe:
e = errno;
assert(e != ENOENT);
if (f) fclose(f);
return e;
}
#define READ_WRITE_EXACTLY(rw, zero_is_eof, constdata) \
\
int libxl_##rw##_exactly(libxl_ctx *ctx, int fd, \
constdata void *data, ssize_t sz, \
const char *source, const char *what) { \
ssize_t got; \
GC_INIT(ctx); \
\
while (sz > 0) { \
got = rw(fd, data, sz); \
if (got == -1) { \
if (errno == EINTR) continue; \
if (!ctx) { GC_FREE; return errno; } \
LOGE(ERROR, "failed to "#rw" %s%s%s", \
what ? what : "", what ? " from " : "", source); \
GC_FREE; \
return errno; \
} \
if (got == 0) { \
if (!ctx) { GC_FREE; return EPROTO; } \
LOG(ERROR, zero_is_eof \
? "file/stream truncated reading %s%s%s" \
: "file/stream write returned 0! writing %s%s%s", \
what ? what : "", what ? " from " : "", source); \
GC_FREE; \
return EPROTO; \
} \
sz -= got; \
data = (char*)data + got; \
} \
GC_FREE; \
return 0; \
}
READ_WRITE_EXACTLY(read, 1, /* */)
READ_WRITE_EXACTLY(write, 0, const)
int libxl__remove_file(libxl__gc *gc, const char *path)
{
for (;;) {
int r = unlink(path);
if (!r) return 0;
if (errno == ENOENT) return 0;
if (errno == EINTR) continue;
LOGE(ERROR, "failed to remove file %s", path);
return ERROR_FAIL;
}
}
int libxl__remove_file_or_directory(libxl__gc *gc, const char *path)
{
for (;;) {
int r = rmdir(path);
if (!r) return 0;
if (errno == ENOENT) return 0;
if (errno == ENOTEMPTY) return libxl__remove_directory(gc, path);
if (errno == ENOTDIR) return libxl__remove_file(gc, path);
if (errno == EINTR) continue;
LOGE(ERROR, "failed to remove %s", path);
return ERROR_FAIL;
}
}
int libxl__remove_directory(libxl__gc *gc, const char *dirpath)
{
int rc = 0;
DIR *d = 0;
d = opendir(dirpath);
if (!d) {
if (errno == ENOENT)
goto out;
LOGE(ERROR, "failed to opendir %s for removal", dirpath);
rc = ERROR_FAIL;
goto out;
}
struct dirent *de;
for (;;) {
errno = 0;
de = readdir(d);
if (!de && errno) {
LOGE(ERROR, "failed to readdir %s for removal", dirpath);
rc = ERROR_FAIL;
break;
}
if (!de)
break;
if (!strcmp(de->d_name, ".") ||
!strcmp(de->d_name, ".."))
continue;
const char *subpath = GCSPRINTF("%s/%s", dirpath, de->d_name);
if (libxl__remove_file_or_directory(gc, subpath))
rc = ERROR_FAIL;
}
for (;;) {
int r = rmdir(dirpath);
if (!r) break;
if (errno == ENOENT) goto out;
if (errno == EINTR) continue;
LOGE(ERROR, "failed to remove emptied directory %s", dirpath);
rc = ERROR_FAIL;
}
out:
if (d) closedir(d);
return rc;
}
int libxl_pipe(libxl_ctx *ctx, int pipes[2])
{
GC_INIT(ctx);
int ret = 0;
if (pipe(pipes) < 0) {
LOG(ERROR, "Failed to create a pipe");
ret = -1;
}
GC_FREE;
return ret;
}
int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits)
{
GC_INIT(ctx);
int sz;
sz = (n_bits + 7) / 8;
bitmap->map = libxl__calloc(NOGC, sizeof(*bitmap->map), sz);
bitmap->size = sz;
GC_FREE;
return 0;
}
void libxl_bitmap_init(libxl_bitmap *map)
{
memset(map, '\0', sizeof(*map));
}
void libxl_bitmap_dispose(libxl_bitmap *map)
{
if (!map)
return;
free(map->map);
map->map = NULL;
map->size = 0;
}
void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr,
const libxl_bitmap *sptr)
{
int sz;
assert(dptr->size == sptr->size);
sz = dptr->size = sptr->size;
memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
}
/* This function copies X bytes from source to destination bitmap,
* where X is the smaller of the two sizes.
*
* If destination's size is larger than source, the extra bytes are
* untouched.
*/
void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
const libxl_bitmap *sptr)
{
int sz;
sz = dptr->size < sptr->size ? dptr->size : sptr->size;
memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map));
}
void libxl_bitmap_copy_alloc(libxl_ctx *ctx,
libxl_bitmap *dptr,
const libxl_bitmap *sptr)
{
GC_INIT(ctx);
dptr->map = libxl__calloc(NOGC, sptr->size, sizeof(*sptr->map));
dptr->size = sptr->size;
memcpy(dptr->map, sptr->map, sptr->size * sizeof(*sptr->map));
GC_FREE;
}
int libxl_bitmap_is_full(const libxl_bitmap *bitmap)
{
int i;
for (i = 0; i < bitmap->size; i++)
if (bitmap->map[i] != (uint8_t)-1)
return 0;
return 1;
}
int libxl_bitmap_is_empty(const libxl_bitmap *bitmap)
{
int i;
for (i = 0; i < bitmap->size; i++)
if (bitmap->map[i])
return 0;
return 1;
}
int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit)
{
if (bit >= bitmap->size * 8 || bit < 0)
return 0;
return (bitmap->map[bit / 8] & (1 << (bit & 7))) ? 1 : 0;
}
void libxl_bitmap_set(libxl_bitmap *bitmap, int bit)
{
if (bit >= bitmap->size * 8 || bit < 0)
return;
bitmap->map[bit / 8] |= 1 << (bit & 7);
}
void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit)
{
if (bit >= bitmap->size * 8 || bit < 0)
return;
bitmap->map[bit / 8] &= ~(1 << (bit & 7));
}
int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map,
const libxl_bitmap *map1, const libxl_bitmap *map2)
{
GC_INIT(ctx);
int rc;
uint32_t i;
const libxl_bitmap *large_map;
const libxl_bitmap *small_map;
if (map1->size > map2->size) {
large_map = map1;
small_map = map2;
} else {
large_map = map2;
small_map = map1;
}
rc = libxl_bitmap_alloc(ctx, or_map, large_map->size * 8);
if (rc)
goto out;
/*
* If bitmaps aren't the same size, their union (logical or) will
* be size of larger bit map. Any bit past the end of the
* smaller bit map, will match the larger one.
*/
for (i = 0; i < small_map->size; i++)
or_map->map[i] = (small_map->map[i] | large_map->map[i]);
for (i = small_map->size; i < large_map->size; i++)
or_map->map[i] = large_map->map[i];
out:
GC_FREE;
return rc;
}
int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map,
const libxl_bitmap *map1, const libxl_bitmap *map2)
{
GC_INIT(ctx);
int rc;
uint32_t i;
const libxl_bitmap *large_map;
const libxl_bitmap *small_map;
if (map1->size > map2->size) {
large_map = map1;
small_map = map2;
} else {
large_map = map2;
small_map = map1;
}
rc = libxl_bitmap_alloc(ctx, and_map, small_map->size * 8);
if (rc)
goto out;
/*
* If bitmaps aren't same size, their 'and' will be size of
* smaller bit map
*/
for (i = 0; i < and_map->size; i++)
and_map->map[i] = (large_map->map[i] & small_map->map[i]);
out:
GC_FREE;
return rc;
}
int libxl_bitmap_count_set(const libxl_bitmap *bitmap)
{
int i, nr_set_bits = 0;
libxl_for_each_set_bit(i, *bitmap)
nr_set_bits++;
return nr_set_bits;
}
/* NB. caller is responsible for freeing the memory */
char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap)
{
GC_INIT(ctx);
int i = bitmap->size;
char *p = libxl__zalloc(NOGC, bitmap->size * 2 + 3);
char *q = p;
strncpy(p, "0x", 3);
p += 2;
while(--i >= 0) {
sprintf(p, "%02x", bitmap->map[i]);
p += 2;
}
*p = '\0';
GC_FREE;
return q;
}
int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus)
{
GC_INIT(ctx);
int rc = 0;
if (max_cpus < 0) {
rc = ERROR_INVAL;
LOG(ERROR, "invalid number of cpus provided");
goto out;
}
if (max_cpus == 0)
max_cpus = libxl_get_max_cpus(ctx);
if (max_cpus < 0) {
LOG(ERROR, "failed to retrieve the maximum number of cpus");
rc = max_cpus;
goto out;
}
/* This can't fail: no need to check and log */
libxl_bitmap_alloc(ctx, cpumap, max_cpus);
out:
GC_FREE;
return rc;
}
int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap,
int max_nodes)
{
GC_INIT(ctx);
int rc = 0;
if (max_nodes < 0) {
rc = ERROR_INVAL;
LOG(ERROR, "invalid number of nodes provided");
goto out;
}
if (max_nodes == 0)
max_nodes = libxl_get_max_nodes(ctx);
if (max_nodes < 0) {
LOG(ERROR, "failed to retrieve the maximum number of nodes");
rc = max_nodes;
goto out;
}
/* This can't fail: no need to check and log */
libxl_bitmap_alloc(ctx, nodemap, max_nodes);
out:
GC_FREE;
return rc;
}
int libxl__count_physical_sockets(libxl__gc *gc, int *sockets)
{
int rc;
libxl_physinfo info;
libxl_physinfo_init(&info);
rc = libxl_get_physinfo(CTX, &info);
if (rc)
return rc;
*sockets = info.nr_cpus / info.threads_per_core
/ info.cores_per_socket;
libxl_physinfo_dispose(&info);
return 0;
}
int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap,
int max_sockets)
{
GC_INIT(ctx);
int rc = 0;
if (max_sockets < 0) {
rc = ERROR_INVAL;
LOG(ERROR, "invalid number of sockets provided");
goto out;
}
if (max_sockets == 0) {
rc = libxl__count_physical_sockets(gc, &max_sockets);
if (rc) {
LOGE(ERROR, "failed to get system socket count");
goto out;
}
}
/* This can't fail: no need to check and log */
libxl_bitmap_alloc(ctx, socketmap, max_sockets);
out:
GC_FREE;
return rc;
}
int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap)
{
libxl_cputopology *tinfo = NULL;
int nr_cpus = 0, i, rc = 0;
tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
if (tinfo == NULL) {
rc = ERROR_FAIL;
goto out;
}
libxl_bitmap_set_none(socketmap);
for (i = 0; i < nr_cpus; i++)
if (tinfo[i].socket != XEN_INVALID_SOCKET_ID
&& !libxl_bitmap_test(socketmap, tinfo[i].socket))
libxl_bitmap_set(socketmap, tinfo[i].socket);
out:
libxl_cputopology_list_free(tinfo, nr_cpus);
return rc;
}
int libxl_nodemap_to_cpumap(libxl_ctx *ctx,
const libxl_bitmap *nodemap,
libxl_bitmap *cpumap)
{
libxl_cputopology *tinfo = NULL;
int nr_cpus = 0, i, rc = 0;
tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
if (tinfo == NULL) {
rc = ERROR_FAIL;
goto out;
}
libxl_bitmap_set_none(cpumap);
for (i = 0; i < nr_cpus; i++) {
if (libxl_bitmap_test(nodemap, tinfo[i].node))
libxl_bitmap_set(cpumap, i);
}
out:
libxl_cputopology_list_free(tinfo, nr_cpus);
return rc;
}
int libxl_node_to_cpumap(libxl_ctx *ctx, int node,
libxl_bitmap *cpumap)
{
libxl_bitmap nodemap;
int rc = 0;
libxl_bitmap_init(&nodemap);
rc = libxl_node_bitmap_alloc(ctx, &nodemap, 0);
if (rc)
goto out;
libxl_bitmap_set_none(&nodemap);
libxl_bitmap_set(&nodemap, node);
rc = libxl_nodemap_to_cpumap(ctx, &nodemap, cpumap);
out:
libxl_bitmap_dispose(&nodemap);
return rc;
}
int libxl_cpumap_to_nodemap(libxl_ctx *ctx,
const libxl_bitmap *cpumap,
libxl_bitmap *nodemap)
{
libxl_cputopology *tinfo = NULL;
int nr_cpus = 0, i, rc = 0;
tinfo = libxl_get_cpu_topology(ctx, &nr_cpus);
if (tinfo == NULL) {
rc = ERROR_FAIL;
goto out;
}
libxl_bitmap_set_none(nodemap);
libxl_for_each_set_bit(i, *cpumap) {
if (i >= nr_cpus)
break;
libxl_bitmap_set(nodemap, tinfo[i].node);
}
out:
libxl_cputopology_list_free(tinfo, nr_cpus);
return rc;
}
int libxl_get_max_cpus(libxl_ctx *ctx)
{
int max_cpus = xc_get_max_cpus(ctx->xch);
return max_cpus < 0 ? ERROR_FAIL : max_cpus;
}
int libxl_get_online_cpus(libxl_ctx *ctx)
{
int online_cpus = xc_get_online_cpus(ctx->xch);
return online_cpus < 0 ? ERROR_FAIL : online_cpus;
}
int libxl_get_max_nodes(libxl_ctx *ctx)
{
int max_nodes = xc_get_max_nodes(ctx->xch);
return max_nodes < 0 ? ERROR_FAIL : max_nodes;
}
int libxl__enum_from_string(const libxl_enum_string_table *t,
const char *s, int *e)
{
if (!t) return ERROR_INVAL;
for( ; t->s; t++) {
if (!strcasecmp(t->s, s)) {
*e = t->v;
return 0;
}
}
return ERROR_FAIL;
}
void libxl_cputopology_list_free(libxl_cputopology *list, int nr)
{
int i;
for (i = 0; i < nr; i++)
libxl_cputopology_dispose(&list[i]);
free(list);
}
void libxl_pcitopology_list_free(libxl_pcitopology *list, int nr)
{
int i;
for (i = 0; i < nr; i++)
libxl_pcitopology_dispose(&list[i]);
free(list);
}
void libxl_numainfo_list_free(libxl_numainfo *list, int nr)
{
int i;
for (i = 0; i < nr; i++)
libxl_numainfo_dispose(&list[i]);
free(list);
}
void libxl_vcpuinfo_list_free(libxl_vcpuinfo *list, int nr)
{
int i;
for (i = 0; i < nr; i++)
libxl_vcpuinfo_dispose(&list[i]);
free(list);
}
int libxl__sendmsg_fds(libxl__gc *gc, int carrier,
const char data,
int nfds, const int fds[], const char *what) {
struct msghdr msg = { 0 };
struct cmsghdr *cmsg;
size_t spaceneeded = nfds * sizeof(fds[0]);
char control[CMSG_SPACE(spaceneeded)];
const size_t datalen = 1;
struct iovec iov;
int r;
iov.iov_base = (void*)&data;
iov.iov_len = datalen;
/* compose the message */
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
/* attach open fd */
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(spaceneeded);
memcpy(CMSG_DATA(cmsg), fds, spaceneeded);
msg.msg_controllen = cmsg->cmsg_len;
while (1) {
r = sendmsg(carrier, &msg, 0);
if (r < 0) {
if (errno == EINTR)
continue;
if (errno == EWOULDBLOCK) {
return ERROR_NOT_READY;
}
LOGE(ERROR, "failed to send fd-carrying message (%s)", what);
return ERROR_FAIL;
}
if (r != datalen) {
LOG(ERROR, "sendmsg have written %d instead of %zu",
r, datalen);
return ERROR_FAIL;
}
break;
};
return 0;
}
int libxl__recvmsg_fds(libxl__gc *gc, int carrier,
void *databuf, size_t datalen,
int nfds, int fds[], const char *what)
{
struct msghdr msg = { 0 };
struct cmsghdr *cmsg;
size_t spaceneeded = nfds * sizeof(fds[0]);
char control[CMSG_SPACE(spaceneeded)];
struct iovec iov;
int r;
iov.iov_base = databuf;
iov.iov_len = datalen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
for (;;) {
r = recvmsg(carrier, &msg, 0);
if (r < 0) {
if (errno == EINTR) continue;
if (errno == EWOULDBLOCK) return -1;
LOGE(ERROR,"recvmsg failed (%s)", what);
return ERROR_FAIL;
}
if (r == 0) {
LOG(ERROR,"recvmsg got EOF (%s)", what);
return ERROR_FAIL;
}
cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg->cmsg_len <= CMSG_LEN(0)) {
LOG(ERROR,"recvmsg got no control msg"
" when expecting fds (%s)", what);
return ERROR_FAIL;
}
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
LOG(ERROR, "recvmsg got unexpected"
" cmsg_level %d (!=%d) or _type %d (!=%d) (%s)",
cmsg->cmsg_level, SOL_SOCKET,
cmsg->cmsg_type, SCM_RIGHTS,
what);
return ERROR_FAIL;
}
if (cmsg->cmsg_len != CMSG_LEN(spaceneeded) ||
msg.msg_controllen != cmsg->cmsg_len) {
LOG(ERROR, "recvmsg got unexpected"
" number of fds or extra control data"
" (%ld bytes' worth, expected %ld) (%s)",
(long)CMSG_LEN(spaceneeded), (long)cmsg->cmsg_len,
what);
int i, fd;
unsigned char *p;
for (i=0, p=CMSG_DATA(cmsg);
CMSG_SPACE(i * sizeof(fds[0]));
i++, i+=sizeof(fd)) {
memcpy(&fd, p, sizeof(fd));
close(fd);
}
return ERROR_FAIL;
}
memcpy(fds, CMSG_DATA(cmsg), spaceneeded);
return 0;
}
}
void libxl_dominfo_list_free(libxl_dominfo *list, int nr)
{
int i;
for (i = 0; i < nr; i++)
libxl_dominfo_dispose(&list[i]);
free(list);
}
void libxl_vminfo_list_free(libxl_vminfo *list, int nr)
{
int i;
for (i = 0; i < nr; i++)
libxl_vminfo_dispose(&list[i]);
free(list);
}
void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nr)
{
int i;
for (i = 0; i < nr; i++)
libxl_cpupoolinfo_dispose(&list[i]);
free(list);
}
int libxl_domid_valid_guest(uint32_t domid)
{
/* returns 1 if the value _could_ be a valid guest domid, 0 otherwise
* does not check whether the domain actually exists */
return domid > 0 && domid < DOMID_FIRST_RESERVED;
}
void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src)
{
GC_INIT(ctx);
if (*src)
*dst = libxl__strdup(NOGC, *src);
else
*dst = NULL;
GC_FREE;
}
/*
* Fill @buf with @len random bytes.
*/
int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len)
{
static const char *dev = "/dev/urandom";
int fd;
int ret;
fd = open(dev, O_RDONLY);
if (fd < 0) {
LOGE(ERROR, "failed to open \"%s\"", dev);
return ERROR_FAIL;
}
ret = libxl_fd_set_cloexec(CTX, fd, 1);
if (ret) {
close(fd);
return ERROR_FAIL;
}
ret = libxl_read_exactly(CTX, fd, buf, len, dev, NULL);
close(fd);
return ret;
}
int libxl__prepare_sockaddr_un(libxl__gc *gc,
struct sockaddr_un *un, const char *path,
const char *what)
{
if (sizeof(un->sun_path) - 1 <= strlen(path)) {
LOG(ERROR, "UNIX socket path '%s' is too long for %s", path, what);
LOG(DEBUG, "Path must be less than %zu bytes", sizeof(un->sun_path) - 1);
return ERROR_INVAL;
}
memset(un, 0, sizeof(struct sockaddr_un));
un->sun_family = AF_UNIX;
strncpy(un->sun_path, path, sizeof(un->sun_path) - 1);
return 0;
}
/*
* Local variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/