mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
Replace the firmware loaded into HVM guests with an OS kernel. Since the HVM builder now uses the PV xc_dom_* set of functions this kernel will be parsed and loaded inside the guest like on PV, but the container is a pure HVM guest. Also, if device_model_version is set to none or a device model for the specified domain is not present unconditinally set the nic type to LIBXL_NIC_TYPE_VIF. Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Acked-by: Wei Liu <wei.liu2@citrix.com> Cc: Ian Jackson <ian.jackson@eu.citrix.com> Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com> Cc: Ian Campbell <ian.campbell@citrix.com> Cc: Wei Liu <wei.liu2@citrix.com>
6853 lines
200 KiB
C
6853 lines
200 KiB
C
/*
|
|
* Copyright (C) 2009 Citrix Ltd.
|
|
* Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
|
|
* 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"
|
|
|
|
#include "libxl_internal.h"
|
|
|
|
#define PAGE_TO_MEMKB(pages) ((pages) * 4)
|
|
#define BACKEND_STRING_SIZE 5
|
|
|
|
/* Utility to read backend xenstore keys */
|
|
#define READ_BACKEND(tgc, subpath) ({ \
|
|
rc = libxl__xs_read_checked(tgc, XBT_NULL, \
|
|
GCSPRINTF("%s/" subpath, be_path), \
|
|
&tmp); \
|
|
if (rc) goto out; \
|
|
(char*)tmp; \
|
|
});
|
|
|
|
int libxl_ctx_alloc(libxl_ctx **pctx, int version,
|
|
unsigned flags, xentoollog_logger * lg)
|
|
{
|
|
libxl_ctx *ctx = NULL;
|
|
libxl__gc gc_buf, *gc = NULL;
|
|
int rc;
|
|
|
|
if (version != LIBXL_VERSION) { rc = ERROR_VERSION; goto out; }
|
|
|
|
ctx = malloc(sizeof(*ctx));
|
|
if (!ctx) {
|
|
xtl_log(lg, XTL_ERROR, errno, "libxl",
|
|
"%s:%d:%s: Failed to allocate context\n",
|
|
__FILE__, __LINE__, __func__);
|
|
rc = ERROR_NOMEM; goto out;
|
|
}
|
|
|
|
memset(ctx, 0, sizeof(libxl_ctx));
|
|
ctx->lg = lg;
|
|
|
|
/* First initialise pointers etc. (cannot fail) */
|
|
|
|
ctx->nogc_gc.alloc_maxsize = -1;
|
|
ctx->nogc_gc.owner = ctx;
|
|
|
|
LIBXL_TAILQ_INIT(&ctx->occurred);
|
|
|
|
ctx->osevent_hooks = 0;
|
|
|
|
ctx->poller_app = 0;
|
|
LIBXL_LIST_INIT(&ctx->pollers_event);
|
|
LIBXL_LIST_INIT(&ctx->pollers_idle);
|
|
LIBXL_LIST_INIT(&ctx->pollers_fds_changed);
|
|
|
|
LIBXL_LIST_INIT(&ctx->efds);
|
|
LIBXL_TAILQ_INIT(&ctx->etimes);
|
|
|
|
ctx->watch_slots = 0;
|
|
LIBXL_SLIST_INIT(&ctx->watch_freeslots);
|
|
libxl__ev_fd_init(&ctx->watch_efd);
|
|
|
|
ctx->xce = 0;
|
|
LIBXL_LIST_INIT(&ctx->evtchns_waiting);
|
|
libxl__ev_fd_init(&ctx->evtchn_efd);
|
|
|
|
LIBXL_LIST_INIT(&ctx->aos_inprogress);
|
|
|
|
LIBXL_TAILQ_INIT(&ctx->death_list);
|
|
libxl__ev_xswatch_init(&ctx->death_watch);
|
|
|
|
ctx->childproc_hooks = &libxl__childproc_default_hooks;
|
|
ctx->childproc_user = 0;
|
|
|
|
ctx->sigchld_selfpipe[0] = -1;
|
|
ctx->sigchld_selfpipe[1] = -1;
|
|
libxl__ev_fd_init(&ctx->sigchld_selfpipe_efd);
|
|
|
|
/* The mutex is special because we can't idempotently destroy it */
|
|
|
|
if (libxl__init_recursive_mutex(ctx, &ctx->lock) < 0) {
|
|
LOG(ERROR, "Failed to initialize mutex");
|
|
free(ctx);
|
|
ctx = 0;
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/* Now ctx is safe for ctx_free; failures simply set rc and "goto out" */
|
|
LIBXL_INIT_GC(gc_buf,ctx);
|
|
gc = &gc_buf;
|
|
/* Now gc is useable */
|
|
|
|
rc = libxl__atfork_init(ctx);
|
|
if (rc) goto out;
|
|
|
|
ctx->poller_app = libxl__poller_get(gc);
|
|
if (!ctx->poller_app) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
ctx->xch = xc_interface_open(lg,lg,0);
|
|
if (!ctx->xch) {
|
|
LOGEV(ERROR, errno, "cannot open libxc handle");
|
|
rc = ERROR_FAIL; goto out;
|
|
}
|
|
|
|
ctx->xsh = xs_daemon_open();
|
|
if (!ctx->xsh)
|
|
ctx->xsh = xs_domain_open();
|
|
if (!ctx->xsh) {
|
|
LOGEV(ERROR, errno, "cannot connect to xenstore");
|
|
rc = ERROR_FAIL; goto out;
|
|
}
|
|
|
|
*pctx = ctx;
|
|
return 0;
|
|
|
|
out:
|
|
if (gc) libxl__free_all(gc);
|
|
libxl_ctx_free(ctx);
|
|
*pctx = NULL;
|
|
return rc;
|
|
}
|
|
|
|
static void free_disable_deaths(libxl__gc *gc,
|
|
struct libxl__evgen_domain_death_list *l) {
|
|
libxl_evgen_domain_death *death;
|
|
while ((death = LIBXL_TAILQ_FIRST(l)))
|
|
libxl__evdisable_domain_death(gc, death);
|
|
}
|
|
|
|
static void discard_events(struct libxl__event_list *l) {
|
|
/* doesn't bother unlinking from the list, so l is corrupt on return */
|
|
libxl_event *ev, *next;
|
|
LIBXL_TAILQ_FOREACH_SAFE(ev, l, link, next)
|
|
libxl_event_free(0, ev);
|
|
}
|
|
|
|
int libxl_ctx_free(libxl_ctx *ctx)
|
|
{
|
|
if (!ctx) return 0;
|
|
|
|
int i;
|
|
GC_INIT(ctx);
|
|
|
|
CTX_LOCK;
|
|
assert(!ctx->osevent_in_hook);
|
|
CTX->osevent_in_hook += 1000; /* make violations easier to debug */
|
|
|
|
/* Deregister all libxl__ev_KINDs: */
|
|
|
|
free_disable_deaths(gc, &CTX->death_list);
|
|
free_disable_deaths(gc, &CTX->death_reported);
|
|
|
|
libxl_evgen_disk_eject *eject;
|
|
while ((eject = LIBXL_LIST_FIRST(&CTX->disk_eject_evgens)))
|
|
libxl__evdisable_disk_eject(gc, eject);
|
|
|
|
libxl_childproc_setmode(CTX,0,0);
|
|
for (i = 0; i < ctx->watch_nslots; i++)
|
|
assert(!libxl__watch_slot_contents(gc, i));
|
|
assert(!libxl__ev_fd_isregistered(&ctx->watch_efd));
|
|
assert(!libxl__ev_fd_isregistered(&ctx->evtchn_efd));
|
|
assert(!libxl__ev_fd_isregistered(&ctx->sigchld_selfpipe_efd));
|
|
|
|
/* Now there should be no more events requested from the application: */
|
|
|
|
assert(LIBXL_LIST_EMPTY(&ctx->efds));
|
|
assert(LIBXL_TAILQ_EMPTY(&ctx->etimes));
|
|
assert(LIBXL_LIST_EMPTY(&ctx->evtchns_waiting));
|
|
assert(LIBXL_LIST_EMPTY(&ctx->aos_inprogress));
|
|
|
|
if (ctx->xch) xc_interface_close(ctx->xch);
|
|
libxl_version_info_dispose(&ctx->version_info);
|
|
if (ctx->xsh) xs_daemon_close(ctx->xsh);
|
|
if (ctx->xce) xc_evtchn_close(ctx->xce);
|
|
|
|
libxl__poller_put(ctx, ctx->poller_app);
|
|
ctx->poller_app = NULL;
|
|
assert(LIBXL_LIST_EMPTY(&ctx->pollers_event));
|
|
assert(LIBXL_LIST_EMPTY(&ctx->pollers_fds_changed));
|
|
libxl__poller *poller, *poller_tmp;
|
|
LIBXL_LIST_FOREACH_SAFE(poller, &ctx->pollers_idle, entry, poller_tmp) {
|
|
libxl__poller_dispose(poller);
|
|
free(poller);
|
|
}
|
|
|
|
free(ctx->watch_slots);
|
|
|
|
discard_events(&ctx->occurred);
|
|
|
|
/* If we have outstanding children, then the application inherits
|
|
* them; we wish the application good luck with understanding
|
|
* this if and when it reaps them. */
|
|
libxl__sigchld_notneeded(gc);
|
|
libxl__pipe_close(ctx->sigchld_selfpipe);
|
|
|
|
CTX_UNLOCK;
|
|
pthread_mutex_destroy(&ctx->lock);
|
|
|
|
GC_FREE;
|
|
free(ctx);
|
|
return 0;
|
|
}
|
|
|
|
void libxl_string_list_dispose(libxl_string_list *psl)
|
|
{
|
|
int i;
|
|
libxl_string_list sl = *psl;
|
|
|
|
if (!sl)
|
|
return;
|
|
|
|
for (i = 0; sl[i] != NULL; i++) {
|
|
free(sl[i]);
|
|
sl[i] = NULL;
|
|
}
|
|
free(sl);
|
|
*psl = NULL;
|
|
}
|
|
|
|
void libxl_string_list_copy(libxl_ctx *ctx,
|
|
libxl_string_list *dst,
|
|
libxl_string_list *src)
|
|
{
|
|
GC_INIT(ctx);
|
|
int i, len;
|
|
|
|
if (!*src) {
|
|
*dst = NULL;
|
|
goto out;
|
|
}
|
|
|
|
len = libxl_string_list_length(src);
|
|
/* one extra slot for sentinel */
|
|
*dst = libxl__calloc(NOGC, len + 1, sizeof(char *));
|
|
|
|
for (i = 0; i < len; i++)
|
|
(*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
|
|
|
|
out:
|
|
GC_FREE;
|
|
}
|
|
|
|
int libxl_string_list_length(const libxl_string_list *psl)
|
|
{
|
|
int i = 0;
|
|
|
|
if (*psl)
|
|
while ((*psl)[i])
|
|
i++;
|
|
|
|
return i;
|
|
}
|
|
|
|
int libxl_key_value_list_length(libxl_key_value_list *pkvl)
|
|
{
|
|
int i = 0;
|
|
libxl_key_value_list kvl = *pkvl;
|
|
|
|
if (kvl) {
|
|
while (kvl[2 * i]) /* Only checks keys */
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void libxl_key_value_list_dispose(libxl_key_value_list *pkvl)
|
|
{
|
|
int i;
|
|
libxl_key_value_list kvl = *pkvl;
|
|
|
|
if (!kvl)
|
|
return;
|
|
|
|
for (i = 0; kvl[i] != NULL; i += 2) {
|
|
free(kvl[i]);
|
|
kvl[i] = NULL;
|
|
if (kvl[i + 1]) {
|
|
free(kvl[i + 1]);
|
|
kvl[i+1] = NULL;
|
|
}
|
|
}
|
|
free(kvl);
|
|
*pkvl = NULL;
|
|
}
|
|
|
|
void libxl_key_value_list_copy(libxl_ctx *ctx,
|
|
libxl_key_value_list *dst,
|
|
libxl_key_value_list *src)
|
|
{
|
|
GC_INIT(ctx);
|
|
int i, len;
|
|
|
|
if (*src == NULL) {
|
|
*dst = NULL;
|
|
goto out;
|
|
}
|
|
|
|
len = libxl_key_value_list_length(src);
|
|
/* one extra slot for sentinel */
|
|
*dst = libxl__calloc(NOGC, len * 2 + 1, sizeof(char *));
|
|
|
|
for (i = 0; i < len * 2; i += 2) {
|
|
(*dst)[i] = libxl__strdup(NOGC, (*src)[i]);
|
|
if ((*src)[i+1])
|
|
(*dst)[i+1] = libxl__strdup(NOGC, (*src)[i+1]);
|
|
else
|
|
(*dst)[i+1] = NULL;
|
|
}
|
|
|
|
out:
|
|
GC_FREE;
|
|
}
|
|
|
|
void libxl_defbool_set(libxl_defbool *db, bool b)
|
|
{
|
|
db->val = b ? LIBXL__DEFBOOL_TRUE : LIBXL__DEFBOOL_FALSE;
|
|
}
|
|
|
|
void libxl_defbool_unset(libxl_defbool *db)
|
|
{
|
|
db->val = LIBXL__DEFBOOL_DEFAULT;
|
|
}
|
|
|
|
bool libxl_defbool_is_default(libxl_defbool db)
|
|
{
|
|
return !db.val;
|
|
}
|
|
|
|
void libxl_defbool_setdefault(libxl_defbool *db, bool b)
|
|
{
|
|
if (libxl_defbool_is_default(*db))
|
|
libxl_defbool_set(db, b);
|
|
}
|
|
|
|
bool libxl_defbool_val(libxl_defbool db)
|
|
{
|
|
assert(!libxl_defbool_is_default(db));
|
|
return db.val > 0;
|
|
}
|
|
|
|
const char *libxl_defbool_to_string(libxl_defbool b)
|
|
{
|
|
if (b.val < 0)
|
|
return LIBXL__DEFBOOL_STR_FALSE;
|
|
else if (b.val > 0)
|
|
return LIBXL__DEFBOOL_STR_TRUE;
|
|
else
|
|
return LIBXL__DEFBOOL_STR_DEFAULT;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
|
|
const char *old_name, const char *new_name,
|
|
xs_transaction_t trans)
|
|
{
|
|
libxl_ctx *ctx = libxl__gc_owner(gc);
|
|
char *dom_path = 0;
|
|
const char *name_path;
|
|
char *got_old_name;
|
|
unsigned int got_old_len;
|
|
xs_transaction_t our_trans = 0;
|
|
uint32_t stub_dm_domid;
|
|
const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL;
|
|
int rc;
|
|
libxl_dominfo info;
|
|
char *uuid;
|
|
const char *vm_name_path;
|
|
|
|
libxl_dominfo_init(&info);
|
|
|
|
dom_path = libxl__xs_get_dompath(gc, domid);
|
|
if (!dom_path) goto x_nomem;
|
|
|
|
name_path= GCSPRINTF("%s/name", dom_path);
|
|
if (!name_path) goto x_nomem;
|
|
|
|
stub_dm_domid = libxl_get_stubdom_id(CTX, domid);
|
|
if (stub_dm_domid) {
|
|
stub_dm_old_name = libxl__stub_dm_name(gc, old_name);
|
|
stub_dm_new_name = libxl__stub_dm_name(gc, new_name);
|
|
}
|
|
|
|
retry_transaction:
|
|
if (!trans) {
|
|
trans = our_trans = xs_transaction_start(ctx->xsh);
|
|
if (!our_trans) {
|
|
LOGEV(ERROR, errno, "create xs transaction for domain (re)name");
|
|
goto x_fail;
|
|
}
|
|
}
|
|
|
|
if (!new_name) {
|
|
LOG(ERROR, "new domain name not specified");
|
|
rc = ERROR_INVAL;
|
|
goto x_rc;
|
|
}
|
|
|
|
if (new_name[0]) {
|
|
/* nonempty names must be unique */
|
|
uint32_t domid_e;
|
|
rc = libxl_name_to_domid(ctx, new_name, &domid_e);
|
|
if (rc == ERROR_INVAL) {
|
|
/* no such domain, good */
|
|
} else if (rc != 0) {
|
|
LOG(ERROR, "unexpected error checking for existing domain");
|
|
goto x_rc;
|
|
} else if (domid_e == domid) {
|
|
/* domain already has this name, ok (but we do still
|
|
* need the rest of the code as we may need to check
|
|
* old_name, for example). */
|
|
} else {
|
|
LOG(ERROR, "domain with name \"%s\" already exists.", new_name);
|
|
rc = ERROR_INVAL;
|
|
goto x_rc;
|
|
}
|
|
}
|
|
|
|
if (old_name) {
|
|
got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len);
|
|
if (!got_old_name) {
|
|
LOGEV(ERROR, errno,
|
|
"check old name for domain %"PRIu32" allegedly named `%s'",
|
|
domid,
|
|
old_name);
|
|
goto x_fail;
|
|
}
|
|
if (strcmp(old_name, got_old_name)) {
|
|
LOG(ERROR,
|
|
"domain %"PRIu32" allegedly named "
|
|
"`%s' is actually named `%s' - racing ?",
|
|
domid,
|
|
old_name,
|
|
got_old_name);
|
|
free(got_old_name);
|
|
goto x_fail;
|
|
}
|
|
free(got_old_name);
|
|
}
|
|
if (!xs_write(ctx->xsh, trans, name_path,
|
|
new_name, strlen(new_name))) {
|
|
LOG(ERROR,
|
|
"failed to write new name `%s'"
|
|
" for domain %"PRIu32" previously named `%s'",
|
|
new_name,
|
|
domid,
|
|
old_name);
|
|
goto x_fail;
|
|
}
|
|
|
|
/* update /vm/<uuid>/name */
|
|
rc = libxl_domain_info(ctx, &info, domid);
|
|
if (rc)
|
|
goto x_rc;
|
|
|
|
uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
|
|
vm_name_path = GCSPRINTF("/vm/%s/name", uuid);
|
|
if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name))
|
|
goto x_fail;
|
|
|
|
if (stub_dm_domid) {
|
|
rc = libxl__domain_rename(gc, stub_dm_domid,
|
|
stub_dm_old_name,
|
|
stub_dm_new_name,
|
|
trans);
|
|
if (rc) {
|
|
LOGE(ERROR, "unable to rename stub-domain");
|
|
goto x_rc;
|
|
}
|
|
}
|
|
|
|
if (our_trans) {
|
|
if (!xs_transaction_end(ctx->xsh, our_trans, 0)) {
|
|
trans = our_trans = 0;
|
|
if (errno != EAGAIN) {
|
|
LOG(ERROR,
|
|
"failed to commit new name `%s'"
|
|
" for domain %"PRIu32" previously named `%s'",
|
|
new_name,
|
|
domid,
|
|
old_name);
|
|
goto x_fail;
|
|
}
|
|
LOG(DEBUG,
|
|
"need to retry rename transaction"
|
|
" for domain %"PRIu32" (name_path=\"%s\", new_name=\"%s\")",
|
|
domid,
|
|
name_path,
|
|
new_name);
|
|
goto retry_transaction;
|
|
}
|
|
our_trans = 0;
|
|
}
|
|
|
|
rc = 0;
|
|
x_rc:
|
|
if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1);
|
|
libxl_dominfo_dispose(&info);
|
|
return rc;
|
|
|
|
x_fail: rc = ERROR_FAIL; goto x_rc;
|
|
x_nomem: rc = ERROR_NOMEM; goto x_rc;
|
|
}
|
|
|
|
int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
|
|
const char *old_name, const char *new_name)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc;
|
|
rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL);
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
|
|
const libxl_asyncop_how *ao_how)
|
|
{
|
|
AO_CREATE(ctx, domid, ao_how);
|
|
int rc = libxl__domain_resume(gc, domid, suspend_cancel);
|
|
libxl__ao_complete(egc, ao, rc);
|
|
return AO_INPROGRESS;
|
|
}
|
|
|
|
/*
|
|
* Preserves a domain but rewrites xenstore etc to make it unique so
|
|
* that the domain can be restarted.
|
|
*
|
|
* Does not modify info so that it may be reused.
|
|
*/
|
|
int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid)
|
|
{
|
|
GC_INIT(ctx);
|
|
struct xs_permissions roperm[2];
|
|
xs_transaction_t t;
|
|
char *preserved_name;
|
|
char *uuid_string;
|
|
char *vm_path;
|
|
char *dom_path;
|
|
|
|
int rc;
|
|
|
|
preserved_name = GCSPRINTF("%s%s", info->name, name_suffix);
|
|
if (!preserved_name) {
|
|
GC_FREE;
|
|
return ERROR_NOMEM;
|
|
}
|
|
|
|
uuid_string = libxl__uuid2string(gc, new_uuid);
|
|
if (!uuid_string) {
|
|
GC_FREE;
|
|
return ERROR_NOMEM;
|
|
}
|
|
|
|
dom_path = libxl__xs_get_dompath(gc, domid);
|
|
if (!dom_path) {
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
vm_path = GCSPRINTF("/vm/%s", uuid_string);
|
|
if (!vm_path) {
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
roperm[0].id = 0;
|
|
roperm[0].perms = XS_PERM_NONE;
|
|
roperm[1].id = domid;
|
|
roperm[1].perms = XS_PERM_READ;
|
|
|
|
retry_transaction:
|
|
t = xs_transaction_start(ctx->xsh);
|
|
|
|
xs_rm(ctx->xsh, t, vm_path);
|
|
xs_mkdir(ctx->xsh, t, vm_path);
|
|
xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm));
|
|
|
|
xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
|
|
rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t);
|
|
if (rc) {
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
|
|
|
|
if (!xs_transaction_end(ctx->xsh, t, 0))
|
|
if (errno == EAGAIN)
|
|
goto retry_transaction;
|
|
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
static void xcinfo2xlinfo(libxl_ctx *ctx,
|
|
const xc_domaininfo_t *xcinfo,
|
|
libxl_dominfo *xlinfo)
|
|
{
|
|
size_t size;
|
|
|
|
memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t));
|
|
xlinfo->domid = xcinfo->domain;
|
|
xlinfo->ssidref = xcinfo->ssidref;
|
|
if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref,
|
|
&xlinfo->ssid_label, &size) < 0)
|
|
xlinfo->ssid_label = NULL;
|
|
|
|
xlinfo->dying = !!(xcinfo->flags&XEN_DOMINF_dying);
|
|
xlinfo->shutdown = !!(xcinfo->flags&XEN_DOMINF_shutdown);
|
|
xlinfo->paused = !!(xcinfo->flags&XEN_DOMINF_paused);
|
|
xlinfo->blocked = !!(xcinfo->flags&XEN_DOMINF_blocked);
|
|
xlinfo->running = !!(xcinfo->flags&XEN_DOMINF_running);
|
|
|
|
if (xlinfo->shutdown)
|
|
xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask;
|
|
else
|
|
xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN;
|
|
|
|
xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages);
|
|
xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages);
|
|
xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages);
|
|
xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages);
|
|
xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages);
|
|
xlinfo->cpu_time = xcinfo->cpu_time;
|
|
xlinfo->vcpu_max_id = xcinfo->max_vcpu_id;
|
|
xlinfo->vcpu_online = xcinfo->nr_online_vcpus;
|
|
xlinfo->cpupool = xcinfo->cpupool;
|
|
xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ?
|
|
LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV;
|
|
}
|
|
|
|
libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out)
|
|
{
|
|
libxl_dominfo *ptr;
|
|
int i, ret;
|
|
xc_domaininfo_t info[1024];
|
|
int size = 1024;
|
|
GC_INIT(ctx);
|
|
|
|
ptr = calloc(size, sizeof(libxl_dominfo));
|
|
if (!ptr) {
|
|
LOGE(ERROR, "allocating domain info");
|
|
GC_FREE;
|
|
return NULL;
|
|
}
|
|
|
|
ret = xc_domain_getinfolist(ctx->xch, 0, 1024, info);
|
|
if (ret<0) {
|
|
LOGE(ERROR, "getting domain info list");
|
|
free(ptr);
|
|
GC_FREE;
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < ret; i++) {
|
|
xcinfo2xlinfo(ctx, &info[i], &ptr[i]);
|
|
}
|
|
*nb_domain_out = ret;
|
|
GC_FREE;
|
|
return ptr;
|
|
}
|
|
|
|
int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r,
|
|
uint32_t domid) {
|
|
xc_domaininfo_t xcinfo;
|
|
int ret;
|
|
GC_INIT(ctx);
|
|
|
|
ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo);
|
|
if (ret<0) {
|
|
LOGE(ERROR, "getting domain info list");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
if (ret==0 || xcinfo.domain != domid) {
|
|
GC_FREE;
|
|
return ERROR_DOMAIN_NOTFOUND;
|
|
}
|
|
|
|
if (info_r)
|
|
xcinfo2xlinfo(ctx, &xcinfo, info_r);
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
/* Returns:
|
|
* 0 - success
|
|
* ERROR_FAIL + errno == ENOENT - no entry found
|
|
* ERROR_$FOO + errno != ENOENT - other failure
|
|
*/
|
|
static int cpupool_info(libxl__gc *gc,
|
|
libxl_cpupoolinfo *info,
|
|
uint32_t poolid,
|
|
bool exact /* exactly poolid or >= poolid */)
|
|
{
|
|
xc_cpupoolinfo_t *xcinfo;
|
|
int rc = ERROR_FAIL;
|
|
|
|
xcinfo = xc_cpupool_getinfo(CTX->xch, poolid);
|
|
if (xcinfo == NULL)
|
|
{
|
|
if (exact || errno != ENOENT)
|
|
LOGE(ERROR, "failed to get info for cpupool%d", poolid);
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
if (exact && xcinfo->cpupool_id != poolid)
|
|
{
|
|
LOG(ERROR, "got info for cpupool%d, wanted cpupool%d\n",
|
|
xcinfo->cpupool_id, poolid);
|
|
goto out;
|
|
}
|
|
|
|
info->poolid = xcinfo->cpupool_id;
|
|
info->pool_name = libxl_cpupoolid_to_name(CTX, info->poolid);
|
|
if (!info->pool_name) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
info->sched = xcinfo->sched_id;
|
|
info->n_dom = xcinfo->n_dom;
|
|
rc = libxl_cpu_bitmap_alloc(CTX, &info->cpumap, 0);
|
|
if (rc)
|
|
goto out;
|
|
|
|
memcpy(info->cpumap.map, xcinfo->cpumap, info->cpumap.size);
|
|
|
|
rc = 0;
|
|
out:
|
|
xc_cpupool_infofree(CTX->xch, xcinfo);
|
|
return rc;
|
|
}
|
|
|
|
int libxl_cpupool_info(libxl_ctx *ctx,
|
|
libxl_cpupoolinfo *info, uint32_t poolid)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc = cpupool_info(gc, info, poolid, true);
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx *ctx, int *nb_pool_out)
|
|
{
|
|
GC_INIT(ctx);
|
|
libxl_cpupoolinfo info, *ptr;
|
|
|
|
int i;
|
|
uint32_t poolid;
|
|
|
|
ptr = NULL;
|
|
|
|
poolid = 0;
|
|
for (i = 0;; i++) {
|
|
libxl_cpupoolinfo_init(&info);
|
|
if (cpupool_info(gc, &info, poolid, false)) {
|
|
libxl_cpupoolinfo_dispose(&info);
|
|
if (errno != ENOENT) goto out;
|
|
break;
|
|
}
|
|
|
|
ptr = libxl__realloc(NOGC, ptr, (i+1) * sizeof(libxl_cpupoolinfo));
|
|
ptr[i] = info;
|
|
poolid = info.poolid + 1;
|
|
/* Don't dispose of info because it will be returned to caller */
|
|
}
|
|
|
|
*nb_pool_out = i;
|
|
|
|
GC_FREE;
|
|
return ptr;
|
|
|
|
out:
|
|
libxl_cpupoolinfo_list_free(ptr, i);
|
|
*nb_pool_out = 0;
|
|
GC_FREE;
|
|
return NULL;
|
|
}
|
|
|
|
/* this API call only list VM running on this host. A VM can
|
|
* be an aggregate of multiple domains. */
|
|
libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out)
|
|
{
|
|
GC_INIT(ctx);
|
|
libxl_vminfo *ptr = NULL;
|
|
int idx, i, ret;
|
|
xc_domaininfo_t info[1024];
|
|
|
|
ret = xc_domain_getinfolist(ctx->xch, 1, ARRAY_SIZE(info), info);
|
|
if (ret < 0) {
|
|
LOGE(ERROR, "getting domain info list");
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Always make sure to allocate at least one element; if we don't and we
|
|
* request zero, libxl__calloc (might) think its internal call to calloc
|
|
* has failed (if it returns null), if so it would kill our process.
|
|
*/
|
|
ptr = libxl__calloc(NOGC, ret ? ret : 1, sizeof(libxl_vminfo));
|
|
|
|
for (idx = i = 0; i < ret; i++) {
|
|
if (libxl_is_stubdom(ctx, info[i].domain, NULL))
|
|
continue;
|
|
memcpy(&(ptr[idx].uuid), info[i].handle, sizeof(xen_domain_handle_t));
|
|
ptr[idx].domid = info[i].domain;
|
|
|
|
idx++;
|
|
}
|
|
*nb_vm_out = idx;
|
|
|
|
out:
|
|
GC_FREE;
|
|
return ptr;
|
|
}
|
|
|
|
static void libxl__remus_setup(libxl__egc *egc,
|
|
libxl__domain_suspend_state *dss);
|
|
static void remus_setup_done(libxl__egc *egc,
|
|
libxl__remus_devices_state *rds, int rc);
|
|
static void remus_setup_failed(libxl__egc *egc,
|
|
libxl__remus_devices_state *rds, int rc);
|
|
static void remus_failover_cb(libxl__egc *egc,
|
|
libxl__domain_suspend_state *dss, int rc);
|
|
|
|
/* TODO: Explicit Checkpoint acknowledgements via recv_fd. */
|
|
int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
|
|
uint32_t domid, int send_fd, int recv_fd,
|
|
const libxl_asyncop_how *ao_how)
|
|
{
|
|
AO_CREATE(ctx, domid, ao_how);
|
|
libxl__domain_suspend_state *dss;
|
|
int rc;
|
|
|
|
libxl_domain_type type = libxl__domain_type(gc, domid);
|
|
if (type == LIBXL_DOMAIN_TYPE_INVALID) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
libxl_defbool_setdefault(&info->allow_unsafe, false);
|
|
libxl_defbool_setdefault(&info->blackhole, false);
|
|
libxl_defbool_setdefault(&info->compression, true);
|
|
libxl_defbool_setdefault(&info->netbuf, true);
|
|
libxl_defbool_setdefault(&info->diskbuf, true);
|
|
|
|
if (!libxl_defbool_val(info->allow_unsafe) &&
|
|
(libxl_defbool_val(info->blackhole) ||
|
|
!libxl_defbool_val(info->netbuf) ||
|
|
!libxl_defbool_val(info->diskbuf))) {
|
|
LOG(ERROR, "Unsafe mode must be enabled to replicate to /dev/null,"
|
|
"disable network buffering and disk replication");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
|
|
GCNEW(dss);
|
|
dss->ao = ao;
|
|
dss->callback = remus_failover_cb;
|
|
dss->domid = domid;
|
|
dss->fd = send_fd;
|
|
/* TODO do something with recv_fd */
|
|
dss->type = type;
|
|
dss->live = 1;
|
|
dss->debug = 0;
|
|
dss->remus = info;
|
|
|
|
assert(info);
|
|
|
|
/* Point of no return */
|
|
libxl__remus_setup(egc, dss);
|
|
return AO_INPROGRESS;
|
|
|
|
out:
|
|
return AO_CREATE_FAIL(rc);
|
|
}
|
|
|
|
static void libxl__remus_setup(libxl__egc *egc,
|
|
libxl__domain_suspend_state *dss)
|
|
{
|
|
/* Convenience aliases */
|
|
libxl__remus_devices_state *const rds = &dss->rds;
|
|
const libxl_domain_remus_info *const info = dss->remus;
|
|
|
|
STATE_AO_GC(dss->ao);
|
|
|
|
if (libxl_defbool_val(info->netbuf)) {
|
|
if (!libxl__netbuffer_enabled(gc)) {
|
|
LOG(ERROR, "Remus: No support for network buffering");
|
|
goto out;
|
|
}
|
|
rds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VIF);
|
|
}
|
|
|
|
if (libxl_defbool_val(info->diskbuf))
|
|
rds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VBD);
|
|
|
|
rds->ao = ao;
|
|
rds->domid = dss->domid;
|
|
rds->callback = remus_setup_done;
|
|
|
|
libxl__remus_devices_setup(egc, rds);
|
|
return;
|
|
|
|
out:
|
|
dss->callback(egc, dss, ERROR_FAIL);
|
|
}
|
|
|
|
static void remus_setup_done(libxl__egc *egc,
|
|
libxl__remus_devices_state *rds, int rc)
|
|
{
|
|
libxl__domain_suspend_state *dss = CONTAINER_OF(rds, *dss, rds);
|
|
STATE_AO_GC(dss->ao);
|
|
|
|
if (!rc) {
|
|
libxl__domain_save(egc, dss);
|
|
return;
|
|
}
|
|
|
|
LOG(ERROR, "Remus: failed to setup device for guest with domid %u, rc %d",
|
|
dss->domid, rc);
|
|
rds->callback = remus_setup_failed;
|
|
libxl__remus_devices_teardown(egc, rds);
|
|
}
|
|
|
|
static void remus_setup_failed(libxl__egc *egc,
|
|
libxl__remus_devices_state *rds, int rc)
|
|
{
|
|
libxl__domain_suspend_state *dss = CONTAINER_OF(rds, *dss, rds);
|
|
STATE_AO_GC(dss->ao);
|
|
|
|
if (rc)
|
|
LOG(ERROR, "Remus: failed to teardown device after setup failed"
|
|
" for guest with domid %u, rc %d", dss->domid, rc);
|
|
|
|
dss->callback(egc, dss, rc);
|
|
}
|
|
|
|
static void remus_failover_cb(libxl__egc *egc,
|
|
libxl__domain_suspend_state *dss, int rc)
|
|
{
|
|
STATE_AO_GC(dss->ao);
|
|
/*
|
|
* With Remus, if we reach this point, it means either
|
|
* backup died or some network error occurred preventing us
|
|
* from sending checkpoints.
|
|
*/
|
|
libxl__ao_complete(egc, ao, rc);
|
|
}
|
|
|
|
static void domain_suspend_cb(libxl__egc *egc,
|
|
libxl__domain_suspend_state *dss, int rc)
|
|
{
|
|
STATE_AO_GC(dss->ao);
|
|
int flrc;
|
|
|
|
flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl);
|
|
/* If suspend has failed already then report that error not this one. */
|
|
if (flrc && !rc) rc = flrc;
|
|
|
|
libxl__ao_complete(egc,ao,rc);
|
|
|
|
}
|
|
|
|
int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
|
|
const libxl_asyncop_how *ao_how)
|
|
{
|
|
AO_CREATE(ctx, domid, ao_how);
|
|
int rc;
|
|
|
|
libxl_domain_type type = libxl__domain_type(gc, domid);
|
|
if (type == LIBXL_DOMAIN_TYPE_INVALID) {
|
|
rc = ERROR_FAIL;
|
|
goto out_err;
|
|
}
|
|
|
|
libxl__domain_suspend_state *dss;
|
|
GCNEW(dss);
|
|
|
|
dss->ao = ao;
|
|
dss->callback = domain_suspend_cb;
|
|
|
|
dss->domid = domid;
|
|
dss->fd = fd;
|
|
dss->type = type;
|
|
dss->live = flags & LIBXL_SUSPEND_LIVE;
|
|
dss->debug = flags & LIBXL_SUSPEND_DEBUG;
|
|
|
|
rc = libxl__fd_flags_modify_save(gc, dss->fd,
|
|
~(O_NONBLOCK|O_NDELAY), 0,
|
|
&dss->fdfl);
|
|
if (rc < 0) goto out_err;
|
|
|
|
libxl__domain_save(egc, dss);
|
|
return AO_INPROGRESS;
|
|
|
|
out_err:
|
|
return AO_CREATE_FAIL(rc);
|
|
}
|
|
|
|
int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
int ret;
|
|
GC_INIT(ctx);
|
|
ret = xc_domain_pause(ctx->xch, domid);
|
|
if (ret<0) {
|
|
LOGE(ERROR, "pausing domain %d", domid);
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
|
|
const char *filename,
|
|
const libxl_asyncop_how *ao_how)
|
|
{
|
|
AO_CREATE(ctx, domid, ao_how);
|
|
int ret, rc;
|
|
|
|
ret = xc_domain_dumpcore(ctx->xch, domid, filename);
|
|
if (ret<0) {
|
|
LOGE(ERROR, "core dumping domain %d to %s", domid, filename);
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
|
|
libxl__ao_complete(egc, ao, rc);
|
|
|
|
return AO_INPROGRESS;
|
|
}
|
|
|
|
int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
GC_INIT(ctx);
|
|
int ret, rc = 0;
|
|
|
|
libxl_domain_type type = libxl__domain_type(gc, domid);
|
|
if (type == LIBXL_DOMAIN_TYPE_INVALID) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
if (type == LIBXL_DOMAIN_TYPE_HVM) {
|
|
if (libxl__device_model_version_running(gc, domid) !=
|
|
LIBXL_DEVICE_MODEL_VERSION_NONE) {
|
|
rc = libxl__domain_resume_device_model(gc, domid);
|
|
if (rc < 0) {
|
|
LOG(ERROR, "failed to unpause device model for domain %u:%d",
|
|
domid, rc);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
ret = xc_domain_unpause(ctx->xch, domid);
|
|
if (ret<0) {
|
|
LOGE(ERROR, "unpausing domain %d", domid);
|
|
rc = ERROR_FAIL;
|
|
}
|
|
out:
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid)
|
|
{
|
|
libxl_ctx *ctx = libxl__gc_owner(gc);
|
|
|
|
uint64_t pvdriver = 0;
|
|
int ret;
|
|
|
|
libxl_domain_type domtype = libxl__domain_type(gc, domid);
|
|
if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
|
|
return ERROR_FAIL;
|
|
|
|
if (domtype == LIBXL_DOMAIN_TYPE_PV)
|
|
return 1;
|
|
|
|
ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver);
|
|
if (ret<0) {
|
|
LOGE(ERROR, "getting HVM callback IRQ");
|
|
return ERROR_FAIL;
|
|
}
|
|
return !!pvdriver;
|
|
}
|
|
|
|
const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid)
|
|
{
|
|
const char *dom_path;
|
|
|
|
dom_path = libxl__xs_get_dompath(gc, domid);
|
|
if (!dom_path)
|
|
return NULL;
|
|
|
|
return GCSPRINTF("%s/control/shutdown", dom_path);
|
|
}
|
|
|
|
char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t,
|
|
uint32_t domid)
|
|
{
|
|
const char *shutdown_path;
|
|
|
|
shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
|
|
if (!shutdown_path)
|
|
return NULL;
|
|
|
|
return libxl__xs_read(gc, t, shutdown_path);
|
|
}
|
|
|
|
int libxl__domain_pvcontrol_write(libxl__gc *gc, xs_transaction_t t,
|
|
uint32_t domid, const char *cmd)
|
|
{
|
|
const char *shutdown_path;
|
|
|
|
shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
|
|
if (!shutdown_path)
|
|
return ERROR_FAIL;
|
|
|
|
return libxl__xs_printf(gc, t, shutdown_path, "%s", cmd);
|
|
}
|
|
|
|
static int libxl__domain_pvcontrol(libxl__gc *gc, uint32_t domid,
|
|
const char *cmd)
|
|
{
|
|
int ret;
|
|
|
|
ret = libxl__domain_pvcontrol_available(gc, domid);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (!ret)
|
|
return ERROR_NOPARAVIRT;
|
|
|
|
return libxl__domain_pvcontrol_write(gc, XBT_NULL, domid, cmd);
|
|
}
|
|
|
|
int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
GC_INIT(ctx);
|
|
int ret;
|
|
ret = libxl__domain_pvcontrol(gc, domid, "poweroff");
|
|
GC_FREE;
|
|
return ret;
|
|
}
|
|
|
|
int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
GC_INIT(ctx);
|
|
int ret;
|
|
ret = libxl__domain_pvcontrol(gc, domid, "reboot");
|
|
GC_FREE;
|
|
return ret;
|
|
}
|
|
|
|
static void domain_death_occurred(libxl__egc *egc,
|
|
libxl_evgen_domain_death **evg_upd,
|
|
const char *why) {
|
|
/* Removes **evg_upd from death_list and puts it on death_reported
|
|
* and advances *evg_upd to the next entry.
|
|
* Call sites in domain_death_xswatch_callback must use "continue". */
|
|
EGC_GC;
|
|
libxl_evgen_domain_death *const evg = *evg_upd;
|
|
|
|
LOG(DEBUG, "%s", why);
|
|
|
|
libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry);
|
|
*evg_upd = evg_next;
|
|
|
|
libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user);
|
|
|
|
libxl__event_occurred(egc, ev);
|
|
|
|
evg->death_reported = 1;
|
|
LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
|
|
LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry);
|
|
}
|
|
|
|
static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
|
|
const char *wpath, const char *epath) {
|
|
EGC_GC;
|
|
libxl_evgen_domain_death *evg;
|
|
int rc;
|
|
|
|
CTX_LOCK;
|
|
|
|
evg = LIBXL_TAILQ_FIRST(&CTX->death_list);
|
|
|
|
for (;;) {
|
|
if (!evg) goto out;
|
|
|
|
int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1;
|
|
xc_domaininfo_t domaininfos[nentries];
|
|
const xc_domaininfo_t *got = domaininfos, *gotend;
|
|
|
|
rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos);
|
|
if (rc == -1) {
|
|
LIBXL__EVENT_DISASTER(egc, "xc_domain_getinfolist failed while"
|
|
" processing @releaseDomain watch event",
|
|
errno, 0);
|
|
goto out;
|
|
}
|
|
gotend = &domaininfos[rc];
|
|
|
|
LOG(DEBUG, "[evg=%p:%"PRIu32"] nentries=%d rc=%d %ld..%ld",
|
|
evg, evg->domid, nentries, rc,
|
|
rc>0 ? (long)domaininfos[0].domain : 0,
|
|
rc>0 ? (long)domaininfos[rc-1].domain : 0);
|
|
|
|
for (;;) {
|
|
if (!evg) {
|
|
LOG(DEBUG, "[evg=0] all reported");
|
|
goto all_reported;
|
|
}
|
|
|
|
LOG(DEBUG, "[evg=%p:%"PRIu32"]"
|
|
" got=domaininfos[%d] got->domain=%ld",
|
|
evg, evg->domid, (int)(got - domaininfos),
|
|
got < gotend ? (long)got->domain : -1L);
|
|
|
|
if (!rc) {
|
|
domain_death_occurred(egc, &evg, "empty list");
|
|
continue;
|
|
}
|
|
|
|
if (got == gotend) {
|
|
LOG(DEBUG, " got==gotend");
|
|
break;
|
|
}
|
|
|
|
if (got->domain > evg->domid) {
|
|
/* ie, the list doesn't contain evg->domid any more so
|
|
* the domain has been destroyed */
|
|
domain_death_occurred(egc, &evg, "missing from list");
|
|
continue;
|
|
}
|
|
|
|
if (got->domain < evg->domid) {
|
|
got++;
|
|
continue;
|
|
}
|
|
|
|
assert(evg->domid == got->domain);
|
|
LOG(DEBUG, " exists shutdown_reported=%d"" dominf.flags=%x",
|
|
evg->shutdown_reported, got->flags);
|
|
|
|
if (got->flags & XEN_DOMINF_dying) {
|
|
domain_death_occurred(egc, &evg, "dying");
|
|
continue;
|
|
}
|
|
|
|
if (!evg->shutdown_reported &&
|
|
(got->flags & XEN_DOMINF_shutdown)) {
|
|
libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN,
|
|
got->domain, evg->user);
|
|
|
|
LOG(DEBUG, " shutdown reporting");
|
|
|
|
ev->u.domain_shutdown.shutdown_reason =
|
|
(got->flags >> XEN_DOMINF_shutdownshift) &
|
|
XEN_DOMINF_shutdownmask;
|
|
libxl__event_occurred(egc, ev);
|
|
|
|
evg->shutdown_reported = 1;
|
|
}
|
|
evg = LIBXL_TAILQ_NEXT(evg, entry);
|
|
}
|
|
|
|
assert(rc); /* rc==0 results in us eating all evgs and quitting */
|
|
}
|
|
all_reported:
|
|
out:
|
|
|
|
LOG(DEBUG, "domain death search done");
|
|
|
|
CTX_UNLOCK;
|
|
}
|
|
|
|
int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
|
|
GC_INIT(ctx);
|
|
libxl_evgen_domain_death *evg, *evg_search;
|
|
int rc;
|
|
|
|
CTX_LOCK;
|
|
|
|
evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; }
|
|
memset(evg, 0, sizeof(*evg));
|
|
evg->domid = domid;
|
|
evg->user = user;
|
|
|
|
LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, ,
|
|
evg->domid > evg_search->domid);
|
|
|
|
if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) {
|
|
rc = libxl__ev_xswatch_register(gc, &ctx->death_watch,
|
|
domain_death_xswatch_callback, "@releaseDomain");
|
|
if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; }
|
|
}
|
|
|
|
*evgen_out = evg;
|
|
rc = 0;
|
|
|
|
out:
|
|
CTX_UNLOCK;
|
|
GC_FREE;
|
|
return rc;
|
|
};
|
|
|
|
void libxl__evdisable_domain_death(libxl__gc *gc,
|
|
libxl_evgen_domain_death *evg) {
|
|
CTX_LOCK;
|
|
|
|
if (!evg->death_reported)
|
|
LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
|
|
else
|
|
LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
|
|
|
|
free(evg);
|
|
|
|
if (!LIBXL_TAILQ_FIRST(&CTX->death_list) &&
|
|
libxl__ev_xswatch_isregistered(&CTX->death_watch))
|
|
libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
|
|
|
|
CTX_UNLOCK;
|
|
}
|
|
|
|
void libxl_evdisable_domain_death(libxl_ctx *ctx,
|
|
libxl_evgen_domain_death *evg) {
|
|
GC_INIT(ctx);
|
|
libxl__evdisable_domain_death(gc, evg);
|
|
GC_FREE;
|
|
}
|
|
|
|
static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
|
|
const char *wpath, const char *epath) {
|
|
EGC_GC;
|
|
libxl_evgen_disk_eject *evg = (void*)w;
|
|
char *backend;
|
|
char *value;
|
|
char backend_type[BACKEND_STRING_SIZE+1];
|
|
|
|
value = libxl__xs_read(gc, XBT_NULL, wpath);
|
|
|
|
if (!value || strcmp(value, "eject"))
|
|
return;
|
|
|
|
if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) {
|
|
LIBXL__EVENT_DISASTER(egc, "xs_write failed acknowledging eject",
|
|
errno, LIBXL_EVENT_TYPE_DISK_EJECT);
|
|
return;
|
|
}
|
|
|
|
libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user);
|
|
libxl_device_disk *disk = &ev->u.disk_eject.disk;
|
|
|
|
backend = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%.*s/backend", (int)strlen(wpath)-6,
|
|
wpath));
|
|
|
|
sscanf(backend,
|
|
"/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE)
|
|
"[a-z]/%*d/%*d",
|
|
&disk->backend_domid, backend_type);
|
|
if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) {
|
|
disk->backend = LIBXL_DISK_BACKEND_TAP;
|
|
} else if (!strcmp(backend_type, "qdisk")) {
|
|
disk->backend = LIBXL_DISK_BACKEND_QDISK;
|
|
} else {
|
|
disk->backend = LIBXL_DISK_BACKEND_UNKNOWN;
|
|
}
|
|
|
|
disk->pdev_path = strdup(""); /* xxx fixme malloc failure */
|
|
disk->format = LIBXL_DISK_FORMAT_EMPTY;
|
|
/* this value is returned to the user: do not free right away */
|
|
disk->vdev = xs_read(CTX->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/dev", backend), NULL);
|
|
disk->removable = 1;
|
|
disk->readwrite = 0;
|
|
disk->is_cdrom = 1;
|
|
|
|
libxl__event_occurred(egc, ev);
|
|
}
|
|
|
|
int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid,
|
|
const char *vdev, libxl_ev_user user,
|
|
libxl_evgen_disk_eject **evgen_out) {
|
|
GC_INIT(ctx);
|
|
CTX_LOCK;
|
|
int rc;
|
|
char *path;
|
|
libxl_evgen_disk_eject *evg = NULL;
|
|
|
|
evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; }
|
|
memset(evg, 0, sizeof(*evg));
|
|
evg->user = user;
|
|
evg->domid = guest_domid;
|
|
LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry);
|
|
|
|
evg->vdev = strdup(vdev);
|
|
if (!evg->vdev) { rc = ERROR_NOMEM; goto out; }
|
|
|
|
uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
|
|
|
|
if (!domid)
|
|
domid = guest_domid;
|
|
|
|
path = GCSPRINTF("%s/device/vbd/%d/eject",
|
|
libxl__xs_get_dompath(gc, domid),
|
|
libxl__device_disk_dev_number(vdev, NULL, NULL));
|
|
if (!path) { rc = ERROR_NOMEM; goto out; }
|
|
|
|
rc = libxl__ev_xswatch_register(gc, &evg->watch,
|
|
disk_eject_xswatch_callback, path);
|
|
if (rc) goto out;
|
|
|
|
*evgen_out = evg;
|
|
CTX_UNLOCK;
|
|
GC_FREE;
|
|
return 0;
|
|
|
|
out:
|
|
if (evg)
|
|
libxl__evdisable_disk_eject(gc, evg);
|
|
CTX_UNLOCK;
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
|
|
CTX_LOCK;
|
|
|
|
LIBXL_LIST_REMOVE(evg, entry);
|
|
|
|
if (libxl__ev_xswatch_isregistered(&evg->watch))
|
|
libxl__ev_xswatch_deregister(gc, &evg->watch);
|
|
|
|
free(evg->vdev);
|
|
free(evg);
|
|
|
|
CTX_UNLOCK;
|
|
}
|
|
|
|
void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
|
|
GC_INIT(ctx);
|
|
libxl__evdisable_disk_eject(gc, evg);
|
|
GC_FREE;
|
|
}
|
|
|
|
/* Callbacks for libxl_domain_destroy */
|
|
|
|
static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
|
|
int rc);
|
|
|
|
int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
|
|
const libxl_asyncop_how *ao_how)
|
|
{
|
|
AO_CREATE(ctx, domid, ao_how);
|
|
libxl__domain_destroy_state *dds;
|
|
|
|
GCNEW(dds);
|
|
dds->ao = ao;
|
|
dds->domid = domid;
|
|
dds->callback = domain_destroy_cb;
|
|
libxl__domain_destroy(egc, dds);
|
|
|
|
return AO_INPROGRESS;
|
|
}
|
|
|
|
static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
|
|
int rc)
|
|
{
|
|
STATE_AO_GC(dds->ao);
|
|
|
|
if (rc)
|
|
LOG(ERROR, "destruction of domain %u failed", dds->domid);
|
|
|
|
libxl__ao_complete(egc, ao, rc);
|
|
}
|
|
|
|
/* Callbacks for libxl__domain_destroy */
|
|
|
|
static void stubdom_destroy_callback(libxl__egc *egc,
|
|
libxl__destroy_domid_state *dis,
|
|
int rc);
|
|
|
|
static void domain_destroy_callback(libxl__egc *egc,
|
|
libxl__destroy_domid_state *dis,
|
|
int rc);
|
|
|
|
static void destroy_finish_check(libxl__egc *egc,
|
|
libxl__domain_destroy_state *dds);
|
|
|
|
void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds)
|
|
{
|
|
STATE_AO_GC(dds->ao);
|
|
uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid);
|
|
|
|
if (stubdomid) {
|
|
dds->stubdom.ao = ao;
|
|
dds->stubdom.domid = stubdomid;
|
|
dds->stubdom.callback = stubdom_destroy_callback;
|
|
dds->stubdom.soft_reset = false;
|
|
libxl__destroy_domid(egc, &dds->stubdom);
|
|
} else {
|
|
dds->stubdom_finished = 1;
|
|
}
|
|
|
|
dds->domain.ao = ao;
|
|
dds->domain.domid = dds->domid;
|
|
dds->domain.callback = domain_destroy_callback;
|
|
dds->domain.soft_reset = dds->soft_reset;
|
|
libxl__destroy_domid(egc, &dds->domain);
|
|
}
|
|
|
|
static void stubdom_destroy_callback(libxl__egc *egc,
|
|
libxl__destroy_domid_state *dis,
|
|
int rc)
|
|
{
|
|
STATE_AO_GC(dis->ao);
|
|
libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom);
|
|
const char *savefile;
|
|
|
|
if (rc) {
|
|
LOG(ERROR, "unable to destroy stubdom with domid %u", dis->domid);
|
|
dds->rc = rc;
|
|
}
|
|
|
|
dds->stubdom_finished = 1;
|
|
savefile = libxl__device_model_savefile(gc, dis->domid);
|
|
rc = libxl__remove_file(gc, savefile);
|
|
/*
|
|
* On suspend libxl__domain_save_device_model will have already
|
|
* unlinked the save file.
|
|
*/
|
|
if (rc) {
|
|
LOG(ERROR, "failed to remove device-model savefile %s", savefile);
|
|
}
|
|
|
|
destroy_finish_check(egc, dds);
|
|
}
|
|
|
|
static void domain_destroy_callback(libxl__egc *egc,
|
|
libxl__destroy_domid_state *dis,
|
|
int rc)
|
|
{
|
|
STATE_AO_GC(dis->ao);
|
|
libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain);
|
|
|
|
if (rc) {
|
|
LOG(ERROR, "unable to destroy guest with domid %u", dis->domid);
|
|
dds->rc = rc;
|
|
}
|
|
|
|
dds->domain_finished = 1;
|
|
destroy_finish_check(egc, dds);
|
|
}
|
|
|
|
static void destroy_finish_check(libxl__egc *egc,
|
|
libxl__domain_destroy_state *dds)
|
|
{
|
|
if (!(dds->domain_finished && dds->stubdom_finished))
|
|
return;
|
|
|
|
dds->callback(egc, dds, dds->rc);
|
|
}
|
|
|
|
/* Callbacks for libxl__destroy_domid */
|
|
static void devices_destroy_cb(libxl__egc *egc,
|
|
libxl__devices_remove_state *drs,
|
|
int rc);
|
|
|
|
static void domain_destroy_domid_cb(libxl__egc *egc,
|
|
libxl__ev_child *destroyer,
|
|
pid_t pid, int status);
|
|
|
|
void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis)
|
|
{
|
|
STATE_AO_GC(dis->ao);
|
|
libxl_ctx *ctx = CTX;
|
|
uint32_t domid = dis->domid;
|
|
char *dom_path;
|
|
char *pid;
|
|
int rc, dm_present;
|
|
|
|
libxl__ev_child_init(&dis->destroyer);
|
|
|
|
rc = libxl_domain_info(ctx, NULL, domid);
|
|
switch(rc) {
|
|
case 0:
|
|
break;
|
|
case ERROR_DOMAIN_NOTFOUND:
|
|
LOG(ERROR, "non-existant domain %d", domid);
|
|
default:
|
|
goto out;
|
|
}
|
|
|
|
switch (libxl__domain_type(gc, domid)) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
if (libxl_get_stubdom_id(CTX, domid)) {
|
|
dm_present = 0;
|
|
break;
|
|
}
|
|
/* fall through */
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
pid = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("/local/domain/%d/image/device-model-pid", domid));
|
|
dm_present = (pid != NULL);
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_INVALID:
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
dom_path = libxl__xs_get_dompath(gc, domid);
|
|
if (!dom_path) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
if (libxl__device_pci_destroy_all(gc, domid) < 0)
|
|
LOG(ERROR, "pci shutdown failed for domid %d", domid);
|
|
rc = xc_domain_pause(ctx->xch, domid);
|
|
if (rc < 0) {
|
|
LOGEV(ERROR, rc, "xc_domain_pause failed for %d", domid);
|
|
}
|
|
if (dm_present) {
|
|
if (libxl__destroy_device_model(gc, domid) < 0)
|
|
LOG(ERROR, "libxl__destroy_device_model failed for %d", domid);
|
|
|
|
libxl__qmp_cleanup(gc, domid);
|
|
}
|
|
dis->drs.ao = ao;
|
|
dis->drs.domid = domid;
|
|
dis->drs.callback = devices_destroy_cb;
|
|
dis->drs.force = 1;
|
|
libxl__devices_destroy(egc, &dis->drs);
|
|
return;
|
|
|
|
out:
|
|
assert(rc);
|
|
dis->callback(egc, dis, rc);
|
|
return;
|
|
}
|
|
|
|
static void devices_destroy_cb(libxl__egc *egc,
|
|
libxl__devices_remove_state *drs,
|
|
int rc)
|
|
{
|
|
STATE_AO_GC(drs->ao);
|
|
libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs);
|
|
libxl_ctx *ctx = CTX;
|
|
uint32_t domid = dis->domid;
|
|
char *dom_path;
|
|
char *vm_path;
|
|
libxl__domain_userdata_lock *lock;
|
|
|
|
dom_path = libxl__xs_get_dompath(gc, domid);
|
|
if (!dom_path) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
if (rc < 0)
|
|
LOG(ERROR, "libxl__devices_destroy failed for %d", domid);
|
|
|
|
vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path));
|
|
if (vm_path)
|
|
if (!xs_rm(ctx->xsh, XBT_NULL, vm_path))
|
|
LOGE(ERROR, "xs_rm failed for %s", vm_path);
|
|
|
|
if (!xs_rm(ctx->xsh, XBT_NULL, dom_path))
|
|
LOGE(ERROR, "xs_rm failed for %s", dom_path);
|
|
|
|
xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid));
|
|
xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid));
|
|
|
|
/* This is async operation, we already hold CTX lock */
|
|
lock = libxl__lock_domain_userdata(gc, domid);
|
|
if (!lock) {
|
|
rc = ERROR_LOCK_FAIL;
|
|
goto out;
|
|
}
|
|
libxl__userdata_destroyall(gc, domid);
|
|
|
|
libxl__unlock_domain_userdata(lock);
|
|
|
|
/* Clean up qemu-save and qemu-resume files. They are
|
|
* intermediate files created by libxc. Unfortunately they
|
|
* don't fit in existing userdata scheme very well. In soft reset
|
|
* case we need to keep the file.
|
|
*/
|
|
if (!dis->soft_reset) {
|
|
rc = libxl__remove_file(gc,
|
|
libxl__device_model_savefile(gc, domid));
|
|
if (rc < 0) goto out;
|
|
}
|
|
rc = libxl__remove_file(gc,
|
|
GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid));
|
|
if (rc < 0) goto out;
|
|
|
|
rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb);
|
|
if (rc < 0) goto out;
|
|
if (!rc) { /* child */
|
|
ctx->xch = xc_interface_open(ctx->lg,0,0);
|
|
if (!ctx->xch) goto badchild;
|
|
|
|
if (!dis->soft_reset) {
|
|
rc = xc_domain_destroy(ctx->xch, domid);
|
|
} else {
|
|
rc = xc_domain_pause(ctx->xch, domid);
|
|
if (rc < 0) goto badchild;
|
|
rc = xc_domain_soft_reset(ctx->xch, domid);
|
|
if (rc < 0) goto badchild;
|
|
rc = xc_domain_unpause(ctx->xch, domid);
|
|
}
|
|
if (rc < 0) goto badchild;
|
|
_exit(0);
|
|
|
|
badchild:
|
|
if (errno > 0 && errno < 126) {
|
|
_exit(errno);
|
|
} else {
|
|
LOGE(ERROR,
|
|
"xc_domain_destroy failed for %d (with difficult errno value %d)",
|
|
domid, errno);
|
|
_exit(-1);
|
|
}
|
|
}
|
|
LOG(DEBUG, "forked pid %ld for destroy of domain %d", (long)rc, domid);
|
|
|
|
return;
|
|
|
|
out:
|
|
dis->callback(egc, dis, rc);
|
|
return;
|
|
}
|
|
|
|
static void domain_destroy_domid_cb(libxl__egc *egc,
|
|
libxl__ev_child *destroyer,
|
|
pid_t pid, int status)
|
|
{
|
|
libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer);
|
|
STATE_AO_GC(dis->ao);
|
|
int rc;
|
|
|
|
if (status) {
|
|
if (WIFEXITED(status) && WEXITSTATUS(status)<126) {
|
|
LOGEV(ERROR, WEXITSTATUS(status),
|
|
"xc_domain_destroy failed for %"PRIu32"", dis->domid);
|
|
} else {
|
|
libxl_report_child_exitstatus(CTX, XTL_ERROR,
|
|
"async domain destroy", pid, status);
|
|
}
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
rc = 0;
|
|
|
|
out:
|
|
dis->callback(egc, dis, rc);
|
|
}
|
|
|
|
int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num,
|
|
libxl_console_type type)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *p = GCSPRINTF("%s/xenconsole", libxl__private_bindir_path());
|
|
char *domid_s = GCSPRINTF("%d", domid);
|
|
char *cons_num_s = GCSPRINTF("%d", cons_num);
|
|
char *cons_type_s;
|
|
|
|
switch (type) {
|
|
case LIBXL_CONSOLE_TYPE_PV:
|
|
cons_type_s = "pv";
|
|
break;
|
|
case LIBXL_CONSOLE_TYPE_SERIAL:
|
|
cons_type_s = "serial";
|
|
break;
|
|
default:
|
|
goto out;
|
|
}
|
|
|
|
execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s, (void *)NULL);
|
|
|
|
out:
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
|
|
libxl_console_type type, char **path)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *dom_path;
|
|
char *tty_path;
|
|
char *tty;
|
|
int rc;
|
|
|
|
dom_path = libxl__xs_get_dompath(gc, domid);
|
|
if (!dom_path) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
switch (type) {
|
|
case LIBXL_CONSOLE_TYPE_SERIAL:
|
|
tty_path = GCSPRINTF("%s/serial/0/tty", dom_path);
|
|
break;
|
|
case LIBXL_CONSOLE_TYPE_PV:
|
|
if (cons_num == 0)
|
|
tty_path = GCSPRINTF("%s/console/tty", dom_path);
|
|
else
|
|
tty_path = GCSPRINTF("%s/device/console/%d/tty", dom_path,
|
|
cons_num);
|
|
break;
|
|
default:
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
|
|
tty = libxl__xs_read(gc, XBT_NULL, tty_path);
|
|
if (!tty || tty[0] == '\0') {
|
|
LOGE(ERROR,"unable to read console tty path `%s'",tty_path);
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
*path = libxl__strdup(NOGC, tty);
|
|
rc = 0;
|
|
out:
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
static int libxl__primary_console_find(libxl_ctx *ctx, uint32_t domid_vm,
|
|
uint32_t *domid, int *cons_num,
|
|
libxl_console_type *type)
|
|
{
|
|
GC_INIT(ctx);
|
|
uint32_t stubdomid = libxl_get_stubdom_id(ctx, domid_vm);
|
|
int rc;
|
|
|
|
if (stubdomid) {
|
|
*domid = stubdomid;
|
|
*cons_num = STUBDOM_CONSOLE_SERIAL;
|
|
*type = LIBXL_CONSOLE_TYPE_PV;
|
|
} else {
|
|
switch (libxl__domain_type(gc, domid_vm)) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
*domid = domid_vm;
|
|
*cons_num = 0;
|
|
*type = LIBXL_CONSOLE_TYPE_SERIAL;
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
*domid = domid_vm;
|
|
*cons_num = 0;
|
|
*type = LIBXL_CONSOLE_TYPE_PV;
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_INVALID:
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
default: abort();
|
|
}
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm)
|
|
{
|
|
uint32_t domid;
|
|
int cons_num;
|
|
libxl_console_type type;
|
|
int rc;
|
|
|
|
rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
|
|
if ( rc ) return rc;
|
|
return libxl_console_exec(ctx, domid, cons_num, type);
|
|
}
|
|
|
|
int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm,
|
|
char **path)
|
|
{
|
|
uint32_t domid;
|
|
int cons_num;
|
|
libxl_console_type type;
|
|
int rc;
|
|
|
|
rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type);
|
|
if ( rc ) return rc;
|
|
return libxl_console_get_tty(ctx, domid, cons_num, type, path);
|
|
}
|
|
|
|
int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass)
|
|
{
|
|
GC_INIT(ctx);
|
|
const char *vnc_port;
|
|
const char *vnc_listen = NULL, *vnc_pass = NULL;
|
|
int port = 0, autopass_fd = -1;
|
|
char *vnc_bin, *args[] = {
|
|
"vncviewer",
|
|
NULL, /* hostname:display */
|
|
NULL, /* -autopass */
|
|
NULL,
|
|
};
|
|
|
|
vnc_port = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF(
|
|
"/local/domain/%d/console/vnc-port", domid));
|
|
if (!vnc_port) {
|
|
LOG(ERROR, "Cannot get vnc-port of domain %d", domid);
|
|
goto x_fail;
|
|
}
|
|
|
|
port = atoi(vnc_port) - 5900;
|
|
|
|
vnc_listen = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("/local/domain/%d/console/vnc-listen",
|
|
domid));
|
|
|
|
if ( autopass )
|
|
vnc_pass = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("/local/domain/%d/console/vnc-pass",
|
|
domid));
|
|
|
|
if ( NULL == vnc_listen )
|
|
vnc_listen = "localhost";
|
|
|
|
if ( (vnc_bin = getenv("VNCVIEWER")) )
|
|
args[0] = vnc_bin;
|
|
|
|
args[1] = GCSPRINTF("%s:%d", vnc_listen, port);
|
|
|
|
if ( vnc_pass ) {
|
|
char tmpname[] = "/tmp/vncautopass.XXXXXX";
|
|
autopass_fd = mkstemp(tmpname);
|
|
if ( autopass_fd < 0 ) {
|
|
LOGE(ERROR, "mkstemp %s failed", tmpname);
|
|
goto x_fail;
|
|
}
|
|
|
|
if ( unlink(tmpname) ) {
|
|
/* should never happen */
|
|
LOGE(ERROR, "unlink %s failed", tmpname);
|
|
goto x_fail;
|
|
}
|
|
|
|
if ( libxl_write_exactly(ctx, autopass_fd, vnc_pass, strlen(vnc_pass),
|
|
tmpname, "vnc password") )
|
|
goto x_fail;
|
|
|
|
if ( lseek(autopass_fd, SEEK_SET, 0) ) {
|
|
LOGE(ERROR, "rewind %s (autopass) failed", tmpname);
|
|
goto x_fail;
|
|
}
|
|
|
|
args[2] = "-autopass";
|
|
}
|
|
|
|
libxl__exec(gc, autopass_fd, -1, -1, args[0], args, NULL);
|
|
|
|
x_fail:
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
int libxl__get_domid(libxl__gc *gc, uint32_t *domid)
|
|
{
|
|
int rc;
|
|
const char *xs_domid;
|
|
|
|
rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid);
|
|
if (rc) goto out;
|
|
if (!xs_domid) {
|
|
LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH);
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
*domid = atoi(xs_domid);
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
/* generic callback for devices that only need to set ao_complete */
|
|
static void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev)
|
|
{
|
|
STATE_AO_GC(aodev->ao);
|
|
|
|
if (aodev->rc) {
|
|
if (aodev->dev) {
|
|
LOG(ERROR, "unable to %s %s with id %u",
|
|
libxl__device_action_to_string(aodev->action),
|
|
libxl__device_kind_to_string(aodev->dev->kind),
|
|
aodev->dev->devid);
|
|
} else {
|
|
LOG(ERROR, "unable to %s device",
|
|
libxl__device_action_to_string(aodev->action));
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
libxl__ao_complete(egc, ao, aodev->rc);
|
|
return;
|
|
}
|
|
|
|
/* common function to get next device id */
|
|
static int libxl__device_nextid(libxl__gc *gc, uint32_t domid, char *device)
|
|
{
|
|
char *dompath, **l;
|
|
unsigned int nb;
|
|
int nextid = -1;
|
|
|
|
if (!(dompath = libxl__xs_get_dompath(gc, domid)))
|
|
return nextid;
|
|
|
|
l = libxl__xs_directory(gc, XBT_NULL,
|
|
GCSPRINTF("%s/device/%s", dompath, device), &nb);
|
|
if (l == NULL || nb == 0)
|
|
nextid = 0;
|
|
else
|
|
nextid = strtoul(l[nb - 1], NULL, 10) + 1;
|
|
|
|
return nextid;
|
|
}
|
|
|
|
static int libxl__resolve_domid(libxl__gc *gc, const char *name,
|
|
uint32_t *domid)
|
|
{
|
|
if (!name)
|
|
return 0;
|
|
return libxl_domain_qualifier_to_domid(CTX, name, domid);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int libxl__device_vtpm_setdefault(libxl__gc *gc, libxl_device_vtpm *vtpm)
|
|
{
|
|
int rc;
|
|
if (libxl_uuid_is_nil(&vtpm->uuid)) {
|
|
libxl_uuid_generate(&vtpm->uuid);
|
|
}
|
|
rc = libxl__resolve_domid(gc, vtpm->backend_domname, &vtpm->backend_domid);
|
|
return rc;
|
|
}
|
|
|
|
static int libxl__device_from_vtpm(libxl__gc *gc, uint32_t domid,
|
|
libxl_device_vtpm *vtpm,
|
|
libxl__device *device)
|
|
{
|
|
device->backend_devid = vtpm->devid;
|
|
device->backend_domid = vtpm->backend_domid;
|
|
device->backend_kind = LIBXL__DEVICE_KIND_VTPM;
|
|
device->devid = vtpm->devid;
|
|
device->domid = domid;
|
|
device->kind = LIBXL__DEVICE_KIND_VTPM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid,
|
|
libxl_device_vtpm *vtpm,
|
|
libxl__ao_device *aodev)
|
|
{
|
|
STATE_AO_GC(aodev->ao);
|
|
flexarray_t *front;
|
|
flexarray_t *back;
|
|
libxl__device *device;
|
|
int rc;
|
|
xs_transaction_t t = XBT_NULL;
|
|
libxl_domain_config d_config;
|
|
libxl_device_vtpm vtpm_saved;
|
|
libxl__domain_userdata_lock *lock = NULL;
|
|
|
|
libxl_domain_config_init(&d_config);
|
|
libxl_device_vtpm_init(&vtpm_saved);
|
|
libxl_device_vtpm_copy(CTX, &vtpm_saved, vtpm);
|
|
|
|
rc = libxl__device_vtpm_setdefault(gc, vtpm);
|
|
if (rc) goto out;
|
|
|
|
front = flexarray_make(gc, 16, 1);
|
|
back = flexarray_make(gc, 16, 1);
|
|
|
|
if (vtpm->devid == -1) {
|
|
if ((vtpm->devid = libxl__device_nextid(gc, domid, "vtpm")) < 0) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
libxl__update_config_vtpm(gc, &vtpm_saved, vtpm);
|
|
|
|
GCNEW(device);
|
|
rc = libxl__device_from_vtpm(gc, domid, vtpm, device);
|
|
if ( rc != 0 ) goto out;
|
|
|
|
flexarray_append(back, "frontend-id");
|
|
flexarray_append(back, GCSPRINTF("%d", domid));
|
|
flexarray_append(back, "online");
|
|
flexarray_append(back, "1");
|
|
flexarray_append(back, "state");
|
|
flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
|
|
flexarray_append(back, "handle");
|
|
flexarray_append(back, GCSPRINTF("%d", vtpm->devid));
|
|
|
|
flexarray_append(back, "uuid");
|
|
flexarray_append(back, GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(vtpm->uuid)));
|
|
flexarray_append(back, "resume");
|
|
flexarray_append(back, "False");
|
|
|
|
flexarray_append(front, "backend-id");
|
|
flexarray_append(front, GCSPRINTF("%d", vtpm->backend_domid));
|
|
flexarray_append(front, "state");
|
|
flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
|
|
flexarray_append(front, "handle");
|
|
flexarray_append(front, GCSPRINTF("%d", vtpm->devid));
|
|
|
|
if (aodev->update_json) {
|
|
lock = libxl__lock_domain_userdata(gc, domid);
|
|
if (!lock) {
|
|
rc = ERROR_LOCK_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__get_domain_configuration(gc, domid, &d_config);
|
|
if (rc) goto out;
|
|
|
|
DEVICE_ADD(vtpm, vtpms, domid, &vtpm_saved, COMPARE_DEVID, &d_config);
|
|
}
|
|
|
|
for (;;) {
|
|
rc = libxl__xs_transaction_start(gc, &t);
|
|
if (rc) goto out;
|
|
|
|
rc = libxl__device_exists(gc, t, device);
|
|
if (rc < 0) goto out;
|
|
if (rc == 1) { /* already exists in xenstore */
|
|
LOG(ERROR, "device already exists in xenstore");
|
|
aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
|
|
rc = ERROR_DEVICE_EXISTS;
|
|
goto out;
|
|
}
|
|
|
|
if (aodev->update_json) {
|
|
rc = libxl__set_domain_configuration(gc, domid, &d_config);
|
|
if (rc) goto out;
|
|
}
|
|
|
|
libxl__device_generic_add(gc, t, device,
|
|
libxl__xs_kvs_of_flexarray(gc, back,
|
|
back->count),
|
|
libxl__xs_kvs_of_flexarray(gc, front,
|
|
front->count),
|
|
NULL);
|
|
|
|
rc = libxl__xs_transaction_commit(gc, &t);
|
|
if (!rc) break;
|
|
if (rc < 0) goto out;
|
|
}
|
|
|
|
aodev->dev = device;
|
|
aodev->action = LIBXL__DEVICE_ACTION_ADD;
|
|
libxl__wait_device_connection(egc, aodev);
|
|
|
|
rc = 0;
|
|
out:
|
|
libxl__xs_transaction_abort(gc, &t);
|
|
if (lock) libxl__unlock_domain_userdata(lock);
|
|
libxl_device_vtpm_dispose(&vtpm_saved);
|
|
libxl_domain_config_dispose(&d_config);
|
|
aodev->rc = rc;
|
|
if(rc) aodev->callback(egc, aodev);
|
|
return;
|
|
}
|
|
|
|
libxl_device_vtpm *libxl_device_vtpm_list(libxl_ctx *ctx, uint32_t domid, int *num)
|
|
{
|
|
GC_INIT(ctx);
|
|
|
|
libxl_device_vtpm* vtpms = NULL;
|
|
char* fe_path = NULL;
|
|
char** dir = NULL;
|
|
unsigned int ndirs = 0;
|
|
|
|
*num = 0;
|
|
|
|
fe_path = GCSPRINTF("%s/device/vtpm", libxl__xs_get_dompath(gc, domid));
|
|
dir = libxl__xs_directory(gc, XBT_NULL, fe_path, &ndirs);
|
|
if (dir && ndirs) {
|
|
vtpms = malloc(sizeof(*vtpms) * ndirs);
|
|
libxl_device_vtpm* vtpm;
|
|
libxl_device_vtpm* end = vtpms + ndirs;
|
|
for(vtpm = vtpms; vtpm < end; ++vtpm, ++dir) {
|
|
char* tmp;
|
|
const char* be_path = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/%s/backend",
|
|
fe_path, *dir));
|
|
|
|
libxl_device_vtpm_init(vtpm);
|
|
|
|
vtpm->devid = atoi(*dir);
|
|
|
|
tmp = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/%s/backend-id",
|
|
fe_path, *dir));
|
|
vtpm->backend_domid = atoi(tmp);
|
|
|
|
tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/uuid", be_path));
|
|
if (tmp) {
|
|
if(libxl_uuid_from_string(&(vtpm->uuid), tmp)) {
|
|
LOG(ERROR, "%s/uuid is a malformed uuid?? (%s) Probably a bug!!\n", be_path, tmp);
|
|
free(vtpms);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*num = ndirs;
|
|
|
|
GC_FREE;
|
|
return vtpms;
|
|
}
|
|
|
|
int libxl_device_vtpm_getinfo(libxl_ctx *ctx,
|
|
uint32_t domid,
|
|
libxl_device_vtpm *vtpm,
|
|
libxl_vtpminfo *vtpminfo)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *dompath, *vtpmpath;
|
|
char *val;
|
|
int rc = 0;
|
|
|
|
libxl_vtpminfo_init(vtpminfo);
|
|
dompath = libxl__xs_get_dompath(gc, domid);
|
|
vtpminfo->devid = vtpm->devid;
|
|
|
|
vtpmpath = GCSPRINTF("%s/device/vtpm/%d", dompath, vtpminfo->devid);
|
|
vtpminfo->backend = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/backend", vtpmpath), NULL);
|
|
if (!vtpminfo->backend) {
|
|
goto err;
|
|
}
|
|
if(!libxl__xs_read(gc, XBT_NULL, vtpminfo->backend)) {
|
|
goto err;
|
|
}
|
|
|
|
val = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/backend-id", vtpmpath));
|
|
vtpminfo->backend_id = val ? strtoul(val, NULL, 10) : -1;
|
|
|
|
val = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/state", vtpmpath));
|
|
vtpminfo->state = val ? strtoul(val, NULL, 10) : -1;
|
|
|
|
val = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/event-channel", vtpmpath));
|
|
vtpminfo->evtch = val ? strtoul(val, NULL, 10) : -1;
|
|
|
|
val = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/ring-ref", vtpmpath));
|
|
vtpminfo->rref = val ? strtoul(val, NULL, 10) : -1;
|
|
|
|
vtpminfo->frontend = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/frontend", vtpminfo->backend), NULL);
|
|
|
|
val = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/frontend-id", vtpminfo->backend));
|
|
vtpminfo->frontend_id = val ? strtoul(val, NULL, 10) : -1;
|
|
|
|
val = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/uuid", vtpminfo->backend));
|
|
if(val == NULL) {
|
|
LOG(ERROR, "%s/uuid does not exist!", vtpminfo->backend);
|
|
goto err;
|
|
}
|
|
if(libxl_uuid_from_string(&(vtpminfo->uuid), val)) {
|
|
LOG(ERROR,
|
|
"%s/uuid is a malformed uuid?? (%s) Probably a bug!\n",
|
|
vtpminfo->backend, val);
|
|
goto err;
|
|
}
|
|
|
|
goto exit;
|
|
err:
|
|
rc = ERROR_FAIL;
|
|
exit:
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_devid_to_device_vtpm(libxl_ctx *ctx,
|
|
uint32_t domid,
|
|
int devid,
|
|
libxl_device_vtpm *vtpm)
|
|
{
|
|
libxl_device_vtpm *vtpms;
|
|
int nb, i;
|
|
int rc;
|
|
|
|
vtpms = libxl_device_vtpm_list(ctx, domid, &nb);
|
|
if (!vtpms)
|
|
return ERROR_FAIL;
|
|
|
|
libxl_device_vtpm_init(vtpm);
|
|
rc = 1;
|
|
for (i = 0; i < nb; ++i) {
|
|
if(devid == vtpms[i].devid) {
|
|
vtpm->backend_domid = vtpms[i].backend_domid;
|
|
vtpm->devid = vtpms[i].devid;
|
|
libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid);
|
|
rc = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
libxl_device_vtpm_list_free(vtpms, nb);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
int libxl__device_disk_setdefault(libxl__gc *gc, libxl_device_disk *disk)
|
|
{
|
|
int rc;
|
|
|
|
libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite);
|
|
|
|
rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid);
|
|
if (rc < 0) return rc;
|
|
|
|
rc = libxl__device_disk_set_backend(gc, disk);
|
|
return rc;
|
|
}
|
|
|
|
int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
|
|
libxl_device_disk *disk,
|
|
libxl__device *device)
|
|
{
|
|
int devid;
|
|
|
|
devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
|
|
if (devid==-1) {
|
|
LOG(ERROR, "Invalid or unsupported"" virtual disk identifier %s",
|
|
disk->vdev);
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
device->backend_domid = disk->backend_domid;
|
|
device->backend_devid = devid;
|
|
|
|
switch (disk->backend) {
|
|
case LIBXL_DISK_BACKEND_PHY:
|
|
device->backend_kind = LIBXL__DEVICE_KIND_VBD;
|
|
break;
|
|
case LIBXL_DISK_BACKEND_TAP:
|
|
device->backend_kind = LIBXL__DEVICE_KIND_VBD;
|
|
break;
|
|
case LIBXL_DISK_BACKEND_QDISK:
|
|
device->backend_kind = LIBXL__DEVICE_KIND_QDISK;
|
|
break;
|
|
default:
|
|
LOG(ERROR, "unrecognized disk backend type: %d", disk->backend);
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
device->domid = domid;
|
|
device->devid = devid;
|
|
device->kind = LIBXL__DEVICE_KIND_VBD;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Specific function called directly only by local disk attach,
|
|
* all other users should instead use the regular
|
|
* libxl__device_disk_add wrapper
|
|
*
|
|
* The (optionally) passed function get_vdev will be used to
|
|
* set the vdev the disk should be attached to. When it is set the caller
|
|
* must also pass get_vdev_user, which will be passed to get_vdev.
|
|
*
|
|
* The passed get_vdev function is also in charge of printing
|
|
* the corresponding error message when appropiate.
|
|
*/
|
|
static void device_disk_add(libxl__egc *egc, uint32_t domid,
|
|
libxl_device_disk *disk,
|
|
libxl__ao_device *aodev,
|
|
char *get_vdev(libxl__gc *, void *,
|
|
xs_transaction_t),
|
|
void *get_vdev_user)
|
|
{
|
|
STATE_AO_GC(aodev->ao);
|
|
flexarray_t *front = NULL;
|
|
flexarray_t *back = NULL;
|
|
char *dev = NULL, *script;
|
|
libxl__device *device;
|
|
int rc;
|
|
libxl_ctx *ctx = gc->owner;
|
|
xs_transaction_t t = XBT_NULL;
|
|
libxl_domain_config d_config;
|
|
libxl_device_disk disk_saved;
|
|
libxl__domain_userdata_lock *lock = NULL;
|
|
|
|
libxl_domain_config_init(&d_config);
|
|
libxl_device_disk_init(&disk_saved);
|
|
libxl_device_disk_copy(ctx, &disk_saved, disk);
|
|
|
|
libxl_domain_type type = libxl__domain_type(gc, domid);
|
|
if (type == LIBXL_DOMAIN_TYPE_INVALID) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* get_vdev != NULL -> local attach
|
|
* get_vdev == NULL -> block attach
|
|
*
|
|
* We don't care about local attach state because it's only
|
|
* intermediate state.
|
|
*/
|
|
if (!get_vdev && aodev->update_json) {
|
|
lock = libxl__lock_domain_userdata(gc, domid);
|
|
if (!lock) {
|
|
rc = ERROR_LOCK_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__get_domain_configuration(gc, domid, &d_config);
|
|
if (rc) goto out;
|
|
|
|
DEVICE_ADD(disk, disks, domid, &disk_saved, COMPARE_DISK, &d_config);
|
|
}
|
|
|
|
for (;;) {
|
|
rc = libxl__xs_transaction_start(gc, &t);
|
|
if (rc) goto out;
|
|
|
|
if (get_vdev) {
|
|
assert(get_vdev_user);
|
|
disk->vdev = get_vdev(gc, get_vdev_user, t);
|
|
if (disk->vdev == NULL) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
rc = libxl__device_disk_setdefault(gc, disk);
|
|
if (rc) goto out;
|
|
|
|
front = flexarray_make(gc, 16, 1);
|
|
back = flexarray_make(gc, 16, 1);
|
|
|
|
GCNEW(device);
|
|
rc = libxl__device_from_disk(gc, domid, disk, device);
|
|
if (rc != 0) {
|
|
LOG(ERROR, "Invalid or unsupported"" virtual disk identifier %s",
|
|
disk->vdev);
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__device_exists(gc, t, device);
|
|
if (rc < 0) goto out;
|
|
if (rc == 1) { /* already exists in xenstore */
|
|
LOG(ERROR, "device already exists in xenstore");
|
|
aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
|
|
rc = ERROR_DEVICE_EXISTS;
|
|
goto out;
|
|
}
|
|
|
|
switch (disk->backend) {
|
|
case LIBXL_DISK_BACKEND_PHY:
|
|
dev = disk->pdev_path;
|
|
|
|
do_backend_phy:
|
|
flexarray_append(back, "params");
|
|
flexarray_append(back, dev);
|
|
|
|
script = libxl__abs_path(gc, disk->script?: "block",
|
|
libxl__xen_script_dir_path());
|
|
flexarray_append_pair(back, "script", script);
|
|
|
|
/* If the user did not supply a block script then we
|
|
* write the physical-device node ourselves.
|
|
*
|
|
* If the user did supply a script then that script is
|
|
* responsible for this since the block device may not
|
|
* exist yet.
|
|
*/
|
|
if (!disk->script &&
|
|
disk->backend_domid == LIBXL_TOOLSTACK_DOMID) {
|
|
int major, minor;
|
|
if (!libxl__device_physdisk_major_minor(dev, &major, &minor))
|
|
flexarray_append_pair(back, "physical-device",
|
|
GCSPRINTF("%x:%x", major, minor));
|
|
}
|
|
|
|
assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD);
|
|
break;
|
|
|
|
case LIBXL_DISK_BACKEND_TAP:
|
|
if (dev == NULL) {
|
|
dev = libxl__blktap_devpath(gc, disk->pdev_path,
|
|
disk->format);
|
|
if (!dev) {
|
|
LOG(ERROR, "failed to get blktap devpath for %p",
|
|
disk->pdev_path);
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
}
|
|
flexarray_append(back, "tapdisk-params");
|
|
flexarray_append(back, GCSPRINTF("%s:%s",
|
|
libxl__device_disk_string_of_format(disk->format),
|
|
disk->pdev_path));
|
|
|
|
/* tap backends with scripts are rejected by
|
|
* libxl__device_disk_set_backend */
|
|
assert(!disk->script);
|
|
|
|
/* now create a phy device to export the device to the guest */
|
|
goto do_backend_phy;
|
|
case LIBXL_DISK_BACKEND_QDISK:
|
|
flexarray_append(back, "params");
|
|
flexarray_append(back, GCSPRINTF("%s:%s",
|
|
libxl__device_disk_string_of_format(disk->format), disk->pdev_path));
|
|
assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK);
|
|
break;
|
|
default:
|
|
LOG(ERROR, "unrecognized disk backend type: %d",
|
|
disk->backend);
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
|
|
flexarray_append(back, "frontend-id");
|
|
flexarray_append(back, GCSPRINTF("%d", domid));
|
|
flexarray_append(back, "online");
|
|
flexarray_append(back, "1");
|
|
flexarray_append(back, "removable");
|
|
flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0));
|
|
flexarray_append(back, "bootable");
|
|
flexarray_append(back, GCSPRINTF("%d", 1));
|
|
flexarray_append(back, "state");
|
|
flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
|
|
flexarray_append(back, "dev");
|
|
flexarray_append(back, disk->vdev);
|
|
flexarray_append(back, "type");
|
|
flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend));
|
|
flexarray_append(back, "mode");
|
|
flexarray_append(back, disk->readwrite ? "w" : "r");
|
|
flexarray_append(back, "device-type");
|
|
flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk");
|
|
if (disk->direct_io_safe) {
|
|
flexarray_append(back, "direct-io-safe");
|
|
flexarray_append(back, "1");
|
|
}
|
|
flexarray_append_pair(back, "discard-enable",
|
|
libxl_defbool_val(disk->discard_enable) ?
|
|
"1" : "0");
|
|
|
|
flexarray_append(front, "backend-id");
|
|
flexarray_append(front, GCSPRINTF("%d", disk->backend_domid));
|
|
flexarray_append(front, "state");
|
|
flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
|
|
flexarray_append(front, "virtual-device");
|
|
flexarray_append(front, GCSPRINTF("%d", device->devid));
|
|
flexarray_append(front, "device-type");
|
|
flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
|
|
|
|
/*
|
|
* Old PV kernel disk frontends before 2.6.26 rely on tool stack to
|
|
* write disk native protocol to frontend node. Xend does this, port
|
|
* this behaviour to xl.
|
|
*
|
|
* New kernels write this node themselves. In that case it just
|
|
* overwrites an existing node which is OK.
|
|
*/
|
|
if (type == LIBXL_DOMAIN_TYPE_PV) {
|
|
const char *protocol =
|
|
xc_domain_get_native_protocol(ctx->xch, domid);
|
|
if (protocol) {
|
|
flexarray_append(front, "protocol");
|
|
flexarray_append(front, libxl__strdup(gc, protocol));
|
|
}
|
|
}
|
|
|
|
if (!get_vdev && aodev->update_json) {
|
|
rc = libxl__set_domain_configuration(gc, domid, &d_config);
|
|
if (rc) goto out;
|
|
}
|
|
|
|
libxl__device_generic_add(gc, t, device,
|
|
libxl__xs_kvs_of_flexarray(gc, back, back->count),
|
|
libxl__xs_kvs_of_flexarray(gc, front, front->count),
|
|
NULL);
|
|
|
|
rc = libxl__xs_transaction_commit(gc, &t);
|
|
if (!rc) break;
|
|
if (rc < 0) goto out;
|
|
}
|
|
|
|
aodev->dev = device;
|
|
aodev->action = LIBXL__DEVICE_ACTION_ADD;
|
|
libxl__wait_device_connection(egc, aodev);
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
libxl__xs_transaction_abort(gc, &t);
|
|
if (lock) libxl__unlock_domain_userdata(lock);
|
|
libxl_device_disk_dispose(&disk_saved);
|
|
libxl_domain_config_dispose(&d_config);
|
|
aodev->rc = rc;
|
|
if (rc) aodev->callback(egc, aodev);
|
|
return;
|
|
}
|
|
|
|
void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
|
|
libxl_device_disk *disk, libxl__ao_device *aodev)
|
|
{
|
|
device_disk_add(egc, domid, disk, aodev, NULL, NULL);
|
|
}
|
|
|
|
static int libxl__device_disk_from_xs_be(libxl__gc *gc,
|
|
const char *be_path,
|
|
libxl_device_disk *disk)
|
|
{
|
|
libxl_ctx *ctx = libxl__gc_owner(gc);
|
|
unsigned int len;
|
|
char *tmp;
|
|
int rc;
|
|
|
|
libxl_device_disk_init(disk);
|
|
|
|
rc = sscanf(be_path, "/local/domain/%d/", &disk->backend_domid);
|
|
if (rc != 1) {
|
|
LOG(ERROR, "Unable to fetch device backend domid from %s", be_path);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* "params" may not be present; but everything else must be. */
|
|
tmp = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/params", be_path), &len);
|
|
if (tmp && strchr(tmp, ':')) {
|
|
disk->pdev_path = strdup(strchr(tmp, ':') + 1);
|
|
free(tmp);
|
|
} else {
|
|
disk->pdev_path = tmp;
|
|
}
|
|
|
|
|
|
tmp = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/type", be_path));
|
|
if (!tmp) {
|
|
LOG(ERROR, "Missing xenstore node %s/type", be_path);
|
|
goto cleanup;
|
|
}
|
|
libxl_string_to_backend(ctx, tmp, &(disk->backend));
|
|
|
|
disk->vdev = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/dev", be_path), &len);
|
|
if (!disk->vdev) {
|
|
LOG(ERROR, "Missing xenstore node %s/dev", be_path);
|
|
goto cleanup;
|
|
}
|
|
|
|
tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf
|
|
(gc, "%s/removable", be_path));
|
|
if (!tmp) {
|
|
LOG(ERROR, "Missing xenstore node %s/removable", be_path);
|
|
goto cleanup;
|
|
}
|
|
disk->removable = atoi(tmp);
|
|
|
|
tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", be_path));
|
|
if (!tmp) {
|
|
LOG(ERROR, "Missing xenstore node %s/mode", be_path);
|
|
goto cleanup;
|
|
}
|
|
if (!strcmp(tmp, "w"))
|
|
disk->readwrite = 1;
|
|
else
|
|
disk->readwrite = 0;
|
|
|
|
tmp = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/device-type", be_path));
|
|
if (!tmp) {
|
|
LOG(ERROR, "Missing xenstore node %s/device-type", be_path);
|
|
goto cleanup;
|
|
}
|
|
disk->is_cdrom = !strcmp(tmp, "cdrom");
|
|
|
|
disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
|
|
|
|
return 0;
|
|
cleanup:
|
|
libxl_device_disk_dispose(disk);
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid,
|
|
const char *vdev, libxl_device_disk *disk)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *dompath, *path;
|
|
int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
|
|
int rc = ERROR_FAIL;
|
|
|
|
if (devid < 0)
|
|
return ERROR_INVAL;
|
|
|
|
libxl_device_disk_init(disk);
|
|
|
|
dompath = libxl__xs_get_dompath(gc, domid);
|
|
if (!dompath) {
|
|
goto out;
|
|
}
|
|
path = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/device/vbd/%d/backend", dompath,
|
|
devid));
|
|
if (!path)
|
|
goto out;
|
|
|
|
rc = libxl__device_disk_from_xs_be(gc, path, disk);
|
|
out:
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int libxl__append_disk_list_of_type(libxl__gc *gc,
|
|
uint32_t domid,
|
|
const char *type,
|
|
libxl_device_disk **disks,
|
|
int *ndisks)
|
|
{
|
|
char *be_path = NULL;
|
|
char **dir = NULL;
|
|
unsigned int n = 0;
|
|
libxl_device_disk *pdisk = NULL, *pdisk_end = NULL;
|
|
int rc=0;
|
|
int initial_disks = *ndisks;
|
|
|
|
be_path = GCSPRINTF("%s/backend/%s/%d",
|
|
libxl__xs_get_dompath(gc, 0), type, domid);
|
|
dir = libxl__xs_directory(gc, XBT_NULL, be_path, &n);
|
|
if (dir && n) {
|
|
libxl_device_disk *tmp;
|
|
tmp = realloc(*disks, sizeof (libxl_device_disk) * (*ndisks + n));
|
|
if (tmp == NULL)
|
|
return ERROR_NOMEM;
|
|
*disks = tmp;
|
|
pdisk = *disks + initial_disks;
|
|
pdisk_end = *disks + initial_disks + n;
|
|
for (; pdisk < pdisk_end; pdisk++, dir++) {
|
|
const char *p;
|
|
p = GCSPRINTF("%s/%s", be_path, *dir);
|
|
if ((rc=libxl__device_disk_from_xs_be(gc, p, pdisk)))
|
|
goto out;
|
|
pdisk->backend_domid = 0;
|
|
*ndisks += 1;
|
|
}
|
|
}
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx, uint32_t domid, int *num)
|
|
{
|
|
GC_INIT(ctx);
|
|
libxl_device_disk *disks = NULL;
|
|
int rc;
|
|
|
|
*num = 0;
|
|
|
|
rc = libxl__append_disk_list_of_type(gc, domid, "vbd", &disks, num);
|
|
if (rc) goto out_err;
|
|
|
|
rc = libxl__append_disk_list_of_type(gc, domid, "tap", &disks, num);
|
|
if (rc) goto out_err;
|
|
|
|
rc = libxl__append_disk_list_of_type(gc, domid, "qdisk", &disks, num);
|
|
if (rc) goto out_err;
|
|
|
|
GC_FREE;
|
|
return disks;
|
|
|
|
out_err:
|
|
LOG(ERROR, "Unable to list disks");
|
|
while (disks && *num) {
|
|
(*num)--;
|
|
libxl_device_disk_dispose(&disks[*num]);
|
|
}
|
|
free(disks);
|
|
return NULL;
|
|
}
|
|
|
|
int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_device_disk *disk, libxl_diskinfo *diskinfo)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *dompath, *diskpath;
|
|
char *val;
|
|
|
|
dompath = libxl__xs_get_dompath(gc, domid);
|
|
diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
|
|
|
|
/* tap devices entries in xenstore are written as vbd devices. */
|
|
diskpath = GCSPRINTF("%s/device/vbd/%d", dompath, diskinfo->devid);
|
|
diskinfo->backend = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/backend", diskpath), NULL);
|
|
if (!diskinfo->backend) {
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/backend-id", diskpath));
|
|
diskinfo->backend_id = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", diskpath));
|
|
diskinfo->state = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", diskpath));
|
|
diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", diskpath));
|
|
diskinfo->rref = val ? strtoul(val, NULL, 10) : -1;
|
|
diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/frontend", diskinfo->backend), NULL);
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/frontend-id", diskinfo->backend));
|
|
diskinfo->frontend_id = val ? strtoul(val, NULL, 10) : -1;
|
|
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
|
|
const libxl_asyncop_how *ao_how)
|
|
{
|
|
AO_CREATE(ctx, domid, ao_how);
|
|
int num = 0, i;
|
|
libxl_device_disk *disks = NULL, disk_saved, disk_empty;
|
|
libxl_domain_config d_config;
|
|
int rc, dm_ver;
|
|
libxl__device device;
|
|
const char * path;
|
|
char * tmp;
|
|
libxl__domain_userdata_lock *lock = NULL;
|
|
xs_transaction_t t = XBT_NULL;
|
|
flexarray_t *insert = NULL, *empty = NULL;
|
|
|
|
libxl_domain_config_init(&d_config);
|
|
libxl_device_disk_init(&disk_empty);
|
|
libxl_device_disk_init(&disk_saved);
|
|
libxl_device_disk_copy(ctx, &disk_saved, disk);
|
|
|
|
disk_empty.format = LIBXL_DISK_FORMAT_EMPTY;
|
|
disk_empty.vdev = libxl__strdup(NOGC, disk->vdev);
|
|
disk_empty.pdev_path = libxl__strdup(NOGC, "");
|
|
disk_empty.is_cdrom = 1;
|
|
libxl__device_disk_setdefault(gc, &disk_empty);
|
|
|
|
libxl_domain_type type = libxl__domain_type(gc, domid);
|
|
if (type == LIBXL_DOMAIN_TYPE_INVALID) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
if (type != LIBXL_DOMAIN_TYPE_HVM) {
|
|
LOG(ERROR, "cdrom-insert requires an HVM domain");
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (libxl_get_stubdom_id(ctx, domid) != 0) {
|
|
LOG(ERROR, "cdrom-insert doesn't work for stub domains");
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
|
|
dm_ver = libxl__device_model_version_running(gc, domid);
|
|
if (dm_ver == -1) {
|
|
LOG(ERROR, "cannot determine device model version");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
disks = libxl_device_disk_list(ctx, domid, &num);
|
|
for (i = 0; i < num; i++) {
|
|
if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev))
|
|
{
|
|
/* Found. Set backend type appropriately. */
|
|
disk->backend=disks[i].backend;
|
|
break;
|
|
}
|
|
}
|
|
if (i == num) {
|
|
LOG(ERROR, "Virtual device not found");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__device_disk_setdefault(gc, disk);
|
|
if (rc) goto out;
|
|
|
|
if (!disk->pdev_path) {
|
|
disk->pdev_path = libxl__strdup(NOGC, "");
|
|
disk->format = LIBXL_DISK_FORMAT_EMPTY;
|
|
}
|
|
|
|
rc = libxl__device_from_disk(gc, domid, disk, &device);
|
|
if (rc) goto out;
|
|
|
|
path = libxl__device_backend_path(gc, &device);
|
|
|
|
insert = flexarray_make(gc, 4, 1);
|
|
|
|
flexarray_append_pair(insert, "type",
|
|
libxl__device_disk_string_of_backend(disk->backend));
|
|
if (disk->format != LIBXL_DISK_FORMAT_EMPTY)
|
|
flexarray_append_pair(insert, "params",
|
|
GCSPRINTF("%s:%s",
|
|
libxl__device_disk_string_of_format(disk->format),
|
|
disk->pdev_path));
|
|
else
|
|
flexarray_append_pair(insert, "params", "");
|
|
|
|
empty = flexarray_make(gc, 4, 1);
|
|
flexarray_append_pair(empty, "type",
|
|
libxl__device_disk_string_of_backend(disk->backend));
|
|
flexarray_append_pair(empty, "params", "");
|
|
|
|
/* Note: CTX lock is already held at this point so lock hierarchy
|
|
* is maintained.
|
|
*/
|
|
lock = libxl__lock_domain_userdata(gc, domid);
|
|
if (!lock) {
|
|
rc = ERROR_LOCK_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/* We need to eject the original image first. This is implemented
|
|
* by inserting empty media. JSON is not updated.
|
|
*/
|
|
if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
|
|
rc = libxl__qmp_insert_cdrom(gc, domid, &disk_empty);
|
|
if (rc) goto out;
|
|
}
|
|
|
|
for (;;) {
|
|
rc = libxl__xs_transaction_start(gc, &t);
|
|
if (rc) goto out;
|
|
/* Sanity check: make sure the backend exists before writing here */
|
|
tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", path));
|
|
if (!tmp)
|
|
{
|
|
LOG(ERROR, "Internal error: %s does not exist",
|
|
GCSPRINTF("%s/frontend", path));
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__xs_writev(gc, t, path,
|
|
libxl__xs_kvs_of_flexarray(gc, empty, empty->count));
|
|
if (rc) goto out;
|
|
|
|
rc = libxl__xs_transaction_commit(gc, &t);
|
|
if (!rc) break;
|
|
if (rc < 0) goto out;
|
|
}
|
|
|
|
rc = libxl__get_domain_configuration(gc, domid, &d_config);
|
|
if (rc) goto out;
|
|
|
|
DEVICE_ADD(disk, disks, domid, &disk_saved, COMPARE_DISK, &d_config);
|
|
|
|
if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
|
|
rc = libxl__qmp_insert_cdrom(gc, domid, disk);
|
|
if (rc) goto out;
|
|
}
|
|
|
|
for (;;) {
|
|
rc = libxl__xs_transaction_start(gc, &t);
|
|
if (rc) goto out;
|
|
/* Sanity check: make sure the backend exists before writing here */
|
|
tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", path));
|
|
if (!tmp)
|
|
{
|
|
LOG(ERROR, "Internal error: %s does not exist",
|
|
GCSPRINTF("%s/frontend", path));
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__set_domain_configuration(gc, domid, &d_config);
|
|
if (rc) goto out;
|
|
|
|
rc = libxl__xs_writev(gc, t, path,
|
|
libxl__xs_kvs_of_flexarray(gc, insert, insert->count));
|
|
if (rc) goto out;
|
|
|
|
rc = libxl__xs_transaction_commit(gc, &t);
|
|
if (!rc) break;
|
|
if (rc < 0) goto out;
|
|
}
|
|
|
|
/* success, no actual async */
|
|
libxl__ao_complete(egc, ao, 0);
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
libxl__xs_transaction_abort(gc, &t);
|
|
for (i = 0; i < num; i++)
|
|
libxl_device_disk_dispose(&disks[i]);
|
|
free(disks);
|
|
libxl_device_disk_dispose(&disk_empty);
|
|
libxl_device_disk_dispose(&disk_saved);
|
|
libxl_domain_config_dispose(&d_config);
|
|
|
|
if (lock) libxl__unlock_domain_userdata(lock);
|
|
|
|
if (rc) return AO_CREATE_FAIL(rc);
|
|
return AO_INPROGRESS;
|
|
}
|
|
|
|
/* libxl__alloc_vdev only works on the local domain, that is the domain
|
|
* where the toolstack is running */
|
|
static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user,
|
|
xs_transaction_t t)
|
|
{
|
|
const char *blkdev_start = (const char *) get_vdev_user;
|
|
int devid = 0, disk = 0, part = 0;
|
|
char *dompath = libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID);
|
|
|
|
libxl__device_disk_dev_number(blkdev_start, &disk, &part);
|
|
if (part != 0) {
|
|
LOG(ERROR, "blkdev_start is invalid");
|
|
return NULL;
|
|
}
|
|
|
|
do {
|
|
devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk),
|
|
NULL, NULL);
|
|
if (devid < 0)
|
|
return NULL;
|
|
if (libxl__xs_read(gc, t,
|
|
GCSPRINTF("%s/device/vbd/%d/backend",
|
|
dompath, devid)) == NULL) {
|
|
if (errno == ENOENT)
|
|
return libxl__devid_to_localdev(gc, devid);
|
|
else
|
|
return NULL;
|
|
}
|
|
disk++;
|
|
} while (1);
|
|
return NULL;
|
|
}
|
|
|
|
/* Callbacks */
|
|
|
|
static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev);
|
|
|
|
void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
|
|
libxl__disk_local_state *dls)
|
|
{
|
|
STATE_AO_GC(dls->ao);
|
|
char *dev = NULL;
|
|
int rc;
|
|
const libxl_device_disk *in_disk = dls->in_disk;
|
|
libxl_device_disk *disk = &dls->disk;
|
|
const char *blkdev_start = dls->blkdev_start;
|
|
|
|
assert(in_disk->pdev_path);
|
|
|
|
memcpy(disk, in_disk, sizeof(libxl_device_disk));
|
|
disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path);
|
|
if (in_disk->script != NULL)
|
|
disk->script = libxl__strdup(gc, in_disk->script);
|
|
disk->vdev = NULL;
|
|
|
|
rc = libxl__device_disk_setdefault(gc, disk);
|
|
if (rc) goto out;
|
|
|
|
/* If this is in a driver domain, or it's not a raw format, or it involves
|
|
* running a script, we have to do a local attach. */
|
|
if (disk->backend_domname != NULL
|
|
|| disk->format != LIBXL_DISK_FORMAT_RAW
|
|
|| disk->script != NULL) {
|
|
libxl__prepare_ao_device(ao, &dls->aodev);
|
|
dls->aodev.callback = local_device_attach_cb;
|
|
device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev,
|
|
libxl__alloc_vdev, (void *) blkdev_start);
|
|
return;
|
|
}
|
|
|
|
LOG(DEBUG, "locally attaching RAW disk %s", disk->pdev_path);
|
|
dev = disk->pdev_path;
|
|
|
|
if (dev != NULL)
|
|
dls->diskpath = libxl__strdup(gc, dev);
|
|
|
|
dls->callback(egc, dls, 0);
|
|
return;
|
|
|
|
out:
|
|
assert(rc);
|
|
dls->rc = rc;
|
|
libxl__device_disk_local_initiate_detach(egc, dls);
|
|
dls->callback(egc, dls, rc);
|
|
}
|
|
|
|
static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev)
|
|
{
|
|
STATE_AO_GC(aodev->ao);
|
|
libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
|
|
char *dev = NULL, *be_path = NULL;
|
|
int rc;
|
|
libxl__device device;
|
|
libxl_device_disk *disk = &dls->disk;
|
|
|
|
rc = aodev->rc;
|
|
if (rc) {
|
|
LOGE(ERROR, "unable to %s %s with id %u",
|
|
libxl__device_action_to_string(aodev->action),
|
|
libxl__device_kind_to_string(aodev->dev->kind),
|
|
aodev->dev->devid);
|
|
goto out;
|
|
}
|
|
|
|
dev = GCSPRINTF("/dev/%s", disk->vdev);
|
|
LOG(DEBUG, "locally attaching disk %s", dev);
|
|
|
|
rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device);
|
|
if (rc < 0)
|
|
goto out;
|
|
be_path = libxl__device_backend_path(gc, &device);
|
|
rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected));
|
|
if (rc < 0)
|
|
goto out;
|
|
|
|
if (dev != NULL)
|
|
dls->diskpath = libxl__strdup(gc, dev);
|
|
|
|
dls->callback(egc, dls, 0);
|
|
return;
|
|
|
|
out:
|
|
assert(rc);
|
|
dls->rc = rc;
|
|
libxl__device_disk_local_initiate_detach(egc, dls);
|
|
return;
|
|
}
|
|
|
|
/* Callbacks for local detach */
|
|
|
|
static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev);
|
|
|
|
void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
|
|
libxl__disk_local_state *dls)
|
|
{
|
|
STATE_AO_GC(dls->ao);
|
|
int rc = 0;
|
|
libxl_device_disk *disk = &dls->disk;
|
|
libxl__device *device;
|
|
libxl__ao_device *aodev = &dls->aodev;
|
|
libxl__prepare_ao_device(ao, aodev);
|
|
|
|
if (!dls->diskpath) goto out;
|
|
|
|
if (disk->vdev != NULL) {
|
|
GCNEW(device);
|
|
rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID,
|
|
disk, device);
|
|
if (rc != 0) goto out;
|
|
|
|
aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
|
|
aodev->dev = device;
|
|
aodev->callback = local_device_detach_cb;
|
|
aodev->force = 0;
|
|
libxl__initiate_device_remove(egc, aodev);
|
|
return;
|
|
}
|
|
|
|
out:
|
|
aodev->rc = rc;
|
|
local_device_detach_cb(egc, aodev);
|
|
return;
|
|
}
|
|
|
|
static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev)
|
|
{
|
|
STATE_AO_GC(aodev->ao);
|
|
libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
|
|
int rc;
|
|
|
|
if (aodev->rc) {
|
|
LOGE(ERROR, "unable to %s %s with id %u",
|
|
libxl__device_action_to_string(aodev->action),
|
|
libxl__device_kind_to_string(aodev->dev->kind),
|
|
aodev->dev->devid);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
/*
|
|
* If there was an error in dls->rc, it means we have been called from
|
|
* a failed execution of libxl__device_disk_local_initiate_attach,
|
|
* so return the original error.
|
|
*/
|
|
rc = dls->rc ? dls->rc : aodev->rc;
|
|
dls->callback(egc, dls, rc);
|
|
return;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int libxl__device_nic_setdefault(libxl__gc *gc, libxl_device_nic *nic,
|
|
uint32_t domid, libxl_domain_build_info *info)
|
|
{
|
|
int rc;
|
|
|
|
if (!nic->mtu)
|
|
nic->mtu = 1492;
|
|
if (!nic->model) {
|
|
nic->model = strdup("rtl8139");
|
|
if (!nic->model) return ERROR_NOMEM;
|
|
}
|
|
if (libxl__mac_is_default(&nic->mac)) {
|
|
const uint8_t *r;
|
|
libxl_uuid uuid;
|
|
|
|
libxl_uuid_generate(&uuid);
|
|
r = libxl_uuid_bytearray(&uuid);
|
|
|
|
nic->mac[0] = 0x00;
|
|
nic->mac[1] = 0x16;
|
|
nic->mac[2] = 0x3e;
|
|
nic->mac[3] = r[0] & 0x7f;
|
|
nic->mac[4] = r[1];
|
|
nic->mac[5] = r[2];
|
|
}
|
|
if (!nic->bridge) {
|
|
nic->bridge = strdup("xenbr0");
|
|
if (!nic->bridge) return ERROR_NOMEM;
|
|
}
|
|
if ( !nic->script && asprintf(&nic->script, "%s/vif-bridge",
|
|
libxl__xen_script_dir_path()) < 0 )
|
|
return ERROR_FAIL;
|
|
|
|
rc = libxl__resolve_domid(gc, nic->backend_domname, &nic->backend_domid);
|
|
if (rc < 0) return rc;
|
|
|
|
switch (libxl__domain_type(gc, domid)) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
if (!nic->nictype) {
|
|
if (info != NULL) {
|
|
/* Path taken at creation time. */
|
|
if (info->device_model_version ==
|
|
LIBXL_DEVICE_MODEL_VERSION_NONE)
|
|
nic->nictype = LIBXL_NIC_TYPE_VIF;
|
|
else
|
|
nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
|
|
} else {
|
|
/* Path taken when hot-adding a nic. */
|
|
if (libxl__device_model_version_running(gc, domid) ==
|
|
LIBXL_DEVICE_MODEL_VERSION_NONE)
|
|
nic->nictype = LIBXL_NIC_TYPE_VIF;
|
|
else
|
|
nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
|
|
}
|
|
}
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
if (nic->nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
|
|
LOG(ERROR, "trying to create PV guest with an emulated interface");
|
|
return ERROR_INVAL;
|
|
}
|
|
nic->nictype = LIBXL_NIC_TYPE_VIF;
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_INVALID:
|
|
return ERROR_FAIL;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int libxl__device_from_nic(libxl__gc *gc, uint32_t domid,
|
|
libxl_device_nic *nic,
|
|
libxl__device *device)
|
|
{
|
|
device->backend_devid = nic->devid;
|
|
device->backend_domid = nic->backend_domid;
|
|
device->backend_kind = LIBXL__DEVICE_KIND_VIF;
|
|
device->devid = nic->devid;
|
|
device->domid = domid;
|
|
device->kind = LIBXL__DEVICE_KIND_VIF;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void libxl__device_nic_add(libxl__egc *egc, uint32_t domid,
|
|
libxl_device_nic *nic, libxl__ao_device *aodev)
|
|
{
|
|
STATE_AO_GC(aodev->ao);
|
|
flexarray_t *front;
|
|
flexarray_t *back;
|
|
libxl__device *device;
|
|
int rc;
|
|
xs_transaction_t t = XBT_NULL;
|
|
libxl_domain_config d_config;
|
|
libxl_device_nic nic_saved;
|
|
libxl__domain_userdata_lock *lock = NULL;
|
|
|
|
libxl_domain_config_init(&d_config);
|
|
libxl_device_nic_init(&nic_saved);
|
|
libxl_device_nic_copy(CTX, &nic_saved, nic);
|
|
|
|
rc = libxl__device_nic_setdefault(gc, nic, domid, NULL);
|
|
if (rc) goto out;
|
|
|
|
front = flexarray_make(gc, 16, 1);
|
|
back = flexarray_make(gc, 18, 1);
|
|
|
|
if (nic->devid == -1) {
|
|
if ((nic->devid = libxl__device_nextid(gc, domid, "vif")) < 0) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
libxl__update_config_nic(gc, &nic_saved, nic);
|
|
|
|
GCNEW(device);
|
|
rc = libxl__device_from_nic(gc, domid, nic, device);
|
|
if ( rc != 0 ) goto out;
|
|
|
|
flexarray_append(back, "frontend-id");
|
|
flexarray_append(back, GCSPRINTF("%d", domid));
|
|
flexarray_append(back, "online");
|
|
flexarray_append(back, "1");
|
|
flexarray_append(back, "state");
|
|
flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
|
|
if (nic->script)
|
|
flexarray_append_pair(back, "script",
|
|
libxl__abs_path(gc, nic->script,
|
|
libxl__xen_script_dir_path()));
|
|
|
|
if (nic->ifname) {
|
|
flexarray_append(back, "vifname");
|
|
flexarray_append(back, nic->ifname);
|
|
}
|
|
|
|
flexarray_append(back, "mac");
|
|
flexarray_append(back,GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
|
|
if (nic->ip) {
|
|
flexarray_append(back, "ip");
|
|
flexarray_append(back, libxl__strdup(gc, nic->ip));
|
|
}
|
|
if (nic->gatewaydev) {
|
|
flexarray_append(back, "gatewaydev");
|
|
flexarray_append(back, libxl__strdup(gc, nic->gatewaydev));
|
|
}
|
|
|
|
if (nic->rate_interval_usecs > 0) {
|
|
flexarray_append(back, "rate");
|
|
flexarray_append(back, GCSPRINTF("%"PRIu64",%"PRIu32"",
|
|
nic->rate_bytes_per_interval,
|
|
nic->rate_interval_usecs));
|
|
}
|
|
|
|
flexarray_append(back, "bridge");
|
|
flexarray_append(back, libxl__strdup(gc, nic->bridge));
|
|
flexarray_append(back, "handle");
|
|
flexarray_append(back, GCSPRINTF("%d", nic->devid));
|
|
flexarray_append(back, "type");
|
|
flexarray_append(back, libxl__strdup(gc,
|
|
libxl_nic_type_to_string(nic->nictype)));
|
|
|
|
flexarray_append(front, "backend-id");
|
|
flexarray_append(front, GCSPRINTF("%d", nic->backend_domid));
|
|
flexarray_append(front, "state");
|
|
flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
|
|
flexarray_append(front, "handle");
|
|
flexarray_append(front, GCSPRINTF("%d", nic->devid));
|
|
flexarray_append(front, "mac");
|
|
flexarray_append(front, GCSPRINTF(
|
|
LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
|
|
|
|
if (aodev->update_json) {
|
|
lock = libxl__lock_domain_userdata(gc, domid);
|
|
if (!lock) {
|
|
rc = ERROR_LOCK_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__get_domain_configuration(gc, domid, &d_config);
|
|
if (rc) goto out;
|
|
|
|
DEVICE_ADD(nic, nics, domid, &nic_saved, COMPARE_DEVID, &d_config);
|
|
}
|
|
|
|
for (;;) {
|
|
rc = libxl__xs_transaction_start(gc, &t);
|
|
if (rc) goto out;
|
|
|
|
rc = libxl__device_exists(gc, t, device);
|
|
if (rc < 0) goto out;
|
|
if (rc == 1) { /* already exists in xenstore */
|
|
LOG(ERROR, "device already exists in xenstore");
|
|
aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
|
|
rc = ERROR_DEVICE_EXISTS;
|
|
goto out;
|
|
}
|
|
|
|
if (aodev->update_json) {
|
|
rc = libxl__set_domain_configuration(gc, domid, &d_config);
|
|
if (rc) goto out;
|
|
}
|
|
|
|
libxl__device_generic_add(gc, t, device,
|
|
libxl__xs_kvs_of_flexarray(gc, back,
|
|
back->count),
|
|
libxl__xs_kvs_of_flexarray(gc, front,
|
|
front->count),
|
|
NULL);
|
|
|
|
rc = libxl__xs_transaction_commit(gc, &t);
|
|
if (!rc) break;
|
|
if (rc < 0) goto out;
|
|
}
|
|
|
|
aodev->dev = device;
|
|
aodev->action = LIBXL__DEVICE_ACTION_ADD;
|
|
libxl__wait_device_connection(egc, aodev);
|
|
|
|
rc = 0;
|
|
out:
|
|
libxl__xs_transaction_abort(gc, &t);
|
|
if (lock) libxl__unlock_domain_userdata(lock);
|
|
libxl_device_nic_dispose(&nic_saved);
|
|
libxl_domain_config_dispose(&d_config);
|
|
aodev->rc = rc;
|
|
if (rc) aodev->callback(egc, aodev);
|
|
return;
|
|
}
|
|
|
|
static int libxl__device_nic_from_xs_be(libxl__gc *gc,
|
|
const char *be_path,
|
|
libxl_device_nic *nic)
|
|
{
|
|
const char *tmp;
|
|
int rc;
|
|
|
|
libxl_device_nic_init(nic);
|
|
|
|
tmp = READ_BACKEND(gc, "handle");
|
|
if (tmp)
|
|
nic->devid = atoi(tmp);
|
|
else
|
|
nic->devid = 0;
|
|
|
|
/* nic->mtu = */
|
|
|
|
tmp = READ_BACKEND(gc, "mac");
|
|
if (tmp) {
|
|
rc = libxl__parse_mac(tmp, nic->mac);
|
|
if (rc) goto out;
|
|
} else {
|
|
memset(nic->mac, 0, sizeof(nic->mac));
|
|
}
|
|
|
|
nic->ip = READ_BACKEND(NOGC, "ip");
|
|
nic->bridge = READ_BACKEND(NOGC, "bridge");
|
|
nic->script = READ_BACKEND(NOGC, "script");
|
|
|
|
/* vif_ioemu nics use the same xenstore entries as vif interfaces */
|
|
tmp = READ_BACKEND(gc, "type");
|
|
if (tmp) {
|
|
rc = libxl_nic_type_from_string(tmp, &nic->nictype);
|
|
if (rc) goto out;
|
|
} else {
|
|
nic->nictype = LIBXL_NIC_TYPE_VIF;
|
|
}
|
|
nic->model = NULL; /* XXX Only for TYPE_IOEMU */
|
|
nic->ifname = NULL; /* XXX Only for TYPE_IOEMU */
|
|
|
|
rc = 0;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
int libxl_devid_to_device_nic(libxl_ctx *ctx, uint32_t domid,
|
|
int devid, libxl_device_nic *nic)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *dompath, *path;
|
|
int rc = ERROR_FAIL;
|
|
|
|
libxl_device_nic_init(nic);
|
|
dompath = libxl__xs_get_dompath(gc, domid);
|
|
if (!dompath)
|
|
goto out;
|
|
|
|
path = libxl__xs_read(gc, XBT_NULL,
|
|
GCSPRINTF("%s/device/vif/%d/backend", dompath,
|
|
devid));
|
|
if (!path)
|
|
goto out;
|
|
|
|
rc = libxl__device_nic_from_xs_be(gc, path, nic);
|
|
if (rc) goto out;
|
|
|
|
rc = 0;
|
|
out:
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
static int libxl__append_nic_list_of_type(libxl__gc *gc,
|
|
uint32_t domid,
|
|
const char *type,
|
|
libxl_device_nic **nics,
|
|
int *nnics)
|
|
{
|
|
char *be_path = NULL;
|
|
char **dir = NULL;
|
|
unsigned int n = 0;
|
|
libxl_device_nic *pnic = NULL, *pnic_end = NULL;
|
|
int rc;
|
|
|
|
be_path = GCSPRINTF("%s/backend/%s/%d", libxl__xs_get_dompath(gc, 0),
|
|
type, domid);
|
|
dir = libxl__xs_directory(gc, XBT_NULL, be_path, &n);
|
|
if (dir && n) {
|
|
libxl_device_nic *tmp;
|
|
tmp = realloc(*nics, sizeof (libxl_device_nic) * (*nnics + n));
|
|
if (tmp == NULL)
|
|
return ERROR_NOMEM;
|
|
*nics = tmp;
|
|
pnic = *nics + *nnics;
|
|
pnic_end = *nics + *nnics + n;
|
|
for (; pnic < pnic_end; pnic++, dir++) {
|
|
const char *p;
|
|
p = GCSPRINTF("%s/%s", be_path, *dir);
|
|
rc = libxl__device_nic_from_xs_be(gc, p, pnic);
|
|
if (rc) goto out;
|
|
pnic->backend_domid = 0;
|
|
}
|
|
*nnics += n;
|
|
}
|
|
return 0;
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx, uint32_t domid, int *num)
|
|
{
|
|
GC_INIT(ctx);
|
|
libxl_device_nic *nics = NULL;
|
|
int rc;
|
|
|
|
*num = 0;
|
|
|
|
rc = libxl__append_nic_list_of_type(gc, domid, "vif", &nics, num);
|
|
if (rc) goto out_err;
|
|
|
|
GC_FREE;
|
|
return nics;
|
|
|
|
out_err:
|
|
LOG(ERROR, "Unable to list nics");
|
|
while (*num) {
|
|
(*num)--;
|
|
libxl_device_nic_dispose(&nics[*num]);
|
|
}
|
|
free(nics);
|
|
return NULL;
|
|
}
|
|
|
|
int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_device_nic *nic, libxl_nicinfo *nicinfo)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *dompath, *nicpath;
|
|
char *val;
|
|
|
|
dompath = libxl__xs_get_dompath(gc, domid);
|
|
nicinfo->devid = nic->devid;
|
|
|
|
nicpath = GCSPRINTF("%s/device/vif/%d", dompath, nicinfo->devid);
|
|
nicinfo->backend = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/backend", nicpath), NULL);
|
|
if (!nicinfo->backend) {
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/backend-id", nicpath));
|
|
nicinfo->backend_id = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", nicpath));
|
|
nicinfo->state = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", nicpath));
|
|
nicinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tx-ring-ref", nicpath));
|
|
nicinfo->rref_tx = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/rx-ring-ref", nicpath));
|
|
nicinfo->rref_rx = val ? strtoul(val, NULL, 10) : -1;
|
|
nicinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/frontend", nicinfo->backend), NULL);
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/frontend-id", nicinfo->backend));
|
|
nicinfo->frontend_id = val ? strtoul(val, NULL, 10) : -1;
|
|
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
const char *libxl__device_nic_devname(libxl__gc *gc,
|
|
uint32_t domid,
|
|
uint32_t devid,
|
|
libxl_nic_type type)
|
|
{
|
|
switch (type) {
|
|
case LIBXL_NIC_TYPE_VIF:
|
|
return GCSPRINTF(NETBACK_NIC_NAME, domid, devid);
|
|
case LIBXL_NIC_TYPE_VIF_IOEMU:
|
|
return GCSPRINTF(NETBACK_NIC_NAME TAP_DEVICE_SUFFIX, domid, devid);
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
|
|
libxl__device_console *console,
|
|
libxl__domain_build_state *state,
|
|
libxl__device *device)
|
|
{
|
|
flexarray_t *front, *ro_front;
|
|
flexarray_t *back;
|
|
int rc;
|
|
|
|
if (console->devid && state) {
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
if (!console->devid && (console->name || console->path)) {
|
|
LOG(ERROR, "Primary console has invalid configuration");
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
|
|
front = flexarray_make(gc, 16, 1);
|
|
ro_front = flexarray_make(gc, 16, 1);
|
|
back = flexarray_make(gc, 16, 1);
|
|
|
|
device->backend_devid = console->devid;
|
|
device->backend_domid = console->backend_domid;
|
|
device->backend_kind = LIBXL__DEVICE_KIND_CONSOLE;
|
|
device->devid = console->devid;
|
|
device->domid = domid;
|
|
device->kind = LIBXL__DEVICE_KIND_CONSOLE;
|
|
|
|
flexarray_append(back, "frontend-id");
|
|
flexarray_append(back, GCSPRINTF("%d", domid));
|
|
flexarray_append(back, "online");
|
|
flexarray_append(back, "1");
|
|
flexarray_append(back, "state");
|
|
flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
|
|
flexarray_append(back, "protocol");
|
|
flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
|
|
|
|
if (console->name) {
|
|
flexarray_append(ro_front, "name");
|
|
flexarray_append(ro_front, console->name);
|
|
}
|
|
if (console->connection) {
|
|
flexarray_append(back, "connection");
|
|
flexarray_append(back, console->connection);
|
|
}
|
|
if (console->path) {
|
|
flexarray_append(back, "path");
|
|
flexarray_append(back, console->path);
|
|
}
|
|
|
|
flexarray_append(front, "backend-id");
|
|
flexarray_append(front, GCSPRINTF("%d", console->backend_domid));
|
|
|
|
flexarray_append(ro_front, "limit");
|
|
flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT));
|
|
flexarray_append(ro_front, "type");
|
|
if (console->consback == LIBXL__CONSOLE_BACKEND_XENCONSOLED)
|
|
flexarray_append(ro_front, "xenconsoled");
|
|
else
|
|
flexarray_append(ro_front, "ioemu");
|
|
flexarray_append(ro_front, "output");
|
|
flexarray_append(ro_front, console->output);
|
|
flexarray_append(ro_front, "tty");
|
|
flexarray_append(ro_front, "");
|
|
|
|
if (state) {
|
|
flexarray_append(ro_front, "port");
|
|
flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->console_port));
|
|
flexarray_append(ro_front, "ring-ref");
|
|
flexarray_append(ro_front, GCSPRINTF("%lu", state->console_mfn));
|
|
} else {
|
|
flexarray_append(front, "state");
|
|
flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
|
|
flexarray_append(front, "protocol");
|
|
flexarray_append(front, LIBXL_XENCONSOLE_PROTOCOL);
|
|
}
|
|
libxl__device_generic_add(gc, XBT_NULL, device,
|
|
libxl__xs_kvs_of_flexarray(gc, back, back->count),
|
|
libxl__xs_kvs_of_flexarray(gc, front, front->count),
|
|
libxl__xs_kvs_of_flexarray(gc, ro_front, ro_front->count));
|
|
rc = 0;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int libxl__init_console_from_channel(libxl__gc *gc,
|
|
libxl__device_console *console,
|
|
int dev_num,
|
|
libxl_device_channel *channel)
|
|
{
|
|
int rc;
|
|
|
|
libxl__device_console_init(console);
|
|
|
|
/* Perform validation first, allocate second. */
|
|
|
|
if (!channel->name) {
|
|
LOG(ERROR, "channel %d has no name", channel->devid);
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
if (channel->backend_domname) {
|
|
rc = libxl_domain_qualifier_to_domid(CTX, channel->backend_domname,
|
|
&channel->backend_domid);
|
|
if (rc < 0) return rc;
|
|
}
|
|
|
|
/* The xenstore 'output' node tells the backend what to connect the console
|
|
to. If the channel has "connection = pty" then the "output" node will be
|
|
set to "pty". If the channel has "connection = socket" then the "output"
|
|
node will be set to "chardev:libxl-channel%d". This tells the qemu
|
|
backend to proxy data between the console ring and the character device
|
|
with id "libxl-channel%d". These character devices are currently defined
|
|
on the qemu command-line via "-chardev" options in libxl_dm.c */
|
|
|
|
switch (channel->connection) {
|
|
case LIBXL_CHANNEL_CONNECTION_UNKNOWN:
|
|
LOG(ERROR, "channel %d has no defined connection; "
|
|
"to where should it be connected?", channel->devid);
|
|
return ERROR_INVAL;
|
|
case LIBXL_CHANNEL_CONNECTION_PTY:
|
|
console->connection = libxl__strdup(NOGC, "pty");
|
|
console->output = libxl__sprintf(NOGC, "pty");
|
|
break;
|
|
case LIBXL_CHANNEL_CONNECTION_SOCKET:
|
|
if (!channel->u.socket.path) {
|
|
LOG(ERROR, "channel %d has no path", channel->devid);
|
|
return ERROR_INVAL;
|
|
}
|
|
console->connection = libxl__strdup(NOGC, "socket");
|
|
console->path = libxl__strdup(NOGC, channel->u.socket.path);
|
|
console->output = libxl__sprintf(NOGC, "chardev:libxl-channel%d",
|
|
channel->devid);
|
|
break;
|
|
default:
|
|
/* We've forgotten to add the clause */
|
|
LOG(ERROR, "%s: missing implementation for channel connection %d",
|
|
__func__, channel->connection);
|
|
abort();
|
|
}
|
|
|
|
console->devid = dev_num;
|
|
console->consback = LIBXL__CONSOLE_BACKEND_IOEMU;
|
|
console->backend_domid = channel->backend_domid;
|
|
console->name = libxl__strdup(NOGC, channel->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int libxl__device_channel_from_xs_be(libxl__gc *gc,
|
|
const char *be_path,
|
|
libxl_device_channel *channel)
|
|
{
|
|
const char *tmp;
|
|
int rc;
|
|
|
|
libxl_device_channel_init(channel);
|
|
|
|
/* READ_BACKEND is from libxl__device_nic_from_xs_be above */
|
|
channel->name = READ_BACKEND(NOGC, "name");
|
|
tmp = READ_BACKEND(gc, "connection");
|
|
if (!strcmp(tmp, "pty")) {
|
|
channel->connection = LIBXL_CHANNEL_CONNECTION_PTY;
|
|
} else if (!strcmp(tmp, "socket")) {
|
|
channel->connection = LIBXL_CHANNEL_CONNECTION_SOCKET;
|
|
channel->u.socket.path = READ_BACKEND(NOGC, "path");
|
|
} else {
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
static int libxl__append_channel_list_of_type(libxl__gc *gc,
|
|
uint32_t domid,
|
|
const char *type,
|
|
libxl_device_channel **channels,
|
|
int *nchannels)
|
|
{
|
|
char *fe_path = NULL, *be_path = NULL;
|
|
char **dir = NULL;
|
|
unsigned int n = 0, devid = 0;
|
|
libxl_device_channel *next = NULL;
|
|
int rc = 0, i;
|
|
|
|
fe_path = GCSPRINTF("%s/device/%s",
|
|
libxl__xs_get_dompath(gc, domid), type);
|
|
dir = libxl__xs_directory(gc, XBT_NULL, fe_path, &n);
|
|
if (!dir || !n)
|
|
goto out;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
const char *p, *name;
|
|
libxl_device_channel *tmp;
|
|
|
|
p = GCSPRINTF("%s/%s", fe_path, dir[i]);
|
|
name = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/name", p));
|
|
/* 'channels' are consoles with names, so ignore all consoles
|
|
without names */
|
|
if (!name) continue;
|
|
be_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/backend", p));
|
|
tmp = realloc(*channels,
|
|
sizeof(libxl_device_channel) * (*nchannels + devid + 1));
|
|
if (!tmp) {
|
|
rc = ERROR_NOMEM;
|
|
goto out;
|
|
}
|
|
*channels = tmp;
|
|
next = *channels + *nchannels + devid;
|
|
rc = libxl__device_channel_from_xs_be(gc, be_path, next);
|
|
if (rc) goto out;
|
|
next->devid = devid;
|
|
devid++;
|
|
}
|
|
*nchannels += devid;
|
|
return 0;
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
|
|
uint32_t domid,
|
|
int *num)
|
|
{
|
|
GC_INIT(ctx);
|
|
libxl_device_channel *channels = NULL;
|
|
int rc;
|
|
|
|
*num = 0;
|
|
|
|
rc = libxl__append_channel_list_of_type(gc, domid, "console", &channels, num);
|
|
if (rc) goto out_err;
|
|
|
|
GC_FREE;
|
|
return channels;
|
|
|
|
out_err:
|
|
LOG(ERROR, "Unable to list channels");
|
|
while (*num) {
|
|
(*num)--;
|
|
libxl_device_channel_dispose(&channels[*num]);
|
|
}
|
|
free(channels);
|
|
return NULL;
|
|
}
|
|
|
|
int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_device_channel *channel,
|
|
libxl_channelinfo *channelinfo)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *dompath, *fe_path;
|
|
char *val;
|
|
|
|
dompath = libxl__xs_get_dompath(gc, domid);
|
|
channelinfo->devid = channel->devid;
|
|
|
|
fe_path = GCSPRINTF("%s/device/console/%d", dompath,
|
|
channelinfo->devid + 1);
|
|
channelinfo->backend = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/backend", fe_path), NULL);
|
|
if (!channelinfo->backend) {
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/backend-id", fe_path));
|
|
channelinfo->backend_id = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
|
|
channelinfo->state = val ? strtoul(val, NULL, 10) : -1;
|
|
channelinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
|
|
GCSPRINTF("%s/frontend",
|
|
channelinfo->backend), NULL);
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/frontend-id",
|
|
channelinfo->backend));
|
|
channelinfo->frontend_id = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
|
|
channelinfo->rref = val ? strtoul(val, NULL, 10) : -1;
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/port", fe_path));
|
|
channelinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
|
|
|
|
channelinfo->connection = channel->connection;
|
|
switch (channel->connection) {
|
|
case LIBXL_CHANNEL_CONNECTION_PTY:
|
|
val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tty", fe_path));
|
|
channelinfo->u.pty.path = strdup(val);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int libxl__device_vkb_setdefault(libxl__gc *gc, libxl_device_vkb *vkb)
|
|
{
|
|
int rc;
|
|
rc = libxl__resolve_domid(gc, vkb->backend_domname, &vkb->backend_domid);
|
|
return rc;
|
|
}
|
|
|
|
static int libxl__device_from_vkb(libxl__gc *gc, uint32_t domid,
|
|
libxl_device_vkb *vkb,
|
|
libxl__device *device)
|
|
{
|
|
device->backend_devid = vkb->devid;
|
|
device->backend_domid = vkb->backend_domid;
|
|
device->backend_kind = LIBXL__DEVICE_KIND_VKBD;
|
|
device->devid = vkb->devid;
|
|
device->domid = domid;
|
|
device->kind = LIBXL__DEVICE_KIND_VKBD;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
|
|
const libxl_asyncop_how *ao_how)
|
|
{
|
|
AO_CREATE(ctx, domid, ao_how);
|
|
int rc;
|
|
|
|
rc = libxl__device_vkb_add(gc, domid, vkb);
|
|
if (rc) {
|
|
LOG(ERROR, "unable to add vkb device");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
libxl__ao_complete(egc, ao, rc);
|
|
return AO_INPROGRESS;
|
|
}
|
|
|
|
int libxl__device_vkb_add(libxl__gc *gc, uint32_t domid,
|
|
libxl_device_vkb *vkb)
|
|
{
|
|
flexarray_t *front;
|
|
flexarray_t *back;
|
|
libxl__device device;
|
|
int rc;
|
|
|
|
rc = libxl__device_vkb_setdefault(gc, vkb);
|
|
if (rc) goto out;
|
|
|
|
front = flexarray_make(gc, 16, 1);
|
|
back = flexarray_make(gc, 16, 1);
|
|
|
|
if (vkb->devid == -1) {
|
|
if ((vkb->devid = libxl__device_nextid(gc, domid, "vkb")) < 0) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
rc = libxl__device_from_vkb(gc, domid, vkb, &device);
|
|
if (rc != 0) goto out;
|
|
|
|
flexarray_append(back, "frontend-id");
|
|
flexarray_append(back, GCSPRINTF("%d", domid));
|
|
flexarray_append(back, "online");
|
|
flexarray_append(back, "1");
|
|
flexarray_append(back, "state");
|
|
flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
|
|
|
|
flexarray_append(front, "backend-id");
|
|
flexarray_append(front, GCSPRINTF("%d", vkb->backend_domid));
|
|
flexarray_append(front, "state");
|
|
flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
|
|
|
|
libxl__device_generic_add(gc, XBT_NULL, &device,
|
|
libxl__xs_kvs_of_flexarray(gc, back, back->count),
|
|
libxl__xs_kvs_of_flexarray(gc, front, front->count),
|
|
NULL);
|
|
rc = 0;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int libxl__device_vfb_setdefault(libxl__gc *gc, libxl_device_vfb *vfb)
|
|
{
|
|
int rc;
|
|
|
|
libxl_defbool_setdefault(&vfb->vnc.enable, true);
|
|
if (libxl_defbool_val(vfb->vnc.enable)) {
|
|
if (!vfb->vnc.listen) {
|
|
vfb->vnc.listen = strdup("127.0.0.1");
|
|
if (!vfb->vnc.listen) return ERROR_NOMEM;
|
|
}
|
|
|
|
libxl_defbool_setdefault(&vfb->vnc.findunused, true);
|
|
} else {
|
|
libxl_defbool_setdefault(&vfb->vnc.findunused, false);
|
|
}
|
|
|
|
libxl_defbool_setdefault(&vfb->sdl.enable, false);
|
|
libxl_defbool_setdefault(&vfb->sdl.opengl, false);
|
|
|
|
rc = libxl__resolve_domid(gc, vfb->backend_domname, &vfb->backend_domid);
|
|
return rc;
|
|
}
|
|
|
|
static int libxl__device_from_vfb(libxl__gc *gc, uint32_t domid,
|
|
libxl_device_vfb *vfb,
|
|
libxl__device *device)
|
|
{
|
|
device->backend_devid = vfb->devid;
|
|
device->backend_domid = vfb->backend_domid;
|
|
device->backend_kind = LIBXL__DEVICE_KIND_VFB;
|
|
device->devid = vfb->devid;
|
|
device->domid = domid;
|
|
device->kind = LIBXL__DEVICE_KIND_VFB;
|
|
return 0;
|
|
}
|
|
|
|
int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
|
|
const libxl_asyncop_how *ao_how)
|
|
{
|
|
AO_CREATE(ctx, domid, ao_how);
|
|
int rc;
|
|
|
|
rc = libxl__device_vfb_add(gc, domid, vfb);
|
|
if (rc) {
|
|
LOG(ERROR, "unable to add vfb device");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
libxl__ao_complete(egc, ao, rc);
|
|
return AO_INPROGRESS;
|
|
}
|
|
|
|
int libxl__device_vfb_add(libxl__gc *gc, uint32_t domid, libxl_device_vfb *vfb)
|
|
{
|
|
flexarray_t *front;
|
|
flexarray_t *back;
|
|
libxl__device device;
|
|
int rc;
|
|
|
|
rc = libxl__device_vfb_setdefault(gc, vfb);
|
|
if (rc) goto out;
|
|
|
|
front = flexarray_make(gc, 16, 1);
|
|
back = flexarray_make(gc, 16, 1);
|
|
|
|
if (vfb->devid == -1) {
|
|
if ((vfb->devid = libxl__device_nextid(gc, domid, "vfb")) < 0) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
rc = libxl__device_from_vfb(gc, domid, vfb, &device);
|
|
if (rc != 0) goto out;
|
|
|
|
flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
|
|
flexarray_append_pair(back, "online", "1");
|
|
flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising));
|
|
flexarray_append_pair(back, "vnc",
|
|
libxl_defbool_val(vfb->vnc.enable) ? "1" : "0");
|
|
flexarray_append_pair(back, "vnclisten", vfb->vnc.listen);
|
|
flexarray_append_pair(back, "vncpasswd", vfb->vnc.passwd);
|
|
flexarray_append_pair(back, "vncdisplay",
|
|
GCSPRINTF("%d", vfb->vnc.display));
|
|
flexarray_append_pair(back, "vncunused",
|
|
libxl_defbool_val(vfb->vnc.findunused) ? "1" : "0");
|
|
flexarray_append_pair(back, "sdl",
|
|
libxl_defbool_val(vfb->sdl.enable) ? "1" : "0");
|
|
flexarray_append_pair(back, "opengl",
|
|
libxl_defbool_val(vfb->sdl.opengl) ? "1" : "0");
|
|
if (vfb->sdl.xauthority) {
|
|
flexarray_append_pair(back, "xauthority", vfb->sdl.xauthority);
|
|
}
|
|
if (vfb->sdl.display) {
|
|
flexarray_append_pair(back, "display", vfb->sdl.display);
|
|
}
|
|
|
|
flexarray_append_pair(front, "backend-id",
|
|
GCSPRINTF("%d", vfb->backend_domid));
|
|
flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising));
|
|
|
|
libxl__device_generic_add(gc, XBT_NULL, &device,
|
|
libxl__xs_kvs_of_flexarray(gc, back, back->count),
|
|
libxl__xs_kvs_of_flexarray(gc, front, front->count),
|
|
NULL);
|
|
rc = 0;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
/* Macro for defining device remove/destroy functions in a compact way */
|
|
/* The following functions are defined:
|
|
* libxl_device_disk_remove
|
|
* libxl_device_disk_destroy
|
|
* libxl_device_nic_remove
|
|
* libxl_device_nic_destroy
|
|
* libxl_device_vtpm_remove
|
|
* libxl_device_vtpm_destroy
|
|
* libxl_device_vkb_remove
|
|
* libxl_device_vkb_destroy
|
|
* libxl_device_vfb_remove
|
|
* libxl_device_vfb_destroy
|
|
*/
|
|
#define DEFINE_DEVICE_REMOVE(type, removedestroy, f) \
|
|
int libxl_device_##type##_##removedestroy(libxl_ctx *ctx, \
|
|
uint32_t domid, libxl_device_##type *type, \
|
|
const libxl_asyncop_how *ao_how) \
|
|
{ \
|
|
AO_CREATE(ctx, domid, ao_how); \
|
|
libxl__device *device; \
|
|
libxl__ao_device *aodev; \
|
|
int rc; \
|
|
\
|
|
GCNEW(device); \
|
|
rc = libxl__device_from_##type(gc, domid, type, device); \
|
|
if (rc != 0) goto out; \
|
|
\
|
|
GCNEW(aodev); \
|
|
libxl__prepare_ao_device(ao, aodev); \
|
|
aodev->action = LIBXL__DEVICE_ACTION_REMOVE; \
|
|
aodev->dev = device; \
|
|
aodev->callback = device_addrm_aocomplete; \
|
|
aodev->force = f; \
|
|
libxl__initiate_device_remove(egc, aodev); \
|
|
\
|
|
out: \
|
|
if (rc) return AO_CREATE_FAIL(rc); \
|
|
return AO_INPROGRESS; \
|
|
}
|
|
|
|
/* Define all remove/destroy functions and undef the macro */
|
|
|
|
/* disk */
|
|
DEFINE_DEVICE_REMOVE(disk, remove, 0)
|
|
DEFINE_DEVICE_REMOVE(disk, destroy, 1)
|
|
|
|
/* nic */
|
|
DEFINE_DEVICE_REMOVE(nic, remove, 0)
|
|
DEFINE_DEVICE_REMOVE(nic, destroy, 1)
|
|
|
|
/* vkb */
|
|
DEFINE_DEVICE_REMOVE(vkb, remove, 0)
|
|
DEFINE_DEVICE_REMOVE(vkb, destroy, 1)
|
|
|
|
/* vfb */
|
|
|
|
DEFINE_DEVICE_REMOVE(vfb, remove, 0)
|
|
DEFINE_DEVICE_REMOVE(vfb, destroy, 1)
|
|
|
|
/* vtpm */
|
|
DEFINE_DEVICE_REMOVE(vtpm, remove, 0)
|
|
DEFINE_DEVICE_REMOVE(vtpm, destroy, 1)
|
|
|
|
/* channel/console hotunplug is not implemented. There are 2 possibilities:
|
|
* 1. add support for secondary consoles to xenconsoled
|
|
* 2. dynamically add/remove qemu chardevs via qmp messages. */
|
|
|
|
#undef DEFINE_DEVICE_REMOVE
|
|
|
|
/******************************************************************************/
|
|
|
|
/* Macro for defining device addition functions in a compact way */
|
|
/* The following functions are defined:
|
|
* libxl_device_disk_add
|
|
* libxl_device_nic_add
|
|
* libxl_device_vtpm_add
|
|
*/
|
|
|
|
#define DEFINE_DEVICE_ADD(type) \
|
|
int libxl_device_##type##_add(libxl_ctx *ctx, \
|
|
uint32_t domid, libxl_device_##type *type, \
|
|
const libxl_asyncop_how *ao_how) \
|
|
{ \
|
|
AO_CREATE(ctx, domid, ao_how); \
|
|
libxl__ao_device *aodev; \
|
|
\
|
|
GCNEW(aodev); \
|
|
libxl__prepare_ao_device(ao, aodev); \
|
|
aodev->action = LIBXL__DEVICE_ACTION_ADD; \
|
|
aodev->callback = device_addrm_aocomplete; \
|
|
aodev->update_json = true; \
|
|
libxl__device_##type##_add(egc, domid, type, aodev); \
|
|
\
|
|
return AO_INPROGRESS; \
|
|
}
|
|
|
|
/* Define alladd functions and undef the macro */
|
|
|
|
/* disk */
|
|
DEFINE_DEVICE_ADD(disk)
|
|
|
|
/* nic */
|
|
DEFINE_DEVICE_ADD(nic)
|
|
|
|
/* vtpm */
|
|
DEFINE_DEVICE_ADD(vtpm)
|
|
|
|
#undef DEFINE_DEVICE_ADD
|
|
|
|
/******************************************************************************/
|
|
|
|
/*
|
|
* Data structures used to track devices handled by driver domains
|
|
*/
|
|
|
|
/*
|
|
* Structure that describes a device handled by a driver domain
|
|
*/
|
|
typedef struct libxl__ddomain_device {
|
|
libxl__device *dev;
|
|
LIBXL_SLIST_ENTRY(struct libxl__ddomain_device) next;
|
|
} libxl__ddomain_device;
|
|
|
|
/*
|
|
* Structure that describes a domain and it's associated devices
|
|
*/
|
|
typedef struct libxl__ddomain_guest {
|
|
uint32_t domid;
|
|
int num_vifs, num_vbds, num_qdisks;
|
|
LIBXL_SLIST_HEAD(, struct libxl__ddomain_device) devices;
|
|
LIBXL_SLIST_ENTRY(struct libxl__ddomain_guest) next;
|
|
} libxl__ddomain_guest;
|
|
|
|
/*
|
|
* Main structure used by a driver domain to keep track of devices
|
|
* currently in use
|
|
*/
|
|
typedef struct {
|
|
libxl__ao *ao;
|
|
libxl__ev_xswatch watch;
|
|
LIBXL_SLIST_HEAD(, struct libxl__ddomain_guest) guests;
|
|
} libxl__ddomain;
|
|
|
|
static libxl__ddomain_guest *search_for_guest(libxl__ddomain *ddomain,
|
|
uint32_t domid)
|
|
{
|
|
libxl__ddomain_guest *dguest;
|
|
|
|
LIBXL_SLIST_FOREACH(dguest, &ddomain->guests, next) {
|
|
if (dguest->domid == domid)
|
|
return dguest;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static libxl__ddomain_device *search_for_device(libxl__ddomain_guest *dguest,
|
|
libxl__device *dev)
|
|
{
|
|
libxl__ddomain_device *ddev;
|
|
|
|
LIBXL_SLIST_FOREACH(ddev, &dguest->devices, next) {
|
|
#define LIBXL_DEVICE_CMP(dev1, dev2, entry) (dev1->entry == dev2->entry)
|
|
if (LIBXL_DEVICE_CMP(ddev->dev, dev, backend_devid) &&
|
|
LIBXL_DEVICE_CMP(ddev->dev, dev, backend_domid) &&
|
|
LIBXL_DEVICE_CMP(ddev->dev, dev, devid) &&
|
|
LIBXL_DEVICE_CMP(ddev->dev, dev, domid) &&
|
|
LIBXL_DEVICE_CMP(ddev->dev, dev, backend_kind) &&
|
|
LIBXL_DEVICE_CMP(ddev->dev, dev, kind))
|
|
return ddev;
|
|
#undef LIBXL_DEVICE_CMP
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void device_complete(libxl__egc *egc, libxl__ao_device *aodev)
|
|
{
|
|
STATE_AO_GC(aodev->ao);
|
|
|
|
LOG(DEBUG, "device %s %s %s",
|
|
libxl__device_backend_path(gc, aodev->dev),
|
|
libxl__device_action_to_string(aodev->action),
|
|
aodev->rc ? "failed" : "succeed");
|
|
|
|
if (aodev->action == LIBXL__DEVICE_ACTION_REMOVE)
|
|
free(aodev->dev);
|
|
|
|
libxl__nested_ao_free(aodev->ao);
|
|
}
|
|
|
|
static void qdisk_spawn_outcome(libxl__egc *egc, libxl__dm_spawn_state *dmss,
|
|
int rc)
|
|
{
|
|
STATE_AO_GC(dmss->spawn.ao);
|
|
|
|
LOG(DEBUG, "qdisk backend spawn for domain %u %s", dmss->guest_domid,
|
|
rc ? "failed" : "succeed");
|
|
|
|
libxl__nested_ao_free(dmss->spawn.ao);
|
|
}
|
|
|
|
/*
|
|
* The following comment applies to both add_device and remove_device.
|
|
*
|
|
* If the return value is greater than 0, it means there's no ao dispatched,
|
|
* so the free of the nested ao should be done by the parent when it has
|
|
* finished.
|
|
*/
|
|
static int add_device(libxl__egc *egc, libxl__ao *ao,
|
|
libxl__ddomain_guest *dguest,
|
|
libxl__ddomain_device *ddev)
|
|
{
|
|
AO_GC;
|
|
libxl__device *dev = ddev->dev;
|
|
libxl__ao_device *aodev;
|
|
libxl__dm_spawn_state *dmss;
|
|
int rc = 0;
|
|
|
|
switch(dev->backend_kind) {
|
|
case LIBXL__DEVICE_KIND_VBD:
|
|
case LIBXL__DEVICE_KIND_VIF:
|
|
if (dev->backend_kind == LIBXL__DEVICE_KIND_VBD) dguest->num_vbds++;
|
|
if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) dguest->num_vifs++;
|
|
|
|
GCNEW(aodev);
|
|
libxl__prepare_ao_device(ao, aodev);
|
|
aodev->dev = dev;
|
|
aodev->action = LIBXL__DEVICE_ACTION_ADD;
|
|
aodev->callback = device_complete;
|
|
libxl__wait_device_connection(egc, aodev);
|
|
|
|
break;
|
|
case LIBXL__DEVICE_KIND_QDISK:
|
|
if (dguest->num_qdisks == 0) {
|
|
GCNEW(dmss);
|
|
dmss->guest_domid = dev->domid;
|
|
dmss->spawn.ao = ao;
|
|
dmss->callback = qdisk_spawn_outcome;
|
|
|
|
libxl__spawn_qdisk_backend(egc, dmss);
|
|
}
|
|
dguest->num_qdisks++;
|
|
|
|
break;
|
|
default:
|
|
rc = 1;
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int remove_device(libxl__egc *egc, libxl__ao *ao,
|
|
libxl__ddomain_guest *dguest,
|
|
libxl__ddomain_device *ddev)
|
|
{
|
|
AO_GC;
|
|
libxl__device *dev = ddev->dev;
|
|
libxl__ao_device *aodev;
|
|
int rc = 0;
|
|
|
|
switch(ddev->dev->backend_kind) {
|
|
case LIBXL__DEVICE_KIND_VBD:
|
|
case LIBXL__DEVICE_KIND_VIF:
|
|
if (dev->backend_kind == LIBXL__DEVICE_KIND_VBD) dguest->num_vbds--;
|
|
if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) dguest->num_vifs--;
|
|
|
|
GCNEW(aodev);
|
|
libxl__prepare_ao_device(ao, aodev);
|
|
aodev->dev = dev;
|
|
aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
|
|
aodev->callback = device_complete;
|
|
libxl__initiate_device_remove(egc, aodev);
|
|
break;
|
|
case LIBXL__DEVICE_KIND_QDISK:
|
|
if (--dguest->num_qdisks == 0) {
|
|
rc = libxl__destroy_qdisk_backend(gc, dev->domid);
|
|
if (rc)
|
|
goto out;
|
|
}
|
|
libxl__device_destroy(gc, dev);
|
|
free(dev);
|
|
/* Fall through to return > 0, no ao has been dispatched */
|
|
default:
|
|
rc = 1;
|
|
break;
|
|
}
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch,
|
|
const char *watch_path,
|
|
const char *event_path)
|
|
{
|
|
libxl__ddomain *ddomain = CONTAINER_OF(watch, *ddomain, watch);
|
|
libxl__ao *nested_ao = libxl__nested_ao_create(ddomain->ao);
|
|
STATE_AO_GC(nested_ao);
|
|
char *p, *path;
|
|
const char *sstate, *sonline;
|
|
int state, online, rc, num_devs;
|
|
libxl__device *dev = NULL;
|
|
libxl__ddomain_device *ddev = NULL;
|
|
libxl__ddomain_guest *dguest = NULL;
|
|
bool free_ao = false;
|
|
|
|
/* Check if event_path ends with "state" or "online" and truncate it. */
|
|
path = libxl__strdup(gc, event_path);
|
|
p = strrchr(path, '/');
|
|
if (p == NULL)
|
|
goto skip;
|
|
if (strcmp(p, "/state") != 0 && strcmp(p, "/online") != 0)
|
|
goto skip;
|
|
/* Truncate the string so it points to the backend directory. */
|
|
*p = '\0';
|
|
|
|
/* Fetch the value of the state and online nodes. */
|
|
rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/state", path),
|
|
&sstate);
|
|
if (rc || !sstate)
|
|
goto skip;
|
|
state = atoi(sstate);
|
|
|
|
rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/online", path),
|
|
&sonline);
|
|
if (rc || !sonline)
|
|
goto skip;
|
|
online = atoi(sonline);
|
|
|
|
dev = libxl__zalloc(NOGC, sizeof(*dev));
|
|
rc = libxl__parse_backend_path(gc, path, dev);
|
|
if (rc)
|
|
goto skip;
|
|
|
|
dguest = search_for_guest(ddomain, dev->domid);
|
|
if (dguest == NULL && state == XenbusStateClosed) {
|
|
/*
|
|
* Spurious state change, device has already been disconnected
|
|
* or never attached.
|
|
*/
|
|
goto skip;
|
|
}
|
|
if (dguest == NULL) {
|
|
/* Create a new guest struct and initialize it */
|
|
dguest = libxl__zalloc(NOGC, sizeof(*dguest));
|
|
dguest->domid = dev->domid;
|
|
LIBXL_SLIST_INIT(&dguest->devices);
|
|
LIBXL_SLIST_INSERT_HEAD(&ddomain->guests, dguest, next);
|
|
LOG(DEBUG, "added domain %u to the list of active guests",
|
|
dguest->domid);
|
|
}
|
|
ddev = search_for_device(dguest, dev);
|
|
if (ddev == NULL && state == XenbusStateClosed) {
|
|
/*
|
|
* Spurious state change, device has already been disconnected
|
|
* or never attached.
|
|
*/
|
|
goto skip;
|
|
} else if (ddev == NULL) {
|
|
/*
|
|
* New device addition, allocate a struct to hold it and add it
|
|
* to the list of active devices for a given guest.
|
|
*/
|
|
ddev = libxl__zalloc(NOGC, sizeof(*ddev));
|
|
ddev->dev = dev;
|
|
LIBXL_SLIST_INSERT_HEAD(&dguest->devices, ddev, next);
|
|
LOG(DEBUG, "added device %s to the list of active devices", path);
|
|
rc = add_device(egc, nested_ao, dguest, ddev);
|
|
if (rc > 0)
|
|
free_ao = true;
|
|
} else if (state == XenbusStateClosed && online == 0) {
|
|
/*
|
|
* Removal of an active device, remove it from the list and
|
|
* free it's data structures if they are no longer needed.
|
|
*
|
|
* The free of the associated libxl__device is left to the
|
|
* helper remove_device function.
|
|
*/
|
|
LIBXL_SLIST_REMOVE(&dguest->devices, ddev, libxl__ddomain_device,
|
|
next);
|
|
LOG(DEBUG, "removed device %s from the list of active devices", path);
|
|
rc = remove_device(egc, nested_ao, dguest, ddev);
|
|
if (rc > 0)
|
|
free_ao = true;
|
|
|
|
free(ddev);
|
|
/* If this was the last device in the domain, remove it from the list */
|
|
num_devs = dguest->num_vifs + dguest->num_vbds + dguest->num_qdisks;
|
|
if (num_devs == 0) {
|
|
LIBXL_SLIST_REMOVE(&ddomain->guests, dguest, libxl__ddomain_guest,
|
|
next);
|
|
LOG(DEBUG, "removed domain %u from the list of active guests",
|
|
dguest->domid);
|
|
/* Clear any leftovers in libxl/<domid> */
|
|
libxl__xs_rm_checked(gc, XBT_NULL,
|
|
GCSPRINTF("libxl/%u", dguest->domid));
|
|
free(dguest);
|
|
}
|
|
}
|
|
|
|
if (free_ao)
|
|
libxl__nested_ao_free(nested_ao);
|
|
|
|
return;
|
|
|
|
skip:
|
|
libxl__nested_ao_free(nested_ao);
|
|
free(dev);
|
|
free(ddev);
|
|
free(dguest);
|
|
return;
|
|
}
|
|
|
|
/* Handler of events for device driver domains */
|
|
int libxl_device_events_handler(libxl_ctx *ctx,
|
|
const libxl_asyncop_how *ao_how)
|
|
{
|
|
AO_CREATE(ctx, 0, ao_how);
|
|
int rc;
|
|
uint32_t domid;
|
|
libxl__ddomain ddomain;
|
|
char *be_path;
|
|
|
|
ddomain.ao = ao;
|
|
LIBXL_SLIST_INIT(&ddomain.guests);
|
|
|
|
rc = libxl__get_domid(gc, &domid);
|
|
if (rc) {
|
|
LOG(ERROR, "unable to get domain id");
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* We use absolute paths because we want xswatch to also return
|
|
* absolute paths that can be parsed by libxl__parse_backend_path.
|
|
*/
|
|
be_path = GCSPRINTF("/local/domain/%u/backend", domid);
|
|
rc = libxl__ev_xswatch_register(gc, &ddomain.watch, backend_watch_callback,
|
|
be_path);
|
|
if (rc) goto out;
|
|
|
|
return AO_INPROGRESS;
|
|
|
|
out:
|
|
return AO_CREATE_FAIL(rc);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint32_t max_memkb)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *mem, *endptr;
|
|
uint32_t memorykb;
|
|
char *dompath = libxl__xs_get_dompath(gc, domid);
|
|
int rc = 1;
|
|
libxl__domain_userdata_lock *lock = NULL;
|
|
|
|
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) {
|
|
LOGE(ERROR, "cannot get memory info from %s/memory/target", dompath);
|
|
goto out;
|
|
}
|
|
memorykb = strtoul(mem, &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
LOGE(ERROR, "invalid memory %s from %s/memory/target\n", mem, dompath);
|
|
goto out;
|
|
}
|
|
|
|
if (max_memkb < memorykb) {
|
|
LOGE(ERROR,
|
|
"memory_static_max must be greater than or or equal to memory_dynamic_max");
|
|
goto out;
|
|
}
|
|
rc = xc_domain_setmaxmem(ctx->xch, domid, max_memkb + LIBXL_MAXMEM_CONSTANT);
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "xc_domain_setmaxmem domid=%d memkb=%d failed ""rc=%d\n",
|
|
domid, max_memkb + LIBXL_MAXMEM_CONSTANT, rc);
|
|
goto out;
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
if (lock) libxl__unlock_domain_userdata(lock);
|
|
CTX_UNLOCK;
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint32_t *target_memkb,
|
|
uint32_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 = strtoul(target, &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
LOGE(ERROR, "invalid memory target %s from %s\n", target,
|
|
target_path);
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (staticmax) {
|
|
*max_memkb = strtoul(staticmax, &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
LOGE(ERROR, "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, "%"PRIu32,
|
|
(uint32_t) info.current_memkb);
|
|
*target_memkb = (uint32_t) info.current_memkb;
|
|
}
|
|
if (staticmax == NULL) {
|
|
libxl__xs_printf(gc, t, max_path, "%"PRIu32,
|
|
(uint32_t) info.max_memkb);
|
|
*max_memkb = (uint32_t) 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,
|
|
int32_t target_memkb, int relative, int enforce)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc = 1, abort_transaction = 0;
|
|
uint64_t memorykb;
|
|
uint32_t videoram = 0;
|
|
uint32_t current_target_memkb = 0, new_target_memkb = 0;
|
|
uint32_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__domain_userdata_lock *lock;
|
|
|
|
CTX_LOCK;
|
|
|
|
lock = libxl__lock_domain_userdata(gc, domid);
|
|
if (!lock) {
|
|
rc = ERROR_LOCK_FAIL;
|
|
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))
|
|
goto out_no_transaction;
|
|
rc = libxl__fill_dom0_memory_info(gc, ¤t_target_memkb,
|
|
¤t_max_memkb);
|
|
if (rc < 0)
|
|
goto out_no_transaction;
|
|
goto retry_transaction;
|
|
} else if (!target) {
|
|
LOGE(ERROR, "cannot get target memory info from %s/memory/target",
|
|
dompath);
|
|
abort_transaction = 1;
|
|
goto out;
|
|
} else {
|
|
current_target_memkb = strtoul(target, &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
LOGE(ERROR, "invalid memory target %s from %s/memory/target\n",
|
|
target, dompath);
|
|
abort_transaction = 1;
|
|
goto out;
|
|
}
|
|
}
|
|
memmax = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/static-max", dompath));
|
|
if (!memmax) {
|
|
LOGE(ERROR, "cannot get memory info from %s/memory/static-max",
|
|
dompath);
|
|
abort_transaction = 1;
|
|
goto out;
|
|
}
|
|
memorykb = strtoul(memmax, &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
LOGE(ERROR, "invalid max memory %s from %s/memory/static-max\n",
|
|
memmax, dompath);
|
|
abort_transaction = 1;
|
|
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 && abs(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) {
|
|
LOG(ERROR,
|
|
"memory_dynamic_max must be less than or equal to"
|
|
" memory_static_max\n");
|
|
abort_transaction = 1;
|
|
goto out;
|
|
}
|
|
|
|
if (!domid && new_target_memkb < LIBXL_MIN_DOM0_MEM) {
|
|
LOG(ERROR, "new target %d for dom0 is below the minimum threshold",
|
|
new_target_memkb);
|
|
abort_transaction = 1;
|
|
goto out;
|
|
}
|
|
|
|
if (enforce) {
|
|
memorykb = new_target_memkb + videoram;
|
|
rc = xc_domain_setmaxmem(ctx->xch, domid, memorykb +
|
|
LIBXL_MAXMEM_CONSTANT);
|
|
if (rc != 0) {
|
|
LOGE(ERROR,
|
|
"xc_domain_setmaxmem domid=%u memkb=%"PRIu64" failed ""rc=%d\n",
|
|
domid,
|
|
memorykb + LIBXL_MAXMEM_CONSTANT,
|
|
rc);
|
|
abort_transaction = 1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
rc = xc_domain_set_pod_target(ctx->xch, domid,
|
|
(new_target_memkb + LIBXL_MAXMEM_CONSTANT) / 4, NULL, NULL, NULL);
|
|
if (rc != 0) {
|
|
LOGE(ERROR,
|
|
"xc_domain_set_pod_target domid=%d, memkb=%d ""failed rc=%d\n",
|
|
domid,
|
|
new_target_memkb / 4,
|
|
rc);
|
|
abort_transaction = 1;
|
|
goto out;
|
|
}
|
|
|
|
libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath),
|
|
"%"PRIu32, new_target_memkb);
|
|
rc = xc_domain_getinfolist(ctx->xch, domid, 1, &info);
|
|
if (rc != 1 || info.domain != domid) {
|
|
abort_transaction = 1;
|
|
goto out;
|
|
}
|
|
|
|
libxl_dominfo_init(&ptr);
|
|
xcinfo2xlinfo(ctx, &info, &ptr);
|
|
uuid = libxl__uuid2string(gc, ptr.uuid);
|
|
libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid),
|
|
"%"PRIu32, new_target_memkb / 1024);
|
|
libxl_dominfo_dispose(&ptr);
|
|
|
|
out:
|
|
if (!xs_transaction_end(ctx->xsh, t, abort_transaction)
|
|
&& !abort_transaction)
|
|
if (errno == EAGAIN)
|
|
goto retry_transaction;
|
|
|
|
out_no_transaction:
|
|
if (lock) libxl__unlock_domain_userdata(lock);
|
|
CTX_UNLOCK;
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
/* out_target_memkb and out_max_memkb can be NULL */
|
|
static int libxl__get_memory_target(libxl__gc *gc, uint32_t domid,
|
|
uint32_t *out_target_memkb,
|
|
uint32_t *out_max_memkb)
|
|
{
|
|
int rc;
|
|
char *target = NULL, *static_max = NULL, *endptr = NULL;
|
|
char *dompath = libxl__xs_get_dompath(gc, domid);
|
|
uint32_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) {
|
|
LOGE(ERROR, "cannot get target memory info from %s/memory/target",
|
|
dompath);
|
|
goto out;
|
|
} else if (!static_max) {
|
|
LOGE(ERROR,
|
|
"cannot get target memory info from %s/memory/static-max",
|
|
dompath);
|
|
goto out;
|
|
} else {
|
|
target_memkb = strtoul(target, &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
LOGE(ERROR, "invalid memory target %s from %s/memory/target\n",
|
|
target, dompath);
|
|
goto out;
|
|
}
|
|
max_memkb = strtoul(static_max, &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
LOGE(ERROR,
|
|
"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;
|
|
}
|
|
|
|
int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid,
|
|
uint32_t *out_target)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc;
|
|
|
|
rc = libxl__get_memory_target(gc, domid, out_target, NULL);
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_domain_need_memory(libxl_ctx *ctx, libxl_domain_build_info *b_info,
|
|
uint32_t *need_memkb)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc;
|
|
|
|
rc = libxl__domain_build_info_setdefault(gc, b_info);
|
|
if (rc) goto out;
|
|
|
|
*need_memkb = b_info->target_memkb;
|
|
switch (b_info->type) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
*need_memkb += b_info->shadow_memkb + LIBXL_HVM_EXTRA_MEMORY;
|
|
if (libxl_defbool_val(b_info->device_model_stubdomain))
|
|
*need_memkb += 32 * 1024;
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
*need_memkb += b_info->shadow_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:
|
|
GC_FREE;
|
|
return rc;
|
|
|
|
}
|
|
|
|
int libxl_get_free_memory(libxl_ctx *ctx, uint32_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_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint32_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;
|
|
uint32_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;
|
|
}
|
|
|
|
int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo)
|
|
{
|
|
xc_physinfo_t xcphysinfo = { 0 };
|
|
int rc;
|
|
long l;
|
|
GC_INIT(ctx);
|
|
|
|
rc = xc_physinfo(ctx->xch, &xcphysinfo);
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "getting physinfo");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
physinfo->threads_per_core = xcphysinfo.threads_per_core;
|
|
physinfo->cores_per_socket = xcphysinfo.cores_per_socket;
|
|
physinfo->max_cpu_id = xcphysinfo.max_cpu_id;
|
|
physinfo->nr_cpus = xcphysinfo.nr_cpus;
|
|
physinfo->cpu_khz = xcphysinfo.cpu_khz;
|
|
physinfo->total_pages = xcphysinfo.total_pages;
|
|
physinfo->free_pages = xcphysinfo.free_pages;
|
|
physinfo->scrub_pages = xcphysinfo.scrub_pages;
|
|
physinfo->outstanding_pages = xcphysinfo.outstanding_pages;
|
|
l = xc_sharing_freed_pages(ctx->xch);
|
|
if (l < 0 && errno == ENOSYS) {
|
|
l = 0;
|
|
} else if (l < 0) {
|
|
LOGEV(ERROR, l, "getting sharing freed pages");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
physinfo->sharing_freed_pages = l;
|
|
l = xc_sharing_used_frames(ctx->xch);
|
|
if (l < 0 && errno == ENOSYS) {
|
|
l = 0;
|
|
} else if (l < 0) {
|
|
LOGEV(ERROR, l, "getting sharing used frames");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
physinfo->sharing_used_frames = l;
|
|
physinfo->nr_nodes = xcphysinfo.nr_nodes;
|
|
memcpy(physinfo->hw_cap,xcphysinfo.hw_cap, sizeof(physinfo->hw_cap));
|
|
|
|
physinfo->cap_hvm = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hvm);
|
|
physinfo->cap_hvm_directio =
|
|
!!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hvm_directio);
|
|
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out)
|
|
{
|
|
GC_INIT(ctx);
|
|
xc_cputopo_t *cputopo;
|
|
libxl_cputopology *ret = NULL;
|
|
int i;
|
|
unsigned num_cpus = 0;
|
|
|
|
/* Setting buffer to NULL makes the call return number of CPUs */
|
|
if (xc_cputopoinfo(ctx->xch, &num_cpus, NULL))
|
|
{
|
|
LOGE(ERROR, "Unable to determine number of CPUS");
|
|
goto out;
|
|
}
|
|
|
|
cputopo = libxl__zalloc(gc, sizeof(*cputopo) * num_cpus);
|
|
|
|
if (xc_cputopoinfo(ctx->xch, &num_cpus, cputopo)) {
|
|
LOGE(ERROR, "CPU topology info hypercall failed");
|
|
goto out;
|
|
}
|
|
|
|
ret = libxl__zalloc(NOGC, sizeof(libxl_cputopology) * num_cpus);
|
|
|
|
for (i = 0; i < num_cpus; i++) {
|
|
#define V(map, i, invalid) ( cputopo[i].map == invalid) ? \
|
|
LIBXL_CPUTOPOLOGY_INVALID_ENTRY : cputopo[i].map
|
|
ret[i].core = V(core, i, XEN_INVALID_CORE_ID);
|
|
ret[i].socket = V(socket, i, XEN_INVALID_SOCKET_ID);
|
|
ret[i].node = V(node, i, XEN_INVALID_NODE_ID);
|
|
#undef V
|
|
}
|
|
|
|
*nb_cpu_out = num_cpus;
|
|
|
|
out:
|
|
GC_FREE;
|
|
return ret;
|
|
}
|
|
|
|
libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs)
|
|
{
|
|
GC_INIT(ctx);
|
|
physdev_pci_device_t *devs;
|
|
uint32_t *nodes;
|
|
libxl_pcitopology *ret = NULL;
|
|
int i, rc;
|
|
|
|
*num_devs = libxl__pci_numdevs(gc);
|
|
if (*num_devs < 0) {
|
|
LOG(ERROR, "Unable to determine number of PCI devices, rc %d",
|
|
*num_devs);
|
|
goto out;
|
|
}
|
|
|
|
devs = libxl__zalloc(gc, sizeof(*devs) * *num_devs);
|
|
nodes = libxl__zalloc(gc, sizeof(*nodes) * *num_devs);
|
|
|
|
rc = libxl__pci_topology_init(gc, devs, *num_devs);
|
|
if (rc) {
|
|
LOG(ERROR, "Cannot initialize PCI hypercall structure, rc %d", rc);
|
|
goto out;
|
|
}
|
|
|
|
if (xc_pcitopoinfo(ctx->xch, *num_devs, devs, nodes) != 0) {
|
|
LOGE(ERROR, "PCI topology info hypercall failed");
|
|
goto out;
|
|
}
|
|
|
|
ret = libxl__zalloc(NOGC, sizeof(libxl_pcitopology) * *num_devs);
|
|
|
|
for (i = 0; i < *num_devs; i++) {
|
|
ret[i].seg = devs[i].seg;
|
|
ret[i].bus = devs[i].bus;
|
|
ret[i].devfn = devs[i].devfn;
|
|
ret[i].node = ((nodes[i] == XEN_INVALID_NODE_ID) ||
|
|
(nodes[i] == XEN_INVALID_DEV)) ?
|
|
LIBXL_PCITOPOLOGY_INVALID_ENTRY : nodes[i];
|
|
}
|
|
|
|
out:
|
|
GC_FREE;
|
|
return ret;
|
|
}
|
|
|
|
libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr)
|
|
{
|
|
GC_INIT(ctx);
|
|
xc_meminfo_t *meminfo;
|
|
uint32_t *distance;
|
|
libxl_numainfo *ret = NULL;
|
|
int i, j;
|
|
unsigned num_nodes = 0;
|
|
|
|
if (xc_numainfo(ctx->xch, &num_nodes, NULL, NULL)) {
|
|
LOGE(ERROR, "Unable to determine number of nodes");
|
|
goto out;
|
|
}
|
|
|
|
meminfo = libxl__zalloc(gc, sizeof(*meminfo) * num_nodes);
|
|
distance = libxl__zalloc(gc, sizeof(*distance) * num_nodes * num_nodes);
|
|
|
|
if (xc_numainfo(ctx->xch, &num_nodes, meminfo, distance)) {
|
|
LOGE(ERROR, "getting numainfo");
|
|
goto out;
|
|
}
|
|
|
|
*nr = num_nodes;
|
|
|
|
ret = libxl__zalloc(NOGC, sizeof(libxl_numainfo) * num_nodes);
|
|
for (i = 0; i < num_nodes; i++)
|
|
ret[i].dists = libxl__calloc(NOGC, num_nodes, sizeof(*distance));
|
|
|
|
for (i = 0; i < num_nodes; i++) {
|
|
#define V(val, invalid) (val == invalid) ? \
|
|
LIBXL_NUMAINFO_INVALID_ENTRY : val
|
|
ret[i].size = V(meminfo[i].memsize, XEN_INVALID_MEM_SZ);
|
|
ret[i].free = V(meminfo[i].memfree, XEN_INVALID_MEM_SZ);
|
|
ret[i].num_dists = num_nodes;
|
|
for (j = 0; j < ret[i].num_dists; j++) {
|
|
unsigned idx = i * num_nodes + j;
|
|
ret[i].dists[j] = V(distance[idx], XEN_INVALID_NODE_DIST);
|
|
}
|
|
#undef V
|
|
}
|
|
|
|
out:
|
|
GC_FREE;
|
|
return ret;
|
|
}
|
|
|
|
const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx)
|
|
{
|
|
union {
|
|
xen_extraversion_t xen_extra;
|
|
xen_compile_info_t xen_cc;
|
|
xen_changeset_info_t xen_chgset;
|
|
xen_capabilities_info_t xen_caps;
|
|
xen_platform_parameters_t p_parms;
|
|
xen_commandline_t xen_commandline;
|
|
} u;
|
|
long xen_version;
|
|
libxl_version_info *info = &ctx->version_info;
|
|
|
|
if (info->xen_version_extra != NULL)
|
|
return info;
|
|
|
|
xen_version = xc_version(ctx->xch, XENVER_version, NULL);
|
|
info->xen_version_major = xen_version >> 16;
|
|
info->xen_version_minor = xen_version & 0xFF;
|
|
|
|
xc_version(ctx->xch, XENVER_extraversion, &u.xen_extra);
|
|
info->xen_version_extra = strdup(u.xen_extra);
|
|
|
|
xc_version(ctx->xch, XENVER_compile_info, &u.xen_cc);
|
|
info->compiler = strdup(u.xen_cc.compiler);
|
|
info->compile_by = strdup(u.xen_cc.compile_by);
|
|
info->compile_domain = strdup(u.xen_cc.compile_domain);
|
|
info->compile_date = strdup(u.xen_cc.compile_date);
|
|
|
|
xc_version(ctx->xch, XENVER_capabilities, &u.xen_caps);
|
|
info->capabilities = strdup(u.xen_caps);
|
|
|
|
xc_version(ctx->xch, XENVER_changeset, &u.xen_chgset);
|
|
info->changeset = strdup(u.xen_chgset);
|
|
|
|
xc_version(ctx->xch, XENVER_platform_parameters, &u.p_parms);
|
|
info->virt_start = u.p_parms.virt_start;
|
|
|
|
info->pagesize = xc_version(ctx->xch, XENVER_pagesize, NULL);
|
|
|
|
xc_version(ctx->xch, XENVER_commandline, &u.xen_commandline);
|
|
info->commandline = strdup(u.xen_commandline);
|
|
|
|
return info;
|
|
}
|
|
|
|
libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
|
|
int *nr_vcpus_out, int *nr_cpus_out)
|
|
{
|
|
GC_INIT(ctx);
|
|
libxl_vcpuinfo *ptr, *ret;
|
|
xc_domaininfo_t domaininfo;
|
|
xc_vcpuinfo_t vcpuinfo;
|
|
|
|
if (xc_domain_getinfolist(ctx->xch, domid, 1, &domaininfo) != 1) {
|
|
LOGE(ERROR, "getting infolist");
|
|
GC_FREE;
|
|
return NULL;
|
|
}
|
|
|
|
if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) {
|
|
GC_FREE;
|
|
return NULL;
|
|
}
|
|
|
|
*nr_cpus_out = libxl_get_max_cpus(ctx);
|
|
ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1,
|
|
sizeof(libxl_vcpuinfo));
|
|
|
|
for (*nr_vcpus_out = 0;
|
|
*nr_vcpus_out <= domaininfo.max_vcpu_id;
|
|
++*nr_vcpus_out, ++ptr) {
|
|
libxl_bitmap_init(&ptr->cpumap);
|
|
if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0))
|
|
goto err;
|
|
libxl_bitmap_init(&ptr->cpumap_soft);
|
|
if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0))
|
|
goto err;
|
|
if (xc_vcpu_getinfo(ctx->xch, domid, *nr_vcpus_out, &vcpuinfo) == -1) {
|
|
LOGE(ERROR, "getting vcpu info");
|
|
goto err;
|
|
}
|
|
|
|
if (xc_vcpu_getaffinity(ctx->xch, domid, *nr_vcpus_out,
|
|
ptr->cpumap.map, ptr->cpumap_soft.map,
|
|
XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) {
|
|
LOGE(ERROR, "getting vcpu affinity");
|
|
goto err;
|
|
}
|
|
ptr->vcpuid = *nr_vcpus_out;
|
|
ptr->cpu = vcpuinfo.cpu;
|
|
ptr->online = !!vcpuinfo.online;
|
|
ptr->blocked = !!vcpuinfo.blocked;
|
|
ptr->running = !!vcpuinfo.running;
|
|
ptr->vcpu_time = vcpuinfo.cpu_time;
|
|
}
|
|
GC_FREE;
|
|
return ret;
|
|
|
|
err:
|
|
libxl_bitmap_dispose(&ptr->cpumap);
|
|
libxl_bitmap_dispose(&ptr->cpumap_soft);
|
|
free(ret);
|
|
GC_FREE;
|
|
return NULL;
|
|
}
|
|
|
|
int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid,
|
|
const libxl_bitmap *cpumap_hard,
|
|
const libxl_bitmap *cpumap_soft)
|
|
{
|
|
GC_INIT(ctx);
|
|
libxl_bitmap hard, soft;
|
|
int rc, flags = 0;
|
|
|
|
libxl_bitmap_init(&hard);
|
|
libxl_bitmap_init(&soft);
|
|
|
|
if (!cpumap_hard && !cpumap_soft) {
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Xen wants writable hard and/or soft cpumaps, to put back in them
|
|
* the effective hard and/or soft affinity that will be used.
|
|
*/
|
|
if (cpumap_hard) {
|
|
rc = libxl_cpu_bitmap_alloc(ctx, &hard, 0);
|
|
if (rc)
|
|
goto out;
|
|
|
|
libxl__bitmap_copy_best_effort(gc, &hard, cpumap_hard);
|
|
flags = XEN_VCPUAFFINITY_HARD;
|
|
}
|
|
if (cpumap_soft) {
|
|
rc = libxl_cpu_bitmap_alloc(ctx, &soft, 0);
|
|
if (rc)
|
|
goto out;
|
|
|
|
libxl__bitmap_copy_best_effort(gc, &soft, cpumap_soft);
|
|
flags |= XEN_VCPUAFFINITY_SOFT;
|
|
}
|
|
|
|
if (xc_vcpu_setaffinity(ctx->xch, domid, vcpuid,
|
|
cpumap_hard ? hard.map : NULL,
|
|
cpumap_soft ? soft.map : NULL,
|
|
flags)) {
|
|
LOGE(ERROR, "setting vcpu affinity");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Let's check the results. Hard affinity will never be empty, but it
|
|
* is possible that Xen will use something different from what we asked
|
|
* for various reasons. If that's the case, report it.
|
|
*/
|
|
if (cpumap_hard &&
|
|
!libxl_bitmap_equal(cpumap_hard, &hard, 0))
|
|
LOG(DEBUG, "New hard affinity for vcpu %d has unreachable cpus",
|
|
vcpuid);
|
|
/*
|
|
* Soft affinity can both be different from what asked and empty. Check
|
|
* for (and report) both.
|
|
*/
|
|
if (cpumap_soft) {
|
|
if (!libxl_bitmap_equal(cpumap_soft, &soft, 0))
|
|
LOG(DEBUG, "New soft affinity for vcpu %d has unreachable cpus",
|
|
vcpuid);
|
|
if (libxl_bitmap_is_empty(&soft))
|
|
LOG(WARN, "all cpus in soft affinity of vcpu %d are unreachable."
|
|
" Only hard affinity will be considered for scheduling",
|
|
vcpuid);
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
libxl_bitmap_dispose(&hard);
|
|
libxl_bitmap_dispose(&soft);
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid,
|
|
unsigned int max_vcpus,
|
|
const libxl_bitmap *cpumap_hard,
|
|
const libxl_bitmap *cpumap_soft)
|
|
{
|
|
GC_INIT(ctx);
|
|
int i, rc = 0;
|
|
|
|
for (i = 0; i < max_vcpus; i++) {
|
|
if (libxl_set_vcpuaffinity(ctx, domid, i, cpumap_hard, cpumap_soft)) {
|
|
LOG(WARN, "failed to set affinity for %d", i);
|
|
rc = ERROR_FAIL;
|
|
}
|
|
}
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_bitmap *nodemap)
|
|
{
|
|
GC_INIT(ctx);
|
|
if (xc_domain_node_setaffinity(ctx->xch, domid, nodemap->map)) {
|
|
LOGE(ERROR, "setting node affinity");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_bitmap *nodemap)
|
|
{
|
|
GC_INIT(ctx);
|
|
if (xc_domain_node_getaffinity(ctx->xch, domid, nodemap->map)) {
|
|
LOGE(ERROR, "getting node affinity");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid,
|
|
libxl_bitmap *cpumap,
|
|
const libxl_dominfo *info)
|
|
{
|
|
char *dompath;
|
|
xs_transaction_t t;
|
|
int i, rc = ERROR_FAIL;
|
|
|
|
if (!(dompath = libxl__xs_get_dompath(gc, domid)))
|
|
goto out;
|
|
|
|
retry_transaction:
|
|
t = xs_transaction_start(CTX->xsh);
|
|
for (i = 0; i <= info->vcpu_max_id; i++)
|
|
libxl__xs_printf(gc, t,
|
|
GCSPRINTF("%s/cpu/%u/availability", dompath, i),
|
|
"%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline");
|
|
if (!xs_transaction_end(CTX->xsh, t, 0)) {
|
|
if (errno == EAGAIN)
|
|
goto retry_transaction;
|
|
} else
|
|
rc = 0;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
static int libxl__set_vcpuonline_qmp(libxl__gc *gc, uint32_t domid,
|
|
libxl_bitmap *cpumap,
|
|
const libxl_dominfo *info)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i <= info->vcpu_max_id; i++) {
|
|
if (libxl_bitmap_test(cpumap, i)) {
|
|
/* Return value is ignore because it does not tell anything useful
|
|
* on the completion of the command.
|
|
* (For instance, "CPU already plugged-in" give the same return
|
|
* value as "command not supported".)
|
|
*/
|
|
libxl__qmp_cpu_add(gc, domid, i);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid, libxl_bitmap *cpumap)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc, maxcpus;
|
|
libxl_dominfo info;
|
|
|
|
libxl_dominfo_init(&info);
|
|
|
|
rc = libxl_domain_info(CTX, &info, domid);
|
|
if (rc < 0) {
|
|
LOGE(ERROR, "getting domain info list");
|
|
goto out;
|
|
}
|
|
|
|
maxcpus = libxl_bitmap_count_set(cpumap);
|
|
if (maxcpus > info.vcpu_max_id + 1)
|
|
{
|
|
LOGE(ERROR, "Requested %d VCPUs, however maxcpus is %d!",
|
|
maxcpus, info.vcpu_max_id + 1);
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
switch (libxl__domain_type(gc, domid)) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
switch (libxl__device_model_version_running(gc, domid)) {
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
|
|
rc = libxl__set_vcpuonline_xenstore(gc, domid, cpumap, &info);
|
|
break;
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
|
|
rc = libxl__set_vcpuonline_qmp(gc, domid, cpumap, &info);
|
|
break;
|
|
default:
|
|
rc = ERROR_INVAL;
|
|
}
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
rc = libxl__set_vcpuonline_xenstore(gc, domid, cpumap, &info);
|
|
break;
|
|
default:
|
|
rc = ERROR_INVAL;
|
|
}
|
|
out:
|
|
libxl_dominfo_dispose(&info);
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
libxl_scheduler libxl_get_scheduler(libxl_ctx *ctx)
|
|
{
|
|
libxl_scheduler sched, ret;
|
|
GC_INIT(ctx);
|
|
if ((ret = xc_sched_id(ctx->xch, (int *)&sched)) != 0) {
|
|
LOGE(ERROR, "getting domain info list");
|
|
return ERROR_FAIL;
|
|
GC_FREE;
|
|
}
|
|
GC_FREE;
|
|
return sched;
|
|
}
|
|
|
|
static int sched_arinc653_domain_set(libxl__gc *gc, uint32_t domid,
|
|
const libxl_domain_sched_params *scinfo)
|
|
{
|
|
/* Currently, the ARINC 653 scheduler does not take any domain-specific
|
|
configuration, so we simply return success. */
|
|
return 0;
|
|
}
|
|
|
|
static int sched_credit_domain_get(libxl__gc *gc, uint32_t domid,
|
|
libxl_domain_sched_params *scinfo)
|
|
{
|
|
struct xen_domctl_sched_credit sdom;
|
|
int rc;
|
|
|
|
rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "getting domain sched credit");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
libxl_domain_sched_params_init(scinfo);
|
|
scinfo->sched = LIBXL_SCHEDULER_CREDIT;
|
|
scinfo->weight = sdom.weight;
|
|
scinfo->cap = sdom.cap;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sched_credit_domain_set(libxl__gc *gc, uint32_t domid,
|
|
const libxl_domain_sched_params *scinfo)
|
|
{
|
|
struct xen_domctl_sched_credit sdom;
|
|
xc_domaininfo_t domaininfo;
|
|
int rc;
|
|
|
|
rc = xc_domain_getinfolist(CTX->xch, domid, 1, &domaininfo);
|
|
if (rc < 0) {
|
|
LOGE(ERROR, "getting domain info list");
|
|
return ERROR_FAIL;
|
|
}
|
|
if (rc != 1 || domaininfo.domain != domid)
|
|
return ERROR_INVAL;
|
|
|
|
rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom);
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "getting domain sched credit");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
|
|
if (scinfo->weight < 1 || scinfo->weight > 65535) {
|
|
LOG(ERROR, "Cpu weight out of range, "
|
|
"valid values are within range from 1 to 65535");
|
|
return ERROR_INVAL;
|
|
}
|
|
sdom.weight = scinfo->weight;
|
|
}
|
|
|
|
if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) {
|
|
if (scinfo->cap < 0
|
|
|| scinfo->cap > (domaininfo.max_vcpu_id + 1) * 100) {
|
|
LOG(ERROR, "Cpu cap out of range, "
|
|
"valid range is from 0 to %d for specified number of vcpus",
|
|
((domaininfo.max_vcpu_id + 1) * 100));
|
|
return ERROR_INVAL;
|
|
}
|
|
sdom.cap = scinfo->cap;
|
|
}
|
|
|
|
rc = xc_sched_credit_domain_set(CTX->xch, domid, &sdom);
|
|
if ( rc < 0 ) {
|
|
LOGE(ERROR, "setting domain sched credit");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid,
|
|
libxl_sched_credit_params *scinfo)
|
|
{
|
|
struct xen_sysctl_credit_schedule sparam;
|
|
int rc;
|
|
GC_INIT(ctx);
|
|
|
|
rc = xc_sched_credit_params_get(ctx->xch, poolid, &sparam);
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "getting sched credit param");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
scinfo->tslice_ms = sparam.tslice_ms;
|
|
scinfo->ratelimit_us = sparam.ratelimit_us;
|
|
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid,
|
|
libxl_sched_credit_params *scinfo)
|
|
{
|
|
struct xen_sysctl_credit_schedule sparam;
|
|
int rc=0;
|
|
GC_INIT(ctx);
|
|
|
|
if (scinfo->tslice_ms < XEN_SYSCTL_CSCHED_TSLICE_MIN
|
|
|| scinfo->tslice_ms > XEN_SYSCTL_CSCHED_TSLICE_MAX) {
|
|
LOG(ERROR, "Time slice out of range, valid range is from %d to %d",
|
|
XEN_SYSCTL_CSCHED_TSLICE_MIN, XEN_SYSCTL_CSCHED_TSLICE_MAX);
|
|
GC_FREE;
|
|
return ERROR_INVAL;
|
|
}
|
|
if (scinfo->ratelimit_us < XEN_SYSCTL_SCHED_RATELIMIT_MIN
|
|
|| scinfo->ratelimit_us > XEN_SYSCTL_SCHED_RATELIMIT_MAX) {
|
|
LOG(ERROR, "Ratelimit out of range, valid range is from %d to %d",
|
|
XEN_SYSCTL_SCHED_RATELIMIT_MIN, XEN_SYSCTL_SCHED_RATELIMIT_MAX);
|
|
GC_FREE;
|
|
return ERROR_INVAL;
|
|
}
|
|
if (scinfo->ratelimit_us > scinfo->tslice_ms*1000) {
|
|
LOG(ERROR, "Ratelimit cannot be greater than timeslice");
|
|
GC_FREE;
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
sparam.tslice_ms = scinfo->tslice_ms;
|
|
sparam.ratelimit_us = scinfo->ratelimit_us;
|
|
|
|
rc = xc_sched_credit_params_set(ctx->xch, poolid, &sparam);
|
|
if ( rc < 0 ) {
|
|
LOGE(ERROR, "setting sched credit param");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
scinfo->tslice_ms = sparam.tslice_ms;
|
|
scinfo->ratelimit_us = sparam.ratelimit_us;
|
|
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
static int sched_credit2_domain_get(libxl__gc *gc, uint32_t domid,
|
|
libxl_domain_sched_params *scinfo)
|
|
{
|
|
struct xen_domctl_sched_credit2 sdom;
|
|
int rc;
|
|
|
|
rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "getting domain sched credit2");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
libxl_domain_sched_params_init(scinfo);
|
|
scinfo->sched = LIBXL_SCHEDULER_CREDIT2;
|
|
scinfo->weight = sdom.weight;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sched_credit2_domain_set(libxl__gc *gc, uint32_t domid,
|
|
const libxl_domain_sched_params *scinfo)
|
|
{
|
|
struct xen_domctl_sched_credit2 sdom;
|
|
int rc;
|
|
|
|
rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom);
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "getting domain sched credit2");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) {
|
|
if (scinfo->weight < 1 || scinfo->weight > 65535) {
|
|
LOG(ERROR, "Cpu weight out of range, "
|
|
"valid values are within range from 1 to 65535");
|
|
return ERROR_INVAL;
|
|
}
|
|
sdom.weight = scinfo->weight;
|
|
}
|
|
|
|
rc = xc_sched_credit2_domain_set(CTX->xch, domid, &sdom);
|
|
if ( rc < 0 ) {
|
|
LOGE(ERROR, "setting domain sched credit2");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sched_rtds_domain_get(libxl__gc *gc, uint32_t domid,
|
|
libxl_domain_sched_params *scinfo)
|
|
{
|
|
struct xen_domctl_sched_rtds sdom;
|
|
int rc;
|
|
|
|
rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "getting domain sched rtds");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
libxl_domain_sched_params_init(scinfo);
|
|
|
|
scinfo->sched = LIBXL_SCHEDULER_RTDS;
|
|
scinfo->period = sdom.period;
|
|
scinfo->budget = sdom.budget;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sched_rtds_domain_set(libxl__gc *gc, uint32_t domid,
|
|
const libxl_domain_sched_params *scinfo)
|
|
{
|
|
struct xen_domctl_sched_rtds sdom;
|
|
int rc;
|
|
|
|
rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom);
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "getting domain sched rtds");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
if (scinfo->period != LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT) {
|
|
if (scinfo->period < 1) {
|
|
LOG(ERROR, "VCPU period is not set or out of range, "
|
|
"valid values are larger than 1");
|
|
return ERROR_INVAL;
|
|
}
|
|
sdom.period = scinfo->period;
|
|
}
|
|
|
|
if (scinfo->budget != LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT) {
|
|
if (scinfo->budget < 1) {
|
|
LOG(ERROR, "VCPU budget is not set or out of range, "
|
|
"valid values are larger than 1");
|
|
return ERROR_INVAL;
|
|
}
|
|
sdom.budget = scinfo->budget;
|
|
}
|
|
|
|
if (sdom.budget > sdom.period) {
|
|
LOG(ERROR, "VCPU budget is larger than VCPU period, "
|
|
"VCPU budget should be no larger than VCPU period");
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
rc = xc_sched_rtds_domain_set(CTX->xch, domid, &sdom);
|
|
if (rc < 0) {
|
|
LOGE(ERROR, "setting domain sched rtds");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid,
|
|
const libxl_domain_sched_params *scinfo)
|
|
{
|
|
GC_INIT(ctx);
|
|
libxl_scheduler sched = scinfo->sched;
|
|
int ret;
|
|
|
|
if (sched == LIBXL_SCHEDULER_UNKNOWN)
|
|
sched = libxl__domain_scheduler(gc, domid);
|
|
|
|
switch (sched) {
|
|
case LIBXL_SCHEDULER_SEDF:
|
|
LOG(ERROR, "SEDF scheduler no longer available");
|
|
ret=ERROR_FEATURE_REMOVED;
|
|
break;
|
|
case LIBXL_SCHEDULER_CREDIT:
|
|
ret=sched_credit_domain_set(gc, domid, scinfo);
|
|
break;
|
|
case LIBXL_SCHEDULER_CREDIT2:
|
|
ret=sched_credit2_domain_set(gc, domid, scinfo);
|
|
break;
|
|
case LIBXL_SCHEDULER_ARINC653:
|
|
ret=sched_arinc653_domain_set(gc, domid, scinfo);
|
|
break;
|
|
case LIBXL_SCHEDULER_RTDS:
|
|
ret=sched_rtds_domain_set(gc, domid, scinfo);
|
|
break;
|
|
default:
|
|
LOG(ERROR, "Unknown scheduler");
|
|
ret=ERROR_INVAL;
|
|
break;
|
|
}
|
|
|
|
GC_FREE;
|
|
return ret;
|
|
}
|
|
|
|
int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_domain_sched_params *scinfo)
|
|
{
|
|
GC_INIT(ctx);
|
|
int ret;
|
|
|
|
libxl_domain_sched_params_init(scinfo);
|
|
|
|
scinfo->sched = libxl__domain_scheduler(gc, domid);
|
|
|
|
switch (scinfo->sched) {
|
|
case LIBXL_SCHEDULER_SEDF:
|
|
LOG(ERROR, "SEDF scheduler no longer available");
|
|
ret=ERROR_FEATURE_REMOVED;
|
|
break;
|
|
case LIBXL_SCHEDULER_CREDIT:
|
|
ret=sched_credit_domain_get(gc, domid, scinfo);
|
|
break;
|
|
case LIBXL_SCHEDULER_CREDIT2:
|
|
ret=sched_credit2_domain_get(gc, domid, scinfo);
|
|
break;
|
|
case LIBXL_SCHEDULER_RTDS:
|
|
ret=sched_rtds_domain_get(gc, domid, scinfo);
|
|
break;
|
|
default:
|
|
LOG(ERROR, "Unknown scheduler");
|
|
ret=ERROR_INVAL;
|
|
break;
|
|
}
|
|
|
|
GC_FREE;
|
|
return ret;
|
|
}
|
|
|
|
static int libxl__domain_s3_resume(libxl__gc *gc, int domid)
|
|
{
|
|
int rc = 0;
|
|
|
|
switch (libxl__domain_type(gc, domid)) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
switch (libxl__device_model_version_running(gc, domid)) {
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
|
|
rc = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0);
|
|
break;
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
|
|
rc = libxl__qmp_system_wakeup(gc, domid);
|
|
break;
|
|
default:
|
|
rc = ERROR_INVAL;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
rc = ERROR_INVAL;
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_trigger trigger, uint32_t vcpuid)
|
|
{
|
|
int rc;
|
|
GC_INIT(ctx);
|
|
|
|
switch (trigger) {
|
|
case LIBXL_TRIGGER_POWER:
|
|
rc = xc_domain_send_trigger(ctx->xch, domid,
|
|
XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid);
|
|
break;
|
|
case LIBXL_TRIGGER_SLEEP:
|
|
rc = xc_domain_send_trigger(ctx->xch, domid,
|
|
XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid);
|
|
break;
|
|
case LIBXL_TRIGGER_NMI:
|
|
rc = xc_domain_send_trigger(ctx->xch, domid,
|
|
XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid);
|
|
break;
|
|
case LIBXL_TRIGGER_INIT:
|
|
rc = xc_domain_send_trigger(ctx->xch, domid,
|
|
XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid);
|
|
break;
|
|
case LIBXL_TRIGGER_RESET:
|
|
rc = xc_domain_send_trigger(ctx->xch, domid,
|
|
XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid);
|
|
break;
|
|
case LIBXL_TRIGGER_S3RESUME:
|
|
rc = libxl__domain_s3_resume(gc, domid);
|
|
break;
|
|
default:
|
|
rc = -1;
|
|
errno = EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (rc != 0) {
|
|
LOGE(ERROR, "Send trigger '%s' failed",
|
|
libxl_trigger_to_string(trigger));
|
|
rc = ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *dompath = libxl__xs_get_dompath(gc, domid);
|
|
|
|
libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/control/sysrq", dompath),
|
|
"%c", sysrq);
|
|
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
int libxl_send_debug_keys(libxl_ctx *ctx, char *keys)
|
|
{
|
|
int ret;
|
|
GC_INIT(ctx);
|
|
ret = xc_send_debug_keys(ctx->xch, keys);
|
|
if ( ret < 0 ) {
|
|
LOGE(ERROR, "sending debug keys");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
libxl_xen_console_reader *
|
|
libxl_xen_console_read_start(libxl_ctx *ctx, int clear)
|
|
{
|
|
GC_INIT(ctx);
|
|
libxl_xen_console_reader *cr;
|
|
unsigned int size = 16384;
|
|
|
|
cr = libxl__zalloc(NOGC, sizeof(libxl_xen_console_reader));
|
|
cr->buffer = libxl__zalloc(NOGC, size);
|
|
cr->size = size;
|
|
cr->count = size;
|
|
cr->clear = clear;
|
|
cr->incremental = 1;
|
|
|
|
GC_FREE;
|
|
return cr;
|
|
}
|
|
|
|
/* return values: *line_r
|
|
* 1 success, whole line obtained from buffer non-0
|
|
* 0 no more lines available right now 0
|
|
* negative error code ERROR_* 0
|
|
* On success *line_r is updated to point to a nul-terminated
|
|
* string which is valid until the next call on the same console
|
|
* reader. The libxl caller may overwrite parts of the string
|
|
* if it wishes. */
|
|
int libxl_xen_console_read_line(libxl_ctx *ctx,
|
|
libxl_xen_console_reader *cr,
|
|
char **line_r)
|
|
{
|
|
int ret;
|
|
GC_INIT(ctx);
|
|
|
|
memset(cr->buffer, 0, cr->size);
|
|
ret = xc_readconsolering(ctx->xch, cr->buffer, &cr->count,
|
|
cr->clear, cr->incremental, &cr->index);
|
|
if (ret < 0) {
|
|
LOGE(ERROR, "reading console ring buffer");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
if (!ret) {
|
|
if (cr->count) {
|
|
*line_r = cr->buffer;
|
|
ret = 1;
|
|
} else {
|
|
*line_r = NULL;
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
GC_FREE;
|
|
return ret;
|
|
}
|
|
|
|
void libxl_xen_console_read_finish(libxl_ctx *ctx,
|
|
libxl_xen_console_reader *cr)
|
|
{
|
|
free(cr->buffer);
|
|
free(cr);
|
|
}
|
|
|
|
uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
GC_INIT(ctx);
|
|
char *dompath = libxl__xs_get_dompath(gc, domid);
|
|
char *vm_path, *start_time;
|
|
uint32_t ret;
|
|
|
|
vm_path = libxl__xs_read(
|
|
gc, XBT_NULL, GCSPRINTF("%s/vm", dompath));
|
|
start_time = libxl__xs_read(
|
|
gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path));
|
|
if (start_time == NULL) {
|
|
LOGEV(ERROR, -1, "Can't get start time of domain '%d'", domid);
|
|
ret = -1;
|
|
}else{
|
|
ret = strtoul(start_time, NULL, 10);
|
|
}
|
|
GC_FREE;
|
|
return ret;
|
|
}
|
|
|
|
char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long)
|
|
{
|
|
int rc;
|
|
char _buf[32768];
|
|
GC_INIT(ctx);
|
|
|
|
rc = xc_tmem_control(ctx->xch, -1, XEN_SYSCTL_TMEM_OP_LIST, domid, 32768, use_long,
|
|
_buf);
|
|
if (rc < 0) {
|
|
LOGEV(ERROR, rc, "Can not get tmem list");
|
|
GC_FREE;
|
|
return NULL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return strdup(_buf);
|
|
}
|
|
|
|
int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
int rc;
|
|
GC_INIT(ctx);
|
|
|
|
rc = xc_tmem_control(ctx->xch, -1, XEN_SYSCTL_TMEM_OP_FREEZE, domid, 0, 0,
|
|
NULL);
|
|
if (rc < 0) {
|
|
LOGEV(ERROR, rc, "Can not freeze tmem pools");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
int rc;
|
|
GC_INIT(ctx);
|
|
|
|
rc = xc_tmem_control(ctx->xch, -1, XEN_SYSCTL_TMEM_OP_THAW, domid, 0, 0,
|
|
NULL);
|
|
if (rc < 0) {
|
|
LOGEV(ERROR, rc, "Can not thaw tmem pools");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
static int32_t tmem_setop_from_string(char *set_name)
|
|
{
|
|
if (!strcmp(set_name, "weight"))
|
|
return XEN_SYSCTL_TMEM_OP_SET_WEIGHT;
|
|
else if (!strcmp(set_name, "cap"))
|
|
return XEN_SYSCTL_TMEM_OP_SET_CAP;
|
|
else if (!strcmp(set_name, "compress"))
|
|
return XEN_SYSCTL_TMEM_OP_SET_COMPRESS;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name, uint32_t set)
|
|
{
|
|
int rc;
|
|
int32_t subop = tmem_setop_from_string(name);
|
|
GC_INIT(ctx);
|
|
|
|
if (subop == -1) {
|
|
LOGEV(ERROR, -1, "Invalid set, valid sets are <weight|cap|compress>");
|
|
GC_FREE;
|
|
return ERROR_INVAL;
|
|
}
|
|
rc = xc_tmem_control(ctx->xch, -1, subop, domid, set, 0, NULL);
|
|
if (rc < 0) {
|
|
LOGEV(ERROR, rc, "Can not set tmem %s", name);
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid,
|
|
char* uuid, int auth)
|
|
{
|
|
int rc;
|
|
GC_INIT(ctx);
|
|
|
|
rc = xc_tmem_auth(ctx->xch, domid, uuid, auth);
|
|
if (rc < 0) {
|
|
LOGEV(ERROR, rc, "Can not set tmem shared auth");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_tmem_freeable(libxl_ctx *ctx)
|
|
{
|
|
int rc;
|
|
GC_INIT(ctx);
|
|
|
|
rc = xc_tmem_control(ctx->xch, -1, XEN_SYSCTL_TMEM_OP_QUERY_FREEABLE_MB, -1, 0, 0, 0);
|
|
if (rc < 0) {
|
|
LOGEV(ERROR, rc, "Can not get tmem freeable memory");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap)
|
|
{
|
|
int ncpus;
|
|
|
|
ncpus = libxl_get_max_cpus(ctx);
|
|
if (ncpus < 0)
|
|
return ncpus;
|
|
|
|
cpumap->map = xc_cpupool_freeinfo(ctx->xch);
|
|
if (cpumap->map == NULL)
|
|
return ERROR_FAIL;
|
|
|
|
cpumap->size = (ncpus + 7) / 8;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
|
|
libxl_scheduler sched,
|
|
libxl_bitmap cpumap, libxl_uuid *uuid,
|
|
uint32_t *poolid)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc;
|
|
int i;
|
|
xs_transaction_t t;
|
|
char *uuid_string;
|
|
|
|
uuid_string = libxl__uuid2string(gc, *uuid);
|
|
if (!uuid_string) {
|
|
GC_FREE;
|
|
return ERROR_NOMEM;
|
|
}
|
|
|
|
rc = xc_cpupool_create(ctx->xch, poolid, sched);
|
|
if (rc) {
|
|
LOGEV(ERROR, rc, "Could not create cpupool");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
libxl_for_each_bit(i, cpumap)
|
|
if (libxl_bitmap_test(&cpumap, i)) {
|
|
rc = xc_cpupool_addcpu(ctx->xch, *poolid, i);
|
|
if (rc) {
|
|
LOGEV(ERROR, rc, "Error moving cpu to cpupool");
|
|
libxl_cpupool_destroy(ctx, *poolid);
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
}
|
|
|
|
for (;;) {
|
|
t = xs_transaction_start(ctx->xsh);
|
|
|
|
xs_mkdir(ctx->xsh, t, GCSPRINTF("/local/pool/%d", *poolid));
|
|
libxl__xs_printf(gc, t,
|
|
GCSPRINTF("/local/pool/%d/uuid", *poolid),
|
|
"%s", uuid_string);
|
|
libxl__xs_printf(gc, t,
|
|
GCSPRINTF("/local/pool/%d/name", *poolid),
|
|
"%s", name);
|
|
|
|
if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN)) {
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc, i;
|
|
xc_cpupoolinfo_t *info;
|
|
xs_transaction_t t;
|
|
libxl_bitmap cpumap;
|
|
|
|
info = xc_cpupool_getinfo(ctx->xch, poolid);
|
|
if (info == NULL) {
|
|
GC_FREE;
|
|
return ERROR_NOMEM;
|
|
}
|
|
|
|
rc = ERROR_INVAL;
|
|
if ((info->cpupool_id != poolid) || (info->n_dom))
|
|
goto out;
|
|
|
|
rc = libxl_cpu_bitmap_alloc(ctx, &cpumap, 0);
|
|
if (rc)
|
|
goto out;
|
|
|
|
memcpy(cpumap.map, info->cpumap, cpumap.size);
|
|
libxl_for_each_bit(i, cpumap)
|
|
if (libxl_bitmap_test(&cpumap, i)) {
|
|
rc = xc_cpupool_removecpu(ctx->xch, poolid, i);
|
|
if (rc) {
|
|
LOGEV(ERROR, rc, "Error removing cpu from cpupool");
|
|
rc = ERROR_FAIL;
|
|
goto out1;
|
|
}
|
|
}
|
|
|
|
rc = xc_cpupool_destroy(ctx->xch, poolid);
|
|
if (rc) {
|
|
LOGEV(ERROR, rc, "Could not destroy cpupool");
|
|
rc = ERROR_FAIL;
|
|
goto out1;
|
|
}
|
|
|
|
for (;;) {
|
|
t = xs_transaction_start(ctx->xsh);
|
|
|
|
xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF("/local/pool/%d", poolid));
|
|
|
|
if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN))
|
|
break;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out1:
|
|
libxl_bitmap_dispose(&cpumap);
|
|
out:
|
|
xc_cpupool_infofree(ctx->xch, info);
|
|
GC_FREE;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid)
|
|
{
|
|
GC_INIT(ctx);
|
|
xs_transaction_t t;
|
|
xc_cpupoolinfo_t *info;
|
|
int rc;
|
|
|
|
info = xc_cpupool_getinfo(ctx->xch, poolid);
|
|
if (info == NULL) {
|
|
GC_FREE;
|
|
return ERROR_NOMEM;
|
|
}
|
|
|
|
rc = ERROR_INVAL;
|
|
if (info->cpupool_id != poolid)
|
|
goto out;
|
|
|
|
rc = 0;
|
|
|
|
for (;;) {
|
|
t = xs_transaction_start(ctx->xsh);
|
|
|
|
libxl__xs_printf(gc, t,
|
|
GCSPRINTF("/local/pool/%d/name", poolid),
|
|
"%s", name);
|
|
|
|
if (xs_transaction_end(ctx->xsh, t, 0))
|
|
break;
|
|
|
|
if (errno == EAGAIN)
|
|
continue;
|
|
|
|
rc = ERROR_FAIL;
|
|
break;
|
|
}
|
|
|
|
out:
|
|
xc_cpupool_infofree(ctx->xch, info);
|
|
GC_FREE;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc = 0;
|
|
|
|
rc = xc_cpupool_addcpu(ctx->xch, poolid, cpu);
|
|
if (rc) {
|
|
LOGE(ERROR, "Error moving cpu %d to cpupool", cpu);
|
|
rc = ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
|
|
const libxl_bitmap *cpumap)
|
|
{
|
|
int c, ncpus = 0, rc = 0;
|
|
|
|
libxl_for_each_set_bit(c, *cpumap) {
|
|
if (!libxl_cpupool_cpuadd(ctx, poolid, c))
|
|
ncpus++;
|
|
}
|
|
|
|
if (ncpus != libxl_bitmap_count_set(cpumap))
|
|
rc = ERROR_FAIL;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
|
|
{
|
|
int rc = 0;
|
|
int cpu, nr;
|
|
libxl_bitmap freemap;
|
|
libxl_cputopology *topology;
|
|
|
|
if (libxl_get_freecpus(ctx, &freemap)) {
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
topology = libxl_get_cpu_topology(ctx, &nr);
|
|
if (!topology) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
*cpus = 0;
|
|
for (cpu = 0; cpu < nr; cpu++) {
|
|
if (libxl_bitmap_test(&freemap, cpu) && (topology[cpu].node == node) &&
|
|
!libxl_cpupool_cpuadd(ctx, poolid, cpu)) {
|
|
(*cpus)++;
|
|
}
|
|
libxl_cputopology_dispose(&topology[cpu]);
|
|
}
|
|
|
|
free(topology);
|
|
out:
|
|
libxl_bitmap_dispose(&freemap);
|
|
return rc;
|
|
}
|
|
|
|
int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc = 0;
|
|
|
|
rc = xc_cpupool_removecpu(ctx->xch, poolid, cpu);
|
|
if (rc) {
|
|
LOGE(ERROR, "Error removing cpu %d from cpupool", cpu);
|
|
rc = ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
|
|
const libxl_bitmap *cpumap)
|
|
{
|
|
int c, ncpus = 0, rc = 0;
|
|
|
|
libxl_for_each_set_bit(c, *cpumap) {
|
|
if (!libxl_cpupool_cpuremove(ctx, poolid, c))
|
|
ncpus++;
|
|
}
|
|
|
|
if (ncpus != libxl_bitmap_count_set(cpumap))
|
|
rc = ERROR_FAIL;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus)
|
|
{
|
|
int ret = 0;
|
|
int n_pools;
|
|
int p;
|
|
int cpu, nr_cpus;
|
|
libxl_cputopology *topology;
|
|
libxl_cpupoolinfo *poolinfo;
|
|
|
|
poolinfo = libxl_list_cpupool(ctx, &n_pools);
|
|
if (!poolinfo) {
|
|
return ERROR_NOMEM;
|
|
}
|
|
|
|
topology = libxl_get_cpu_topology(ctx, &nr_cpus);
|
|
if (!topology) {
|
|
ret = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
*cpus = 0;
|
|
for (p = 0; p < n_pools; p++) {
|
|
if (poolinfo[p].poolid == poolid) {
|
|
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
|
if ((topology[cpu].node == node) &&
|
|
libxl_bitmap_test(&poolinfo[p].cpumap, cpu) &&
|
|
!libxl_cpupool_cpuremove(ctx, poolid, cpu)) {
|
|
(*cpus)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
libxl_cputopology_list_free(topology, nr_cpus);
|
|
|
|
out:
|
|
libxl_cpupoolinfo_list_free(poolinfo, n_pools);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc;
|
|
|
|
rc = xc_cpupool_movedomain(ctx->xch, poolid, domid);
|
|
if (rc) {
|
|
LOGEV(ERROR, rc, "Error moving domain to cpupool");
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
static int fd_set_flags(libxl_ctx *ctx, int fd,
|
|
int fcntlgetop, int fcntlsetop, const char *fl,
|
|
int flagmask, int set_p)
|
|
{
|
|
int flags, r;
|
|
GC_INIT(ctx);
|
|
|
|
flags = fcntl(fd, fcntlgetop);
|
|
if (flags == -1) {
|
|
LOGE(ERROR, "fcntl(,F_GET%s) failed", fl);
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
if (set_p)
|
|
flags |= flagmask;
|
|
else
|
|
flags &= ~flagmask;
|
|
|
|
r = fcntl(fd, fcntlsetop, flags);
|
|
if (r == -1) {
|
|
LOGE(ERROR, "fcntl(,F_SET%s) failed", fl);
|
|
GC_FREE;
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
GC_FREE;
|
|
return 0;
|
|
}
|
|
|
|
int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec)
|
|
{ return fd_set_flags(ctx,fd, F_GETFD,F_SETFD,"FD", FD_CLOEXEC, cloexec); }
|
|
|
|
int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock)
|
|
{ return fd_set_flags(ctx,fd, F_GETFL,F_SETFL,"FL", O_NONBLOCK, nonblock); }
|
|
|
|
int libxl__fd_flags_modify_save(libxl__gc *gc, int fd,
|
|
int mask, int val, int *r_oldflags)
|
|
{
|
|
int rc, ret, fdfl;
|
|
|
|
fdfl = fcntl(fd, F_GETFL);
|
|
if (fdfl < 0) {
|
|
LOGE(ERROR, "failed to fcntl.F_GETFL for fd %d", fd);
|
|
rc = ERROR_FAIL;
|
|
goto out_err;
|
|
}
|
|
|
|
LOG(DEBUG, "fnctl F_GETFL flags for fd %d are 0x%x", fd, fdfl);
|
|
|
|
if (r_oldflags)
|
|
*r_oldflags = fdfl;
|
|
|
|
fdfl &= mask;
|
|
fdfl |= val;
|
|
|
|
LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
|
|
|
|
ret = fcntl(fd, F_SETFL, fdfl);
|
|
if (ret < 0) {
|
|
LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
|
|
rc = ERROR_FAIL;
|
|
goto out_err;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out_err:
|
|
return rc;
|
|
}
|
|
|
|
int libxl__fd_flags_restore(libxl__gc *gc, int fd, int fdfl)
|
|
{
|
|
int ret, rc;
|
|
|
|
LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl);
|
|
|
|
ret = fcntl(fd, F_SETFL, fdfl);
|
|
if (ret < 0) {
|
|
LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd);
|
|
rc = ERROR_FAIL;
|
|
goto out_err;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out_err:
|
|
return rc;
|
|
|
|
}
|
|
|
|
void libxl_hwcap_copy(libxl_ctx *ctx,libxl_hwcap *dst, libxl_hwcap *src)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
(*dst)[i] = (*src)[i];
|
|
}
|
|
|
|
void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, libxl_mac *src)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
(*dst)[i] = (*src)[i];
|
|
}
|
|
|
|
int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
GC_INIT(ctx);
|
|
int rc;
|
|
libxl__domain_userdata_lock *lock = NULL;
|
|
|
|
CTX_LOCK;
|
|
|
|
lock = libxl__lock_domain_userdata(gc, domid);
|
|
if (!lock) {
|
|
rc = ERROR_LOCK_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__get_domain_configuration(gc, domid, d_config);
|
|
if (rc) {
|
|
LOG(ERROR, "fail to get domain configuration for domain %d", domid);
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/* Domain name */
|
|
{
|
|
char *domname;
|
|
domname = libxl_domid_to_name(ctx, domid);
|
|
if (!domname) {
|
|
LOG(ERROR, "fail to get domain name for domain %d", domid);
|
|
goto out;
|
|
}
|
|
free(d_config->c_info.name);
|
|
d_config->c_info.name = domname; /* steals allocation */
|
|
}
|
|
|
|
/* Domain UUID */
|
|
{
|
|
libxl_dominfo info;
|
|
libxl_dominfo_init(&info);
|
|
rc = libxl_domain_info(ctx, &info, domid);
|
|
if (rc) {
|
|
LOG(ERROR, "fail to get domain info for domain %d", domid);
|
|
libxl_dominfo_dispose(&info);
|
|
goto out;
|
|
}
|
|
libxl_uuid_copy(ctx, &d_config->c_info.uuid, &info.uuid);
|
|
libxl_dominfo_dispose(&info);
|
|
}
|
|
|
|
/* Memory limits:
|
|
*
|
|
* Currently there are three memory limits:
|
|
* 1. "target" in xenstore (originally memory= in config file)
|
|
* 2. "static-max" in xenstore (originally maxmem= in config file)
|
|
* 3. "max_memkb" in hypervisor
|
|
*
|
|
* The third one is not visible and currently managed by
|
|
* toolstack. In order to rebuild a domain we only need to have
|
|
* "target" and "static-max".
|
|
*/
|
|
{
|
|
uint32_t target_memkb = 0, max_memkb = 0;
|
|
|
|
/* "target" */
|
|
rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb);
|
|
if (rc) {
|
|
LOG(ERROR, "fail to get memory target for domain %d", domid);
|
|
goto out;
|
|
}
|
|
/* Target memory in xenstore is different from what user has
|
|
* asked for. The difference is video_memkb. See
|
|
* libxl_set_memory_target.
|
|
*/
|
|
d_config->b_info.target_memkb = target_memkb +
|
|
d_config->b_info.video_memkb;
|
|
|
|
d_config->b_info.max_memkb = max_memkb;
|
|
}
|
|
|
|
/* Devices: disk, nic, vtpm, pcidev etc. */
|
|
|
|
/* The MERGE macro implements following logic:
|
|
* 0. retrieve JSON (done by now)
|
|
* 1. retrieve list of device from xenstore
|
|
* 2. use xenstore entries as primary reference and compare JSON
|
|
* entries with them.
|
|
* a. if a device is present in xenstore and in JSON, merge the
|
|
* two views.
|
|
* b. if a device is not present in xenstore but in JSON, delete
|
|
* it from the result.
|
|
* c. it's impossible to have an entry present in xenstore but
|
|
* not in JSON, because we maintain an invariant that every
|
|
* entry in xenstore must have a corresponding entry in JSON.
|
|
* 3. "merge" operates on "src" and "dst". "src" points to the
|
|
* entry retrieved from xenstore while "dst" points to the entry
|
|
* retrieve from JSON.
|
|
*/
|
|
#define MERGE(type, ptr, compare, merge) \
|
|
do { \
|
|
libxl_device_##type *p = NULL; \
|
|
int i, j, num; \
|
|
\
|
|
p = libxl_device_##type##_list(CTX, domid, &num); \
|
|
if (p == NULL) { \
|
|
LOG(DEBUG, \
|
|
"no %s from xenstore for domain %d", \
|
|
#type, domid); \
|
|
} \
|
|
\
|
|
for (i = 0; i < d_config->num_##ptr; i++) { \
|
|
libxl_device_##type *q = &d_config->ptr[i]; \
|
|
for (j = 0; j < num; j++) { \
|
|
if (compare(&p[j], q)) \
|
|
break; \
|
|
} \
|
|
\
|
|
if (j < num) { /* found in xenstore */ \
|
|
libxl_device_##type *dst, *src; \
|
|
dst = q; \
|
|
src = &p[j]; \
|
|
merge; \
|
|
} else { /* not found in xenstore */ \
|
|
LOG(WARN, \
|
|
"Device present in JSON but not in xenstore, ignored"); \
|
|
\
|
|
libxl_device_##type##_dispose(q); \
|
|
\
|
|
for (j = i; j < d_config->num_##ptr - 1; j++) \
|
|
memcpy(&d_config->ptr[j], &d_config->ptr[j+1], \
|
|
sizeof(libxl_device_##type)); \
|
|
\
|
|
d_config->ptr = \
|
|
libxl__realloc(NOGC, d_config->ptr, \
|
|
sizeof(libxl_device_##type) * \
|
|
(d_config->num_##ptr - 1)); \
|
|
\
|
|
/* rewind counters */ \
|
|
d_config->num_##ptr--; \
|
|
i--; \
|
|
} \
|
|
} \
|
|
\
|
|
for (i = 0; i < num; i++) \
|
|
libxl_device_##type##_dispose(&p[i]); \
|
|
free(p); \
|
|
} while (0);
|
|
|
|
MERGE(nic, nics, COMPARE_DEVID, {});
|
|
|
|
MERGE(vtpm, vtpms, COMPARE_DEVID, {});
|
|
|
|
MERGE(pci, pcidevs, COMPARE_PCI, {});
|
|
|
|
/* Take care of removable device. We maintain invariant in the
|
|
* insert / remove operation so that:
|
|
* 1. if xenstore is "empty" while JSON is not, the result
|
|
* is "empty"
|
|
* 2. if xenstore has a different media than JSON, use the
|
|
* one in JSON
|
|
* 3. if xenstore and JSON have the same media, well, you
|
|
* know the answer :-)
|
|
*
|
|
* Currently there is only one removable device -- CDROM.
|
|
* Look for libxl_cdrom_insert for reference.
|
|
*/
|
|
MERGE(disk, disks, COMPARE_DISK, {
|
|
if (src->removable) {
|
|
if (!src->pdev_path || *src->pdev_path == '\0') {
|
|
/* 1, no media in drive */
|
|
free(dst->pdev_path);
|
|
dst->pdev_path = libxl__strdup(NOGC, "");
|
|
dst->format = LIBXL_DISK_FORMAT_EMPTY;
|
|
} else {
|
|
/* 2 and 3, use JSON, no need to touch anything */
|
|
;
|
|
}
|
|
}
|
|
});
|
|
|
|
#undef MERGE
|
|
|
|
out:
|
|
if (lock) libxl__unlock_domain_userdata(lock);
|
|
CTX_UNLOCK;
|
|
GC_FREE;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|