From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:60371) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z2AQ8-0004uh-0t for qemu-devel@nongnu.org; Mon, 08 Jun 2015 23:41:49 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Z2AQ4-00016k-OX for qemu-devel@nongnu.org; Mon, 08 Jun 2015 23:41:47 -0400 Received: from [59.151.112.132] (port=12998 helo=heian.cn.fujitsu.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z2APe-0000LN-7w for qemu-devel@nongnu.org; Mon, 08 Jun 2015 23:41:44 -0400 From: Chen Fan Date: Tue, 9 Jun 2015 11:37:39 +0800 Message-ID: <0b09272e0161da6e3021633e4db6d0751b846246.1433812962.git.chen.fan.fnst@cn.fujitsu.com> In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain Subject: [Qemu-devel] [RFC v9 11/18] vfio: add check host bus reset is support or not List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: izumi.taku@jp.fujitsu.com, alex.williamson@redhat.com we introduce a has_bus_reset capability to sign the vfio devices if support host bus reset. Signed-off-by: Chen Fan --- hw/vfio/pci.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 06006ce..97af143 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -32,6 +32,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/pci/pci_bridge.h" #include "qemu-common.h" #include "qemu/error-report.h" @@ -2921,6 +2922,169 @@ out: return ret; } +struct VFIODeviceFind { + PCIDevice *pdev; + bool found; +}; + +static void find_devices(PCIBus *bus, void *opaque) +{ + struct VFIODeviceFind *find = opaque; + int i; + + if (find->found) { + return; + } + + for (i = 0; i < ARRAY_SIZE(bus->devices); i++) { + if (!bus->devices[i]) { + continue; + } + + if (bus->devices[i] == find->pdev) { + find->found = true; + break; + } + } +} + +static void vfio_check_device_reset(PCIBus *bus, void *opaque) +{ + int i; + PCIDevice *dev; + VFIOPCIDevice *vdev; + struct VFIODeviceFind *find = opaque; + + if (find->found) { + return; + } + + for (i = 0; i < ARRAY_SIZE(bus->devices); i++) { + if (!bus->devices[i]) { + continue; + } + dev = bus->devices[i]; + if (!object_dynamic_cast(OBJECT(dev), "vfio-pci")) { + continue; + } + vdev = DO_UPCAST(VFIOPCIDevice, pdev, dev); + if (!(vdev->features & VFIO_FEATURE_ENABLE_AER) && + !vdev->vbasedev.reset_works && + !vdev->has_flr && + !vdev->has_pm_reset) { + find->pdev = dev; + find->found = true; + break; + } + } +} + +static int vfio_check_host_bus_reset(VFIOPCIDevice *vdev) +{ + PCIBus *bus = vdev->pdev.bus; + struct vfio_pci_hot_reset_info *info = NULL; + struct vfio_pci_dependent_device *devices; + VFIOGroup *group; + int ret, i; + struct VFIODeviceFind find; + + ret = vfio_get_hot_reset_info(vdev, &info); + if (ret) { + goto out; + } + + /* List all affected devices by bus reset */ + devices = &info->devices[0]; + + /* Verify that we have all the groups required */ + for (i = 0; i < info->count; i++) { + PCIHostDeviceAddress host; + VFIOPCIDevice *tmp; + VFIODevice *vbasedev_iter; + + host.domain = devices[i].segment; + host.bus = devices[i].bus; + host.slot = PCI_SLOT(devices[i].devfn); + host.function = PCI_FUNC(devices[i].devfn); + + /* Skip the current device */ + if (vfio_pci_host_match(&host, &vdev->host)) { + continue; + } + + /* Ensure we own the group of the affected device */ + QLIST_FOREACH(group, &vfio_group_list, next) { + if (group->groupid == devices[i].group_id) { + break; + } + } + + if (!group) { + ret = -1; + goto out; + } + + /* Ensure affected devices for reset under the same bus */ + QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { + if (vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + continue; + } + tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); + if (vfio_pci_host_match(&host, &tmp->host)) { + find.pdev = &tmp->pdev; + find.found = false; + + pci_for_each_bus(bus, find_devices, &find); + if (!find.found) { + ret = -1; + goto out; + } + break; + } + } + } + + /* + * Check the all vfio pci devices on or below the target bus + * have a reset mechanism at least. + */ + find.pdev = NULL; + find.found = false; + pci_for_each_bus(bus, vfio_check_device_reset, &find); + if (find.found) { + ret = -1; + goto out; + } + + ret = 0; +out: + g_free(info); + return ret; +} + +static int vfio_check_devices_host_bus_reset(void) +{ + VFIOGroup *group; + VFIODevice *vbasedev; + VFIOPCIDevice *vdev; + + /* Check All vfio-pci devices if have bus reset capability */ + QLIST_FOREACH(group, &vfio_group_list, next) { + QLIST_FOREACH(vbasedev, &group->device_list, next) { + vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + if ((vdev->features & VFIO_FEATURE_ENABLE_AER) && + vfio_check_host_bus_reset(vdev)) { + error_report("vfio: Cannot enable AER for device %s, " + "which does not support host bus reset.", + vdev->vbasedev.name); + return -1; + } + } + } + + return 0; +} + static int vfio_setup_aer(VFIOPCIDevice *vdev, uint8_t cap_ver, int pos, uint16_t size) { @@ -2963,6 +3127,11 @@ static int vfio_setup_aer(VFIOPCIDevice *vdev, uint8_t cap_ver, goto error; } + /* Make sure this devices does conflict the existing aer topology */ + if (vfio_check_devices_host_bus_reset()) { + goto error; + } + errcap = vfio_pci_read_config(pdev, pdev->exp.aer_cap + PCI_ERR_CAP, 4); /* * The ability to record multiple headers is depending on -- 1.9.3