|
|
@@ -105,7 +105,7 @@ static void pcistub_device_release(struct kref *kref)
|
|
|
*/
|
|
|
__pci_reset_function_locked(dev);
|
|
|
if (pci_load_and_free_saved_state(dev, &dev_data->pci_saved_state))
|
|
|
- dev_dbg(&dev->dev, "Could not reload PCI state\n");
|
|
|
+ dev_info(&dev->dev, "Could not reload PCI state\n");
|
|
|
else
|
|
|
pci_restore_state(dev);
|
|
|
|
|
|
@@ -250,11 +250,15 @@ struct pci_dev *pcistub_get_pci_dev(struct xen_pcibk_device *pdev,
|
|
|
* - 'echo BDF > unbind' with a guest still using it. See pcistub_remove
|
|
|
*
|
|
|
* As such we have to be careful.
|
|
|
+ *
|
|
|
+ * To make this easier, the caller has to hold the device lock.
|
|
|
*/
|
|
|
void pcistub_put_pci_dev(struct pci_dev *dev)
|
|
|
{
|
|
|
struct pcistub_device *psdev, *found_psdev = NULL;
|
|
|
unsigned long flags;
|
|
|
+ struct xen_pcibk_dev_data *dev_data;
|
|
|
+ int ret;
|
|
|
|
|
|
spin_lock_irqsave(&pcistub_devices_lock, flags);
|
|
|
|
|
|
@@ -276,13 +280,20 @@ void pcistub_put_pci_dev(struct pci_dev *dev)
|
|
|
/* Cleanup our device
|
|
|
* (so it's ready for the next domain)
|
|
|
*/
|
|
|
+ device_lock_assert(&dev->dev);
|
|
|
+ __pci_reset_function_locked(dev);
|
|
|
|
|
|
- /* This is OK - we are running from workqueue context
|
|
|
- * and want to inhibit the user from fiddling with 'reset'
|
|
|
- */
|
|
|
- pci_reset_function(dev);
|
|
|
- pci_restore_state(dev);
|
|
|
-
|
|
|
+ dev_data = pci_get_drvdata(dev);
|
|
|
+ ret = pci_load_saved_state(dev, dev_data->pci_saved_state);
|
|
|
+ if (!ret) {
|
|
|
+ /*
|
|
|
+ * The usual sequence is pci_save_state & pci_restore_state
|
|
|
+ * but the guest might have messed the configuration space up.
|
|
|
+ * Use the initial version (when device was bound to us).
|
|
|
+ */
|
|
|
+ pci_restore_state(dev);
|
|
|
+ } else
|
|
|
+ dev_info(&dev->dev, "Could not reload PCI state\n");
|
|
|
/* This disables the device. */
|
|
|
xen_pcibk_reset_device(dev);
|
|
|
|
|
|
@@ -554,12 +565,14 @@ static void pcistub_remove(struct pci_dev *dev)
|
|
|
spin_unlock_irqrestore(&pcistub_devices_lock, flags);
|
|
|
|
|
|
if (found_psdev) {
|
|
|
- dev_dbg(&dev->dev, "found device to remove - in use? %p\n",
|
|
|
- found_psdev->pdev);
|
|
|
+ dev_dbg(&dev->dev, "found device to remove %s\n",
|
|
|
+ found_psdev->pdev ? "- in-use" : "");
|
|
|
|
|
|
if (found_psdev->pdev) {
|
|
|
- pr_warn("****** removing device %s while still in-use! ******\n",
|
|
|
- pci_name(found_psdev->dev));
|
|
|
+ int domid = xen_find_device_domain_owner(dev);
|
|
|
+
|
|
|
+ pr_warn("****** removing device %s while still in-use by domain %d! ******\n",
|
|
|
+ pci_name(found_psdev->dev), domid);
|
|
|
pr_warn("****** driver domain may still access this device's i/o resources!\n");
|
|
|
pr_warn("****** shutdown driver domain before binding device\n");
|
|
|
pr_warn("****** to other drivers or domains\n");
|
|
|
@@ -567,7 +580,8 @@ static void pcistub_remove(struct pci_dev *dev)
|
|
|
/* N.B. This ends up calling pcistub_put_pci_dev which ends up
|
|
|
* doing the FLR. */
|
|
|
xen_pcibk_release_pci_dev(found_psdev->pdev,
|
|
|
- found_psdev->dev);
|
|
|
+ found_psdev->dev,
|
|
|
+ false /* caller holds the lock. */);
|
|
|
}
|
|
|
|
|
|
spin_lock_irqsave(&pcistub_devices_lock, flags);
|
|
|
@@ -629,10 +643,12 @@ static pci_ers_result_t common_process(struct pcistub_device *psdev,
|
|
|
{
|
|
|
pci_ers_result_t res = result;
|
|
|
struct xen_pcie_aer_op *aer_op;
|
|
|
+ struct xen_pcibk_device *pdev = psdev->pdev;
|
|
|
+ struct xen_pci_sharedinfo *sh_info = pdev->sh_info;
|
|
|
int ret;
|
|
|
|
|
|
/*with PV AER drivers*/
|
|
|
- aer_op = &(psdev->pdev->sh_info->aer_op);
|
|
|
+ aer_op = &(sh_info->aer_op);
|
|
|
aer_op->cmd = aer_cmd ;
|
|
|
/*useful for error_detected callback*/
|
|
|
aer_op->err = state;
|
|
|
@@ -653,36 +669,36 @@ static pci_ers_result_t common_process(struct pcistub_device *psdev,
|
|
|
* this flag to judge whether we need to check pci-front give aer
|
|
|
* service ack signal
|
|
|
*/
|
|
|
- set_bit(_PCIB_op_pending, (unsigned long *)&psdev->pdev->flags);
|
|
|
+ set_bit(_PCIB_op_pending, (unsigned long *)&pdev->flags);
|
|
|
|
|
|
/*It is possible that a pcifront conf_read_write ops request invokes
|
|
|
* the callback which cause the spurious execution of wake_up.
|
|
|
* Yet it is harmless and better than a spinlock here
|
|
|
*/
|
|
|
set_bit(_XEN_PCIB_active,
|
|
|
- (unsigned long *)&psdev->pdev->sh_info->flags);
|
|
|
+ (unsigned long *)&sh_info->flags);
|
|
|
wmb();
|
|
|
- notify_remote_via_irq(psdev->pdev->evtchn_irq);
|
|
|
+ notify_remote_via_irq(pdev->evtchn_irq);
|
|
|
|
|
|
ret = wait_event_timeout(xen_pcibk_aer_wait_queue,
|
|
|
!(test_bit(_XEN_PCIB_active, (unsigned long *)
|
|
|
- &psdev->pdev->sh_info->flags)), 300*HZ);
|
|
|
+ &sh_info->flags)), 300*HZ);
|
|
|
|
|
|
if (!ret) {
|
|
|
if (test_bit(_XEN_PCIB_active,
|
|
|
- (unsigned long *)&psdev->pdev->sh_info->flags)) {
|
|
|
+ (unsigned long *)&sh_info->flags)) {
|
|
|
dev_err(&psdev->dev->dev,
|
|
|
"pcifront aer process not responding!\n");
|
|
|
clear_bit(_XEN_PCIB_active,
|
|
|
- (unsigned long *)&psdev->pdev->sh_info->flags);
|
|
|
+ (unsigned long *)&sh_info->flags);
|
|
|
aer_op->err = PCI_ERS_RESULT_NONE;
|
|
|
return res;
|
|
|
}
|
|
|
}
|
|
|
- clear_bit(_PCIB_op_pending, (unsigned long *)&psdev->pdev->flags);
|
|
|
+ clear_bit(_PCIB_op_pending, (unsigned long *)&pdev->flags);
|
|
|
|
|
|
if (test_bit(_XEN_PCIF_active,
|
|
|
- (unsigned long *)&psdev->pdev->sh_info->flags)) {
|
|
|
+ (unsigned long *)&sh_info->flags)) {
|
|
|
dev_dbg(&psdev->dev->dev,
|
|
|
"schedule pci_conf service in " DRV_NAME "\n");
|
|
|
xen_pcibk_test_and_schedule_op(psdev->pdev);
|
|
|
@@ -1502,6 +1518,53 @@ parse_error:
|
|
|
fs_initcall(pcistub_init);
|
|
|
#endif
|
|
|
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+static struct pcistub_device *find_vfs(const struct pci_dev *pdev)
|
|
|
+{
|
|
|
+ struct pcistub_device *psdev = NULL;
|
|
|
+ unsigned long flags;
|
|
|
+ bool found = false;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&pcistub_devices_lock, flags);
|
|
|
+ list_for_each_entry(psdev, &pcistub_devices, dev_list) {
|
|
|
+ if (!psdev->pdev && psdev->dev != pdev
|
|
|
+ && pci_physfn(psdev->dev) == pdev) {
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&pcistub_devices_lock, flags);
|
|
|
+ if (found)
|
|
|
+ return psdev;
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static int pci_stub_notifier(struct notifier_block *nb,
|
|
|
+ unsigned long action, void *data)
|
|
|
+{
|
|
|
+ struct device *dev = data;
|
|
|
+ const struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
+
|
|
|
+ if (action != BUS_NOTIFY_UNBIND_DRIVER)
|
|
|
+ return NOTIFY_DONE;
|
|
|
+
|
|
|
+ if (!pdev->is_physfn)
|
|
|
+ return NOTIFY_DONE;
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ struct pcistub_device *psdev = find_vfs(pdev);
|
|
|
+ if (!psdev)
|
|
|
+ break;
|
|
|
+ device_release_driver(&psdev->dev->dev);
|
|
|
+ }
|
|
|
+ return NOTIFY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+static struct notifier_block pci_stub_nb = {
|
|
|
+ .notifier_call = pci_stub_notifier,
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
static int __init xen_pcibk_init(void)
|
|
|
{
|
|
|
int err;
|
|
|
@@ -1523,12 +1586,19 @@ static int __init xen_pcibk_init(void)
|
|
|
err = xen_pcibk_xenbus_register();
|
|
|
if (err)
|
|
|
pcistub_exit();
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+ else
|
|
|
+ bus_register_notifier(&pci_bus_type, &pci_stub_nb);
|
|
|
+#endif
|
|
|
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
static void __exit xen_pcibk_cleanup(void)
|
|
|
{
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+ bus_unregister_notifier(&pci_bus_type, &pci_stub_nb);
|
|
|
+#endif
|
|
|
xen_pcibk_xenbus_unregister();
|
|
|
pcistub_exit();
|
|
|
}
|