George Dunlap
2013-Apr-11 15:20 UTC
[PATCH RFC 0.2 1/2] libxl: Introduce functions to add and remove USB devices to an HVM guest
This is a "beta" of v4, suitable for comment, and also to allow (potential) concurrent development of the PV path of the prototype. THIS IS STILL A PROTOTYPE. This patch exposes a generic interface which can be expanded in the future to implement USB for PVUSB, qemu, and stubdoms. It can also be extended to include other types of USB other than host USB (for example, tablets, mice, or keyboards). For each device removed or added, one of two protocols is available: * PVUSB * qemu (DEVICEMODEL) The caller can additionally specify "AUTO", in which case the library will try to determine the best protocol automatically. At the moment, the only protocol implemented is DEVICEMODEL, and the only device type impelmented is HOSTDEV. This uses the qmp functionality, and is thus only available for qemu-xen, not qemu-traditional. v4 (RFC): - Remove STUBDOM type v3 (RFC): - Complete re-write with new generalized protocol - Store list of assigned USB devices in xenstore v2: - Make interface async-ready - usb_del takes libxl_device_host_usb struct - usb_del will re-construct original id given device info if no id is given Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com> --- tools/libxl/Makefile | 2 +- tools/libxl/libxl.c | 10 +- tools/libxl/libxl.h | 36 ++++ tools/libxl/libxl_create.c | 12 +- tools/libxl/libxl_internal.h | 26 ++- tools/libxl/libxl_qmp.c | 66 ++++++++ tools/libxl/libxl_types.idl | 22 +++ tools/libxl/libxl_usb.c | 364 ++++++++++++++++++++++++++++++++++++++++ tools/ocaml/libs/xl/genwrap.py | 1 + 9 files changed, 528 insertions(+), 11 deletions(-) create mode 100644 tools/libxl/libxl_usb.c diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile index 2984051..866960a 100644 --- a/tools/libxl/Makefile +++ b/tools/libxl/Makefile @@ -74,7 +74,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ libxl_internal.o libxl_utils.o libxl_uuid.o \ libxl_json.o libxl_aoutils.o libxl_numa.o \ libxl_save_callout.o _libxl_save_msgs_callout.o \ - libxl_qmp.o libxl_event.o libxl_fork.o $(LIBXL_OBJS-y) + libxl_qmp.o libxl_event.o libxl_fork.o libxl_usb.o $(LIBXL_OBJS-y) LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o $(LIBXL_OBJS): CFLAGS += $(CFLAGS_LIBXL) -include $(XEN_ROOT)/tools/config.h diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index 572c2c6..bddac17 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -63,7 +63,7 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version, ctx->childproc_hooks = &libxl__childproc_default_hooks; ctx->childproc_user = 0; - + ctx->sigchld_selfpipe[0] = -1; /* The mutex is special because we can''t idempotently destroy it */ @@ -1073,7 +1073,7 @@ int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid, 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; } @@ -1145,7 +1145,7 @@ static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w, 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, libxl__sprintf(gc, "%.*s/backend", (int)strlen(wpath)-6, wpath)); @@ -1237,7 +1237,7 @@ 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 */ @@ -1446,7 +1446,7 @@ static void devices_destroy_cb(libxl__egc *egc, } if (rc < 0) - LIBXL__LOG(ctx, LIBXL__LOG_ERROR, + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "libxl__devices_destroy failed for %d", domid); vm_path = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/vm", dom_path)); diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index d18d22c..f7c7bd8 100644 --- a/tools/libxl/libxl.h +++ b/tools/libxl/libxl.h @@ -75,6 +75,11 @@ #define LIBXL_HAVE_FIRMWARE_PASSTHROUGH 1 /* + * FIXME: Update comment + */ +#define LIBXL_HAVE_USB 1 + +/* * libxl ABI compatibility * * The only guarantee which libxl makes regarding ABI compatibility @@ -735,6 +740,37 @@ int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; +/* + * USB + * + * For each device removed or added, one of three protocols is available: + * - PVUSB + * - qemu (DEVICEMODEL) + * + * The caller can additionally specify "AUTO", in which case the library will + * try to determine the best protocol automatically. + * + * At the moment, the only protocol implemented is DEVICEMODEL, and the only + * device type impelmented is HOSTDEV. + * + * This uses the qmp functionality, and is thus only available for + * qemu-xen, not qemu-traditional. + * + */ +#define LIBXL_DEVICE_USB_BACKEND_DEFAULT (-1) +int libxl_device_usb_add(libxl_ctx *ctx, uint32_t domid, + libxl_device_usb *dev, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; +int libxl_device_usb_remove(libxl_ctx *ctx, uint32_t domid, + libxl_device_usb *dev, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; +int libxl_device_usb_list(libxl_ctx *ctx, uint32_t domid, + libxl_device_usb **dev, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; + /* Network Interfaces */ int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic, const libxl_asyncop_how *ao_how) diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c index 30a4507..c620d6d 100644 --- a/tools/libxl/libxl_create.c +++ b/tools/libxl/libxl_create.c @@ -391,7 +391,7 @@ int libxl__domain_make(libxl__gc *gc, libxl_domain_create_info *info, libxl_ctx *ctx = libxl__gc_owner(gc); int flags, ret, rc, nb_vm; char *uuid_string; - char *dom_path, *vm_path, *libxl_path; + char *dom_path, *vm_path, *libxl_path, *libxl_usb_path; struct xs_permissions roperm[2]; struct xs_permissions rwperm[1]; struct xs_permissions noperm[1]; @@ -452,6 +452,13 @@ int libxl__domain_make(libxl__gc *gc, libxl_domain_create_info *info, goto out; } + libxl_usb_path = libxl__sprintf(gc, "%s/usb", libxl_path); + if (!libxl_usb_path) { + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "cannot allocate create paths"); + rc = ERROR_FAIL; + goto out; + } + noperm[0].id = 0; noperm[0].perms = XS_PERM_NONE; @@ -475,6 +482,9 @@ retry_transaction: xs_rm(ctx->xsh, t, libxl_path); libxl__xs_mkdir(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm)); + xs_rm(ctx->xsh, t, libxl_usb_path); + libxl__xs_mkdir(gc, t, libxl_usb_path, noperm, ARRAY_SIZE(noperm)); + xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/vm", dom_path), vm_path, strlen(vm_path)); rc = libxl__domain_rename(gc, *domid, 0, info->name, t); if (rc) diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index 3ba3a21..5e31ef1 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -213,7 +213,7 @@ struct libxl__ev_xswatch { typedef struct libxl__ev_watch_slot { LIBXL_SLIST_ENTRY(struct libxl__ev_watch_slot) empty; } libxl__ev_watch_slot; - + _hidden libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum); @@ -346,7 +346,7 @@ struct libxl__ctx { death_list /* sorted by domid */, death_reported; libxl__ev_xswatch death_watch; - + LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens; const libxl_childproc_hooks *childproc_hooks; @@ -1112,7 +1112,7 @@ _hidden void libxl__spawn_init(libxl__spawn_state*); * * what: string describing the spawned process, used for logging * - * Logs errors. A copy of "what" is taken. + * Logs errors. A copy of "what" is taken. * Return values: * < 0 error, *spawn is now Idle and need not be detached * +1 caller is the parent, *spawn is Attached and must be detached @@ -1412,6 +1412,24 @@ _hidden int libxl__qmp_save(libxl__gc *gc, int domid, const char *filename); /* Set dirty bitmap logging status */ _hidden int libxl__qmp_set_global_dirty_log(libxl__gc *gc, int domid, bool enable); _hidden int libxl__qmp_insert_cdrom(libxl__gc *gc, int domid, const libxl_device_disk *disk); +/* Same as normal, but "translated" */ +typedef struct libxl__device_usb { + libxl_usb_protocol protocol; + libxl_domid target_domid; + libxl_domid backend_domid; + libxl_domid dm_domid; + libxl_device_usb_type type; + union { + struct { + int hostbus; + int hostaddr; + } hostdev; + } u; +} libxl__device_usb; +_hidden int libxl__qmp_usb_add(libxl__gc *gc, int domid, + libxl__device_usb *dev); +_hidden int libxl__qmp_usb_remove(libxl__gc *gc, int domid, + libxl__device_usb *dev); /* close and free the QMP handler */ _hidden void libxl__qmp_close(libxl__qmp_handler *qmp); /* remove the socket file, if the file has already been removed, @@ -2639,7 +2657,7 @@ _hidden const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid); */ #define GCNEW_ARRAY(var, nmemb) \ ((var) = libxl__calloc((gc), (nmemb), sizeof(*(var)))) - + /* * Expression statement <type> *GCREALLOC_ARRAY(<type> *var, size_t nmemb); * Uses libxl__gc *gc; diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c index 644d2c0..ae55695 100644 --- a/tools/libxl/libxl_qmp.c +++ b/tools/libxl/libxl_qmp.c @@ -42,6 +42,7 @@ #define QMP_RECEIVE_BUFFER_SIZE 4096 #define PCI_PT_QDEV_ID "pci-pt-%02x_%02x.%01x" +#define HOST_USB_QDEV_ID "usb-hostdev-%04x.%04x" typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp, const libxl__json_object *tree, @@ -929,6 +930,71 @@ int libxl__qmp_insert_cdrom(libxl__gc *gc, int domid, } } +static int libxl__qmp_usb_hostdev_add(libxl__gc *gc, int domid, + libxl__device_usb *dev) +{ + libxl__json_object *args = NULL; + char *id; + + id = libxl__sprintf(NOGC, HOST_USB_QDEV_ID, + (uint16_t) dev->u.hostdev.hostbus, + (uint16_t) dev->u.hostdev.hostaddr); + + qmp_parameters_add_string(gc, &args, "driver", "usb-host"); + QMP_PARAMETERS_SPRINTF(&args, "hostbus", "0x%x", dev->u.hostdev.hostbus); + QMP_PARAMETERS_SPRINTF(&args, "hostaddr", "0x%x", dev->u.hostdev.hostaddr); + + qmp_parameters_add_string(gc, &args, "id", id); + + return qmp_run_command(gc, domid, "device_add", args, NULL, NULL); +} + +int libxl__qmp_usb_add(libxl__gc *gc, int domid, + libxl__device_usb *usbdev) +{ + int rc; + switch(usbdev->type) { + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: + rc = libxl__qmp_usb_hostdev_add(gc, domid, usbdev); + break; + default: + return ERROR_INVAL; + } + return rc; +} + + +static int libxl__qmp_usb_hostdev_remove(libxl__gc *gc, int domid, + libxl__device_usb *dev) +{ + libxl__json_object *args = NULL; + char *id; + + id = libxl__sprintf(NOGC, HOST_USB_QDEV_ID, + (uint16_t) dev->u.hostdev.hostbus, + (uint16_t) dev->u.hostdev.hostaddr); + + qmp_parameters_add_string(gc, &args, "id", id); + + return qmp_run_command(gc, domid, "device_del", args, NULL, NULL); +} + +int libxl__qmp_usb_remove(libxl__gc *gc, int domid, + libxl__device_usb *usbdev) +{ + int rc; + switch(usbdev->type) { + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: + rc = libxl__qmp_usb_hostdev_remove(gc, domid, usbdev); + break; + default: + return ERROR_INVAL; + } + return rc; +} + + + int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid, const libxl_domain_config *guest_config) { diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl index 6cb6de6..9e0a435 100644 --- a/tools/libxl/libxl_types.idl +++ b/tools/libxl/libxl_types.idl @@ -407,6 +407,28 @@ libxl_device_vtpm = Struct("device_vtpm", [ ("uuid", libxl_uuid), ]) +libxl_device_usb_protocol = Enumeration("usb_protocol", [ + (0, "AUTO"), + (1, "PV"), + (2, "DEVICEMODEL"), + ]) + +libxl_device_usb_type = Enumeration("device_usb_type", [ + (1, "HOSTDEV"), + ]) + +libxl_device_usb = Struct("device_usb", [ + ("protocol", libxl_device_usb_protocol, + {''init_val'': ''LIBXL_USB_PROTOCOL_AUTO''}), + ("backend_domid", libxl_domid, + {''init_val'': ''LIBXL_DEVICE_USB_BACKEND_DEFAULT''}), + ("u", KeyedUnion(None, libxl_device_usb_type, "type", + [("hostdev", Struct(None, [ + ("hostbus", integer), + ("hostaddr", integer) ])) + ])) + ]) + libxl_domain_config = Struct("domain_config", [ ("c_info", libxl_domain_create_info), ("b_info", libxl_domain_build_info), diff --git a/tools/libxl/libxl_usb.c b/tools/libxl/libxl_usb.c new file mode 100644 index 0000000..547a738 --- /dev/null +++ b/tools/libxl/libxl_usb.c @@ -0,0 +1,364 @@ +/* + * 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" /* must come before any other headers */ + +#include "libxl_internal.h" + +/* + * /libxl/<domid>/usb/<devid>/{type, protocol, backend, [typeinfo]} + */ +#define USB_INFO_PATH "%s/usb" +#define USB_HOSTDEV_ID "hostdev-%04x-%04x" + +/* + * Just use plain numbers for now. Replace these with strings at some point. + */ +#define protocol_to_str(gc, t) libxl__sprintf((gc), "%d", (t)) +#define str_to_protocol(s) atoi((s)) +#define type_to_str(gc, t) libxl__sprintf((gc), "%d", (t)) +#define str_to_typeprotocol(s) atoi((s)) + +static char * create_hostdev_xenstore_entry(libxl__gc *gc, uint32_t domid, + libxl__device_usb *usbdev, flexarray_t *a) +{ + char * path; + + path = libxl__sprintf(gc, USB_INFO_PATH "/%s", + libxl__xs_libxl_path(gc, domid), + libxl__sprintf(gc, USB_HOSTDEV_ID, + (uint16_t)usbdev->u.hostdev.hostbus, + (uint16_t)usbdev->u.hostdev.hostaddr)); + + flexarray_append_pair(a, "hostbus", + libxl__sprintf(gc, "%d", + usbdev->u.hostdev.hostbus)); + flexarray_append_pair(a, "hostaddr", + libxl__sprintf(gc, "%d", + usbdev->u.hostdev.hostaddr)); + + return path; +} + +static int read_hostdev_xenstore_entry(libxl__gc *gc, const char * path, + libxl__device_usb *usbdev) +{ + int rc = 0; + char * val; + + val = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/hostbus", path)); + if(!val) { + LOG(ERROR, "Internal error (missing hostbus)"); + rc = ERROR_FAIL; + goto out; + } + usbdev->u.hostdev.hostbus = atoi(val); + + val = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/hostaddr", path)); + if(!val) { + LOG(ERROR, "Internal error (missing hostaddr)"); + rc = ERROR_FAIL; + goto out; + } + usbdev->u.hostdev.hostaddr = atoi(val); + +out: + return rc; +} + +static int usb_read_xenstore(libxl__gc *gc, const char * path, + libxl__device_usb *usbdev) +{ + char *val; + int rc = 0; + + val = libxl__xs_read(gc, XBT_NULL, + libxl__sprintf(gc, "%s/protocol", path)); + if(!val) { + LOG(ERROR, "Internal error (missing protocol)"); + rc = ERROR_FAIL; + goto out; + } + usbdev->protocol = atoi(val); + + val = libxl__xs_read(gc, XBT_NULL, + libxl__sprintf(gc, "%s/backend_domid", path)); + if(!val) { + LOG(ERROR, "Internal error (missing backend_domid)"); + rc = ERROR_FAIL; + goto out; + } + usbdev->backend_domid = atoi(val); + + val = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/type", path)); + if(!val) { + LOG(ERROR, "Internal error (missing type)"); + rc = ERROR_FAIL; + goto out; + } + usbdev->type = atoi(val); + + switch(usbdev->type) { + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: + if ((rc = read_hostdev_xenstore_entry(gc, path, usbdev)) < 0) + goto out; + break; + default: + LOG(ERROR, "Internal error (unimplemented type)"); + rc = ERROR_FAIL; + goto out; + } + +out: + return rc; +} + +static int usb_add_xenstore(libxl__gc *gc, uint32_t domid, + libxl__device_usb *usbdev) +{ + libxl_ctx *ctx = libxl__gc_owner(gc); + flexarray_t *dev; + char *dev_path; + xs_transaction_t t; + struct xs_permissions noperm[1]; + + noperm[0].id = 0; + noperm[0].perms = XS_PERM_NONE; + + dev = flexarray_make(gc, 16, 1); + + flexarray_append_pair(dev, "protocol", + libxl__sprintf(gc, "%d", usbdev->protocol)); + + flexarray_append_pair(dev, "backend_domid", + libxl__sprintf(gc, "%d", usbdev->backend_domid)); + + flexarray_append_pair(dev, "type", + libxl__sprintf(gc, "%d", usbdev->type)); + + switch(usbdev->type) { + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: + dev_path = create_hostdev_xenstore_entry(gc, domid, usbdev, dev); + break; + default: + LOG(ERROR, "Invalid device type: %d", usbdev->type); + return ERROR_FAIL; + } + + LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Adding new usb device to xenstore"); + +retry_transaction: + t = xs_transaction_start(ctx->xsh); + libxl__xs_mkdir(gc, t, dev_path, noperm, ARRAY_SIZE(noperm)); + libxl__xs_writev(gc, t, dev_path, + libxl__xs_kvs_of_flexarray(gc, dev, dev->count)); + if (!xs_transaction_end(ctx->xsh, t, 0)) + if (errno == EAGAIN) + goto retry_transaction; + + return 0; +} + +static int get_assigned_devices(libxl__gc *gc, uint32_t domid, + libxl__device_usb **list, int *num, int do_gc) +{ + char **devlist, *dompath; + unsigned int nd = 0; + int i; + + *list = NULL; + *num = 0; + + dompath = libxl__sprintf(gc, USB_INFO_PATH, + libxl__xs_libxl_path(gc, domid)); + + devlist = libxl__xs_directory(gc, XBT_NULL, dompath, &nd); + if ( !devlist ) + goto out; + for(i = 0; i < nd; i++) { + char *path; + libxl__device_usb t; + + path = libxl__sprintf(gc, "%s/%s", dompath, devlist[i]); + if ( !usb_read_xenstore(gc, path, &t) ) { + *list = realloc(*list, sizeof(libxl__device_usb) * ((*num) +1)); + if (*list == NULL) + return ERROR_NOMEM; + *list[*num] = t; + (*num)++; + } + } + if(do_gc) + libxl__ptr_add(gc, *list); + +out: + return 0; +} + +static int is_usbdev_type_hostdev_equal(libxl__device_usb *a, + libxl__device_usb *b) +{ + if ( !memcmp(&a->u.hostdev, &b->u.hostdev, sizeof(a->u.hostdev) ) ) + return 1; + else + return 0; +} + +static int is_usbdev_in_array(libxl__device_usb *assigned, int num_assigned, + libxl__device_usb *dev) +{ + int i; + + for(i = 0; i < num_assigned; i++) { + if (assigned[i].protocol != dev->protocol + || assigned[i].type != dev->type) + continue; + + switch(dev->type) { + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: + if (!is_usbdev_type_hostdev_equal(dev, assigned+i)) + continue; + } + + return 1; + } + + return 0; +} + +static int do_usb_add(libxl__gc *gc, uint32_t domid, libxl__device_usb *usbdev) +{ + int rc; + + switch (usbdev->protocol) { + case LIBXL_USB_PROTOCOL_DEVICEMODEL: + switch (libxl__device_model_version_running(gc, domid)) { + case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: + LOG(ERROR, "usb-add not yet implemented for qemu-traditional"); + return ERROR_INVAL; + case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: + if ( (rc = libxl__qmp_usb_add(gc, domid, usbdev)) < 0 ) + goto out; + break; + default: + return ERROR_INVAL; + } + break; + default: + return ERROR_FAIL; + } + + rc = usb_add_xenstore(gc, domid, usbdev); +out: + return rc; +} + +static void usbdev_ext_to_int(libxl__device_usb *dev_int, + libxl_device_usb *dev_ext) +{ + dev_int->protocol = dev_ext->protocol; + + if (dev_ext->backend_domid == LIBXL_DEVICE_USB_BACKEND_DEFAULT) + dev_int->backend_domid = 0; + else + dev_int->backend_domid = dev_ext->backend_domid; + + dev_int->type = dev_ext->type; + memcpy(&dev_int->u, &dev_ext->u, sizeof(dev_ext->u)); +} + +#if 0 +/* Make gcc happy */ +static void usbdev_int_to_ext(libxl_device_usb *dev_ext, + libxl__device_usb *dev_int) +{ + dev_ext->protocol = dev_int->protocol; + + dev_ext->backend_domid = dev_int->backend_domid; + + dev_ext->type = dev_int->type; + memcpy(&dev_ext->u, &dev_int->u, sizeof(dev_ext->u)); +} +#endif + +static int libxl__device_usb_add(libxl__gc *gc, uint32_t domid, + libxl_device_usb *dev_ext) +{ + libxl_ctx *ctx = libxl__gc_owner(gc); + libxl__device_usb *assigned, _usbdev, *usbdev; + int rc = ERROR_FAIL, num_assigned; + libxl_domain_type domtype = libxl__domain_type(gc, domid); + + /* Interpret incoming */ + usbdev = &_usbdev; + + usbdev->target_domid = domid; + usbdev->dm_domid = libxl_get_stubdom_id(ctx, domid); + + usbdev_ext_to_int(usbdev, dev_ext); + + if ( usbdev->protocol == LIBXL_USB_PROTOCOL_AUTO ) { + if ( domtype == LIBXL_DOMAIN_TYPE_PV ) { + usbdev->protocol = LIBXL_USB_PROTOCOL_PV; + } else if (domtype == LIBXL_DOMAIN_TYPE_HVM) { + /* FIXME: See if we can detect PV frontend */ + usbdev->protocol = LIBXL_USB_PROTOCOL_DEVICEMODEL; + } + } + + /* Check to make sure we''re doing something that''s impemented */ + if ( usbdev->protocol != LIBXL_USB_PROTOCOL_DEVICEMODEL ) { + rc = ERROR_FAIL; + LOG(ERROR, "Protocol not implemented"); + goto out; + } + + if ( usbdev->dm_domid != 0 ) { + rc = ERROR_FAIL; + LOG(ERROR, "Stubdoms not yet supported"); + goto out; + } + + /* Double-check to make sure it''s not already assigned */ + rc = get_assigned_devices(gc, domid, &assigned, &num_assigned, 1); + if ( rc ) { + LOG(ERROR, "cannot determine if device is assigned, refusing to continue"); + goto out; + } + if ( is_usbdev_in_array(assigned, num_assigned, usbdev) ) { + LOG(ERROR, "USB device already attached to a domain"); + rc = ERROR_FAIL; + goto out; + } + + /* Do the add */ + if ( do_usb_add(gc, domid, usbdev) ) + rc = ERROR_FAIL; + +out: + return rc; +} + +int libxl_device_usb_add(libxl_ctx *ctx, uint32_t domid, + libxl_device_usb *usbdev, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + int rc; + rc = libxl__device_usb_add(gc, domid, usbdev); + libxl__ao_complete(egc, ao, rc); + return AO_INPROGRESS; +} diff --git a/tools/ocaml/libs/xl/genwrap.py b/tools/ocaml/libs/xl/genwrap.py index ea978bf..aab65f3 100644 --- a/tools/ocaml/libs/xl/genwrap.py +++ b/tools/ocaml/libs/xl/genwrap.py @@ -286,6 +286,7 @@ if __name__ == ''__main__'': "domain_config", "vcpuinfo", "event", + "device_usb" ] for t in blacklist: -- 1.7.9.5
George Dunlap
2013-Apr-11 15:20 UTC
[PATCH RFC 0.2 2/2] xl: Add usb-add and usb-del commands
Add commands to add and remove host USB to HVM guests. WARNING: STILL A PROTOTYPE Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com> --- docs/man/xl.pod.1 | 51 ++++++++++++++++++++++ tools/libxl/xl.h | 2 + tools/libxl/xl_cmdimpl.c | 105 +++++++++++++++++++++++++++++++++++++++++++++ tools/libxl/xl_cmdtable.c | 5 +++ 4 files changed, 163 insertions(+) diff --git a/docs/man/xl.pod.1 b/docs/man/xl.pod.1 index a0e298e..2b5e6d7 100644 --- a/docs/man/xl.pod.1 +++ b/docs/man/xl.pod.1 @@ -1110,6 +1110,57 @@ List virtual network interfaces for a domain. =back +=head2 HVM DEVICES + +=over 4 + +=item B<hvm-host-usb-add> I<-d domain-id> I<-v host-device-spec> [I<-i devname>] + +Passes through the host USB device specified by I<host-device-spec> to the +HVM domain I<domain-id>. Host-device-spec can be one of the following: + +=over 4 + +=item <hostbus>.<hostaddr> + +=item <vendorid>:<productid> + +=item <hostbus>.<hostaddr>:<vendorid>:<productid> + +=back + +The best way to find out the information for the device is typically using +lsusb. + +Using the I<-i> option, you can specify a I<devname> for the +device. This is an arbitrary string that can be used by +I<hvm-host-usb-del> to remove the device later. If no name is +specified, then one will be chosen automatically. In any case the +devname will be printed to stdout if the device is successfully added. + +If the I<-i> option is used, then it must be used on device removal; +I<host-device-spec> cannot be used. + +This command is only available for domains using qemu-xen, not +qemu-traditional. + +=item B<hvm-host-usb-del> I<-d domain-id> (I<-i devname> | I<-v host-device-spec>) + +Remove the host USB device from I<domain-id> which is specified either +by I<devname> or I<host-device-spec>. Exactly one of the two must be +specified. I<devname> is a string that was specified when the device +was inserted by B<hvm-host-usb-add>. I<host-device-spec> can only be +used if no name I<devname> was specified when the device was added, +and it must match exactly the specification given at that time. + +Devices specified in the config file do not have an associated +devname, and thus cannot be removed using this command. + +This command is only available for domains using qemu-xen, not +qemu-traditional. + +=back + =head2 VTPM DEVICES =over 4 diff --git a/tools/libxl/xl.h b/tools/libxl/xl.h index b881f92..1af0f3b 100644 --- a/tools/libxl/xl.h +++ b/tools/libxl/xl.h @@ -35,6 +35,8 @@ int main_info(int argc, char **argv); int main_sharing(int argc, char **argv); int main_cd_eject(int argc, char **argv); int main_cd_insert(int argc, char **argv); +int main_usb_add(int argc, char **argv); +int main_usb_del(int argc, char **argv); int main_console(int argc, char **argv); int main_vncviewer(int argc, char **argv); int main_pcilist(int argc, char **argv); diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c index 61f7b96..502a677 100644 --- a/tools/libxl/xl_cmdimpl.c +++ b/tools/libxl/xl_cmdimpl.c @@ -2600,6 +2600,111 @@ int main_cd_insert(int argc, char **argv) return 0; } + + +static int parse_usb_hostdev_specifier(libxl_device_usb *dev, const char *s) +{ + const char * hostbus, *hostaddr, *p; + + hostbus = s; + hostaddr=NULL; + +#define is_dec(_c) ((_c) >= ''0'' && (_c) <= ''9'') +#define is_hex(_c) (is_dec(_c) || ((_c) >= ''a'' && (_c) <= ''f'')) + + /* Match [0-9]+\.[0-9] */ + if (!is_dec(*hostbus)) + return -1; + + for(p=s; *p; p++) { + if(*p == ''.'') { + if ( !hostaddr ) + hostaddr = p+1; + else { + return -1; + } + } else if (!is_dec(*p)) { + return -1; + } + } + if (!hostaddr || !is_dec(*hostaddr)) + return -1; + dev->u.hostdev.hostbus = strtoul(hostbus, NULL, 10); + dev->u.hostdev.hostaddr = strtoul(hostaddr, NULL, 10); +#undef is_dec +#undef is_hex + + return 0; +} + +static int usb_add(uint32_t domid, libxl_device_usb_type type, + const char * device) +{ + libxl_device_usb usbdev; + int rc; + + libxl_device_usb_init(&usbdev); + + usbdev.type = type; + + switch(type) { + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: + if ( parse_usb_hostdev_specifier(&usbdev, device) < 0 ) { + rc = ERROR_FAIL; + goto out; + } + break; + default: + fprintf(stderr, "INTERNAL ERROR: Unimplemented type.\n"); + rc = ERROR_FAIL; + goto out; + } + + if ( (rc = libxl_device_usb_add(ctx, domid, &usbdev, NULL)) < 0 ) + fprintf(stderr, "libxl_usb_add failed.\n"); + + libxl_device_usb_dispose(&usbdev); + +out: + return rc; +} + +int main_usb_add(int argc, char **argv) +{ + uint32_t domid = -1; + int opt = 0, rc; + const char *device = NULL; + int type = 0; + + SWITCH_FOREACH_OPT(opt, "d:v:", NULL, "usb-add", 0) { + case ''d'': + domid = find_domain(optarg); + break; + case ''v'': + type = LIBXL_DEVICE_USB_TYPE_HOSTDEV; + device = optarg; + break; + } + + if ( domid == -1 ) { + fprintf(stderr, "Must specify domid\n\n"); + help("usb-add"); + return 2; + } + + if ( !device ) { + fprintf(stderr, "Must specify a device\n\n"); + help("usb-add"); + return 2; + } + + rc = usb_add(domid, type, device); + if ( rc < 0 ) + return 1; + else + return 0; +} + int main_console(int argc, char **argv) { uint32_t domid; diff --git a/tools/libxl/xl_cmdtable.c b/tools/libxl/xl_cmdtable.c index b4a87ca..948dae1 100644 --- a/tools/libxl/xl_cmdtable.c +++ b/tools/libxl/xl_cmdtable.c @@ -187,6 +187,11 @@ struct cmd_spec cmd_table[] = { "Eject a cdrom from a guest''s cd drive", "<Domain> <VirtualDevice>", }, + { "usb-add", + &main_usb_add, 1, 1, + "Hot-plug a usb device to a domain.", + "-d <Domain> [-v <hostbus.hostaddr>]", + }, { "mem-max", &main_memmax, 0, 1, "Set the maximum amount reservation for a domain", -- 1.7.9.5
George Dunlap
2013-Apr-11 15:22 UTC
Re: [PATCH RFC 0.2 2/2] xl: Add usb-add and usb-del commands
On 11/04/13 16:20, George Dunlap wrote:> Add commands to add and remove host USB to HVM guests. > > WARNING: STILL A PROTOTYPE > > Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com> > --- > docs/man/xl.pod.1 | 51 ++++++++++++++++++++++ > tools/libxl/xl.h | 2 + > tools/libxl/xl_cmdimpl.c | 105 +++++++++++++++++++++++++++++++++++++++++++++ > tools/libxl/xl_cmdtable.c | 5 +++ > 4 files changed, 163 insertions(+) > > diff --git a/docs/man/xl.pod.1 b/docs/man/xl.pod.1 > index a0e298e..2b5e6d7 100644 > --- a/docs/man/xl.pod.1 > +++ b/docs/man/xl.pod.1 > @@ -1110,6 +1110,57 @@ List virtual network interfaces for a domain. > > =back > > +=head2 HVM DEVICES > + > +=over 4 > + > +=item B<hvm-host-usb-add> I<-d domain-id> I<-v host-device-spec> [I<-i devname>]Hmm, it should be obvious that this bit is pure fiction... I''ll of course fix it up before the final submission. -G> + > +Passes through the host USB device specified by I<host-device-spec> to the > +HVM domain I<domain-id>. Host-device-spec can be one of the following: > + > +=over 4 > + > +=item <hostbus>.<hostaddr> > + > +=item <vendorid>:<productid> > + > +=item <hostbus>.<hostaddr>:<vendorid>:<productid> > + > +=back > + > +The best way to find out the information for the device is typically using > +lsusb. > + > +Using the I<-i> option, you can specify a I<devname> for the > +device. This is an arbitrary string that can be used by > +I<hvm-host-usb-del> to remove the device later. If no name is > +specified, then one will be chosen automatically. In any case the > +devname will be printed to stdout if the device is successfully added. > + > +If the I<-i> option is used, then it must be used on device removal; > +I<host-device-spec> cannot be used. > + > +This command is only available for domains using qemu-xen, not > +qemu-traditional. > + > +=item B<hvm-host-usb-del> I<-d domain-id> (I<-i devname> | I<-v host-device-spec>) > + > +Remove the host USB device from I<domain-id> which is specified either > +by I<devname> or I<host-device-spec>. Exactly one of the two must be > +specified. I<devname> is a string that was specified when the device > +was inserted by B<hvm-host-usb-add>. I<host-device-spec> can only be > +used if no name I<devname> was specified when the device was added, > +and it must match exactly the specification given at that time. > + > +Devices specified in the config file do not have an associated > +devname, and thus cannot be removed using this command. > + > +This command is only available for domains using qemu-xen, not > +qemu-traditional. > + > +=back > + > =head2 VTPM DEVICES > > =over 4 > diff --git a/tools/libxl/xl.h b/tools/libxl/xl.h > index b881f92..1af0f3b 100644 > --- a/tools/libxl/xl.h > +++ b/tools/libxl/xl.h > @@ -35,6 +35,8 @@ int main_info(int argc, char **argv); > int main_sharing(int argc, char **argv); > int main_cd_eject(int argc, char **argv); > int main_cd_insert(int argc, char **argv); > +int main_usb_add(int argc, char **argv); > +int main_usb_del(int argc, char **argv); > int main_console(int argc, char **argv); > int main_vncviewer(int argc, char **argv); > int main_pcilist(int argc, char **argv); > diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c > index 61f7b96..502a677 100644 > --- a/tools/libxl/xl_cmdimpl.c > +++ b/tools/libxl/xl_cmdimpl.c > @@ -2600,6 +2600,111 @@ int main_cd_insert(int argc, char **argv) > return 0; > } > > + > + > +static int parse_usb_hostdev_specifier(libxl_device_usb *dev, const char *s) > +{ > + const char * hostbus, *hostaddr, *p; > + > + hostbus = s; > + hostaddr=NULL; > + > +#define is_dec(_c) ((_c) >= ''0'' && (_c) <= ''9'') > +#define is_hex(_c) (is_dec(_c) || ((_c) >= ''a'' && (_c) <= ''f'')) > + > + /* Match [0-9]+\.[0-9] */ > + if (!is_dec(*hostbus)) > + return -1; > + > + for(p=s; *p; p++) { > + if(*p == ''.'') { > + if ( !hostaddr ) > + hostaddr = p+1; > + else { > + return -1; > + } > + } else if (!is_dec(*p)) { > + return -1; > + } > + } > + if (!hostaddr || !is_dec(*hostaddr)) > + return -1; > + dev->u.hostdev.hostbus = strtoul(hostbus, NULL, 10); > + dev->u.hostdev.hostaddr = strtoul(hostaddr, NULL, 10); > +#undef is_dec > +#undef is_hex > + > + return 0; > +} > + > +static int usb_add(uint32_t domid, libxl_device_usb_type type, > + const char * device) > +{ > + libxl_device_usb usbdev; > + int rc; > + > + libxl_device_usb_init(&usbdev); > + > + usbdev.type = type; > + > + switch(type) { > + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: > + if ( parse_usb_hostdev_specifier(&usbdev, device) < 0 ) { > + rc = ERROR_FAIL; > + goto out; > + } > + break; > + default: > + fprintf(stderr, "INTERNAL ERROR: Unimplemented type.\n"); > + rc = ERROR_FAIL; > + goto out; > + } > + > + if ( (rc = libxl_device_usb_add(ctx, domid, &usbdev, NULL)) < 0 ) > + fprintf(stderr, "libxl_usb_add failed.\n"); > + > + libxl_device_usb_dispose(&usbdev); > + > +out: > + return rc; > +} > + > +int main_usb_add(int argc, char **argv) > +{ > + uint32_t domid = -1; > + int opt = 0, rc; > + const char *device = NULL; > + int type = 0; > + > + SWITCH_FOREACH_OPT(opt, "d:v:", NULL, "usb-add", 0) { > + case ''d'': > + domid = find_domain(optarg); > + break; > + case ''v'': > + type = LIBXL_DEVICE_USB_TYPE_HOSTDEV; > + device = optarg; > + break; > + } > + > + if ( domid == -1 ) { > + fprintf(stderr, "Must specify domid\n\n"); > + help("usb-add"); > + return 2; > + } > + > + if ( !device ) { > + fprintf(stderr, "Must specify a device\n\n"); > + help("usb-add"); > + return 2; > + } > + > + rc = usb_add(domid, type, device); > + if ( rc < 0 ) > + return 1; > + else > + return 0; > +} > + > int main_console(int argc, char **argv) > { > uint32_t domid; > diff --git a/tools/libxl/xl_cmdtable.c b/tools/libxl/xl_cmdtable.c > index b4a87ca..948dae1 100644 > --- a/tools/libxl/xl_cmdtable.c > +++ b/tools/libxl/xl_cmdtable.c > @@ -187,6 +187,11 @@ struct cmd_spec cmd_table[] = { > "Eject a cdrom from a guest''s cd drive", > "<Domain> <VirtualDevice>", > }, > + { "usb-add", > + &main_usb_add, 1, 1, > + "Hot-plug a usb device to a domain.", > + "-d <Domain> [-v <hostbus.hostaddr>]", > + }, > { "mem-max", > &main_memmax, 0, 1, > "Set the maximum amount reservation for a domain",
George Dunlap
2013-Apr-11 15:26 UTC
Re: [PATCH RFC 0.2 1/2] libxl: Introduce functions to add and remove USB devices to an HVM guest
On 11/04/13 16:20, George Dunlap wrote:> This is a "beta" of v4, suitable for comment, and also to allow (potential) > concurrent development of the PV path of the prototype. > > THIS IS STILL A PROTOTYPE. > > This patch exposes a generic interface which can be expanded in the > future to implement USB for PVUSB, qemu, and stubdoms. It can also be > extended to include other types of USB other than host USB (for example, > tablets, mice, or keyboards). > > For each device removed or added, one of two protocols is available: > * PVUSB > * qemu (DEVICEMODEL) > > The caller can additionally specify "AUTO", in which case the library will > try to determine the best protocol automatically. > > At the moment, the only protocol implemented is DEVICEMODEL, and the only > device type impelmented is HOSTDEV. > > This uses the qmp functionality, and is thus only available for > qemu-xen, not qemu-traditional. > > v4 (RFC): > - Remove STUBDOM type > v3 (RFC): > - Complete re-write with new generalized protocol > - Store list of assigned USB devices in xenstore > v2: > - Make interface async-ready > - usb_del takes libxl_device_host_usb struct > - usb_del will re-construct original id given device info if no id is given > > Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com> > --- > tools/libxl/Makefile | 2 +- > tools/libxl/libxl.c | 10 +- > tools/libxl/libxl.h | 36 ++++ > tools/libxl/libxl_create.c | 12 +- > tools/libxl/libxl_internal.h | 26 ++- > tools/libxl/libxl_qmp.c | 66 ++++++++ > tools/libxl/libxl_types.idl | 22 +++ > tools/libxl/libxl_usb.c | 364 ++++++++++++++++++++++++++++++++++++++++ > tools/ocaml/libs/xl/genwrap.py | 1 + > 9 files changed, 528 insertions(+), 11 deletions(-) > create mode 100644 tools/libxl/libxl_usb.c > > diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile > index 2984051..866960a 100644 > --- a/tools/libxl/Makefile > +++ b/tools/libxl/Makefile > @@ -74,7 +74,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ > libxl_internal.o libxl_utils.o libxl_uuid.o \ > libxl_json.o libxl_aoutils.o libxl_numa.o \ > libxl_save_callout.o _libxl_save_msgs_callout.o \ > - libxl_qmp.o libxl_event.o libxl_fork.o $(LIBXL_OBJS-y) > + libxl_qmp.o libxl_event.o libxl_fork.o libxl_usb.o $(LIBXL_OBJS-y) > LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o > > $(LIBXL_OBJS): CFLAGS += $(CFLAGS_LIBXL) -include $(XEN_ROOT)/tools/config.h > diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c > index 572c2c6..bddac17 100644 > --- a/tools/libxl/libxl.c > +++ b/tools/libxl/libxl.c > @@ -63,7 +63,7 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version, > > ctx->childproc_hooks = &libxl__childproc_default_hooks; > ctx->childproc_user = 0; > - > + > ctx->sigchld_selfpipe[0] = -1; > > /* The mutex is special because we can''t idempotently destroy it */ > @@ -1073,7 +1073,7 @@ int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid, > 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; } > @@ -1145,7 +1145,7 @@ static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w, > > 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, > libxl__sprintf(gc, "%.*s/backend", > (int)strlen(wpath)-6, wpath)); > @@ -1237,7 +1237,7 @@ 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 */ > > @@ -1446,7 +1446,7 @@ static void devices_destroy_cb(libxl__egc *egc, > } > > if (rc < 0) > - LIBXL__LOG(ctx, LIBXL__LOG_ERROR, > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, > "libxl__devices_destroy failed for %d", domid); > > vm_path = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/vm", dom_path)); > diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h > index d18d22c..f7c7bd8 100644 > --- a/tools/libxl/libxl.h > +++ b/tools/libxl/libxl.h > @@ -75,6 +75,11 @@ > #define LIBXL_HAVE_FIRMWARE_PASSTHROUGH 1 > > /* > + * FIXME: Update comment > + */ > +#define LIBXL_HAVE_USB 1 > + > +/* > * libxl ABI compatibility > * > * The only guarantee which libxl makes regarding ABI compatibility > @@ -735,6 +740,37 @@ int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, > const libxl_asyncop_how *ao_how) > LIBXL_EXTERNAL_CALLERS_ONLY; > > +/* > + * USB > + * > + * For each device removed or added, one of three protocols is available: > + * - PVUSB > + * - qemu (DEVICEMODEL) > + * > + * The caller can additionally specify "AUTO", in which case the library will > + * try to determine the best protocol automatically. > + * > + * At the moment, the only protocol implemented is DEVICEMODEL, and the only > + * device type impelmented is HOSTDEV. > + * > + * This uses the qmp functionality, and is thus only available for > + * qemu-xen, not qemu-traditional. > + * > + */ > +#define LIBXL_DEVICE_USB_BACKEND_DEFAULT (-1) > +int libxl_device_usb_add(libxl_ctx *ctx, uint32_t domid, > + libxl_device_usb *dev, > + const libxl_asyncop_how *ao_how) > + LIBXL_EXTERNAL_CALLERS_ONLY; > +int libxl_device_usb_remove(libxl_ctx *ctx, uint32_t domid, > + libxl_device_usb *dev, > + const libxl_asyncop_how *ao_how) > + LIBXL_EXTERNAL_CALLERS_ONLY; > +int libxl_device_usb_list(libxl_ctx *ctx, uint32_t domid, > + libxl_device_usb **dev, > + const libxl_asyncop_how *ao_how) > + LIBXL_EXTERNAL_CALLERS_ONLY; > + > /* Network Interfaces */ > int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic, > const libxl_asyncop_how *ao_how) > diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c > index 30a4507..c620d6d 100644 > --- a/tools/libxl/libxl_create.c > +++ b/tools/libxl/libxl_create.c > @@ -391,7 +391,7 @@ int libxl__domain_make(libxl__gc *gc, libxl_domain_create_info *info, > libxl_ctx *ctx = libxl__gc_owner(gc); > int flags, ret, rc, nb_vm; > char *uuid_string; > - char *dom_path, *vm_path, *libxl_path; > + char *dom_path, *vm_path, *libxl_path, *libxl_usb_path; > struct xs_permissions roperm[2]; > struct xs_permissions rwperm[1]; > struct xs_permissions noperm[1]; > @@ -452,6 +452,13 @@ int libxl__domain_make(libxl__gc *gc, libxl_domain_create_info *info, > goto out; > } > > + libxl_usb_path = libxl__sprintf(gc, "%s/usb", libxl_path); > + if (!libxl_usb_path) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "cannot allocate create paths"); > + rc = ERROR_FAIL; > + goto out; > + } > + > noperm[0].id = 0; > noperm[0].perms = XS_PERM_NONE; > > @@ -475,6 +482,9 @@ retry_transaction: > xs_rm(ctx->xsh, t, libxl_path); > libxl__xs_mkdir(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm)); > > + xs_rm(ctx->xsh, t, libxl_usb_path); > + libxl__xs_mkdir(gc, t, libxl_usb_path, noperm, ARRAY_SIZE(noperm)); > + > xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/vm", dom_path), vm_path, strlen(vm_path)); > rc = libxl__domain_rename(gc, *domid, 0, info->name, t); > if (rc) > diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h > index 3ba3a21..5e31ef1 100644 > --- a/tools/libxl/libxl_internal.h > +++ b/tools/libxl/libxl_internal.h > @@ -213,7 +213,7 @@ struct libxl__ev_xswatch { > typedef struct libxl__ev_watch_slot { > LIBXL_SLIST_ENTRY(struct libxl__ev_watch_slot) empty; > } libxl__ev_watch_slot; > - > + > _hidden libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, > int slotnum); > > @@ -346,7 +346,7 @@ struct libxl__ctx { > death_list /* sorted by domid */, > death_reported; > libxl__ev_xswatch death_watch; > - > + > LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens; > > const libxl_childproc_hooks *childproc_hooks; > @@ -1112,7 +1112,7 @@ _hidden void libxl__spawn_init(libxl__spawn_state*); > * > * what: string describing the spawned process, used for logging > * > - * Logs errors. A copy of "what" is taken. > + * Logs errors. A copy of "what" is taken. > * Return values: > * < 0 error, *spawn is now Idle and need not be detached > * +1 caller is the parent, *spawn is Attached and must be detached > @@ -1412,6 +1412,24 @@ _hidden int libxl__qmp_save(libxl__gc *gc, int domid, const char *filename); > /* Set dirty bitmap logging status */ > _hidden int libxl__qmp_set_global_dirty_log(libxl__gc *gc, int domid, bool enable); > _hidden int libxl__qmp_insert_cdrom(libxl__gc *gc, int domid, const libxl_device_disk *disk); > +/* Same as normal, but "translated" */ > +typedef struct libxl__device_usb { > + libxl_usb_protocol protocol; > + libxl_domid target_domid; > + libxl_domid backend_domid; > + libxl_domid dm_domid; > + libxl_device_usb_type type; > + union { > + struct { > + int hostbus; > + int hostaddr; > + } hostdev; > + } u; > +} libxl__device_usb; > +_hidden int libxl__qmp_usb_add(libxl__gc *gc, int domid, > + libxl__device_usb *dev); > +_hidden int libxl__qmp_usb_remove(libxl__gc *gc, int domid, > + libxl__device_usb *dev); > /* close and free the QMP handler */ > _hidden void libxl__qmp_close(libxl__qmp_handler *qmp); > /* remove the socket file, if the file has already been removed, > @@ -2639,7 +2657,7 @@ _hidden const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid); > */ > #define GCNEW_ARRAY(var, nmemb) \ > ((var) = libxl__calloc((gc), (nmemb), sizeof(*(var)))) > - > + > /* > * Expression statement <type> *GCREALLOC_ARRAY(<type> *var, size_t nmemb); > * Uses libxl__gc *gc; > diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c > index 644d2c0..ae55695 100644 > --- a/tools/libxl/libxl_qmp.c > +++ b/tools/libxl/libxl_qmp.c > @@ -42,6 +42,7 @@ > > #define QMP_RECEIVE_BUFFER_SIZE 4096 > #define PCI_PT_QDEV_ID "pci-pt-%02x_%02x.%01x" > +#define HOST_USB_QDEV_ID "usb-hostdev-%04x.%04x" > > typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp, > const libxl__json_object *tree, > @@ -929,6 +930,71 @@ int libxl__qmp_insert_cdrom(libxl__gc *gc, int domid, > } > } > > +static int libxl__qmp_usb_hostdev_add(libxl__gc *gc, int domid, > + libxl__device_usb *dev) > +{ > + libxl__json_object *args = NULL; > + char *id; > + > + id = libxl__sprintf(NOGC, HOST_USB_QDEV_ID, > + (uint16_t) dev->u.hostdev.hostbus, > + (uint16_t) dev->u.hostdev.hostaddr); > + > + qmp_parameters_add_string(gc, &args, "driver", "usb-host"); > + QMP_PARAMETERS_SPRINTF(&args, "hostbus", "0x%x", dev->u.hostdev.hostbus); > + QMP_PARAMETERS_SPRINTF(&args, "hostaddr", "0x%x", dev->u.hostdev.hostaddr); > + > + qmp_parameters_add_string(gc, &args, "id", id); > + > + return qmp_run_command(gc, domid, "device_add", args, NULL, NULL); > +} > + > +int libxl__qmp_usb_add(libxl__gc *gc, int domid, > + libxl__device_usb *usbdev) > +{ > + int rc; > + switch(usbdev->type) { > + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: > + rc = libxl__qmp_usb_hostdev_add(gc, domid, usbdev); > + break; > + default: > + return ERROR_INVAL; > + } > + return rc; > +} > + > + > +static int libxl__qmp_usb_hostdev_remove(libxl__gc *gc, int domid, > + libxl__device_usb *dev) > +{ > + libxl__json_object *args = NULL; > + char *id; > + > + id = libxl__sprintf(NOGC, HOST_USB_QDEV_ID, > + (uint16_t) dev->u.hostdev.hostbus, > + (uint16_t) dev->u.hostdev.hostaddr); > + > + qmp_parameters_add_string(gc, &args, "id", id); > + > + return qmp_run_command(gc, domid, "device_del", args, NULL, NULL); > +} > + > +int libxl__qmp_usb_remove(libxl__gc *gc, int domid, > + libxl__device_usb *usbdev) > +{ > + int rc; > + switch(usbdev->type) { > + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: > + rc = libxl__qmp_usb_hostdev_remove(gc, domid, usbdev); > + break; > + default: > + return ERROR_INVAL; > + } > + return rc; > +} > + > + > + > int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid, > const libxl_domain_config *guest_config) > { > diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl > index 6cb6de6..9e0a435 100644 > --- a/tools/libxl/libxl_types.idl > +++ b/tools/libxl/libxl_types.idl > @@ -407,6 +407,28 @@ libxl_device_vtpm = Struct("device_vtpm", [ > ("uuid", libxl_uuid), > ]) > > +libxl_device_usb_protocol = Enumeration("usb_protocol", [ > + (0, "AUTO"), > + (1, "PV"), > + (2, "DEVICEMODEL"), > + ]) > + > +libxl_device_usb_type = Enumeration("device_usb_type", [ > + (1, "HOSTDEV"), > + ]) > + > +libxl_device_usb = Struct("device_usb", [ > + ("protocol", libxl_device_usb_protocol, > + {''init_val'': ''LIBXL_USB_PROTOCOL_AUTO''}), > + ("backend_domid", libxl_domid, > + {''init_val'': ''LIBXL_DEVICE_USB_BACKEND_DEFAULT''}), > + ("u", KeyedUnion(None, libxl_device_usb_type, "type", > + [("hostdev", Struct(None, [ > + ("hostbus", integer), > + ("hostaddr", integer) ])) > + ])) > + ]) > + > libxl_domain_config = Struct("domain_config", [ > ("c_info", libxl_domain_create_info), > ("b_info", libxl_domain_build_info), > diff --git a/tools/libxl/libxl_usb.c b/tools/libxl/libxl_usb.c > new file mode 100644 > index 0000000..547a738 > --- /dev/null > +++ b/tools/libxl/libxl_usb.c > @@ -0,0 +1,364 @@ > +/* > + * 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" /* must come before any other headers */ > + > +#include "libxl_internal.h" > + > +/* > + * /libxl/<domid>/usb/<devid>/{type, protocol, backend, [typeinfo]} > + */ > +#define USB_INFO_PATH "%s/usb" > +#define USB_HOSTDEV_ID "hostdev-%04x-%04x" > + > +/* > + * Just use plain numbers for now. Replace these with strings at some point. > + */ > +#define protocol_to_str(gc, t) libxl__sprintf((gc), "%d", (t)) > +#define str_to_protocol(s) atoi((s)) > +#define type_to_str(gc, t) libxl__sprintf((gc), "%d", (t)) > +#define str_to_typeprotocol(s) atoi((s)) > + > +static char * create_hostdev_xenstore_entry(libxl__gc *gc, uint32_t domid, > + libxl__device_usb *usbdev, flexarray_t *a) > +{ > + char * path; > + > + path = libxl__sprintf(gc, USB_INFO_PATH "/%s", > + libxl__xs_libxl_path(gc, domid), > + libxl__sprintf(gc, USB_HOSTDEV_ID, > + (uint16_t)usbdev->u.hostdev.hostbus, > + (uint16_t)usbdev->u.hostdev.hostaddr)); > + > + flexarray_append_pair(a, "hostbus", > + libxl__sprintf(gc, "%d", > + usbdev->u.hostdev.hostbus)); > + flexarray_append_pair(a, "hostaddr", > + libxl__sprintf(gc, "%d", > + usbdev->u.hostdev.hostaddr)); > + > + return path; > +} > + > +static int read_hostdev_xenstore_entry(libxl__gc *gc, const char * path, > + libxl__device_usb *usbdev) > +{ > + int rc = 0; > + char * val; > + > + val = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/hostbus", path)); > + if(!val) { > + LOG(ERROR, "Internal error (missing hostbus)"); > + rc = ERROR_FAIL; > + goto out; > + } > + usbdev->u.hostdev.hostbus = atoi(val); > + > + val = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/hostaddr", path)); > + if(!val) { > + LOG(ERROR, "Internal error (missing hostaddr)"); > + rc = ERROR_FAIL; > + goto out; > + } > + usbdev->u.hostdev.hostaddr = atoi(val); > + > +out: > + return rc; > +} > + > +static int usb_read_xenstore(libxl__gc *gc, const char * path, > + libxl__device_usb *usbdev) > +{ > + char *val; > + int rc = 0; > + > + val = libxl__xs_read(gc, XBT_NULL, > + libxl__sprintf(gc, "%s/protocol", path)); > + if(!val) { > + LOG(ERROR, "Internal error (missing protocol)"); > + rc = ERROR_FAIL; > + goto out; > + } > + usbdev->protocol = atoi(val); > + > + val = libxl__xs_read(gc, XBT_NULL, > + libxl__sprintf(gc, "%s/backend_domid", path)); > + if(!val) { > + LOG(ERROR, "Internal error (missing backend_domid)"); > + rc = ERROR_FAIL; > + goto out; > + } > + usbdev->backend_domid = atoi(val); > + > + val = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/type", path)); > + if(!val) { > + LOG(ERROR, "Internal error (missing type)"); > + rc = ERROR_FAIL; > + goto out; > + } > + usbdev->type = atoi(val); > + > + switch(usbdev->type) { > + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: > + if ((rc = read_hostdev_xenstore_entry(gc, path, usbdev)) < 0) > + goto out; > + break; > + default: > + LOG(ERROR, "Internal error (unimplemented type)"); > + rc = ERROR_FAIL; > + goto out; > + } > + > +out: > + return rc; > +} > + > +static int usb_add_xenstore(libxl__gc *gc, uint32_t domid, > + libxl__device_usb *usbdev) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + flexarray_t *dev; > + char *dev_path; > + xs_transaction_t t; > + struct xs_permissions noperm[1]; > + > + noperm[0].id = 0; > + noperm[0].perms = XS_PERM_NONE; > + > + dev = flexarray_make(gc, 16, 1); > + > + flexarray_append_pair(dev, "protocol", > + libxl__sprintf(gc, "%d", usbdev->protocol)); > + > + flexarray_append_pair(dev, "backend_domid", > + libxl__sprintf(gc, "%d", usbdev->backend_domid)); > + > + flexarray_append_pair(dev, "type", > + libxl__sprintf(gc, "%d", usbdev->type)); > + > + switch(usbdev->type) { > + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: > + dev_path = create_hostdev_xenstore_entry(gc, domid, usbdev, dev); > + break; > + default: > + LOG(ERROR, "Invalid device type: %d", usbdev->type); > + return ERROR_FAIL; > + } > + > + LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Adding new usb device to xenstore"); > + > +retry_transaction: > + t = xs_transaction_start(ctx->xsh); > + libxl__xs_mkdir(gc, t, dev_path, noperm, ARRAY_SIZE(noperm)); > + libxl__xs_writev(gc, t, dev_path, > + libxl__xs_kvs_of_flexarray(gc, dev, dev->count)); > + if (!xs_transaction_end(ctx->xsh, t, 0)) > + if (errno == EAGAIN) > + goto retry_transaction; > + > + return 0; > +} > + > +static int get_assigned_devices(libxl__gc *gc, uint32_t domid, > + libxl__device_usb **list, int *num, int do_gc) > +{ > + char **devlist, *dompath; > + unsigned int nd = 0; > + int i; > + > + *list = NULL; > + *num = 0; > + > + dompath = libxl__sprintf(gc, USB_INFO_PATH, > + libxl__xs_libxl_path(gc, domid)); > + > + devlist = libxl__xs_directory(gc, XBT_NULL, dompath, &nd); > + if ( !devlist ) > + goto out; > + for(i = 0; i < nd; i++) { > + char *path; > + libxl__device_usb t; > + > + path = libxl__sprintf(gc, "%s/%s", dompath, devlist[i]); > + if ( !usb_read_xenstore(gc, path, &t) ) { > + *list = realloc(*list, sizeof(libxl__device_usb) * ((*num) +1)); > + if (*list == NULL) > + return ERROR_NOMEM; > + *list[*num] = t; > + (*num)++; > + } > + } > + if(do_gc) > + libxl__ptr_add(gc, *list); > + > +out: > + return 0; > +} > + > +static int is_usbdev_type_hostdev_equal(libxl__device_usb *a, > + libxl__device_usb *b) > +{ > + if ( !memcmp(&a->u.hostdev, &b->u.hostdev, sizeof(a->u.hostdev) ) ) > + return 1; > + else > + return 0; > +} > + > +static int is_usbdev_in_array(libxl__device_usb *assigned, int num_assigned, > + libxl__device_usb *dev) > +{ > + int i; > + > + for(i = 0; i < num_assigned; i++) { > + if (assigned[i].protocol != dev->protocol > + || assigned[i].type != dev->type) > + continue; > + > + switch(dev->type) { > + case LIBXL_DEVICE_USB_TYPE_HOSTDEV: > + if (!is_usbdev_type_hostdev_equal(dev, assigned+i)) > + continue; > + } > + > + return 1; > + } > + > + return 0; > +} > + > +static int do_usb_add(libxl__gc *gc, uint32_t domid, libxl__device_usb *usbdev) > +{ > + int rc; > + > + switch (usbdev->protocol) { > + case LIBXL_USB_PROTOCOL_DEVICEMODEL: > + switch (libxl__device_model_version_running(gc, domid)) { > + case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: > + LOG(ERROR, "usb-add not yet implemented for qemu-traditional"); > + return ERROR_INVAL; > + case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: > + if ( (rc = libxl__qmp_usb_add(gc, domid, usbdev)) < 0 ) > + goto out; > + break;Stefan: Basically, you just need to add a new case here to do the xenstore writes to set up the PVUSB stuff. And then...> + default: > + return ERROR_INVAL; > + } > + break; > + default: > + return ERROR_FAIL; > + } > + > + rc = usb_add_xenstore(gc, domid, usbdev); > +out: > + return rc; > +} > + > +static void usbdev_ext_to_int(libxl__device_usb *dev_int, > + libxl_device_usb *dev_ext) > +{ > + dev_int->protocol = dev_ext->protocol; > + > + if (dev_ext->backend_domid == LIBXL_DEVICE_USB_BACKEND_DEFAULT) > + dev_int->backend_domid = 0; > + else > + dev_int->backend_domid = dev_ext->backend_domid; > + > + dev_int->type = dev_ext->type; > + memcpy(&dev_int->u, &dev_ext->u, sizeof(dev_ext->u)); > +} > + > +#if 0 > +/* Make gcc happy */ > +static void usbdev_int_to_ext(libxl_device_usb *dev_ext, > + libxl__device_usb *dev_int) > +{ > + dev_ext->protocol = dev_int->protocol; > + > + dev_ext->backend_domid = dev_int->backend_domid; > + > + dev_ext->type = dev_int->type; > + memcpy(&dev_ext->u, &dev_int->u, sizeof(dev_ext->u)); > +} > +#endif > + > +static int libxl__device_usb_add(libxl__gc *gc, uint32_t domid, > + libxl_device_usb *dev_ext) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + libxl__device_usb *assigned, _usbdev, *usbdev; > + int rc = ERROR_FAIL, num_assigned; > + libxl_domain_type domtype = libxl__domain_type(gc, domid); > + > + /* Interpret incoming */ > + usbdev = &_usbdev; > + > + usbdev->target_domid = domid; > + usbdev->dm_domid = libxl_get_stubdom_id(ctx, domid); > + > + usbdev_ext_to_int(usbdev, dev_ext); > + > + if ( usbdev->protocol == LIBXL_USB_PROTOCOL_AUTO ) { > + if ( domtype == LIBXL_DOMAIN_TYPE_PV ) { > + usbdev->protocol = LIBXL_USB_PROTOCOL_PV; > + } else if (domtype == LIBXL_DOMAIN_TYPE_HVM) { > + /* FIXME: See if we can detect PV frontend */ > + usbdev->protocol = LIBXL_USB_PROTOCOL_DEVICEMODEL; > + } > + } > + > + /* Check to make sure we''re doing something that''s impemented */ > + if ( usbdev->protocol != LIBXL_USB_PROTOCOL_DEVICEMODEL ) { > + rc = ERROR_FAIL; > + LOG(ERROR, "Protocol not implemented"); > + goto out; > + }...take out this check, and I think the xl stuff should "just work" for PV domains. -George
George Dunlap
2013-Apr-11 15:47 UTC
Re: [PATCH RFC 0.2 1/2] libxl: Introduce functions to add and remove USB devices to an HVM guest
(Adding xen-devel back to the cc list) On 11/04/13 16:43, Stefan wrote:> Thank you George. I''m going to look at that today and will send a > message soon. > Since it''s a prototype, does it qualify as a feature for the next release?Well the code freeze is coming up shortly. If you can get your PV patches tomorrow, they can definitely be considered. If you get them by early next week, there''s still a possibility -- but we''ll have to see. I''ll be a bit biased (since they''re a lot of my own work), so I''ll need feedback from the community. But it seems like it should be do-able. -George