[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 3 of 4] libxl: Introduce pci_assignable_add and pci_assignable_remove
On Wed, 2012-05-09 at 11:28 +0100, George Dunlap wrote: > Introduce libxl helper functions to prepare devices to be passed through to > guests. This is meant to replace of all the manual sysfs commands which > are currently required. > > pci_assignable_add accepts a BDF for a device and will: > * Unbind a device from its current driver, if any > * If "rebind" is set, it will store the path of the driver from which we > unplugged it in /libxl/pciback/$BDF/driver_path Since you don't know whether the user is going to pass -r to assignable_remove why not always store this? > * If necessary, create a slot for it in pciback I must confess I'm a bit fuzzy on the relationship between slots and bindings, where does the "if necessary" come into it? I was wondering while reading the patch if unconditionally adding a removing the slot might simplify a bunch of stuff, but I suspect I just don't appreciate some particular aspect of how pciback works... > * Bind the device to pciback > > At this point it will show up in pci_assignable_list, and is ready to be > passed > through to a guest. > > pci_assignable_remove accepts a BDF for a device and will: > * Unbind the device from pciback > * Remove the slot from pciback > * If "rebind" is set, and /libx/pciback/$BDF/driver_path exists, it will > attempt to rebind the device to its original driver. > > NB that "$BDF" in this case uses dashes instead of : and ., because : and . > are > illegal characters in xenstore paths. > > Signed-off-by: George Dunlap <george.dunlap@xxxxxxxxxxxxx> > > diff -r 5b5070d487d9 -r 17a5b562d1e7 tools/libxl/libxl.h > --- a/tools/libxl/libxl.h Wed May 09 11:21:19 2012 +0100 > +++ b/tools/libxl/libxl.h Wed May 09 11:21:28 2012 +0100 > @@ -658,10 +658,25 @@ int libxl_device_pci_destroy(libxl_ctx * > libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid, int > *num); > > /* > - * Similar to libxl_device_pci_list but returns all devices which > - * could be assigned to a domain (i.e. are bound to the backend > - * driver) but are not currently. > + * Functions related to making devices assignable -- that is, bound to > + * the pciback driver, ready to be given to a guest via > + * libxl_pci_device_add. > + * > + * - ..._add() will unbind the device from its current driver (if > + * already bound) and re-bind it to pciback; at that point it will be > + * ready to be assigned to a VM. If rebind is set, it will store the > + * path to the old driver in xenstore so that it can be handed back to > + * dom0 on restore. > + * > + * - ..._remove() will unbind the device from pciback, and if > + * rebind is non-zero, attempt to assign it back to the driver > + * from whence it came. > + * > + * - ..._list() will return a list of the PCI devices available to be > + * assigned. > */ > +int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci > *pcidev, int rebind); > +int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci > *pcidev, int rebind); > libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num); > > /* CPUID handling */ > diff -r 5b5070d487d9 -r 17a5b562d1e7 tools/libxl/libxl_pci.c > --- a/tools/libxl/libxl_pci.c Wed May 09 11:21:19 2012 +0100 > +++ b/tools/libxl/libxl_pci.c Wed May 09 11:21:28 2012 +0100 > @@ -21,6 +21,7 @@ > #define PCI_BDF "%04x:%02x:%02x.%01x" > #define PCI_BDF_SHORT "%02x:%02x.%01x" > #define PCI_BDF_VDEVFN "%04x:%02x:%02x.%01x@%02x" > +#define PCI_BDF_XSPATH "%04x-%02x-%02x-%01x" > > static unsigned int pcidev_encode_bdf(libxl_device_pci *pcidev) > { > @@ -408,6 +409,347 @@ out: > return pcidevs; > } > > +/* Unbind device from its current driver, if any. If driver_path is > non-NULL, > + * store the path to the original driver in it. */ > +static int sysfs_dev_unbind(libxl__gc *gc, libxl_device_pci *pcidev, char > **driver_path) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + char * spath, *_dp, **dp; > + struct stat st; > + > + if ( driver_path ) > + dp = driver_path; > + else > + dp = &_dp; > + > + spath = libxl__sprintf(gc, SYSFS_PCI_DEV"/"PCI_BDF"/driver", > + pcidev->domain, > + pcidev->bus, > + pcidev->dev, > + pcidev->func); > + if ( !lstat(spath, &st) ) { > + /* Find the canonical path to the driver. */ > + *dp = libxl__zalloc(gc, PATH_MAX); Should we be actually using fpathconf / sysconf here? > + *dp = realpath(spath, *dp); > + if ( !*dp ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "realpath() failed"); Since errno is meaningful you want LIBXL__LOGERRNO here. Or the short form LOGE(). Where you have pointer output params (like driver_path here) in general I think it is preferable to do everything with a local (single indirection) pointer and only update the output parameter on success. This means you avoid leaking/exposing a partial result on error but also means you can drop a lot of "*" from around the function. Maybe the gc makes this moot, but the "if ( driver_path )" stuff at the top of the fn took me several seconds to work out also ;-). > + return -1; > + } > + > + LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Driver re-plug path: %s", > + *dp); > + > + /* Unbind from the old driver */ > + spath = libxl__sprintf(gc, "%s/unbind", *dp); > + if ( sysfs_write_bdf(gc, spath, pcidev) < 0 ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Couldn't unbind device"); Not sure what errno is like here, worth printing it. Looking back at patch #1 I suspect sysfs_write_bdf should preserve errno over the call to close, so that suitable reporting can happen in the caller. > + return -1; > + } > + } > + return 0; > +} > + > +/* Scan through /sys/.../pciback/slots looking for pcidev's BDF */ > +static int pciback_dev_has_slot(libxl__gc *gc, libxl_device_pci *pcidev) Is the concept of "having a slot" distinct from being bound to pciback? > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + FILE *f; > + int rc = 0; > + unsigned bus, dev, func; > + > + f = fopen(SYSFS_PCIBACK_DRIVER"/slots", "r"); > + > + if (f == NULL) { > + LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Couldn't open %s", > + SYSFS_PCIBACK_DRIVER"/slots"); > + return ERROR_FAIL; > + } > + > + while(fscanf(f, "0000:%x:%x.%x\n", &bus, &dev, &func)==3) { Jan recently added support for PCI domains, does that have any impact on the hardcoded 0000 here? I suppose that would require PCI domains support in the dom0 kernel -- but that seems likely to already be there (or be imminent) I think the 0000 should be %x into domain compared with pcidev->domain. > + if(bus == pcidev->bus > + && dev == pcidev->dev > + && func == pcidev->func) { > + rc = 1; > + goto out; > + } > + } > +out: > + fclose(f); > + return rc; > +} > + > +static int pciback_dev_is_assigned(libxl__gc *gc, libxl_device_pci *pcidev) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + char * spath; > + int rc; > + struct stat st; > + > + spath = libxl__sprintf(gc, SYSFS_PCIBACK_DRIVER"/"PCI_BDF, > + pcidev->domain, pcidev->bus, > + pcidev->dev, pcidev->func); > + rc = lstat(spath, &st); > + > + if( rc == 0 ) > + return 1; > + if ( rc < 0 && errno == ENOENT ) > + return 0; > + LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Accessing %s", spath); > + return -1; > +} > + > +static int pciback_dev_assign(libxl__gc *gc, libxl_device_pci *pcidev) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + int rc; > + > + if ( (rc=pciback_dev_has_slot(gc, pcidev)) < 0 ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, > + "Error checking for pciback slot"); Log errno? Also pciback_dev_has_slot itself always logs on error, you probably don't need both. > + return ERROR_FAIL; > + } else if (rc == 0) { > + if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/new_slot", > + pcidev) < 0 ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, > + "Couldn't bind device to pciback!"); ERRNO here too. > + return ERROR_FAIL; > + } > + } > + > + if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/bind", pcidev) < 0 ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Couldn't bind device to > pciback!"); ERRNO here too. Or since there are loads of these perhaps sysfs_write_bf should log on failure, either just the fact of the failed write to a path (which implies the underlying failure was to bind/unbind) or you could add a "const char *what" param to use in logging. > + return ERROR_FAIL; > + } > + return 0; > +} > + > +static int pciback_dev_unassign(libxl__gc *gc, libxl_device_pci *pcidev) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + > + /* Remove from pciback */ > + if ( sysfs_dev_unbind(gc, pcidev, NULL) < 0 ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Couldn't unbind device!"); > + return ERROR_FAIL; > + } > + > + /* Remove slot if necessary */ > + if ( pciback_dev_has_slot(gc, pcidev) > 0 ) { > + if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/remove_slot", > + pcidev) < 0 ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, > + "Couldn't remove pciback slot"); ERRNO > + return ERROR_FAIL; > + } > + } > + return 0; > +} > + > +/* FIXME */ Leftover? > +#define PCIBACK_INFO_PATH "/libxl/pciback" > + > +static void pci_assignable_driver_path_write(libxl__gc *gc, > + libxl_device_pci *pcidev, > + char *driver_path) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + char *path; > + xs_transaction_t t = 0; > + struct xs_permissions perm[1]; > + > + perm[0].id = 0; > + perm[0].perms = XS_PERM_NONE; > + > +retry: > + t = xs_transaction_start(ctx->xsh); > + > + path = libxl__sprintf(gc, > PCIBACK_INFO_PATH"/"PCI_BDF_XSPATH"/driver_path", > + pcidev->domain, > + pcidev->bus, > + pcidev->dev, > + pcidev->func); > + xs_rm(ctx->xsh, t, path); Why do you need to rm first? Won't the write just replace whatever it is (and that means the need for a transaction goes away too) In any case you should create path outside the retry loop instead of needlessly recreating it each time around. > + if ( libxl__xs_write(gc, XBT_NULL, path, "%s", driver_path) < 0 ) { > + LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, > + "Write of %s to node %s failed", > + driver_path, path); > + } > + > + if (!xs_transaction_end(ctx->xsh, t, 0)) { > + if (errno == EAGAIN) { > + t = 0; > + goto retry; > + } > + LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, > + "xenstore transaction commit failed; device " > + " will not be rebound to original driver."); > + } > +} > + > +static char * pci_assignable_driver_path_read(libxl__gc *gc, > + libxl_device_pci *pcidev) > +{ > + return libxl__xs_read(gc, XBT_NULL, > + libxl__sprintf(gc, > + PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH > "/driver_path", > + pcidev->domain, > + pcidev->bus, > + pcidev->dev, > + pcidev->func)); > +} > + > +static void pci_assignable_driver_path_remove(libxl__gc *gc, > + libxl_device_pci *pcidev) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + char * path; > + xs_transaction_t t; > + > + /* Remove the xenstore entry */ > +retry: > + t = xs_transaction_start(ctx->xsh); > + path = libxl__sprintf(gc, PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH, > + pcidev->domain, > + pcidev->bus, > + pcidev->dev, > + pcidev->func); > + xs_rm(ctx->xsh, t, path); You don't need a transaction for a single operation. (and if you did then "path = ..." could have been hoisted out) > + if (!xs_transaction_end(ctx->xsh, t, 0)) { > + if (errno == EAGAIN) > + goto retry; > + else > + LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, > + "Failed to remove xenstore entry"); > + } > +} > + > +static int libxl__device_pci_assignable_add(libxl__gc *gc, > + libxl_device_pci *pcidev, > + int rebind) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + unsigned dom, bus, dev, func; > + char *spath, *driver_path = NULL; > + struct stat st; > + > + /* Local copy for convenience */ > + dom = pcidev->domain; > + bus = pcidev->bus; > + dev = pcidev->dev; > + func = pcidev->func; > + > + /* See if the device exists */ > + spath = libxl__sprintf(gc, SYSFS_PCI_DEV"/"PCI_BDF, dom, bus, dev, func); > + if ( lstat(spath, &st) ) { > + LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Couldn't lstat %s", spath); > + return ERROR_FAIL; > + } > + > + /* Check to see if it's already assigned to pciback */ > + if ( pciback_dev_is_assigned(gc, pcidev) ) { > + LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, PCI_BDF" already assigned to > pciback", > + dom, bus, dev, func); > + return 0; > + } > + > + /* Check to see if there's already a driver that we need to unbind from > */ > + if ( sysfs_dev_unbind(gc, pcidev, &driver_path ) ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Couldn't unbind "PCI_BDF" from > driver", > + dom, bus, dev, func); > + return ERROR_FAIL; > + } > + > + /* Store driver_path for rebinding to dom0 */ > + if ( rebind ) { > + if ( driver_path ) { > + pci_assignable_driver_path_write(gc, pcidev, driver_path); > + } else { > + LIBXL__LOG(ctx, LIBXL__LOG_WARNING, > + PCI_BDF" not bound to a driver, will not be rebound.", > + dom, bus, dev, func); > + } > + } > + > + if ( pciback_dev_assign(gc, pcidev) ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Couldn't bind device to > pciback!"); > + return ERROR_FAIL; > + } > + > + return 0; > +} > + > +static int libxl__device_pci_assignable_remove(libxl__gc *gc, > + libxl_device_pci *pcidev, > + int rebind) > +{ > + libxl_ctx *ctx = libxl__gc_owner(gc); > + int rc; > + char *driver_path; > + > + /* Unbind from pciback */ > + if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Checking if pciback was > assigned"); > + return ERROR_FAIL; > + } else if ( rc ) { > + pciback_dev_unassign(gc, pcidev); > + } else { > + LIBXL__LOG(ctx, LIBXL__LOG_WARNING, > + "Not bound to pciback"); > + } > + > + /* Rebind if necessary */ > + driver_path = pci_assignable_driver_path_read(gc, pcidev); > + > + if ( driver_path ) { > + if ( rebind ) { > + LIBXL__LOG(ctx, LIBXL__LOG_INFO, "Rebinding to driver at %s", > + driver_path); > + > + if ( sysfs_write_bdf(gc, > + libxl__sprintf(gc, "%s/bind", driver_path), > + pcidev) < 0 ) { > + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, > + "Couldn't bind device to %s", driver_path); > + return -1; > + } > + } > + > + pci_assignable_driver_path_remove(gc, pcidev); > + } else { > + if ( rebind ) { > + LIBXL__LOG(ctx, LIBXL__LOG_WARNING, > + "Couldn't find path for original driver; not > rebinding"); > + } > + } > + > + return 0; > +} > + > +int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev, > + int rebind) > +{ > + GC_INIT(ctx); > + int rc; > + > + rc = libxl__device_pci_assignable_add(gc, pcidev, rebind); > + > + GC_FREE; > + return rc; > +} Are there internal callers of libxl__device_pci_assignable_add/remove? If not then there's no reason to split into internal/external functions. Doesn't matter much I guess. > + > + > +int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci > *pcidev, > + int rebind) > +{ > + GC_INIT(ctx); > + int rc; > + > + rc = libxl__device_pci_assignable_remove(gc, pcidev, rebind); > + > + GC_FREE; > + return rc; > +} > + > /* > * This function checks that all functions of a device are bound to pciback > * driver. It also initialises a bit-mask of which function numbers are > present > > _______________________________________________ > Xen-devel mailing list > Xen-devel@xxxxxxxxxxxxx > http://lists.xen.org/xen-devel _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |