[libvirt] [PATCH 7/8] Simplify PCI hostdev prepare/re-attach using a pciDeviceList type
Daniel P. Berrange
berrange at redhat.com
Mon Aug 17 19:11:38 UTC 2009
On Mon, Aug 17, 2009 at 03:10:20PM +0100, Mark McLoughlin wrote:
> The qemuPrepareHostDevices() and qemuDomainReAttachHostDevices()
> functions are clutter with a bunch of calls to pciGetDevice() and
> pciFreeDevice() obscuring the basic logic.
>
> Add a pciDeviceList type and add a qemuGetPciHostDeviceList() function
> to build a list from a domain definition. Use this in prepare/re-attach
> fto simplify things and eliminate the multiple pciGetDevice calls.
>
> This is especially useful because in the next patch we need to iterate
> the hostdevs list a third time and we also need a list type for keeping
> track of active devices.
>
> * src/pci.[ch]: add pciDeviceList type and also a per-device 'managed'
> property
>
> * src/libvirt_private.syms: export the new functions
>
> * src/qemu_driver.c: add qemuGetPciHostDeviceList() and re-write
> qemuPrepareHostDevices() and qemuDomainReAttachHostDevices() to use it
> ---
> src/libvirt_private.syms | 7 ++-
> src/pci.c | 109 ++++++++++++++++++++++++++++++
> src/pci.h | 20 ++++++
> src/qemu_driver.c | 167 +++++++++++++++++++--------------------------
> 4 files changed, 206 insertions(+), 97 deletions(-)
Yeah that's nicer readability in the QEMU driver.
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index 642c2bc..2bf4e15 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -285,7 +285,12 @@ pciFreeDevice;
> pciDettachDevice;
> pciReAttachDevice;
> pciResetDevice;
> -
> +pciDeviceSetManaged;
> +pciDeviceGetManaged;
> +pciDeviceListNew;
> +pciDeviceListFree;
> +pciDeviceListAdd;
> +pciDeviceListDel;
>
> # qparams.h
> qparam_get_query;
> diff --git a/src/pci.c b/src/pci.c
> index 74f7ef0..a37eaf7 100644
> --- a/src/pci.c
> +++ b/src/pci.c
> @@ -63,6 +63,7 @@ struct _pciDevice {
> unsigned pci_pm_cap_pos;
> unsigned has_flr : 1;
> unsigned has_pm_reset : 1;
> + unsigned managed : 1;
> };
>
> /* For virReportOOMError() and virReportSystemError() */
> @@ -890,8 +891,116 @@ pciGetDevice(virConnectPtr conn,
> void
> pciFreeDevice(virConnectPtr conn ATTRIBUTE_UNUSED, pciDevice *dev)
> {
> + if (!dev)
> + return;
> VIR_DEBUG("%s %s: freeing", dev->id, dev->name);
> if (dev->fd >= 0)
> close(dev->fd);
> VIR_FREE(dev);
> }
> +
> +void pciDeviceSetManaged(pciDevice *dev, unsigned managed)
> +{
> + dev->managed = !!managed;
> +}
> +
> +unsigned pciDeviceGetManaged(pciDevice *dev)
> +{
> + return dev->managed;
> +}
> +
> +pciDeviceList *
> +pciDeviceListNew(virConnectPtr conn)
> +{
> + pciDeviceList *list;
> +
> + if (VIR_ALLOC(list) < 0) {
> + virReportOOMError(conn);
> + return NULL;
> + }
> +
> + return list;
> +}
> +
> +void
> +pciDeviceListFree(virConnectPtr conn,
> + pciDeviceList *list)
> +{
> + int i;
> +
> + if (!list)
> + return;
> +
> + for (i = 0; i < list->count; i++) {
> + pciFreeDevice(conn, list->devs[i]);
> + list->devs[i] = NULL;
> + }
> +
> + list->count = 0;
> + VIR_FREE(list->devs);
> + VIR_FREE(list);
> +}
> +
> +int
> +pciDeviceListAdd(virConnectPtr conn,
> + pciDeviceList *list,
> + pciDevice *dev)
> +{
> + if (pciDeviceListFind(list, dev)) {
> + pciReportError(conn, VIR_ERR_INTERNAL_ERROR,
> + _("Device %s is already in use"), dev->name);
> + return -1;
> + }
> +
> + if (VIR_REALLOC_N(list->devs, list->count+1) < 0) {
> + virReportOOMError(conn);
> + return -1;
> + }
> +
> + list->devs[list->count++] = dev;
> +
> + return 0;
> +}
> +
> +void
> +pciDeviceListDel(virConnectPtr conn ATTRIBUTE_UNUSED,
> + pciDeviceList *list,
> + pciDevice *dev)
> +{
> + int i;
> +
> + for (i = 0; i < list->count; i++) {
> + if (list->devs[i]->domain != dev->domain ||
> + list->devs[i]->bus != dev->bus ||
> + list->devs[i]->slot != dev->slot ||
> + list->devs[i]->function != dev->function)
> + continue;
> +
> + pciFreeDevice(conn, list->devs[i]);
> +
> + if (i != --list->count)
> + memmove(&list->devs[i],
> + &list->devs[i+1],
> + sizeof(*list->devs) * (list->count-i));
> +
> + if (VIR_REALLOC_N(list->devs, list->count) < 0) {
> + ; /* not fatal */
> + }
> +
> + break;
> + }
> +}
> +
> +pciDevice *
> +pciDeviceListFind(pciDeviceList *list, pciDevice *dev)
> +{
> + int i;
> +
> + for (i = 0; i < list->count; i++)
> + if (list->devs[i]->domain == dev->domain &&
> + list->devs[i]->bus == dev->bus &&
> + list->devs[i]->slot == dev->slot &&
> + list->devs[i]->function == dev->function)
> + return list->devs[i];
> + return NULL;
> +}
> diff --git a/src/pci.h b/src/pci.h
> index 47882ef..75fbd51 100644
> --- a/src/pci.h
> +++ b/src/pci.h
> @@ -27,6 +27,11 @@
>
> typedef struct _pciDevice pciDevice;
>
> +typedef struct {
> + unsigned count;
> + pciDevice **devs;
> +} pciDeviceList;
> +
> pciDevice *pciGetDevice (virConnectPtr conn,
> unsigned domain,
> unsigned bus,
> @@ -40,5 +45,20 @@ int pciReAttachDevice (virConnectPtr conn,
> pciDevice *dev);
> int pciResetDevice (virConnectPtr conn,
> pciDevice *dev);
> +void pciDeviceSetManaged(pciDevice *dev,
> + unsigned managed);
> +unsigned pciDeviceGetManaged(pciDevice *dev);
> +
> +pciDeviceList *pciDeviceListNew (virConnectPtr conn);
> +void pciDeviceListFree (virConnectPtr conn,
> + pciDeviceList *list);
> +int pciDeviceListAdd (virConnectPtr conn,
> + pciDeviceList *list,
> + pciDevice *dev);
> +void pciDeviceListDel (virConnectPtr conn,
> + pciDeviceList *list,
> + pciDevice *dev);
> +pciDevice * pciDeviceListFind (pciDeviceList *list,
> + pciDevice *dev);
>
> #endif /* __VIR_PCI_H__ */
> diff --git a/src/qemu_driver.c b/src/qemu_driver.c
> index a9da387..f2b807b 100644
> --- a/src/qemu_driver.c
> +++ b/src/qemu_driver.c
> @@ -1329,48 +1329,16 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
> return -1;
> }
>
> -static int qemuPrepareHostDevices(virConnectPtr conn,
> - virDomainDefPtr def) {
> +static pciDeviceList *
> +qemuGetPciHostDeviceList(virConnectPtr conn,
> + virDomainDefPtr def)
> +{
> + pciDeviceList *list;
> int i;
>
> - /* We have to use 2 loops here. *All* devices must
> - * be detached before we reset any of them, because
> - * in some cases you have to reset the whole PCI,
> - * which impacts all devices on it
> - */
> -
> - for (i = 0 ; i < def->nhostdevs ; i++) {
> - virDomainHostdevDefPtr hostdev = def->hostdevs[i];
> -
> - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> - continue;
> - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> - continue;
> -
> - if (hostdev->managed) {
> - pciDevice *dev = pciGetDevice(conn,
> - hostdev->source.subsys.u.pci.domain,
> - hostdev->source.subsys.u.pci.bus,
> - hostdev->source.subsys.u.pci.slot,
> - hostdev->source.subsys.u.pci.function);
> - if (!dev)
> - goto error;
> -
> - if (pciDettachDevice(conn, dev) < 0) {
> - pciFreeDevice(conn, dev);
> - goto error;
> - }
> -
> - pciFreeDevice(conn, dev);
> - } /* else {
> - XXX validate that non-managed device isn't in use, eg
> - by checking that device is either un-bound, or bound
> - to pci-stub.ko
> - } */
> - }
> + if (!(list = pciDeviceListNew(conn)))
> + return NULL;
>
> - /* Now that all the PCI hostdevs have be dettached, we can safely
> - * reset them */
> for (i = 0 ; i < def->nhostdevs ; i++) {
> virDomainHostdevDefPtr hostdev = def->hostdevs[i];
> pciDevice *dev;
> @@ -1385,95 +1353,102 @@ static int qemuPrepareHostDevices(virConnectPtr conn,
> hostdev->source.subsys.u.pci.bus,
> hostdev->source.subsys.u.pci.slot,
> hostdev->source.subsys.u.pci.function);
> - if (!dev)
> - goto error;
> + if (!dev) {
> + pciDeviceListFree(conn, list);
> + return NULL;
> + }
>
> - if (pciResetDevice(conn, dev) < 0) {
> + if (pciDeviceListAdd(conn, list, dev) < 0) {
> pciFreeDevice(conn, dev);
> - goto error;
> + pciDeviceListFree(conn, list);
> + return NULL;
> }
>
> - pciFreeDevice(conn, dev);
> + pciDeviceSetManaged(dev, hostdev->managed);
> }
>
> + return list;
> +}
> +
> +static int
> +qemuPrepareHostDevices(virConnectPtr conn, virDomainDefPtr def)
> +{
> + pciDeviceList *pcidevs;
> + int i;
> +
> + if (!def->nhostdevs)
> + return 0;
> +
> + if (!(pcidevs = qemuGetPciHostDeviceList(conn, def)))
> + return -1;
> +
> + /* We have to use 2 loops here. *All* devices must
> + * be detached before we reset any of them, because
> + * in some cases you have to reset the whole PCI,
> + * which impacts all devices on it
> + */
> +
> + /* XXX validate that non-managed device isn't in use, eg
> + * by checking that device is either un-bound, or bound
> + * to pci-stub.ko
> + */
> +
> + for (i = 0; i < pcidevs->count; i++)
> + if (pciDeviceGetManaged(pcidevs->devs[i]) &&
> + pciDettachDevice(conn, pcidevs->devs[i]) < 0)
> + goto error;
> +
> + /* Now that all the PCI hostdevs have be dettached, we can safely
> + * reset them */
> + for (i = 0; i < pcidevs->count; i++)
> + if (pciResetDevice(conn, pcidevs->devs[i]) < 0)
> + goto error;
> +
> + pciDeviceListFree(conn, pcidevs);
> return 0;
>
> error:
> + pciDeviceListFree(conn, pcidevs);
> return -1;
> }
>
> static void
> qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def)
> {
> + pciDeviceList *pcidevs;
> int i;
>
> - /* Again 2 loops; reset all the devices before re-attach */
> -
> - for (i = 0 ; i < def->nhostdevs ; i++) {
> - virDomainHostdevDefPtr hostdev = def->hostdevs[i];
> - pciDevice *dev;
> -
> - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> - continue;
> - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> - continue;
> -
> - dev = pciGetDevice(conn,
> - hostdev->source.subsys.u.pci.domain,
> - hostdev->source.subsys.u.pci.bus,
> - hostdev->source.subsys.u.pci.slot,
> - hostdev->source.subsys.u.pci.function);
> - if (!dev) {
> - virErrorPtr err = virGetLastError();
> - VIR_ERROR(_("Failed to allocate pciDevice: %s\n"),
> - err ? err->message : "");
> - virResetError(err);
> - continue;
> - }
> -
> - if (pciResetDevice(conn, dev) < 0) {
> - virErrorPtr err = virGetLastError();
> - VIR_ERROR(_("Failed to reset PCI device: %s\n"),
> - err ? err->message : "");
> - virResetError(err);
> - }
> + if (!def->nhostdevs)
> + return;
>
> - pciFreeDevice(conn, dev);
> + if (!(pcidevs = qemuGetPciHostDeviceList(conn, def))) {
> + virErrorPtr err = virGetLastError();
> + VIR_ERROR(_("Failed to allocate pciDeviceList: %s\n"),
> + err ? err->message : "");
> + virResetError(err);
> + return;
> }
>
> - for (i = 0 ; i < def->nhostdevs ; i++) {
> - virDomainHostdevDefPtr hostdev = def->hostdevs[i];
> - pciDevice *dev;
> -
> - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> - continue;
> - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> - continue;
> - if (!hostdev->managed)
> - continue;
> + /* Again 2 loops; reset all the devices before re-attach */
>
> - dev = pciGetDevice(conn,
> - hostdev->source.subsys.u.pci.domain,
> - hostdev->source.subsys.u.pci.bus,
> - hostdev->source.subsys.u.pci.slot,
> - hostdev->source.subsys.u.pci.function);
> - if (!dev) {
> + for (i = 0; i < pcidevs->count; i++)
> + if (pciResetDevice(conn, pcidevs->devs[i]) < 0) {
> virErrorPtr err = virGetLastError();
> - VIR_ERROR(_("Failed to allocate pciDevice: %s\n"),
> + VIR_ERROR(_("Failed to reset PCI device: %s\n"),
> err ? err->message : "");
> virResetError(err);
> - continue;
> }
>
> - if (pciReAttachDevice(conn, dev) < 0) {
> + for (i = 0; i < pcidevs->count; i++)
> + if (pciDeviceGetManaged(pcidevs->devs[i]) &&
> + pciReAttachDevice(conn, pcidevs->devs[i]) < 0) {
> virErrorPtr err = virGetLastError();
> VIR_ERROR(_("Failed to re-attach PCI device: %s\n"),
> err ? err->message : "");
> virResetError(err);
> }
>
> - pciFreeDevice(conn, dev);
> - }
> + pciDeviceListFree(conn, pcidevs);
> }
>
> static const char *const defaultDeviceACL[] = {
> --
ACK
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
More information about the libvir-list
mailing list