Commit b1df4a56 authored by Konrad Rzeszutek Wilk's avatar Konrad Rzeszutek Wilk Committed by David Vrabel

xen/pciback: Restore configuration space when detaching from a guest.

The commit "xen/pciback: Don't deadlock when unbinding." was using
the version of pci_reset_function which would lock the device lock.
That is no good as we can dead-lock. As such we swapped to using
the lock-less version and requiring that the callers
of 'pcistub_put_pci_dev' take the device lock. And as such
this bug got exposed.

Using the lock-less version is  OK, except that we tried to
use 'pci_restore_state' after the lock-less version of
__pci_reset_function_locked - which won't work as 'state_saved'
is set to false. Said 'state_saved' is a toggle boolean that
is to be used by the sequence of a) pci_save_state/pci_restore_state
or b) pci_load_and_free_saved_state/pci_restore_state. We don't
want to use a) as the guest might have messed up the PCI
configuration space and we want it to revert to the state
when the PCI device was binded to us. Therefore we pick
b) to restore the configuration space.

We restore from our 'golden' version of PCI configuration space, when an:
 - Device is unbinded from pciback
 - Device is detached from a guest.
Reported-by: default avatarSander Eikelenboom <linux@eikelenboom.it>
Signed-off-by: default avatarKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: default avatarDavid Vrabel <david.vrabel@citrix.com>
parent 98d9b271
...@@ -105,7 +105,7 @@ static void pcistub_device_release(struct kref *kref) ...@@ -105,7 +105,7 @@ static void pcistub_device_release(struct kref *kref)
*/ */
__pci_reset_function_locked(dev); __pci_reset_function_locked(dev);
if (pci_load_and_free_saved_state(dev, &dev_data->pci_saved_state)) 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 else
pci_restore_state(dev); pci_restore_state(dev);
...@@ -257,6 +257,8 @@ void pcistub_put_pci_dev(struct pci_dev *dev) ...@@ -257,6 +257,8 @@ void pcistub_put_pci_dev(struct pci_dev *dev)
{ {
struct pcistub_device *psdev, *found_psdev = NULL; struct pcistub_device *psdev, *found_psdev = NULL;
unsigned long flags; unsigned long flags;
struct xen_pcibk_dev_data *dev_data;
int ret;
spin_lock_irqsave(&pcistub_devices_lock, flags); spin_lock_irqsave(&pcistub_devices_lock, flags);
...@@ -280,8 +282,18 @@ void pcistub_put_pci_dev(struct pci_dev *dev) ...@@ -280,8 +282,18 @@ void pcistub_put_pci_dev(struct pci_dev *dev)
*/ */
device_lock_assert(&dev->dev); device_lock_assert(&dev->dev);
__pci_reset_function_locked(dev); __pci_reset_function_locked(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. */ /* This disables the device. */
xen_pcibk_reset_device(dev); xen_pcibk_reset_device(dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment