|
|
9ae3a8 |
From 8ea5e0ba0b1ba054d71bb10f8c45c167dd3d7792 Mon Sep 17 00:00:00 2001
|
|
|
9ae3a8 |
From: Alex Williamson <alex.williamson@redhat.com>
|
|
|
9ae3a8 |
Date: Tue, 5 Nov 2013 17:31:11 +0100
|
|
|
9ae3a8 |
Subject: [PATCH 19/25] vfio-pci: Implement PCI hot reset
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
RH-Author: Alex Williamson <alex.williamson@redhat.com>
|
|
|
9ae3a8 |
Message-id: <20131105173110.19372.22420.stgit@bling.home>
|
|
|
9ae3a8 |
Patchwork-id: 55440
|
|
|
9ae3a8 |
O-Subject: [RHEL7 qemu-kvm PATCH 2/2] vfio-pci: Implement PCI hot reset
|
|
|
9ae3a8 |
Bugzilla: 1025472
|
|
|
9ae3a8 |
RH-Acked-by: Bandan Das <bsd@redhat.com>
|
|
|
9ae3a8 |
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
9ae3a8 |
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
|
|
|
9ae3a8 |
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Bugzilla: 1025472
|
|
|
9ae3a8 |
Upstream commit: f16f39c3fc973c5d7cbc2224eefb4ef5eb1e64ff
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Now that VFIO has a PCI hot reset interface, take advantage of it.
|
|
|
9ae3a8 |
There are two modes that we need to consider. The first is when only
|
|
|
9ae3a8 |
one device within the set of devices affected is actually assigned to
|
|
|
9ae3a8 |
the guest. In this case the other devices are are just held by VFIO
|
|
|
9ae3a8 |
for isolation and we can pretend they're not there, doing an entire
|
|
|
9ae3a8 |
bus reset whenever the device reset callback is triggered. Supporting
|
|
|
9ae3a8 |
this case separately allows us to do the best reset we can do of the
|
|
|
9ae3a8 |
device even if the device is hotplugged.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
The second mode is when multiple affected devices are all exposed to
|
|
|
9ae3a8 |
the guest. In this case we can only do a hot reset when the entire
|
|
|
9ae3a8 |
system is being reset. However, this also allows us to track which
|
|
|
9ae3a8 |
individual devices are affected by a reset and only do them once.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
We split our reset function into pre- and post-reset helper functions
|
|
|
9ae3a8 |
prioritize the types of device resets available to us, and create
|
|
|
9ae3a8 |
separate _one vs _multi reset interfaces to handle the distinct cases
|
|
|
9ae3a8 |
above.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
|
|
|
9ae3a8 |
---
|
|
|
9ae3a8 |
hw/misc/vfio.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++------
|
|
|
9ae3a8 |
1 file changed, 300 insertions(+), 38 deletions(-)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
9ae3a8 |
---
|
|
|
9ae3a8 |
hw/misc/vfio.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++-------
|
|
|
9ae3a8 |
1 files changed, 300 insertions(+), 38 deletions(-)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
|
|
|
9ae3a8 |
index 6178221..331ae5f 100644
|
|
|
9ae3a8 |
--- a/hw/misc/vfio.c
|
|
|
9ae3a8 |
+++ b/hw/misc/vfio.c
|
|
|
9ae3a8 |
@@ -188,6 +188,7 @@ typedef struct VFIODevice {
|
|
|
9ae3a8 |
bool pci_aer;
|
|
|
9ae3a8 |
bool has_flr;
|
|
|
9ae3a8 |
bool has_pm_reset;
|
|
|
9ae3a8 |
+ bool needs_reset;
|
|
|
9ae3a8 |
} VFIODevice;
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
typedef struct VFIOGroup {
|
|
|
9ae3a8 |
@@ -2759,6 +2760,279 @@ static int vfio_add_capabilities(VFIODevice *vdev)
|
|
|
9ae3a8 |
return vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
+static void vfio_pci_pre_reset(VFIODevice *vdev)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ PCIDevice *pdev = &vdev->pdev;
|
|
|
9ae3a8 |
+ uint16_t cmd;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ vfio_disable_interrupts(vdev);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /* Make sure the device is in D0 */
|
|
|
9ae3a8 |
+ if (vdev->pm_cap) {
|
|
|
9ae3a8 |
+ uint16_t pmcsr;
|
|
|
9ae3a8 |
+ uint8_t state;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2);
|
|
|
9ae3a8 |
+ state = pmcsr & PCI_PM_CTRL_STATE_MASK;
|
|
|
9ae3a8 |
+ if (state) {
|
|
|
9ae3a8 |
+ pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
|
|
|
9ae3a8 |
+ vfio_pci_write_config(pdev, vdev->pm_cap + PCI_PM_CTRL, pmcsr, 2);
|
|
|
9ae3a8 |
+ /* vfio handles the necessary delay here */
|
|
|
9ae3a8 |
+ pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2);
|
|
|
9ae3a8 |
+ state = pmcsr & PCI_PM_CTRL_STATE_MASK;
|
|
|
9ae3a8 |
+ if (state) {
|
|
|
9ae3a8 |
+ error_report("vfio: Unable to power on device, stuck in D%d\n",
|
|
|
9ae3a8 |
+ state);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /*
|
|
|
9ae3a8 |
+ * Stop any ongoing DMA by disconecting I/O, MMIO, and bus master.
|
|
|
9ae3a8 |
+ * Also put INTx Disable in known state.
|
|
|
9ae3a8 |
+ */
|
|
|
9ae3a8 |
+ cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2);
|
|
|
9ae3a8 |
+ cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
|
|
|
9ae3a8 |
+ PCI_COMMAND_INTX_DISABLE);
|
|
|
9ae3a8 |
+ vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2);
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+static void vfio_pci_post_reset(VFIODevice *vdev)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ vfio_enable_intx(vdev);
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+static bool vfio_pci_host_match(PCIHostDeviceAddress *host1,
|
|
|
9ae3a8 |
+ PCIHostDeviceAddress *host2)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ return (host1->domain == host2->domain && host1->bus == host2->bus &&
|
|
|
9ae3a8 |
+ host1->slot == host2->slot && host1->function == host2->function);
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+static int vfio_pci_hot_reset(VFIODevice *vdev, bool single)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ VFIOGroup *group;
|
|
|
9ae3a8 |
+ struct vfio_pci_hot_reset_info *info;
|
|
|
9ae3a8 |
+ struct vfio_pci_dependent_device *devices;
|
|
|
9ae3a8 |
+ struct vfio_pci_hot_reset *reset;
|
|
|
9ae3a8 |
+ int32_t *fds;
|
|
|
9ae3a8 |
+ int ret, i, count;
|
|
|
9ae3a8 |
+ bool multi = false;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ DPRINTF("%s(%04x:%02x:%02x.%x) %s\n", __func__, vdev->host.domain,
|
|
|
9ae3a8 |
+ vdev->host.bus, vdev->host.slot, vdev->host.function,
|
|
|
9ae3a8 |
+ single ? "one" : "multi");
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ vfio_pci_pre_reset(vdev);
|
|
|
9ae3a8 |
+ vdev->needs_reset = false;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ info = g_malloc0(sizeof(*info));
|
|
|
9ae3a8 |
+ info->argsz = sizeof(*info);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, info);
|
|
|
9ae3a8 |
+ if (ret && errno != ENOSPC) {
|
|
|
9ae3a8 |
+ ret = -errno;
|
|
|
9ae3a8 |
+ if (!vdev->has_pm_reset) {
|
|
|
9ae3a8 |
+ error_report("vfio: Cannot reset device %04x:%02x:%02x.%x, "
|
|
|
9ae3a8 |
+ "no available reset mechanism.", vdev->host.domain,
|
|
|
9ae3a8 |
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ goto out_single;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ count = info->count;
|
|
|
9ae3a8 |
+ info = g_realloc(info, sizeof(*info) + (count * sizeof(*devices)));
|
|
|
9ae3a8 |
+ info->argsz = sizeof(*info) + (count * sizeof(*devices));
|
|
|
9ae3a8 |
+ devices = &info->devices[0];
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, info);
|
|
|
9ae3a8 |
+ if (ret) {
|
|
|
9ae3a8 |
+ ret = -errno;
|
|
|
9ae3a8 |
+ error_report("vfio: hot reset info failed: %m");
|
|
|
9ae3a8 |
+ goto out_single;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ DPRINTF("%04x:%02x:%02x.%x: hot reset dependent devices:\n",
|
|
|
9ae3a8 |
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
|
|
|
9ae3a8 |
+ vdev->host.function);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /* Verify that we have all the groups required */
|
|
|
9ae3a8 |
+ for (i = 0; i < info->count; i++) {
|
|
|
9ae3a8 |
+ PCIHostDeviceAddress host;
|
|
|
9ae3a8 |
+ VFIODevice *tmp;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ host.domain = devices[i].segment;
|
|
|
9ae3a8 |
+ host.bus = devices[i].bus;
|
|
|
9ae3a8 |
+ host.slot = PCI_SLOT(devices[i].devfn);
|
|
|
9ae3a8 |
+ host.function = PCI_FUNC(devices[i].devfn);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ DPRINTF("\t%04x:%02x:%02x.%x group %d\n", host.domain,
|
|
|
9ae3a8 |
+ host.bus, host.slot, host.function, devices[i].group_id);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (vfio_pci_host_match(&host, &vdev->host)) {
|
|
|
9ae3a8 |
+ continue;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ QLIST_FOREACH(group, &group_list, next) {
|
|
|
9ae3a8 |
+ if (group->groupid == devices[i].group_id) {
|
|
|
9ae3a8 |
+ break;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (!group) {
|
|
|
9ae3a8 |
+ if (!vdev->has_pm_reset) {
|
|
|
9ae3a8 |
+ error_report("vfio: Cannot reset device %04x:%02x:%02x.%x, "
|
|
|
9ae3a8 |
+ "depends on group %d which is not owned.",
|
|
|
9ae3a8 |
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
|
|
|
9ae3a8 |
+ vdev->host.function, devices[i].group_id);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ ret = -EPERM;
|
|
|
9ae3a8 |
+ goto out;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /* Prep dependent devices for reset and clear our marker. */
|
|
|
9ae3a8 |
+ QLIST_FOREACH(tmp, &group->device_list, next) {
|
|
|
9ae3a8 |
+ if (vfio_pci_host_match(&host, &tmp->host)) {
|
|
|
9ae3a8 |
+ if (single) {
|
|
|
9ae3a8 |
+ DPRINTF("vfio: found another in-use device "
|
|
|
9ae3a8 |
+ "%04x:%02x:%02x.%x\n", host.domain, host.bus,
|
|
|
9ae3a8 |
+ host.slot, host.function);
|
|
|
9ae3a8 |
+ ret = -EINVAL;
|
|
|
9ae3a8 |
+ goto out_single;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ vfio_pci_pre_reset(tmp);
|
|
|
9ae3a8 |
+ tmp->needs_reset = false;
|
|
|
9ae3a8 |
+ multi = true;
|
|
|
9ae3a8 |
+ break;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (!single && !multi) {
|
|
|
9ae3a8 |
+ DPRINTF("vfio: No other in-use devices for multi hot reset\n");
|
|
|
9ae3a8 |
+ ret = -EINVAL;
|
|
|
9ae3a8 |
+ goto out_single;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /* Determine how many group fds need to be passed */
|
|
|
9ae3a8 |
+ count = 0;
|
|
|
9ae3a8 |
+ QLIST_FOREACH(group, &group_list, next) {
|
|
|
9ae3a8 |
+ for (i = 0; i < info->count; i++) {
|
|
|
9ae3a8 |
+ if (group->groupid == devices[i].group_id) {
|
|
|
9ae3a8 |
+ count++;
|
|
|
9ae3a8 |
+ break;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ reset = g_malloc0(sizeof(*reset) + (count * sizeof(*fds)));
|
|
|
9ae3a8 |
+ reset->argsz = sizeof(*reset) + (count * sizeof(*fds));
|
|
|
9ae3a8 |
+ fds = &reset->group_fds[0];
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /* Fill in group fds */
|
|
|
9ae3a8 |
+ QLIST_FOREACH(group, &group_list, next) {
|
|
|
9ae3a8 |
+ for (i = 0; i < info->count; i++) {
|
|
|
9ae3a8 |
+ if (group->groupid == devices[i].group_id) {
|
|
|
9ae3a8 |
+ fds[reset->count++] = group->fd;
|
|
|
9ae3a8 |
+ break;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ /* Bus reset! */
|
|
|
9ae3a8 |
+ ret = ioctl(vdev->fd, VFIO_DEVICE_PCI_HOT_RESET, reset);
|
|
|
9ae3a8 |
+ g_free(reset);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ DPRINTF("%04x:%02x:%02x.%x hot reset: %s\n", vdev->host.domain,
|
|
|
9ae3a8 |
+ vdev->host.bus, vdev->host.slot, vdev->host.function,
|
|
|
9ae3a8 |
+ ret ? "%m" : "Success");
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+out:
|
|
|
9ae3a8 |
+ /* Re-enable INTx on affected devices */
|
|
|
9ae3a8 |
+ for (i = 0; i < info->count; i++) {
|
|
|
9ae3a8 |
+ PCIHostDeviceAddress host;
|
|
|
9ae3a8 |
+ VFIODevice *tmp;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ host.domain = devices[i].segment;
|
|
|
9ae3a8 |
+ host.bus = devices[i].bus;
|
|
|
9ae3a8 |
+ host.slot = PCI_SLOT(devices[i].devfn);
|
|
|
9ae3a8 |
+ host.function = PCI_FUNC(devices[i].devfn);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (vfio_pci_host_match(&host, &vdev->host)) {
|
|
|
9ae3a8 |
+ continue;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ QLIST_FOREACH(group, &group_list, next) {
|
|
|
9ae3a8 |
+ if (group->groupid == devices[i].group_id) {
|
|
|
9ae3a8 |
+ break;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (!group) {
|
|
|
9ae3a8 |
+ break;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ QLIST_FOREACH(tmp, &group->device_list, next) {
|
|
|
9ae3a8 |
+ if (vfio_pci_host_match(&host, &tmp->host)) {
|
|
|
9ae3a8 |
+ vfio_pci_post_reset(tmp);
|
|
|
9ae3a8 |
+ break;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+out_single:
|
|
|
9ae3a8 |
+ vfio_pci_post_reset(vdev);
|
|
|
9ae3a8 |
+ g_free(info);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ return ret;
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+/*
|
|
|
9ae3a8 |
+ * We want to differentiate hot reset of mulitple in-use devices vs hot reset
|
|
|
9ae3a8 |
+ * of a single in-use device. VFIO_DEVICE_RESET will already handle the case
|
|
|
9ae3a8 |
+ * of doing hot resets when there is only a single device per bus. The in-use
|
|
|
9ae3a8 |
+ * here refers to how many VFIODevices are affected. A hot reset that affects
|
|
|
9ae3a8 |
+ * multiple devices, but only a single in-use device, means that we can call
|
|
|
9ae3a8 |
+ * it from our bus ->reset() callback since the extent is effectively a single
|
|
|
9ae3a8 |
+ * device. This allows us to make use of it in the hotplug path. When there
|
|
|
9ae3a8 |
+ * are multiple in-use devices, we can only trigger the hot reset during a
|
|
|
9ae3a8 |
+ * system reset and thus from our reset handler. We separate _one vs _multi
|
|
|
9ae3a8 |
+ * here so that we don't overlap and do a double reset on the system reset
|
|
|
9ae3a8 |
+ * path where both our reset handler and ->reset() callback are used. Calling
|
|
|
9ae3a8 |
+ * _one() will only do a hot reset for the one in-use devices case, calling
|
|
|
9ae3a8 |
+ * _multi() will do nothing if a _one() would have been sufficient.
|
|
|
9ae3a8 |
+ */
|
|
|
9ae3a8 |
+static int vfio_pci_hot_reset_one(VFIODevice *vdev)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ return vfio_pci_hot_reset(vdev, true);
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+static int vfio_pci_hot_reset_multi(VFIODevice *vdev)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ return vfio_pci_hot_reset(vdev, false);
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+static void vfio_pci_reset_handler(void *opaque)
|
|
|
9ae3a8 |
+{
|
|
|
9ae3a8 |
+ VFIOGroup *group;
|
|
|
9ae3a8 |
+ VFIODevice *vdev;
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ QLIST_FOREACH(group, &group_list, next) {
|
|
|
9ae3a8 |
+ QLIST_FOREACH(vdev, &group->device_list, next) {
|
|
|
9ae3a8 |
+ if (!vdev->reset_works || (!vdev->has_flr && vdev->has_pm_reset)) {
|
|
|
9ae3a8 |
+ vdev->needs_reset = true;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ QLIST_FOREACH(group, &group_list, next) {
|
|
|
9ae3a8 |
+ QLIST_FOREACH(vdev, &group->device_list, next) {
|
|
|
9ae3a8 |
+ if (vdev->needs_reset) {
|
|
|
9ae3a8 |
+ vfio_pci_hot_reset_multi(vdev);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+}
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
static int vfio_connect_container(VFIOGroup *group)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
VFIOContainer *container;
|
|
|
9ae3a8 |
@@ -2901,6 +3175,10 @@ static VFIOGroup *vfio_get_group(int groupid)
|
|
|
9ae3a8 |
return NULL;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
+ if (QLIST_EMPTY(&group_list)) {
|
|
|
9ae3a8 |
+ qemu_register_reset(vfio_pci_reset_handler, NULL);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
QLIST_INSERT_HEAD(&group_list, group, next);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
return group;
|
|
|
9ae3a8 |
@@ -2917,6 +3195,10 @@ static void vfio_put_group(VFIOGroup *group)
|
|
|
9ae3a8 |
DPRINTF("vfio_put_group: close group->fd\n");
|
|
|
9ae3a8 |
close(group->fd);
|
|
|
9ae3a8 |
g_free(group);
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if (QLIST_EMPTY(&group_list)) {
|
|
|
9ae3a8 |
+ qemu_unregister_reset(vfio_pci_reset_handler, NULL);
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
|
|
|
9ae3a8 |
@@ -2955,9 +3237,6 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
vdev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET);
|
|
|
9ae3a8 |
- if (!vdev->reset_works) {
|
|
|
9ae3a8 |
- error_report("Warning, device %s does not support reset", name);
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
if (dev_info.num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) {
|
|
|
9ae3a8 |
error_report("vfio: unexpected number of io regions %u",
|
|
|
9ae3a8 |
@@ -3363,51 +3642,34 @@ static void vfio_pci_reset(DeviceState *dev)
|
|
|
9ae3a8 |
{
|
|
|
9ae3a8 |
PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev);
|
|
|
9ae3a8 |
VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
|
|
|
9ae3a8 |
- uint16_t cmd;
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
|
|
|
9ae3a8 |
vdev->host.bus, vdev->host.slot, vdev->host.function);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- vfio_disable_interrupts(vdev);
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- /* Make sure the device is in D0 */
|
|
|
9ae3a8 |
- if (vdev->pm_cap) {
|
|
|
9ae3a8 |
- uint16_t pmcsr;
|
|
|
9ae3a8 |
- uint8_t state;
|
|
|
9ae3a8 |
+ vfio_pci_pre_reset(vdev);
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2);
|
|
|
9ae3a8 |
- state = pmcsr & PCI_PM_CTRL_STATE_MASK;
|
|
|
9ae3a8 |
- if (state) {
|
|
|
9ae3a8 |
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
|
|
|
9ae3a8 |
- vfio_pci_write_config(pdev, vdev->pm_cap + PCI_PM_CTRL, pmcsr, 2);
|
|
|
9ae3a8 |
- /* vfio handles the necessary delay here */
|
|
|
9ae3a8 |
- pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2);
|
|
|
9ae3a8 |
- state = pmcsr & PCI_PM_CTRL_STATE_MASK;
|
|
|
9ae3a8 |
- if (state) {
|
|
|
9ae3a8 |
- error_report("vfio: Unable to power on device, stuck in D%d\n",
|
|
|
9ae3a8 |
- state);
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
+ if (vdev->reset_works && (vdev->has_flr || !vdev->has_pm_reset) &&
|
|
|
9ae3a8 |
+ !ioctl(vdev->fd, VFIO_DEVICE_RESET)) {
|
|
|
9ae3a8 |
+ DPRINTF("%04x:%02x:%02x.%x FLR/VFIO_DEVICE_RESET\n", vdev->host.domain,
|
|
|
9ae3a8 |
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
|
|
|
9ae3a8 |
+ goto post_reset;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- /*
|
|
|
9ae3a8 |
- * Stop any ongoing DMA by disconecting I/O, MMIO, and bus master.
|
|
|
9ae3a8 |
- * Also put INTx Disable in known state.
|
|
|
9ae3a8 |
- */
|
|
|
9ae3a8 |
- cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2);
|
|
|
9ae3a8 |
- cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
|
|
|
9ae3a8 |
- PCI_COMMAND_INTX_DISABLE);
|
|
|
9ae3a8 |
- vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2);
|
|
|
9ae3a8 |
+ /* See if we can do our own bus reset */
|
|
|
9ae3a8 |
+ if (!vfio_pci_hot_reset_one(vdev)) {
|
|
|
9ae3a8 |
+ goto post_reset;
|
|
|
9ae3a8 |
+ }
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- if (vdev->reset_works) {
|
|
|
9ae3a8 |
- if (ioctl(vdev->fd, VFIO_DEVICE_RESET)) {
|
|
|
9ae3a8 |
- error_report("vfio: Error unable to reset physical device "
|
|
|
9ae3a8 |
- "(%04x:%02x:%02x.%x): %m", vdev->host.domain,
|
|
|
9ae3a8 |
- vdev->host.bus, vdev->host.slot, vdev->host.function);
|
|
|
9ae3a8 |
- }
|
|
|
9ae3a8 |
+ /* If nothing else works and the device supports PM reset, use it */
|
|
|
9ae3a8 |
+ if (vdev->reset_works && vdev->has_pm_reset &&
|
|
|
9ae3a8 |
+ !ioctl(vdev->fd, VFIO_DEVICE_RESET)) {
|
|
|
9ae3a8 |
+ DPRINTF("%04x:%02x:%02x.%x PCI PM Reset\n", vdev->host.domain,
|
|
|
9ae3a8 |
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
|
|
|
9ae3a8 |
+ goto post_reset;
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- vfio_enable_intx(vdev);
|
|
|
9ae3a8 |
+post_reset:
|
|
|
9ae3a8 |
+ vfio_pci_post_reset(vdev);
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
static Property vfio_pci_dev_properties[] = {
|
|
|
9ae3a8 |
--
|
|
|
9ae3a8 |
1.7.1
|
|
|
9ae3a8 |
|