From: George Dunlap <george.dunlap@eu.citrix.com>
To: Ian Campbell <Ian.Campbell@citrix.com>
Cc: Anthony Perard <anthony.perard@citrix.com>,
"sstanisi@cbnco.com" <sstanisi@cbnco.com>,
Roger Pau Monne <roger.pau@citrix.com>,
Ian Jackson <Ian.Jackson@eu.citrix.com>,
"xen-devel@lists.xen.org" <xen-devel@lists.xen.org>
Subject: Re: [PATCH v6 1/2] libxl: Introduce functions to add and remove USB devices to an HVM guest
Date: Wed, 24 Apr 2013 14:32:53 +0100 [thread overview]
Message-ID: <5177DF05.50907@eu.citrix.com> (raw)
In-Reply-To: <1366807135.20256.328.camel@zakaz.uk.xensource.com>
On 24/04/13 13:38, Ian Campbell wrote:
> I didn't see an implementation of libxl__device_usb_setdefault?
You mean, for the internal structure?
>
> On Fri, 2013-04-19 at 16:59 +0100, George Dunlap wrote:
>> 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..d2e00fa 100644
>> --- a/tools/libxl/libxl_internal.h
>> +++ b/tools/libxl/libxl_internal.h
>> @@ -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" */
> You can use the IDL to do internal stuff too -- see
> tools/libxl/libxl_types_internal.idl
>
> What does "translated" mean? Which fields and how? Might it be better to
> include a pointer to the original libxl_device_usb instead of
> duplicating some/all of these fields?
I think one of the biggest thing was avoiding having to look up the
stubdomain in a bunch of different functions -- that's the dm_domid.
There's also the translation of "AUTO" protocol into PV or HVM, and
(originally) the translation of BACKEND_DEFAULT into the actual backend
domain.
If we were willing to have the library change the protocol in the
structure passed to it, then a pointer might work as well.
>
>> +typedef struct libxl__device_usb {
>> + libxl_usb_protocol protocol;
>> + libxl_domid target_domid;
>> + libxl_domid backend_domid;
>> + libxl_domid dm_domid;
> The only use of this I see is:
> + usbdev->dm_domid = libxl_get_stubdom_id(ctx, domid);
> ...
> + if (usbdev->dm_domid != 0) {
>
> twice, both times all in the same function so you could just use a local
> variable.
A big part of that is because we don't support stubdomains yet. When we
do, there will be more complicated cases to consider.
> If you remove this and pass target_domid as a parameter to various
> functions (which is the convention in libxl) then the need for this
> weird internal/external representation goes away.
There's also the resolution of protocol from ANY into PV or HVM; but if
we're willing to change the structure passed in by the caller, then yes,
we can get rid of this struct for now and consider re-introducing it
if/when we actually need it.
>
>> + 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,
>> diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c
>> index 644d2c0..9ad3e59 100644
>> --- a/tools/libxl/libxl_qmp.c
>> +++ b/tools/libxl/libxl_qmp.c
> I'd like Anthony's feedback on the QMP bits (CCd)
>
>> @@ -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,70 @@ 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 = GCSPRINTF(HOST_USB_QDEV_ID,
>> + (uint16_t)dev->u.hostdev.hostbus,
>> + (uint16_t)dev->u.hostdev.hostaddr);
> You could make these uint16 in the IDL if you wanted to restrict it like
> that. Even without that is the cast really necessary though, given the
> %x format string?
>
> If you do use uint16_t then you probably want to use PRIx16 too.
This actually a vestigial artifact from the fact that you could
originally specify ANY in the field, which was encoded as -1. I'll just
take out now.
>> +
>> + 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:
> Will this function ever handle anything other than HOSTDEV? AIUI the
> only other potential type is PV which won't come anywhere near here?
You're mixing up protocol and device type. PV will only ever handle
HOSTDEV types, but qmp allows a wide range of virtual devices: tablets,
mice, keyboards, thumb drives, &c. I'd like at some point for all USB
devices specified in the config file to be able to be listed and
hot-plugged / hot-unplugged.
>
> (same comments on the remove bits)
>
>> diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
>> index fcb1ecd..3c6a709 100644
>> --- a/tools/libxl/libxl_types.idl
>> +++ b/tools/libxl/libxl_types.idl
>> @@ -408,6 +408,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),
> Do you not want to add a list of USB devices to the domain_build_info?
I don't think I can get that functionality working for 4.3. It's
definitely on my plans for the future, though.
>
>> diff --git a/tools/libxl/libxl_usb.c b/tools/libxl/libxl_usb.c
>> new file mode 100644
>> index 0000000..c320487
>> --- /dev/null
>> +++ b/tools/libxl/libxl_usb.c
>> @@ -0,0 +1,526 @@
>> +/*
>> + * Copyright (C) 2009 Citrix Ltd.
>> + * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
>> + * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
> Probably not true?
Haha -- oops...
>
>> +/*
>> + * /libxl/<domid>/usb/<devid>/{type, protocol, backend, [typeinfo]}
>> + */
>> +#define USB_INFO_PATH "%s/usb"
> This duplicates something you added in libxl_create.c I think. I nearly
> suggested moving that code into this file as a libxl__domain_usb_init or
> something like that.
The other option would be to make a function like
libxl__xs_libxl_path(), but that seemed a bit heavy-handed to me. I
guess libxl__domain_usb_init sounds good.
>
>> +#define USB_HOSTDEV_ID "hostdev-%04x-%04x"
>> +
>> +/*
>> + * Just use plain numbers for now. Replace these with strings at some point.
>> + */
>> +static char * hostdev_xs_path(libxl__gc *gc, uint32_t domid,
>> + libxl__device_usb *usbdev)
>> +{
>> + return GCSPRINTF(USB_INFO_PATH "/%s",
>> + libxl__xs_libxl_path(gc, domid),
>> + GCSPRINTF(USB_HOSTDEV_ID,
>> + (uint16_t)usbdev->u.hostdev.hostbus,
>> + (uint16_t)usbdev->u.hostdev.hostaddr));
>> +}
>> +
>> +static void hostdev_xs_append_params(libxl__gc *gc, libxl__device_usb *usbdev,
>> + flexarray_t *a)
>> +{
>> + flexarray_append_pair(a, "hostbus",
>> + GCSPRINTF("%d",
>> + usbdev->u.hostdev.hostbus));
>> + flexarray_append_pair(a, "hostaddr",
>> + GCSPRINTF("%d",
>> + usbdev->u.hostdev.hostaddr));
> Are the second and third lines > 80 chars in total?
You mean, "Why did you break the GSPRINTF into two lines?" Yes, because
on one line it's > 80 chars.
>
> [...]
>> +static int usb_add_xenstore(libxl__gc *gc, uint32_t domid,
>> + libxl__device_usb *usbdev)
>> +{
> [...]
>> + default:
>> + LOG(ERROR, "Invalid device type: %d", usbdev->type);
>> + return ERROR_FAIL;
>> + }
>> +
>> + LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Adding new usb device to xenstore");
> You use a mixture of this and the LOG(DEBUG,...) form in this patch, we
> tend to prefer the latter for new code.
Ack.
>
>> +
>> + for(;;) {
>> + rc = libxl__xs_transaction_start(gc, &t);
>> + if (rc) goto out;
>> +
>> + /* Helpfully, mkdir returns 0 on failure, 1 on success */
>> + if (!libxl__xs_mkdir(gc, t, dev_path, noperm, ARRAY_SIZE(noperm))) {
>> + rc = ERROR_FAIL;
>> + goto out;
>> + }
>> +
>> + /* And libxl__xs_writev *always* returns 0 no matter what */
> Strictly speaking the *current* implementation always returns 0, but if
> someone changes that your code will break. But none of the other callers
> check it either, so meh ;-)
In fact, I already have tried changing it, and the result is that domain
creation fails -- so whoever does change it will have a lot of cleaning
up to do anyway. :-) (This is on my to-do list for the 4.4 dev cycle.)
>> + libxl__xs_writev(gc, t, dev_path,
>> + libxl__xs_kvs_of_flexarray(gc, dev, dev->count));
>> +
>> + rc = libxl__xs_transaction_commit(gc, &t);
>> + if (!rc) break;
>> + if (rc <0) goto out;
>> + }
>> +
>> +out:
> According to libxl_internal.h you need to call
> libxl__xs_transaction_abort on the error path.
Good catch -- sorry I missed that.
>
> [...]
>
>> +
>> +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)))
> I'm not 100% sure it is permissible to compare structs for equality in
> this way. What if the compiler decides to add some padding which doesn't
> get initialised?
>
> I'm afraid this probably means you have to compare field by field.
Ack.
>
>> + return 1;
>> + else
>> + return 0;
>> +}
>> +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));
> I don't think you can be sure that these two things have been laid out
> the same by the compiler.
Ack.
>
> If you make dev_ext->backend_domid default to 0 then I'm not entirely
> sure what the point of this internal/external distinction is.
>
>> +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 */
> "implemented"
>
>> + 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);
>> + 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;
>> +}
> Please define this (and the remove) using the DEFINE_DEVICE_ADD/REMOVE
> macros in libxl.c. Not least because I'm not convinced your handling of
> the AO stuff is correct (or indeed present).
>
> This will probably require the libxl__device_usb_add/remove to change to
> do the AIO stuff too -- i.e. at the end:
> aodev->rc = rc;
> if (rc) aodev->callback(egc, aodev);
> return;
These follow the template set by
libxl_pci.c:libxl_device_pci_{add,remove,destroy}(), which were chosen
specifically because they essentially do a null ASYNC, but
interface-wise allow for real async to be added in the future.
-George
next prev parent reply other threads:[~2013-04-24 13:32 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-04-19 15:59 [PATCH v6 1/2] libxl: Introduce functions to add and remove USB devices to an HVM guest George Dunlap
2013-04-19 15:59 ` [PATCH v6 2/2] xl: Add commands for usb hot-plug George Dunlap
2013-04-24 12:45 ` Ian Campbell
2013-04-25 10:16 ` George Dunlap
2013-04-25 11:38 ` Ian Campbell
2013-04-25 11:57 ` George Dunlap
2013-04-25 12:04 ` George Dunlap
2013-04-25 12:08 ` Ian Campbell
2013-04-25 12:14 ` George Dunlap
2013-04-25 12:21 ` George Dunlap
2013-04-25 12:45 ` Ian Campbell
2013-04-25 12:47 ` Ian Campbell
2013-04-25 13:42 ` Pasi Kärkkäinen
2013-04-25 13:48 ` George Dunlap
2013-04-24 11:56 ` [PATCH v6 1/2] libxl: Introduce functions to add and remove USB devices to an HVM guest Ian Campbell
2013-04-24 12:48 ` George Dunlap
2013-04-24 12:53 ` Ian Campbell
2013-04-24 13:37 ` George Dunlap
2013-04-24 13:53 ` Ian Campbell
2013-04-24 12:38 ` Ian Campbell
2013-04-24 13:32 ` George Dunlap [this message]
2013-04-24 13:47 ` Ian Campbell
2013-04-24 13:51 ` Ian Campbell
2013-04-24 15:45 ` George Dunlap
2013-04-24 15:51 ` Ian Campbell
2013-04-24 17:49 ` Pasi Kärkkäinen
2013-04-25 7:44 ` Ian Campbell
2013-04-25 7:54 ` Pasi Kärkkäinen
2013-04-25 9:56 ` George Dunlap
2013-04-25 10:17 ` Pasi Kärkkäinen
2013-04-25 14:19 ` Anthony PERARD
2013-04-25 14:31 ` George Dunlap
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5177DF05.50907@eu.citrix.com \
--to=george.dunlap@eu.citrix.com \
--cc=Ian.Campbell@citrix.com \
--cc=Ian.Jackson@eu.citrix.com \
--cc=anthony.perard@citrix.com \
--cc=roger.pau@citrix.com \
--cc=sstanisi@cbnco.com \
--cc=xen-devel@lists.xen.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).