All pastes #2579452 Raw Edit

Something

public unlisted text v1 · immutable
#2579452 ·published 2014-01-23 18:41 UTC
rendered paste body
diff -rupN a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
--- a/drivers/pci/hotplug/pci_hotplug_core.c    2013-06-22 19:47:31.000000000 +0000
+++ b/drivers/pci/hotplug/pci_hotplug_core.c    2013-06-27 16:04:16.082714806 +0000
@@ -528,6 +528,29 @@ int pci_hp_change_slot_info(struct hotpl
        return 0;
 }
 
+/**
+ * pci_hp_reset_slot - reset slot
+ *
+ * @hotplug: pointer to hotplug slot to reset
+ * @probe: reset slot (0) or just probe
+ *
+ * Returns 0 if successful, anything else for an error.
+ */
+int pci_hp_reset_slot(struct hotplug_slot *hotplug, int probe)
+{
+       int result = -ENOTTY;
+
+       if (!hotplug || !try_module_get(hotplug->ops->owner))
+               return result;
+
+       if (hotplug->ops->reset_slot)
+               result = hotplug->ops->reset_slot(hotplug, probe);
+
+       module_put(hotplug->ops->owner);
+
+       return result;
+}
+
 static int __init pci_hotplug_init (void)
 {
        int result;
@@ -561,3 +584,4 @@ MODULE_PARM_DESC(debug, "Debugging mode
 EXPORT_SYMBOL_GPL(__pci_hp_register);
 EXPORT_SYMBOL_GPL(pci_hp_deregister);
 EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
+EXPORT_SYMBOL_GPL(pci_hp_reset_slot);
diff -rupN a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
--- a/drivers/pci/hotplug/pciehp.h      2013-06-22 19:47:31.000000000 +0000
+++ b/drivers/pci/hotplug/pciehp.h      2013-06-27 16:04:16.084714807 +0000
@@ -155,6 +155,7 @@ void pciehp_green_led_off(struct slot *s
 void pciehp_green_led_blink(struct slot *slot);
 int pciehp_check_link_status(struct controller *ctrl);
 void pciehp_release_ctrl(struct controller *ctrl);
+int pciehp_reset_slot(struct slot *slot, int probe);
 
 static inline const char *slot_name(struct slot *slot)
 {
diff -rupN a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
--- a/drivers/pci/hotplug/pciehp_core.c 2013-06-22 19:47:31.000000000 +0000
+++ b/drivers/pci/hotplug/pciehp_core.c 2013-06-27 16:04:16.084714807 +0000
@@ -69,6 +69,7 @@ static int get_power_status   (struct hotp
 static int get_attention_status        (struct hotplug_slot *slot, u8 *value);
 static int get_latch_status    (struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status  (struct hotplug_slot *slot, u8 *value);
+static int reset_slot          (struct hotplug_slot *slot, int probe);
 
 /**
  * release_slot - free up the memory used by a slot
@@ -111,6 +112,7 @@ static int init_slot(struct controller *
        ops->disable_slot = disable_slot;
        ops->get_power_status = get_power_status;
        ops->get_adapter_status = get_adapter_status;
+       ops->reset_slot = reset_slot;
        if (MRL_SENS(ctrl))
                ops->get_latch_status = get_latch_status;
        if (ATTN_LED(ctrl)) {
@@ -223,6 +225,16 @@ static int get_adapter_status(struct hot
        return pciehp_get_adapter_status(slot, value);
 }
 
+static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
+{
+       struct slot *slot = hotplug_slot->private;
+
+       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+                __func__, slot_name(slot));
+
+       return pciehp_reset_slot(slot, probe);
+}
+
 static int pciehp_probe(struct pcie_device *dev)
 {
        int rc;
diff -rupN a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
--- a/drivers/pci/hotplug/pciehp_hpc.c  2013-06-22 19:47:31.000000000 +0000
+++ b/drivers/pci/hotplug/pciehp_hpc.c  2013-06-27 16:04:16.085714807 +0000
@@ -749,6 +749,37 @@ static void pcie_disable_notification(st
                ctrl_warn(ctrl, "Cannot disable software notification\n");
 }
 
+/*
+ * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
+ * bus reset of the bridge, but if the slot supports surprise removal we need
+ * to disable presence detection around the bus reset and clear any spurious
+ * events after.
+ */
+int pciehp_reset_slot(struct slot *slot, int probe)
+{
+       struct controller *ctrl = slot->ctrl;
+
+       if (probe)
+               return 0;
+
+       if (HP_SUPR_RM(ctrl)) {
+               pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_PDCE);
+               if (pciehp_poll_mode)
+                       del_timer_sync(&ctrl->poll_timer);
+       }
+
+       pci_reset_bridge_secondary_bus(ctrl->pcie->port);
+
+       if (HP_SUPR_RM(ctrl)) {
+               pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC);
+               pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE);
+               if (pciehp_poll_mode)
+                       int_poll_timeout(ctrl->poll_timer.data);
+       }
+
+       return 0;
+}
+
 int pcie_init_notification(struct controller *ctrl)
 {
        if (pciehp_request_irq(ctrl))
diff -rupN a/drivers/pci/pci.c b/drivers/pci/pci.c
--- a/drivers/pci/pci.c 2013-06-22 19:47:31.000000000 +0000
+++ b/drivers/pci/pci.c 2013-06-27 16:04:16.124714810 +0000
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/pm_runtime.h>
+#include <linux/pci_hotplug.h>
 #include <asm-generic/pci-bridge.h>
 #include <asm/setup.h>
 #include "pci.h"
@@ -3205,9 +3206,30 @@ static int pci_pm_reset(struct pci_dev *
        return 0;
 }
 
-static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
+/**
+ * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge.
+ * @dev: Bridge device
+ *
+ * Use the bridge control register to assert reset on the secondary bus.
+ * Devices on the secondary bus are left in power-on state.
+ */
+void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
 {
        u16 ctrl;
+
+       pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
+       ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
+       pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+       msleep(100);
+
+       ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+       pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+       msleep(100);
+}
+EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
+
+static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
+{
        struct pci_dev *pdev;
 
        if (pci_is_root_bus(dev->bus) || dev->subordinate || !dev->bus->self)
@@ -3220,18 +3242,25 @@ static int pci_parent_bus_reset(struct p
        if (probe)
                return 0;
 
-       pci_read_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, &ctrl);
-       ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
-       pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
-       msleep(100);
-
-       ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
-       pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
-       msleep(100);
+       pci_reset_bridge_secondary_bus(dev->bus->self);
 
        return 0;
 }
 
+static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
+{
+       struct pci_dev *pdev;
+
+       if (dev->subordinate || !dev->slot)
+               return -ENOTTY;
+
+       list_for_each_entry(pdev, &dev->bus->devices, bus_list)
+               if (pdev != dev && pdev->slot == dev->slot)
+                       return -ENOTTY;
+
+       return pci_hp_reset_slot(dev->slot->hotplug, probe);
+}
+
 static int __pci_dev_reset(struct pci_dev *dev, int probe)
 {
        int rc;
@@ -3254,27 +3283,62 @@ static int __pci_dev_reset(struct pci_de
        if (rc != -ENOTTY)
                goto done;
 
+       rc = pci_dev_reset_slot_function(dev, probe);
+       if (rc != -ENOTTY)
+               goto done;
+
        rc = pci_parent_bus_reset(dev, probe);
 done:
        return rc;
 }
 
+static void pci_dev_lock(struct pci_dev *dev)
+{
+       pci_cfg_access_lock(dev);
+       /* block PM suspend, driver probe, etc. */
+       device_lock(&dev->dev);
+}
+
+static void pci_dev_unlock(struct pci_dev *dev)
+{
+       device_unlock(&dev->dev);
+       pci_cfg_access_unlock(dev);
+}
+
+static void pci_dev_save(struct pci_dev *dev)
+{
+       /*
+        * Wake-up device prior to save.  PM registers default to D0 after
+        * reset and a simple register restore doesn't reliably return
+        * to a non-D0 state anyway.
+        */
+       pci_set_power_state(dev, PCI_D0);
+
+       pci_save_state(dev);
+       /*
+        * both INTx and MSI are disabled after the Interrupt Disable bit
+        * is set and the Bus Master bit is cleared.
+        */
+       pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+}
+
+static void pci_dev_restore(struct pci_dev *dev)
+{
+       pci_restore_state(dev);
+}
+
 static int pci_dev_reset(struct pci_dev *dev, int probe)
 {
        int rc;
 
-       if (!probe) {
-               pci_cfg_access_lock(dev);
-               /* block PM suspend, driver probe, etc. */
-               device_lock(&dev->dev);
-       }
+       if (!probe)
+               pci_dev_lock(dev);
 
        rc = __pci_dev_reset(dev, probe);
 
-       if (!probe) {
-               device_unlock(&dev->dev);
-               pci_cfg_access_unlock(dev);
-       }
+       if (!probe)
+               pci_dev_unlock(dev);
+
        return rc;
 }
 /**
@@ -3365,22 +3429,215 @@ int pci_reset_function(struct pci_dev *d
        if (rc)
                return rc;
 
-       pci_save_state(dev);
-
-       /*
-        * both INTx and MSI are disabled after the Interrupt Disable bit
-        * is set and the Bus Master bit is cleared.
-        */
-       pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+       pci_dev_save(dev);
 
        rc = pci_dev_reset(dev, 0);
 
-       pci_restore_state(dev);
+       pci_dev_restore(dev);
 
        return rc;
 }
 EXPORT_SYMBOL_GPL(pci_reset_function);
 
+static void pci_bus_lock(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               pci_dev_lock(dev);
+               if (dev->subordinate)
+                       pci_bus_lock(dev->subordinate);
+       }
+}
+
+static void pci_bus_unlock(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               pci_dev_unlock(dev);
+               if (dev->subordinate)
+                       pci_bus_unlock(dev->subordinate);
+       }
+}
+
+static void pci_slot_lock(struct pci_slot *slot)
+{
+       struct pci_dev *dev;
+
+       BUG_ON(!slot);
+
+       list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+               if (dev->slot && dev->slot == slot)
+                       pci_dev_lock(dev);
+               if (dev->subordinate)
+                       pci_bus_lock(dev->subordinate);
+       }
+}
+
+static void pci_slot_unlock(struct pci_slot *slot)
+{
+       struct pci_dev *dev;
+
+       BUG_ON(!slot);
+
+       list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+               if (dev->slot && dev->slot == slot)
+                       pci_dev_unlock(dev);
+               if (dev->subordinate)
+                       pci_bus_unlock(dev->subordinate);
+       }
+}
+
+static void pci_bus_save(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               pci_dev_save(dev);
+               if (dev->subordinate)
+                       pci_bus_save(dev->subordinate);
+       }
+}
+
+static void pci_bus_restore(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               pci_dev_restore(dev);
+               if (dev->subordinate)
+                       pci_bus_restore(dev->subordinate);
+       }
+}
+
+static void pci_slot_save(struct pci_slot *slot)
+{
+       struct pci_dev *dev;
+
+       BUG_ON(!slot);
+
+       list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+               if (dev->slot && dev->slot == slot)
+                       pci_dev_save(dev);
+               if (dev->subordinate)
+                       pci_bus_save(dev->subordinate);
+       }
+}
+
+static void pci_slot_restore(struct pci_slot *slot)
+{
+       struct pci_dev *dev;
+
+       BUG_ON(!slot);
+
+       list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+               if (dev->slot && dev->slot == slot)
+                       pci_dev_restore(dev);
+               if (dev->subordinate)
+                       pci_bus_restore(dev->subordinate);
+       }
+}
+
+static int pci_slot_reset(struct pci_slot *slot, int probe)
+{
+       int rc;
+
+       if (!slot)
+               return -ENOTTY;
+
+       if (!probe)
+               pci_slot_lock(slot);
+
+       might_sleep();
+
+       rc = pci_hp_reset_slot(slot->hotplug, probe);
+
+       if (!probe)
+               pci_slot_unlock(slot);
+
+       return rc;
+}
+
+/**
+ * pci_reset_slot - reset a PCI slot
+ * @slot: PCI slot to reset
+ *
+ * A PCI bus may host multiple slots, each slot may support a reset mechanism
+ * independent of other slots.  For instance, some slots may support slot power
+ * control.  In the case of a 1:1 bus to slot architecture, this function may
+ * wrap the bus reset to avoid spurious slot related events, such as hotplug.
+ * Generally a slot reset should be attempted before a bus reset.  All of the
+ * function of the slot and any subordinate buses behind the slot are reset
+ * through this function.  PCI config space of all devices in the slot and
+ * behind the slot is saved before and restored after reset.
+ *
+ * Return 0 on success, non-zero on error.
+ */
+int pci_reset_slot(struct pci_slot *slot)
+{
+       int rc;
+
+       rc = pci_slot_reset(slot, 1);
+       if (rc)
+               return rc;
+
+       pci_slot_save(slot);
+
+       rc = pci_slot_reset(slot, 0);
+
+       pci_slot_restore(slot);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(pci_reset_slot);
+
+static int pci_bus_reset(struct pci_bus *bus, int probe)
+{
+       if (!bus->self)
+               return -ENOTTY;
+
+       if (probe)
+               return 0;
+
+       pci_bus_lock(bus);
+
+       might_sleep();
+
+       pci_reset_bridge_secondary_bus(bus->self);
+
+       pci_bus_unlock(bus);
+
+       return 0;
+}
+
+/**
+ * pci_reset_bus - reset a PCI bus
+ * @bus: top level PCI bus to reset
+ *
+ * Do a bus reset on the given bus and any subordinate buses, saving
+ * and restoring state of all devices.
+ *
+ * Return 0 on success, non-zero on error.
+ */
+int pci_reset_bus(struct pci_bus *bus)
+{
+       int rc;
+
+       rc = pci_bus_reset(bus, 1);
+       if (rc)
+               return rc;
+
+       pci_bus_save(bus);
+
+       rc = pci_bus_reset(bus, 0);
+
+       pci_bus_restore(bus);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(pci_reset_bus);
+
 /**
  * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
  * @dev: PCI device to query
diff -rupN a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
--- a/drivers/vfio/pci/vfio_pci.c       2013-06-22 19:47:31.000000000 +0000
+++ b/drivers/vfio/pci/vfio_pci.c       2013-06-27 16:04:16.125714811 +0000
@@ -208,6 +208,62 @@ static int vfio_pci_get_irq_count(struct
        return 0;
 }
 
+static bool vfio_pci_bus_contained(struct vfio_container *container,
+                                  struct pci_bus *bus)
+{
+       struct pci_dev *pdev;
+
+       list_for_each_entry(pdev, &bus->devices, bus_list)
+               if (!vfio_container_includes_locked(container, &pdev->dev) ||
+                   (pdev->subordinate &&
+                    !vfio_pci_bus_contained(container, pdev->subordinate)))
+                       return false;
+
+       return true;
+}
+
+static bool vfio_pci_slot_contained(struct vfio_container *container,
+                                   struct pci_slot *slot)
+{
+       struct pci_dev *pdev;
+
+       list_for_each_entry(pdev, &slot->bus->devices, bus_list) {
+               if (!pdev->slot || pdev->slot != slot)
+                       continue;
+
+               if (!vfio_container_includes_locked(container, &pdev->dev) ||
+                   (pdev->subordinate &&
+                    !vfio_pci_bus_contained(container, pdev->subordinate)))
+                       return false;
+       }
+
+       return true;
+}
+
+static long vfio_pci_reset_bus(struct vfio_pci_device *vdev)
+{
+       struct vfio_container *container;
+       long ret = -EBUSY;
+
+       container = vfio_container_lock_from_dev(&vdev->pdev->dev);
+       if (IS_ERR(container))
+               return PTR_ERR(container);
+
+       if (vdev->pdev->slot &&
+           vfio_pci_slot_contained(container, vdev->pdev->slot)) {
+               ret = pci_reset_slot(vdev->pdev->slot);
+               if (!ret)
+                       goto out;
+       }
+
+       if (vfio_pci_bus_contained(container, vdev->pdev->bus))
+               ret = pci_reset_bus(vdev->pdev->bus);
+out:
+       vfio_container_unlock(container);
+
+       return ret;
+}
+
 static long vfio_pci_ioctl(void *device_data,
                           unsigned int cmd, unsigned long arg)
 {
@@ -391,6 +447,8 @@ static long vfio_pci_ioctl(void *device_
        } else if (cmd == VFIO_DEVICE_RESET)
                return vdev->reset_works ?
                        pci_reset_function(vdev->pdev) : -EINVAL;
+       else if (cmd == VFIO_DEVICE_PCI_BUS_RESET)
+               return vfio_pci_reset_bus(vdev);
 
        return -ENOTTY;
 }
diff -rupN a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
--- a/drivers/vfio/vfio.c       2013-06-22 19:47:31.000000000 +0000
+++ b/drivers/vfio/vfio.c       2013-06-27 16:04:16.125714811 +0000
@@ -713,7 +713,11 @@ static long vfio_ioctl_check_extension(s
        driver = container->iommu_driver;
 
        switch (arg) {
-               /* No base extensions yet */
+#ifdef CONFIG_VFIO_PCI
+               case VFIO_CAP_DEVICE_PCI_BUS_RESET:
+                       ret = 1;
+                       break;
+#endif
        default:
                /*
                 * If no driver is set, poll all registered drivers for
@@ -1355,6 +1359,48 @@ static const struct file_operations vfio
        .mmap           = vfio_device_fops_mmap,
 };
 
+struct vfio_container *vfio_container_lock_from_dev(struct device *dev)
+{
+       struct vfio_device *device = vfio_device_get_from_dev(dev);
+       struct vfio_group *group = device->group;
+       struct vfio_container *container = group->container;
+
+       down_read(&container->group_lock);
+
+       return container;
+}
+EXPORT_SYMBOL_GPL(vfio_container_lock_from_dev);
+
+bool vfio_container_includes_locked(struct vfio_container *container,
+                                   struct device *dev)
+{
+       struct iommu_group *iommu_group;
+       struct vfio_group *group;
+       bool ret = false;
+
+       iommu_group = iommu_group_get(dev);
+       if (!iommu_group)
+               return ret;
+
+       list_for_each_entry(group, &container->group_list, container_next) {
+               if (group->iommu_group == iommu_group) {
+                       ret = true;
+                       break;
+               }
+       }
+
+       iommu_group_put(iommu_group);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(vfio_container_includes_locked);
+
+void vfio_container_unlock(struct vfio_container *container)
+{
+       up_read(&container->group_lock);
+}
+EXPORT_SYMBOL_GPL(vfio_container_unlock);
+
 /**
  * Module/class support
  */
diff -rupN a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h       2013-06-22 19:47:31.000000000 +0000
+++ b/include/linux/pci.h       2013-06-27 16:04:16.124714810 +0000
@@ -923,6 +923,9 @@ int pcie_set_mps(struct pci_dev *dev, in
 int __pci_reset_function(struct pci_dev *dev);
 int __pci_reset_function_locked(struct pci_dev *dev);
 int pci_reset_function(struct pci_dev *dev);
+int pci_reset_slot(struct pci_slot *slot);
+int pci_reset_bus(struct pci_bus *bus);
+void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
 void pci_update_resource(struct pci_dev *dev, int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
diff -rupN a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h
--- a/include/linux/pci_hotplug.h       2013-06-22 19:47:31.000000000 +0000
+++ b/include/linux/pci_hotplug.h       2013-06-27 16:05:47.609723605 +0000
@@ -63,6 +63,9 @@ enum pcie_link_width {
  * @get_adapter_status: Called to get see if an adapter is present in the slot or not.
  *     If this field is NULL, the value passed in the struct hotplug_slot_info
  *     will be used when this value is requested by a user.
+ * @reset_slot: Optional interface to allow override of a bus reset for the
+ *     slot for cases where a secondary bus reset can result in spurious
+ *     hotplug events or where a slot can be reset independent of the bus.
  *
  * The table of function pointers that is passed to the hotplug pci core by a
  * hotplug pci driver.  These functions are called by the hotplug pci core when
@@ -80,6 +83,7 @@ struct hotplug_slot_ops {
        int (*get_attention_status)     (struct hotplug_slot *slot, u8 *value);
        int (*get_latch_status)         (struct hotplug_slot *slot, u8 *value);
        int (*get_adapter_status)       (struct hotplug_slot *slot, u8 *value);
+       int (*reset_slot)               (struct hotplug_slot *slot, int probe);
 };
 
 /**
@@ -132,6 +136,15 @@ int pci_hp_deregister(struct hotplug_slo
 int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
                                         struct hotplug_slot_info *info);
 
+#ifdef CONFIG_HOTPLUG_PCI
+int pci_hp_reset_slot(struct hotplug_slot *slot, int probe);
+#else
+static inline int pci_hp_reset_slot(struct hotplug_slot *slot, int probe)
+{
+       return -ENOTTY;
+}
+#endif
+
 /* use a define to avoid include chaining to get THIS_MODULE & friends */
 #define pci_hp_register(slot, pbus, devnr, name) \
        __pci_hp_register(slot, pbus, devnr, name, THIS_MODULE, KBUILD_MODNAME)
diff -rupN a/include/linux/vfio.h b/include/linux/vfio.h
--- a/include/linux/vfio.h      2013-06-22 19:47:31.000000000 +0000
+++ b/include/linux/vfio.h      2013-06-27 16:04:16.125714811 +0000
@@ -49,6 +49,11 @@ extern struct vfio_device *vfio_device_g
 extern void vfio_device_put(struct vfio_device *device);
 extern void *vfio_device_data(struct vfio_device *device);
 
+extern struct vfio_container *vfio_container_lock_from_dev(struct device *dev);
+extern bool vfio_container_includes_locked(struct vfio_container *container,
+                                          struct device *dev);
+extern void vfio_container_unlock(struct vfio_container *container);
+
 /**
  * struct vfio_iommu_driver_ops - VFIO IOMMU driver callbacks
  */
diff -rupN a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
--- a/include/uapi/linux/vfio.h 2013-06-22 19:47:31.000000000 +0000
+++ b/include/uapi/linux/vfio.h 2013-06-27 16:04:16.125714811 +0000
@@ -59,6 +59,9 @@
  */
 #define VFIO_CHECK_EXTENSION           _IO(VFIO_TYPE, VFIO_BASE + 1)
 
+/* Support for VFIO_DEVICE_PCI_BUS_RESET */
+#define VFIO_CAP_DEVICE_PCI_BUS_RESET 1
+
 /**
  * VFIO_SET_IOMMU - _IOW(VFIO_TYPE, VFIO_BASE + 2, __s32)
  *
@@ -323,6 +326,17 @@ enum {
        VFIO_PCI_NUM_IRQS
 };
 
+/* VFIO-PCI defines the following VFIO-PCI specific device ioctl(s) */
+
+/**
+ * VFIO_DEVICE_PCI_BUS_RESET - _IO(VFIO_TYPE, VFIO_BASE + 12)
+ *
+ * Reset the PCI slot/bus of the device.  If available, a slot reset
+ * will be used instead of a bus reset.  All of the devices in or below
+ * the slot/bus will be reset and MUST be attached to the same container.
+ */
+#define VFIO_DEVICE_PCI_BUS_RESET      _IO(VFIO_TYPE, VFIO_BASE + 12)
+
 /* -------- API for Type1 VFIO IOMMU -------- */
 
 /**