diff --git a/po/POTFILES.in b/po/POTFILES.in index 562c4028f692b0ad1d51885d6297ec5cefb42f28..12f427a16e6566add7ea7df9b5d6b355ffd02d0c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -57,6 +57,7 @@ src/qemu/qemu_command.c src/qemu/qemu_conf.c src/qemu/qemu_domain.c src/qemu/qemu_driver.c +src/qemu/qemu_hostdev.c src/qemu/qemu_monitor.c src/qemu/qemu_monitor_json.c src/qemu/qemu_monitor_text.c diff --git a/src/Makefile.am b/src/Makefile.am index da3bd6f919c441da2e234938bb65c36c3835b813..d30774b631af9eee59ef2ddcaff8813a43662ba7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -272,6 +272,7 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_domain.c qemu/qemu_domain.h \ qemu/qemu_audit.c qemu/qemu_audit.h \ qemu/qemu_cgroup.c qemu/qemu_cgroup.c \ + qemu/qemu_hostdev.c qemu/qemu_hostdev.h \ qemu/qemu_conf.c qemu/qemu_conf.h \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 585b25963a51434f7ec43cd1d59c1cad0957d199..5d8aef3fcc7e46395bcd5be993cf132577c2cb01 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -56,6 +56,7 @@ #include "qemu_capabilities.h" #include "qemu_command.h" #include "qemu_cgroup.h" +#include "qemu_hostdev.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" #include "qemu_audit.h" @@ -144,9 +145,6 @@ static int qemudDomainGetMaxVcpus(virDomainPtr dom); static int qemuDetectVcpuPIDs(struct qemud_driver *driver, virDomainObjPtr vm); -static int qemuUpdateActivePciHostdevs(struct qemud_driver *driver, - virDomainDefPtr def); - static int qemudVMFiltersInstantiate(virConnectPtr conn, virDomainDefPtr def); @@ -2758,192 +2756,6 @@ cleanup: } -static pciDeviceList * -qemuGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) -{ - pciDeviceList *list; - int i; - - if (!(list = pciDeviceListNew())) - return NULL; - - for (i = 0 ; i < nhostdevs ; i++) { - virDomainHostdevDefPtr hostdev = 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(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) { - pciDeviceListFree(list); - return NULL; - } - - if (pciDeviceListAdd(list, dev) < 0) { - pciFreeDevice(dev); - pciDeviceListFree(list); - return NULL; - } - - pciDeviceSetManaged(dev, hostdev->managed); - } - - return list; -} - -static int -qemuUpdateActivePciHostdevs(struct qemud_driver *driver, - virDomainDefPtr def) -{ - pciDeviceList *pcidevs; - int i; - int ret = -1; - - if (!def->nhostdevs) - return 0; - - if (!(pcidevs = qemuGetPciHostDeviceList(def->hostdevs, def->nhostdevs))) - return -1; - - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - pciDeviceListSteal(pcidevs, dev); - if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) { - pciFreeDevice(dev); - goto cleanup; - } - } - - ret = 0; - -cleanup: - pciDeviceListFree(pcidevs); - return ret; -} - - -static int -qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - pciDeviceList *pcidevs; - int i; - int ret = -1; - - if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) - return -1; - - /* We have to use 3 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. Also, all devices - * must be reset before being marked as active. - */ - - /* 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 < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - if (!pciDeviceIsAssignable(dev, !driver->relaxedACS)) - goto cleanup; - - if (pciDeviceGetManaged(dev) && - pciDettachDevice(dev, driver->activePciHostdevs) < 0) - goto cleanup; - } - - /* Now that all the PCI hostdevs have be dettached, we can safely - * reset them */ - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) - goto cleanup; - } - - /* Now mark all the devices as active */ - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - pciDeviceListSteal(pcidevs, dev); - if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) { - pciFreeDevice(dev); - goto cleanup; - } - } - - ret = 0; - -cleanup: - pciDeviceListFree(pcidevs); - return ret; -} - -static int -qemuPrepareHostPCIDevices(struct qemud_driver *driver, - virDomainDefPtr def) -{ - return qemuPrepareHostdevPCIDevices(driver, def->hostdevs, def->nhostdevs); -} - - -static int -qemuPrepareHostUSBDevices(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainDefPtr def) -{ - int i; - 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_USB) - continue; - - /* Resolve a vendor/product to bus/device */ - if (hostdev->source.subsys.u.usb.vendor) { - usbDevice *usb - = usbFindDevice(hostdev->source.subsys.u.usb.vendor, - hostdev->source.subsys.u.usb.product); - - if (!usb) - return -1; - - hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); - hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); - - usbFreeDevice(usb); - } - } - - return 0; -} - -static int -qemuPrepareHostDevices(struct qemud_driver *driver, - virDomainDefPtr def) -{ - if (!def->nhostdevs) - return 0; - - if (qemuPrepareHostPCIDevices(driver, def) < 0) - return -1; - - if (qemuPrepareHostUSBDevices(driver, def) < 0) - return -1; - - return 0; -} - - static int qemuPrepareChardevDevice(virDomainDefPtr def ATTRIBUTE_UNUSED, virDomainChrDefPtr dev, @@ -2966,80 +2778,6 @@ qemuPrepareChardevDevice(virDomainDefPtr def ATTRIBUTE_UNUSED, } -static void -qemudReattachPciDevice(pciDevice *dev, struct qemud_driver *driver) -{ - int retries = 100; - - while (pciWaitForDeviceCleanup(dev, "kvm_assigned_device") - && retries) { - usleep(100*1000); - retries--; - } - - if (pciDeviceGetManaged(dev)) { - if (pciReAttachDevice(dev, driver->activePciHostdevs) < 0) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to re-attach PCI device: %s"), - err ? err->message : _("unknown error")); - virResetError(err); - } - } -} - -static void -qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - pciDeviceList *pcidevs; - int i; - - if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to allocate pciDeviceList: %s"), - err ? err->message : _("unknown error")); - virResetError(err); - return; - } - - /* Again 3 loops; mark all devices as inactive before reset - * them and reset all the devices before re-attach */ - - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - pciDeviceListDel(driver->activePciHostdevs, dev); - } - - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to reset PCI device: %s"), - err ? err->message : _("unknown error")); - virResetError(err); - } - } - - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - qemudReattachPciDevice(dev, driver); - } - - pciDeviceListFree(pcidevs); -} - -static void -qemuDomainReAttachHostDevices(struct qemud_driver *driver, - virDomainDefPtr def) -{ - if (!def->nhostdevs) - return; - - qemuDomainReAttachHostdevDevices(driver, def->hostdevs, def->nhostdevs); -} - - struct qemudHookData { virConnectPtr conn; virDomainObjPtr vm; @@ -8767,7 +8505,7 @@ static int qemudDomainDetachHostPciDevice(struct qemud_driver *driver, pciDeviceListDel(driver->activePciHostdevs, pci); if (pciResetDevice(pci, driver->activePciHostdevs, NULL) < 0) ret = -1; - qemudReattachPciDevice(pci, driver); + qemuReattachPciDevice(pci, driver); pciFreeDevice(pci); } diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c new file mode 100644 index 0000000000000000000000000000000000000000..f4b210856094b290917bdc988ae375c70b4b9ef3 --- /dev/null +++ b/src/qemu/qemu_hostdev.c @@ -0,0 +1,289 @@ +/* + * qemu_hostdev.c: QEMU hostdev management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange + */ + +#include + +#include "qemu_hostdev.h" +#include "logging.h" +#include "virterror_internal.h" +#include "memory.h" +#include "pci.h" +#include "hostusb.h" + +static pciDeviceList * +qemuGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) +{ + pciDeviceList *list; + int i; + + if (!(list = pciDeviceListNew())) + return NULL; + + for (i = 0 ; i < nhostdevs ; i++) { + virDomainHostdevDefPtr hostdev = 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(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) { + pciDeviceListFree(list); + return NULL; + } + + if (pciDeviceListAdd(list, dev) < 0) { + pciFreeDevice(dev); + pciDeviceListFree(list); + return NULL; + } + + pciDeviceSetManaged(dev, hostdev->managed); + } + + return list; +} + + +int qemuUpdateActivePciHostdevs(struct qemud_driver *driver, + virDomainDefPtr def) +{ + pciDeviceList *pcidevs; + int i; + int ret = -1; + + if (!def->nhostdevs) + return 0; + + if (!(pcidevs = qemuGetPciHostDeviceList(def->hostdevs, def->nhostdevs))) + return -1; + + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + pciDeviceListSteal(pcidevs, dev); + if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) { + pciFreeDevice(dev); + goto cleanup; + } + } + + ret = 0; + +cleanup: + pciDeviceListFree(pcidevs); + return ret; +} + + + +int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + pciDeviceList *pcidevs; + int i; + int ret = -1; + + if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) + return -1; + + /* We have to use 3 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. Also, all devices + * must be reset before being marked as active. + */ + + /* 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 < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (!pciDeviceIsAssignable(dev, !driver->relaxedACS)) + goto cleanup; + + if (pciDeviceGetManaged(dev) && + pciDettachDevice(dev, driver->activePciHostdevs) < 0) + goto cleanup; + } + + /* Now that all the PCI hostdevs have be dettached, we can safely + * reset them */ + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) + goto cleanup; + } + + /* Now mark all the devices as active */ + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + pciDeviceListSteal(pcidevs, dev); + if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) { + pciFreeDevice(dev); + goto cleanup; + } + } + + ret = 0; + +cleanup: + pciDeviceListFree(pcidevs); + return ret; +} + +static int +qemuPrepareHostPCIDevices(struct qemud_driver *driver, + virDomainDefPtr def) +{ + return qemuPrepareHostdevPCIDevices(driver, def->hostdevs, def->nhostdevs); +} + + +static int +qemuPrepareHostUSBDevices(struct qemud_driver *driver ATTRIBUTE_UNUSED, + virDomainDefPtr def) +{ + int i; + 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_USB) + continue; + + /* Resolve a vendor/product to bus/device */ + if (hostdev->source.subsys.u.usb.vendor) { + usbDevice *usb + = usbFindDevice(hostdev->source.subsys.u.usb.vendor, + hostdev->source.subsys.u.usb.product); + + if (!usb) + return -1; + + hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); + hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); + + usbFreeDevice(usb); + } + } + + return 0; +} + + +int qemuPrepareHostDevices(struct qemud_driver *driver, + virDomainDefPtr def) +{ + if (!def->nhostdevs) + return 0; + + if (qemuPrepareHostPCIDevices(driver, def) < 0) + return -1; + + if (qemuPrepareHostUSBDevices(driver, def) < 0) + return -1; + + return 0; +} + + +void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver) +{ + int retries = 100; + + while (pciWaitForDeviceCleanup(dev, "kvm_assigned_device") + && retries) { + usleep(100*1000); + retries--; + } + + if (pciDeviceGetManaged(dev)) { + if (pciReAttachDevice(dev, driver->activePciHostdevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to re-attach PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + } +} + + +void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + pciDeviceList *pcidevs; + int i; + + if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to allocate pciDeviceList: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + return; + } + + /* Again 3 loops; mark all devices as inactive before reset + * them and reset all the devices before re-attach */ + + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + pciDeviceListDel(driver->activePciHostdevs, dev); + } + + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to reset PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + } + + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + qemuReattachPciDevice(dev, driver); + } + + pciDeviceListFree(pcidevs); +} + + +void qemuDomainReAttachHostDevices(struct qemud_driver *driver, + virDomainDefPtr def) +{ + if (!def->nhostdevs) + return; + + qemuDomainReAttachHostdevDevices(driver, def->hostdevs, def->nhostdevs); +} diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h new file mode 100644 index 0000000000000000000000000000000000000000..1f3d1bc16e17b4f474833e29e2bf7c4350abd9de --- /dev/null +++ b/src/qemu/qemu_hostdev.h @@ -0,0 +1,45 @@ +/* + * qemu_hostdev.h: QEMU hostdev management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange + */ + +#ifndef __QEMU_HOSTDEV_H__ +# define __QEMU_HOSTDEV_H__ + +# include "qemu_conf.h" +# include "domain_conf.h" + +int qemuUpdateActivePciHostdevs(struct qemud_driver *driver, + virDomainDefPtr def); +int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +int qemuPrepareHostDevices(struct qemud_driver *driver, + virDomainDefPtr def); +void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver); +void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void qemuDomainReAttachHostDevices(struct qemud_driver *driver, + virDomainDefPtr def); + + +#endif /* __QEMU_HOSTDEV_H__ */