From patchwork Sun Nov 27 11:32:29 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cao jin X-Patchwork-Id: 699658 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3tRSRt2Yy0z9vDx for ; Sun, 27 Nov 2016 22:35:54 +1100 (AEDT) Received: from localhost ([::1]:53797 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cAxkN-0004vU-A1 for incoming@patchwork.ozlabs.org; Sun, 27 Nov 2016 06:35:51 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:43813) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cAxeI-0008Rt-Fp for qemu-devel@nongnu.org; Sun, 27 Nov 2016 06:29:36 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cAxeG-00010T-Th for qemu-devel@nongnu.org; Sun, 27 Nov 2016 06:29:34 -0500 Received: from [59.151.112.132] (port=10151 helo=heian.cn.fujitsu.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cAxeF-0000yT-IC for qemu-devel@nongnu.org; Sun, 27 Nov 2016 06:29:32 -0500 X-IronPort-AV: E=Sophos;i="5.22,518,1449504000"; d="scan'208";a="13345168" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 27 Nov 2016 19:29:21 +0800 Received: from G08CNEXCHPEKD02.g08.fujitsu.local (unknown [10.167.33.83]) by cn.fujitsu.com (Postfix) with ESMTP id 9C3CD47AC8A1; Sun, 27 Nov 2016 19:29:21 +0800 (CST) Received: from G08FNSTD140223.g08.fujitsu.local (10.167.226.69) by G08CNEXCHPEKD02.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.319.2; Sun, 27 Nov 2016 19:29:31 +0800 From: Cao jin To: Date: Sun, 27 Nov 2016 19:32:29 +0800 Message-ID: <1480246353-10297-7-git-send-email-caoj.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1480246353-10297-1-git-send-email-caoj.fnst@cn.fujitsu.com> References: <1480246353-10297-1-git-send-email-caoj.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.69] X-yoursite-MailScanner-ID: 9C3CD47AC8A1.AF15C X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: caoj.fnst@cn.fujitsu.com X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 59.151.112.132 Subject: [Qemu-devel] [PATCH v10 06/10] vfio: add check host bus reset is support or not X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Chen Fan , izumi.taku@jp.fujitsu.com, alex.williamson@redhat.com, Dou Liyang , mst@redhat.com Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" From: Chen Fan When assigning a vfio device with AER enabled, we must check whether the device supports host bus reset (ie. hot reset) as this may be used by the guest OS to recover the device from an AER error. Therefore, QEMU must have the ability to perform a physical host bus reset using the existing vfio APIs in response to a virtual bus reset in the VM. A physical bus reset affects all devices under the bus, for easier life, we need a few configuration restriction on the VM: 1. All physical functions in a single card must be assigned to the VM with AER enabled on each and configured on the same virtual bus. 2. Don't place other devices under the virtual bus in 1, no matter physical, emulated, or paravirtual, even if other device supporting AER signaling. In other words, users wishing to enable AER on a multifunction device need to assign all functions of the device to the same virtual bus and enable AER support for each function. This is should be checked after machine init done. Signed-off-by: Chen Fan Signed-off-by: Dou Liyang Signed-off-by: Cao jin --- hw/vfio/pci.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ hw/vfio/pci.h | 1 + 2 files changed, 237 insertions(+), 23 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 95cb3c2..8687668 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1777,6 +1777,29 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) } } +static int vfio_pci_name_to_addr(const char *name, PCIHostDeviceAddress *addr) +{ + if (strlen(name) != 12 || + sscanf(name, "%04x:%02x:%02x.%1x", &addr->domain, + &addr->bus, &addr->slot, &addr->function) != 4) { + return -EINVAL; + } + + return 0; +} + +static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) +{ + PCIHostDeviceAddress tmp; + + if (vfio_pci_name_to_addr(name, &tmp)) { + return false; + } + + return (tmp.domain == addr->domain && tmp.bus == addr->bus && + tmp.slot == addr->slot && tmp.function == addr->function); +} + /* return -errno on failure, return 0 on success. */ static int vfio_get_hot_reset_info(VFIOPCIDevice *vdev, struct vfio_pci_hot_reset_info **ret_info, @@ -1937,6 +1960,195 @@ out: return 0; } +static int vfio_device_range_limit(PCIBus *bus) +{ + PCIDevice *br = NULL; + + br = pci_bridge_get_device(bus); + if (br && + pcie_cap_is_arifwd_enabled(br)) { + return 255; + } + + return 8; +} + +static void vfio_check_hot_bus_reset(VFIOPCIDevice *vdev, Error **errp) +{ + PCIBus *bus = vdev->pdev.bus; + struct vfio_pci_hot_reset_info *info = NULL; + struct vfio_pci_dependent_device *devices; + VFIOGroup *group; + int ret, i, devfn, range_limit; + + ret = vfio_get_hot_reset_info(vdev, &info, errp); + if (ret) { + return; + } + + /* 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; + bool found = false; + + 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->vbasedev.name)) { + 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) { + error_setg(errp, "vfio: Cannot enable AER for device %s, " + "depends on group %d which is not owned.", + vdev->vbasedev.name, devices[i].group_id); + goto out; + } + + /* Ensure all affected devices are on 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->vbasedev.name)) { + /* + * AER errors will be broadcast to all functions of a multi- + * function endpoint. If any of those sibling functions are + * also assigned, they need to have AER enabled or else an + * error may continue to cause a vm_stop condition. IOW, + * AER setup of this function would be pointless. + */ + if (!(tmp->features & VFIO_FEATURE_ENABLE_AER)) { + error_setg(errp, "vfio: Cannot enable AER for device %s," + " which is dependent on device %s on the same" + " slot, which does not enable AER", + vdev->vbasedev.name, tmp->vbasedev.name); + goto out; + } + + if (tmp->pdev.bus != bus) { + error_setg(errp, "vfio: Cannot enable AER for device %s," + " the dependent device %s is not on the same" + " virtual bus", + vdev->vbasedev.name, tmp->vbasedev.name); + goto out; + } + found = true; + break; + } + } + + /* Ensure all affected devices assigned to VM */ + if (!found) { + error_setg(errp, "vfio: Cannot enable AER for device %s, " + "the dependent device %04x:%02x:%02x.%x " + "is not assigned to VM.", + vdev->vbasedev.name, host.domain, host.bus, + host.slot, host.function); + goto out; + } + } + + /* + * The above code verified that all functions in a single device affected + * by a bus reset exist on the same bus in the VM. To further simplify, + * we also require that there are no other devices beyond those on the + * same VM bus. + */ + range_limit = vfio_device_range_limit(bus); + for (devfn = 0; devfn < range_limit; devfn++) { + VFIOPCIDevice *tmp; + PCIDevice *dev; + bool found = false; + + dev = pci_find_device(bus, pci_bus_num(bus), + PCI_DEVFN(PCI_SLOT(vdev->pdev.devfn), devfn)); + + if (!dev) { + continue; + } + + if (!object_dynamic_cast(OBJECT(dev), "vfio-pci")) { + error_setg(errp, "vfio: Cannot enable AER for device %s, device" + " %s in slot %d function%d must not be configured" + " on the same virtual bus", + vdev->vbasedev.name, dev->name, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + goto out; + } + + tmp = DO_UPCAST(VFIOPCIDevice, pdev, dev); + for (i = 0; i < info->count; i++) { + PCIHostDeviceAddress host; + + host.domain = devices[i].segment; + host.bus = devices[i].bus; + host.slot = PCI_SLOT(devices[i].devfn); + host.function = PCI_FUNC(devices[i].devfn); + + if (vfio_pci_host_match(&host, tmp->vbasedev.name)) { + found = true; + break; + } + } + + if (!found) { + error_setg(errp, "vfio: Cannot enable AER for device %s," + " affected device %s is not configured" + " on the same virtual bus", + vdev->vbasedev.name, tmp->vbasedev.name); + goto out; + } + } + +out: + g_free(info); + return; +} + +static void vfio_aer_check_host_bus_reset(Error **errp) +{ + VFIOGroup *group; + VFIODevice *vbasedev; + VFIOPCIDevice *vdev; + Error *err = NULL; + + /* Check if all vfio-pci devices have bus reset capability */ + QLIST_FOREACH(group, &vfio_group_list, next) { + QLIST_FOREACH(vbasedev, &group->device_list, next) { + if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { + continue; + } + vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + if (vdev->features & VFIO_FEATURE_ENABLE_AER) { + vfio_check_hot_bus_reset(vdev, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + } + } + + return; +} + static int vfio_setup_aer(VFIOPCIDevice *vdev, uint8_t cap_ver, int pos, uint16_t size, Error **errp) { @@ -2164,29 +2376,6 @@ static void vfio_pci_post_reset(VFIOPCIDevice *vdev) } } -static int vfio_pci_name_to_addr(const char *name, PCIHostDeviceAddress *addr) -{ - if (strlen(name) != 12 || - sscanf(name, "%04x:%02x:%02x.%1x", &addr->domain, - &addr->bus, &addr->slot, &addr->function) != 4) { - return -EINVAL; - } - - return 0; -} - -static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) -{ - PCIHostDeviceAddress tmp; - - if (vfio_pci_name_to_addr(name, &tmp)) { - return false; - } - - return (tmp.domain == addr->domain && tmp.bus == addr->bus && - tmp.slot == addr->slot && tmp.function == addr->function); -} - static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) { VFIOGroup *group; @@ -2717,6 +2906,21 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) vdev->req_enabled = false; } +static void vfio_pci_machine_done_notify(Notifier *notifier, void *unused) +{ + Error *err = NULL; + + vfio_aer_check_host_bus_reset(&err); + if (err) { + error_report_err(err); + exit(1); + } +} + +static Notifier machine_notifier = { + .notify = vfio_pci_machine_done_notify, +}; + static void vfio_realize(PCIDevice *pdev, Error **errp) { VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); @@ -3122,6 +3326,15 @@ static const TypeInfo vfio_pci_dev_info = { static void register_vfio_pci_dev_type(void) { type_register_static(&vfio_pci_dev_info); + + /* + * The AER configuration may depend on multiple devices, so we cannot + * validate consistency after each device is initialized. We can only + * depend on function initialization order (function 0 last) for hotplug + * devices, therefore a machine-init-done notifier is used to validate + * the configuration after all cold-plug devices are processed. + */ + qemu_add_machine_init_done_notifier(&machine_notifier); } type_init(register_vfio_pci_dev_type) diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 64701c4..4c21a46 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -15,6 +15,7 @@ #include "qemu-common.h" #include "exec/memory.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "hw/pci/pci_bridge.h" #include "hw/vfio/vfio-common.h" #include "qemu/event_notifier.h"