aboutsummaryrefslogtreecommitdiff
path: root/drivers/xen/xen-pciback
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen/xen-pciback')
-rw-r--r--drivers/xen/xen-pciback/conf_space.c10
-rw-r--r--drivers/xen/xen-pciback/conf_space_header.c21
-rw-r--r--drivers/xen/xen-pciback/conf_space_quirks.c3
-rw-r--r--drivers/xen/xen-pciback/passthrough.c34
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c376
-rw-r--r--drivers/xen/xen-pciback/pciback.h35
-rw-r--r--drivers/xen/xen-pciback/pciback_ops.c35
-rw-r--r--drivers/xen/xen-pciback/vpci.c61
-rw-r--r--drivers/xen/xen-pciback/xenbus.c63
9 files changed, 404 insertions, 234 deletions
diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c
index a8031445d94..46ae0f9f02a 100644
--- a/drivers/xen/xen-pciback/conf_space.c
+++ b/drivers/xen/xen-pciback/conf_space.c
@@ -10,13 +10,13 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include "pciback.h"
#include "conf_space.h"
#include "conf_space_quirks.h"
-#define DRV_NAME "xen-pciback"
-static int permissive;
+static bool permissive;
module_param(permissive, bool, 0644);
/* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word,
@@ -124,7 +124,7 @@ static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask,
return val;
}
-static int pcibios_err_to_errno(int err)
+static int xen_pcibios_err_to_errno(int err)
{
switch (err) {
case PCIBIOS_SUCCESSFUL:
@@ -202,7 +202,7 @@ out:
pci_name(dev), size, offset, value);
*ret_val = value;
- return pcibios_err_to_errno(err);
+ return xen_pcibios_err_to_errno(err);
}
int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value)
@@ -290,7 +290,7 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value)
}
}
- return pcibios_err_to_errno(err);
+ return xen_pcibios_err_to_errno(err);
}
void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev)
diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c
index da3cbdfcb5d..c5ee82587e8 100644
--- a/drivers/xen/xen-pciback/conf_space_header.c
+++ b/drivers/xen/xen-pciback/conf_space_header.c
@@ -4,6 +4,8 @@
* Author: Ryan Wilson <hap9@epoch.ncsc.mil>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/pci.h>
#include "pciback.h"
@@ -15,7 +17,6 @@ struct pci_bar_info {
int which;
};
-#define DRV_NAME "xen-pciback"
#define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO))
#define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER)
@@ -25,7 +26,7 @@ static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
int ret;
ret = xen_pcibk_read_config_word(dev, offset, value, data);
- if (!atomic_read(&dev->enable_cnt))
+ if (!pci_is_enabled(dev))
return ret;
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
@@ -76,10 +77,8 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
pci_name(dev));
err = pci_set_mwi(dev);
if (err) {
- printk(KERN_WARNING
- DRV_NAME ": %s: cannot enable "
- "memory-write-invalidate (%d)\n",
- pci_name(dev), err);
+ pr_warn("%s: cannot enable memory-write-invalidate (%d)\n",
+ pci_name(dev), err);
value &= ~PCI_COMMAND_INVALIDATE;
}
}
@@ -92,7 +91,7 @@ static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data)
struct pci_bar_info *bar = data;
if (unlikely(!bar)) {
- printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n",
+ pr_warn(DRV_NAME ": driver data not found for %s\n",
pci_name(dev));
return XEN_PCI_ERR_op_failed;
}
@@ -126,7 +125,7 @@ static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data)
struct pci_bar_info *bar = data;
if (unlikely(!bar)) {
- printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n",
+ pr_warn(DRV_NAME ": driver data not found for %s\n",
pci_name(dev));
return XEN_PCI_ERR_op_failed;
}
@@ -154,7 +153,7 @@ static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data)
struct pci_bar_info *bar = data;
if (unlikely(!bar)) {
- printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n",
+ pr_warn(DRV_NAME ": driver data not found for %s\n",
pci_name(dev));
return XEN_PCI_ERR_op_failed;
}
@@ -187,7 +186,7 @@ static inline void read_dev_bar(struct pci_dev *dev,
bar_info->val = res[pos].start |
(res[pos].flags & PCI_REGION_FLAG_MASK);
- bar_info->len_val = res[pos].end - res[pos].start + 1;
+ bar_info->len_val = resource_size(&res[pos]);
}
static void *bar_init(struct pci_dev *dev, int offset)
@@ -376,7 +375,7 @@ int xen_pcibk_config_header_add_fields(struct pci_dev *dev)
default:
err = -EINVAL;
- printk(KERN_ERR DRV_NAME ": %s: Unsupported header type %d!\n",
+ pr_err("%s: Unsupported header type %d!\n",
pci_name(dev), dev->hdr_type);
break;
}
diff --git a/drivers/xen/xen-pciback/conf_space_quirks.c b/drivers/xen/xen-pciback/conf_space_quirks.c
index 921a889e65e..7476791cab4 100644
--- a/drivers/xen/xen-pciback/conf_space_quirks.c
+++ b/drivers/xen/xen-pciback/conf_space_quirks.c
@@ -12,7 +12,6 @@
#include "conf_space_quirks.h"
LIST_HEAD(xen_pcibk_quirks);
-#define DRV_NAME "xen-pciback"
static inline const struct pci_device_id *
match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
{
@@ -36,7 +35,7 @@ static struct xen_pcibk_config_quirk *xen_pcibk_find_quirk(struct pci_dev *dev)
goto out;
tmp_quirk = NULL;
printk(KERN_DEBUG DRV_NAME
- ":quirk didn't match any device xen_pciback knows about\n");
+ ": quirk didn't match any device known\n");
out:
return tmp_quirk;
}
diff --git a/drivers/xen/xen-pciback/passthrough.c b/drivers/xen/xen-pciback/passthrough.c
index 1d32a9a42c0..828dddc360d 100644
--- a/drivers/xen/xen-pciback/passthrough.c
+++ b/drivers/xen/xen-pciback/passthrough.c
@@ -7,13 +7,13 @@
#include <linux/list.h>
#include <linux/pci.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include "pciback.h"
struct passthrough_dev_data {
/* Access to dev_list must be protected by lock */
struct list_head dev_list;
- spinlock_t lock;
+ struct mutex lock;
};
static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev,
@@ -24,9 +24,8 @@ static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev,
struct passthrough_dev_data *dev_data = pdev->pci_dev_data;
struct pci_dev_entry *dev_entry;
struct pci_dev *dev = NULL;
- unsigned long flags;
- spin_lock_irqsave(&dev_data->lock, flags);
+ mutex_lock(&dev_data->lock);
list_for_each_entry(dev_entry, &dev_data->dev_list, list) {
if (domain == (unsigned int)pci_domain_nr(dev_entry->dev->bus)
@@ -37,7 +36,7 @@ static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev,
}
}
- spin_unlock_irqrestore(&dev_data->lock, flags);
+ mutex_unlock(&dev_data->lock);
return dev;
}
@@ -48,7 +47,6 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
{
struct passthrough_dev_data *dev_data = pdev->pci_dev_data;
struct pci_dev_entry *dev_entry;
- unsigned long flags;
unsigned int domain, bus, devfn;
int err;
@@ -57,9 +55,9 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
return -ENOMEM;
dev_entry->dev = dev;
- spin_lock_irqsave(&dev_data->lock, flags);
+ mutex_lock(&dev_data->lock);
list_add_tail(&dev_entry->list, &dev_data->dev_list);
- spin_unlock_irqrestore(&dev_data->lock, flags);
+ mutex_unlock(&dev_data->lock);
/* Publish this device. */
domain = (unsigned int)pci_domain_nr(dev->bus);
@@ -76,9 +74,8 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev,
struct passthrough_dev_data *dev_data = pdev->pci_dev_data;
struct pci_dev_entry *dev_entry, *t;
struct pci_dev *found_dev = NULL;
- unsigned long flags;
- spin_lock_irqsave(&dev_data->lock, flags);
+ mutex_lock(&dev_data->lock);
list_for_each_entry_safe(dev_entry, t, &dev_data->dev_list, list) {
if (dev_entry->dev == dev) {
@@ -88,7 +85,7 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev,
}
}
- spin_unlock_irqrestore(&dev_data->lock, flags);
+ mutex_unlock(&dev_data->lock);
if (found_dev)
pcistub_put_pci_dev(found_dev);
@@ -102,7 +99,7 @@ static int __xen_pcibk_init_devices(struct xen_pcibk_device *pdev)
if (!dev_data)
return -ENOMEM;
- spin_lock_init(&dev_data->lock);
+ mutex_init(&dev_data->lock);
INIT_LIST_HEAD(&dev_data->dev_list);
@@ -116,14 +113,14 @@ static int __xen_pcibk_publish_pci_roots(struct xen_pcibk_device *pdev,
{
int err = 0;
struct passthrough_dev_data *dev_data = pdev->pci_dev_data;
- struct pci_dev_entry *dev_entry, *e, *tmp;
+ struct pci_dev_entry *dev_entry, *e;
struct pci_dev *dev;
int found;
unsigned int domain, bus;
- spin_lock(&dev_data->lock);
+ mutex_lock(&dev_data->lock);
- list_for_each_entry_safe(dev_entry, tmp, &dev_data->dev_list, list) {
+ list_for_each_entry(dev_entry, &dev_data->dev_list, list) {
/* Only publish this device as a root if none of its
* parent bridges are exported
*/
@@ -142,16 +139,13 @@ static int __xen_pcibk_publish_pci_roots(struct xen_pcibk_device *pdev,
bus = (unsigned int)dev_entry->dev->bus->number;
if (!found) {
- spin_unlock(&dev_data->lock);
err = publish_root_cb(pdev, domain, bus);
if (err)
break;
- spin_lock(&dev_data->lock);
}
}
- if (!err)
- spin_unlock(&dev_data->lock);
+ mutex_unlock(&dev_data->lock);
return err;
}
@@ -182,7 +176,7 @@ static int __xen_pcibk_get_pcifront_dev(struct pci_dev *pcidev,
return 1;
}
-struct xen_pcibk_backend xen_pcibk_passthrough_backend = {
+const struct xen_pcibk_backend xen_pcibk_passthrough_backend = {
.name = "passthrough",
.init = __xen_pcibk_init_devices,
.free = __xen_pcibk_release_devices,
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index aec214ac0a1..d57a173685f 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -4,6 +4,9 @@
* Ryan Wilson <hap9@epoch.ncsc.mil>
* Chris Bookholt <hap10@epoch.ncsc.mil>
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rwsem.h>
@@ -17,12 +20,11 @@
#include <xen/events.h>
#include <asm/xen/pci.h>
#include <asm/xen/hypervisor.h>
+#include <xen/interface/physdev.h>
#include "pciback.h"
#include "conf_space.h"
#include "conf_space_quirks.h"
-#define DRV_NAME "xen-pciback"
-
static char *pci_devs_to_hide;
wait_queue_head_t xen_pcibk_aer_wait_queue;
/*Add sem for sync AER handling and xen_pcibk remove/reconfigue ops,
@@ -87,21 +89,52 @@ static struct pcistub_device *pcistub_device_alloc(struct pci_dev *dev)
static void pcistub_device_release(struct kref *kref)
{
struct pcistub_device *psdev;
+ struct pci_dev *dev;
+ struct xen_pcibk_dev_data *dev_data;
psdev = container_of(kref, struct pcistub_device, kref);
+ dev = psdev->dev;
+ dev_data = pci_get_drvdata(dev);
+
+ dev_dbg(&dev->dev, "pcistub_device_release\n");
- dev_dbg(&psdev->dev->dev, "pcistub_device_release\n");
+ xen_unregister_device_domain_owner(dev);
- xen_unregister_device_domain_owner(psdev->dev);
+ /* Call the reset function which does not take lock as this
+ * is called from "unbind" which takes a device_lock mutex.
+ */
+ __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");
+ else
+ pci_restore_state(dev);
+
+ if (dev->msix_cap) {
+ struct physdev_pci_device ppdev = {
+ .seg = pci_domain_nr(dev->bus),
+ .bus = dev->bus->number,
+ .devfn = dev->devfn
+ };
+ int err = HYPERVISOR_physdev_op(PHYSDEVOP_release_msix,
+ &ppdev);
+
+ if (err)
+ dev_warn(&dev->dev, "MSI-X release failed (%d)\n",
+ err);
+ }
+
+ /* Disable the device */
+ xen_pcibk_reset_device(dev);
+
+ kfree(dev_data);
+ pci_set_drvdata(dev, NULL);
/* Clean-up the device */
- xen_pcibk_reset_device(psdev->dev);
- xen_pcibk_config_free_dyn_fields(psdev->dev);
- xen_pcibk_config_free_dev(psdev->dev);
- kfree(pci_get_drvdata(psdev->dev));
- pci_set_drvdata(psdev->dev, NULL);
+ xen_pcibk_config_free_dyn_fields(dev);
+ xen_pcibk_config_free_dev(dev);
- pci_dev_put(psdev->dev);
+ dev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
+ pci_dev_put(dev);
kfree(psdev);
}
@@ -128,7 +161,8 @@ static struct pcistub_device *pcistub_device_find(int domain, int bus,
if (psdev->dev != NULL
&& domain == pci_domain_nr(psdev->dev->bus)
&& bus == psdev->dev->bus->number
- && PCI_DEVFN(slot, func) == psdev->dev->devfn) {
+ && slot == PCI_SLOT(psdev->dev->devfn)
+ && func == PCI_FUNC(psdev->dev->devfn)) {
pcistub_device_get(psdev);
goto out;
}
@@ -177,7 +211,8 @@ struct pci_dev *pcistub_get_pci_dev_by_slot(struct xen_pcibk_device *pdev,
if (psdev->dev != NULL
&& domain == pci_domain_nr(psdev->dev->bus)
&& bus == psdev->dev->bus->number
- && PCI_DEVFN(slot, func) == psdev->dev->devfn) {
+ && slot == PCI_SLOT(psdev->dev->devfn)
+ && func == PCI_FUNC(psdev->dev->devfn)) {
found_dev = pcistub_device_get_pci_dev(pdev, psdev);
break;
}
@@ -207,6 +242,15 @@ struct pci_dev *pcistub_get_pci_dev(struct xen_pcibk_device *pdev,
return found_dev;
}
+/*
+ * Called when:
+ * - XenBus state has been reconfigure (pci unplug). See xen_pcibk_remove_device
+ * - XenBus state has been disconnected (guest shutdown). See xen_pcibk_xenbus_remove
+ * - 'echo BDF > unbind' on pciback module with no guest attached. See pcistub_remove
+ * - 'echo BDF > unbind' with a guest still using it. See pcistub_remove
+ *
+ * As such we have to be careful.
+ */
void pcistub_put_pci_dev(struct pci_dev *dev)
{
struct pcistub_device *psdev, *found_psdev = NULL;
@@ -222,6 +266,8 @@ void pcistub_put_pci_dev(struct pci_dev *dev)
}
spin_unlock_irqrestore(&pcistub_devices_lock, flags);
+ if (WARN_ON(!found_psdev))
+ return;
/*hold this lock for avoiding breaking link between
* pcistub and xen_pcibk when AER is in processing
@@ -230,9 +276,21 @@ void pcistub_put_pci_dev(struct pci_dev *dev)
/* Cleanup our device
* (so it's ready for the next domain)
*/
- xen_pcibk_reset_device(found_psdev->dev);
- xen_pcibk_config_free_dyn_fields(found_psdev->dev);
- xen_pcibk_config_reset_dev(found_psdev->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);
+
+ /* This disables the device. */
+ xen_pcibk_reset_device(dev);
+
+ /* And cleanup up our emulated fields. */
+ xen_pcibk_config_reset_dev(dev);
+ xen_pcibk_config_free_dyn_fields(dev);
+
+ xen_unregister_device_domain_owner(dev);
spin_lock_irqsave(&found_psdev->lock, flags);
found_psdev->pdev = NULL;
@@ -242,8 +300,8 @@ void pcistub_put_pci_dev(struct pci_dev *dev)
up_write(&pcistub_sem);
}
-static int __devinit pcistub_match_one(struct pci_dev *dev,
- struct pcistub_device_id *pdev_id)
+static int pcistub_match_one(struct pci_dev *dev,
+ struct pcistub_device_id *pdev_id)
{
/* Match the specified device by domain, bus, slot, func and also if
* any of the device's parent bridges match.
@@ -262,7 +320,7 @@ static int __devinit pcistub_match_one(struct pci_dev *dev,
return 0;
}
-static int __devinit pcistub_match(struct pci_dev *dev)
+static int pcistub_match(struct pci_dev *dev)
{
struct pcistub_device_id *pdev_id;
unsigned long flags;
@@ -280,7 +338,7 @@ static int __devinit pcistub_match(struct pci_dev *dev)
return found;
}
-static int __devinit pcistub_init_device(struct pci_dev *dev)
+static int pcistub_init_device(struct pci_dev *dev)
{
struct xen_pcibk_dev_data *dev_data;
int err = 0;
@@ -325,12 +383,37 @@ static int __devinit pcistub_init_device(struct pci_dev *dev)
if (err)
goto config_release;
+ if (dev->msix_cap) {
+ struct physdev_pci_device ppdev = {
+ .seg = pci_domain_nr(dev->bus),
+ .bus = dev->bus->number,
+ .devfn = dev->devfn
+ };
+
+ err = HYPERVISOR_physdev_op(PHYSDEVOP_prepare_msix, &ppdev);
+ if (err)
+ dev_err(&dev->dev, "MSI-X preparation failed (%d)\n",
+ err);
+ }
+
+ /* We need the device active to save the state. */
+ dev_dbg(&dev->dev, "save state of device\n");
+ pci_save_state(dev);
+ dev_data->pci_saved_state = pci_store_saved_state(dev);
+ if (!dev_data->pci_saved_state)
+ dev_err(&dev->dev, "Could not store PCI conf saved state!\n");
+ else {
+ dev_dbg(&dev->dev, "resetting (FLR, D3, etc) the device\n");
+ __pci_reset_function_locked(dev);
+ pci_restore_state(dev);
+ }
/* Now disable the device (this also ensures some private device
* data is setup before we export)
*/
dev_dbg(&dev->dev, "reset device\n");
xen_pcibk_reset_device(dev);
+ dev->dev_flags |= PCI_DEV_FLAGS_ASSIGNED;
return 0;
config_release:
@@ -354,8 +437,6 @@ static int __init pcistub_init_devices_late(void)
unsigned long flags;
int err = 0;
- pr_debug(DRV_NAME ": pcistub_init_devices_late\n");
-
spin_lock_irqsave(&pcistub_devices_lock, flags);
while (!list_empty(&seized_devices)) {
@@ -386,7 +467,7 @@ static int __init pcistub_init_devices_late(void)
return 0;
}
-static int __devinit pcistub_seize(struct pci_dev *dev)
+static int pcistub_seize(struct pci_dev *dev)
{
struct pcistub_device *psdev;
unsigned long flags;
@@ -421,8 +502,9 @@ static int __devinit pcistub_seize(struct pci_dev *dev)
return err;
}
-static int __devinit pcistub_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
+/* Called when 'bind'. This means we must _NOT_ call pci_reset_function or
+ * other functions that take the sysfs lock. */
+static int pcistub_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int err = 0;
@@ -449,6 +531,8 @@ out:
return err;
}
+/* Called when 'unbind'. This means we must _NOT_ call pci_reset_function or
+ * other functions that take the sysfs lock. */
static void pcistub_remove(struct pci_dev *dev)
{
struct pcistub_device *psdev, *found_psdev = NULL;
@@ -474,16 +558,14 @@ static void pcistub_remove(struct pci_dev *dev)
found_psdev->pdev);
if (found_psdev->pdev) {
- printk(KERN_WARNING DRV_NAME ": ****** removing device "
- "%s while still in-use! ******\n",
+ pr_warn("****** removing device %s while still in-use! ******\n",
pci_name(found_psdev->dev));
- printk(KERN_WARNING DRV_NAME ": ****** driver domain may"
- " still access this device's i/o resources!\n");
- printk(KERN_WARNING DRV_NAME ": ****** shutdown driver "
- "domain before binding device\n");
- printk(KERN_WARNING DRV_NAME ": ****** to other drivers "
- "or domains\n");
+ 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");
+ /* 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);
}
@@ -514,12 +596,9 @@ static void kill_domain_by_device(struct pcistub_device *psdev)
int err;
char nodename[PCI_NODENAME_MAX];
- if (!psdev)
- dev_err(&psdev->dev->dev,
- "device is NULL when do AER recovery/kill_domain\n");
+ BUG_ON(!psdev);
snprintf(nodename, PCI_NODENAME_MAX, "/local/domain/0/backend/pci/%d/0",
psdev->pdev->xdev->otherend_id);
- nodename[strlen(nodename)] = '\0';
again:
err = xenbus_transaction_start(&xbt);
@@ -605,7 +684,7 @@ static pci_ers_result_t common_process(struct pcistub_device *psdev,
if (test_bit(_XEN_PCIF_active,
(unsigned long *)&psdev->pdev->sh_info->flags)) {
dev_dbg(&psdev->dev->dev,
- "schedule pci_conf service in xen_pcibk\n");
+ "schedule pci_conf service in " DRV_NAME "\n");
xen_pcibk_test_and_schedule_op(psdev->pdev);
}
@@ -645,14 +724,14 @@ static pci_ers_result_t xen_pcibk_slot_reset(struct pci_dev *dev)
dev_err(&dev->dev, DRV_NAME " device is not connected or owned"
" by HVM, kill it\n");
kill_domain_by_device(psdev);
- goto release;
+ goto end;
}
if (!test_bit(_XEN_PCIB_AERHANDLER,
(unsigned long *)&psdev->pdev->sh_info->flags)) {
dev_err(&dev->dev,
"guest with no AER driver should have been killed\n");
- goto release;
+ goto end;
}
result = common_process(psdev, 1, XEN_PCI_OP_aer_slotreset, result);
@@ -662,9 +741,9 @@ static pci_ers_result_t xen_pcibk_slot_reset(struct pci_dev *dev)
"No AER slot_reset service or disconnected!\n");
kill_domain_by_device(psdev);
}
-release:
- pcistub_device_put(psdev);
end:
+ if (psdev)
+ pcistub_device_put(psdev);
up_write(&pcistub_sem);
return result;
@@ -703,14 +782,14 @@ static pci_ers_result_t xen_pcibk_mmio_enabled(struct pci_dev *dev)
dev_err(&dev->dev, DRV_NAME " device is not connected or owned"
" by HVM, kill it\n");
kill_domain_by_device(psdev);
- goto release;
+ goto end;
}
if (!test_bit(_XEN_PCIB_AERHANDLER,
(unsigned long *)&psdev->pdev->sh_info->flags)) {
dev_err(&dev->dev,
"guest with no AER driver should have been killed\n");
- goto release;
+ goto end;
}
result = common_process(psdev, 1, XEN_PCI_OP_aer_mmio, result);
@@ -720,9 +799,9 @@ static pci_ers_result_t xen_pcibk_mmio_enabled(struct pci_dev *dev)
"No AER mmio_enabled service or disconnected!\n");
kill_domain_by_device(psdev);
}
-release:
- pcistub_device_put(psdev);
end:
+ if (psdev)
+ pcistub_device_put(psdev);
up_write(&pcistub_sem);
return result;
}
@@ -761,7 +840,7 @@ static pci_ers_result_t xen_pcibk_error_detected(struct pci_dev *dev,
dev_err(&dev->dev, DRV_NAME " device is not connected or owned"
" by HVM, kill it\n");
kill_domain_by_device(psdev);
- goto release;
+ goto end;
}
/*Guest owns the device yet no aer handler regiested, kill guest*/
@@ -769,7 +848,7 @@ static pci_ers_result_t xen_pcibk_error_detected(struct pci_dev *dev,
(unsigned long *)&psdev->pdev->sh_info->flags)) {
dev_dbg(&dev->dev, "guest may have no aer driver, kill it\n");
kill_domain_by_device(psdev);
- goto release;
+ goto end;
}
result = common_process(psdev, error, XEN_PCI_OP_aer_detected, result);
@@ -779,9 +858,9 @@ static pci_ers_result_t xen_pcibk_error_detected(struct pci_dev *dev,
"No AER error_detected service or disconnected!\n");
kill_domain_by_device(psdev);
}
-release:
- pcistub_device_put(psdev);
end:
+ if (psdev)
+ pcistub_device_put(psdev);
up_write(&pcistub_sem);
return result;
}
@@ -815,7 +894,7 @@ static void xen_pcibk_error_resume(struct pci_dev *dev)
dev_err(&dev->dev, DRV_NAME " device is not connected or owned"
" by HVM, kill it\n");
kill_domain_by_device(psdev);
- goto release;
+ goto end;
}
if (!test_bit(_XEN_PCIB_AERHANDLER,
@@ -823,19 +902,19 @@ static void xen_pcibk_error_resume(struct pci_dev *dev)
dev_err(&dev->dev,
"guest with no AER driver should have been killed\n");
kill_domain_by_device(psdev);
- goto release;
+ goto end;
}
common_process(psdev, 1, XEN_PCI_OP_aer_resume,
PCI_ERS_RESULT_RECOVERED);
-release:
- pcistub_device_put(psdev);
end:
+ if (psdev)
+ pcistub_device_put(psdev);
up_write(&pcistub_sem);
return;
}
/*add xen_pcibk AER handling*/
-static struct pci_error_handlers xen_pcibk_error_handler = {
+static const struct pci_error_handlers xen_pcibk_error_handler = {
.error_detected = xen_pcibk_error_detected,
.mmio_enabled = xen_pcibk_mmio_enabled,
.slot_reset = xen_pcibk_slot_reset,
@@ -860,18 +939,35 @@ static struct pci_driver xen_pcibk_pci_driver = {
static inline int str_to_slot(const char *buf, int *domain, int *bus,
int *slot, int *func)
{
- int err;
-
- err = sscanf(buf, " %x:%x:%x.%x", domain, bus, slot, func);
- if (err == 4)
+ int parsed = 0;
+
+ switch (sscanf(buf, " %x:%x:%x.%x %n", domain, bus, slot, func,
+ &parsed)) {
+ case 3:
+ *func = -1;
+ sscanf(buf, " %x:%x:%x.* %n", domain, bus, slot, &parsed);
+ break;
+ case 2:
+ *slot = *func = -1;
+ sscanf(buf, " %x:%x:*.* %n", domain, bus, &parsed);
+ break;
+ }
+ if (parsed && !buf[parsed])
return 0;
- else if (err < 0)
- return -EINVAL;
/* try again without domain */
*domain = 0;
- err = sscanf(buf, " %x:%x.%x", bus, slot, func);
- if (err == 3)
+ switch (sscanf(buf, " %x:%x.%x %n", bus, slot, func, &parsed)) {
+ case 2:
+ *func = -1;
+ sscanf(buf, " %x:%x.* %n", bus, slot, &parsed);
+ break;
+ case 1:
+ *slot = *func = -1;
+ sscanf(buf, " %x:*.* %n", bus, &parsed);
+ break;
+ }
+ if (parsed && !buf[parsed])
return 0;
return -EINVAL;
@@ -880,13 +976,20 @@ static inline int str_to_slot(const char *buf, int *domain, int *bus,
static inline int str_to_quirk(const char *buf, int *domain, int *bus, int
*slot, int *func, int *reg, int *size, int *mask)
{
- int err;
+ int parsed = 0;
- err =
- sscanf(buf, " %04x:%02x:%02x.%1x-%08x:%1x:%08x", domain, bus, slot,
- func, reg, size, mask);
- if (err == 7)
+ sscanf(buf, " %x:%x:%x.%x-%x:%x:%x %n", domain, bus, slot, func,
+ reg, size, mask, &parsed);
+ if (parsed && !buf[parsed])
return 0;
+
+ /* try again without domain */
+ *domain = 0;
+ sscanf(buf, " %x:%x.%x-%x:%x:%x %n", bus, slot, func, reg, size,
+ mask, &parsed);
+ if (parsed && !buf[parsed])
+ return 0;
+
return -EINVAL;
}
@@ -894,6 +997,30 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func)
{
struct pcistub_device_id *pci_dev_id;
unsigned long flags;
+ int rc = 0, devfn = PCI_DEVFN(slot, func);
+
+ if (slot < 0) {
+ for (slot = 0; !rc && slot < 32; ++slot)
+ rc = pcistub_device_id_add(domain, bus, slot, func);
+ return rc;
+ }
+
+ if (func < 0) {
+ for (func = 0; !rc && func < 8; ++func)
+ rc = pcistub_device_id_add(domain, bus, slot, func);
+ return rc;
+ }
+
+ if ((
+#if !defined(MODULE) /* pci_domains_supported is not being exported */ \
+ || !defined(CONFIG_PCI_DOMAINS)
+ !pci_domains_supported ? domain :
+#endif
+ domain < 0 || domain > 0xffff)
+ || bus < 0 || bus > 0xff
+ || PCI_SLOT(devfn) != slot
+ || PCI_FUNC(devfn) != func)
+ return -EINVAL;
pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
if (!pci_dev_id)
@@ -901,9 +1028,9 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func)
pci_dev_id->domain = domain;
pci_dev_id->bus = bus;
- pci_dev_id->devfn = PCI_DEVFN(slot, func);
+ pci_dev_id->devfn = devfn;
- pr_debug(DRV_NAME ": wants to seize %04x:%02x:%02x.%01x\n",
+ pr_debug("wants to seize %04x:%02x:%02x.%d\n",
domain, bus, slot, func);
spin_lock_irqsave(&device_ids_lock, flags);
@@ -916,15 +1043,15 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func)
static int pcistub_device_id_remove(int domain, int bus, int slot, int func)
{
struct pcistub_device_id *pci_dev_id, *t;
- int devfn = PCI_DEVFN(slot, func);
int err = -ENOENT;
unsigned long flags;
spin_lock_irqsave(&device_ids_lock, flags);
list_for_each_entry_safe(pci_dev_id, t, &pcistub_device_ids,
slot_list) {
- if (pci_dev_id->domain == domain
- && pci_dev_id->bus == bus && pci_dev_id->devfn == devfn) {
+ if (pci_dev_id->domain == domain && pci_dev_id->bus == bus
+ && (slot < 0 || PCI_SLOT(pci_dev_id->devfn) == slot)
+ && (func < 0 || PCI_FUNC(pci_dev_id->devfn) == func)) {
/* Don't break; here because it's possible the same
* slot could be in the list more than once
*/
@@ -933,8 +1060,8 @@ static int pcistub_device_id_remove(int domain, int bus, int slot, int func)
err = 0;
- pr_debug(DRV_NAME ": removed %04x:%02x:%02x.%01x from "
- "seize list\n", domain, bus, slot, func);
+ pr_debug("removed %04x:%02x:%02x.%d from seize list\n",
+ domain, bus, slot, func);
}
}
spin_unlock_irqrestore(&device_ids_lock, flags);
@@ -942,16 +1069,20 @@ static int pcistub_device_id_remove(int domain, int bus, int slot, int func)
return err;
}
-static int pcistub_reg_add(int domain, int bus, int slot, int func, int reg,
- int size, int mask)
+static int pcistub_reg_add(int domain, int bus, int slot, int func,
+ unsigned int reg, unsigned int size,
+ unsigned int mask)
{
int err = 0;
struct pcistub_device *psdev;
struct pci_dev *dev;
struct config_field *field;
+ if (reg > 0xfff || (size < 4 && (mask >> (size * 8))))
+ return -EINVAL;
+
psdev = pcistub_device_find(domain, bus, slot, func);
- if (!psdev || !psdev->dev) {
+ if (!psdev) {
err = -ENODEV;
goto out;
}
@@ -975,6 +1106,8 @@ static int pcistub_reg_add(int domain, int bus, int slot, int func, int reg,
if (err)
kfree(field);
out:
+ if (psdev)
+ pcistub_device_put(psdev);
return err;
}
@@ -995,8 +1128,7 @@ out:
err = count;
return err;
}
-
-DRIVER_ATTR(new_slot, S_IWUSR, NULL, pcistub_slot_add);
+static DRIVER_ATTR(new_slot, S_IWUSR, NULL, pcistub_slot_add);
static ssize_t pcistub_slot_remove(struct device_driver *drv, const char *buf,
size_t count)
@@ -1015,8 +1147,7 @@ out:
err = count;
return err;
}
-
-DRIVER_ATTR(remove_slot, S_IWUSR, NULL, pcistub_slot_remove);
+static DRIVER_ATTR(remove_slot, S_IWUSR, NULL, pcistub_slot_remove);
static ssize_t pcistub_slot_show(struct device_driver *drv, char *buf)
{
@@ -1030,7 +1161,7 @@ static ssize_t pcistub_slot_show(struct device_driver *drv, char *buf)
break;
count += scnprintf(buf + count, PAGE_SIZE - count,
- "%04x:%02x:%02x.%01x\n",
+ "%04x:%02x:%02x.%d\n",
pci_dev_id->domain, pci_dev_id->bus,
PCI_SLOT(pci_dev_id->devfn),
PCI_FUNC(pci_dev_id->devfn));
@@ -1039,8 +1170,7 @@ static ssize_t pcistub_slot_show(struct device_driver *drv, char *buf)
return count;
}
-
-DRIVER_ATTR(slots, S_IRUSR, pcistub_slot_show, NULL);
+static DRIVER_ATTR(slots, S_IRUSR, pcistub_slot_show, NULL);
static ssize_t pcistub_irq_handler_show(struct device_driver *drv, char *buf)
{
@@ -1069,8 +1199,7 @@ static ssize_t pcistub_irq_handler_show(struct device_driver *drv, char *buf)
spin_unlock_irqrestore(&pcistub_devices_lock, flags);
return count;
}
-
-DRIVER_ATTR(irq_handlers, S_IRUSR, pcistub_irq_handler_show, NULL);
+static DRIVER_ATTR(irq_handlers, S_IRUSR, pcistub_irq_handler_show, NULL);
static ssize_t pcistub_irq_handler_switch(struct device_driver *drv,
const char *buf,
@@ -1079,20 +1208,23 @@ static ssize_t pcistub_irq_handler_switch(struct device_driver *drv,
struct pcistub_device *psdev;
struct xen_pcibk_dev_data *dev_data;
int domain, bus, slot, func;
- int err = -ENOENT;
+ int err;
err = str_to_slot(buf, &domain, &bus, &slot, &func);
if (err)
- goto out;
+ return err;
psdev = pcistub_device_find(domain, bus, slot, func);
-
- if (!psdev)
+ if (!psdev) {
+ err = -ENOENT;
goto out;
+ }
dev_data = pci_get_drvdata(psdev->dev);
- if (!dev_data)
+ if (!dev_data) {
+ err = -ENOENT;
goto out;
+ }
dev_dbg(&psdev->dev->dev, "%s fake irq handler: %d->%d\n",
dev_data->irq_name, dev_data->isr_on,
@@ -1102,11 +1234,14 @@ static ssize_t pcistub_irq_handler_switch(struct device_driver *drv,
if (dev_data->isr_on)
dev_data->ack_intr = 1;
out:
+ if (psdev)
+ pcistub_device_put(psdev);
if (!err)
err = count;
return err;
}
-DRIVER_ATTR(irq_handler_state, S_IWUSR, NULL, pcistub_irq_handler_switch);
+static DRIVER_ATTR(irq_handler_state, S_IWUSR, NULL,
+ pcistub_irq_handler_switch);
static ssize_t pcistub_quirk_add(struct device_driver *drv, const char *buf,
size_t count)
@@ -1170,8 +1305,8 @@ out:
return count;
}
-
-DRIVER_ATTR(quirks, S_IRUSR | S_IWUSR, pcistub_quirk_show, pcistub_quirk_add);
+static DRIVER_ATTR(quirks, S_IRUSR | S_IWUSR, pcistub_quirk_show,
+ pcistub_quirk_add);
static ssize_t permissive_add(struct device_driver *drv, const char *buf,
size_t count)
@@ -1180,18 +1315,17 @@ static ssize_t permissive_add(struct device_driver *drv, const char *buf,
int err;
struct pcistub_device *psdev;
struct xen_pcibk_dev_data *dev_data;
+
err = str_to_slot(buf, &domain, &bus, &slot, &func);
if (err)
goto out;
+
psdev = pcistub_device_find(domain, bus, slot, func);
if (!psdev) {
err = -ENODEV;
goto out;
}
- if (!psdev->dev) {
- err = -ENODEV;
- goto release;
- }
+
dev_data = pci_get_drvdata(psdev->dev);
/* the driver data for a device should never be null at this point */
if (!dev_data) {
@@ -1236,8 +1370,8 @@ static ssize_t permissive_show(struct device_driver *drv, char *buf)
spin_unlock_irqrestore(&pcistub_devices_lock, flags);
return count;
}
-
-DRIVER_ATTR(permissive, S_IRUSR | S_IWUSR, permissive_show, permissive_add);
+static DRIVER_ATTR(permissive, S_IRUSR | S_IWUSR, permissive_show,
+ permissive_add);
static void pcistub_exit(void)
{
@@ -1269,22 +1403,51 @@ static int __init pcistub_init(void)
err = sscanf(pci_devs_to_hide + pos,
" (%x:%x:%x.%x) %n",
&domain, &bus, &slot, &func, &parsed);
- if (err != 4) {
+ switch (err) {
+ case 3:
+ func = -1;
+ sscanf(pci_devs_to_hide + pos,
+ " (%x:%x:%x.*) %n",
+ &domain, &bus, &slot, &parsed);
+ break;
+ case 2:
+ slot = func = -1;
+ sscanf(pci_devs_to_hide + pos,
+ " (%x:%x:*.*) %n",
+ &domain, &bus, &parsed);
+ break;
+ }
+
+ if (!parsed) {
domain = 0;
err = sscanf(pci_devs_to_hide + pos,
" (%x:%x.%x) %n",
&bus, &slot, &func, &parsed);
- if (err != 3)
- goto parse_error;
+ switch (err) {
+ case 2:
+ func = -1;
+ sscanf(pci_devs_to_hide + pos,
+ " (%x:%x.*) %n",
+ &bus, &slot, &parsed);
+ break;
+ case 1:
+ slot = func = -1;
+ sscanf(pci_devs_to_hide + pos,
+ " (%x:*.*) %n",
+ &bus, &parsed);
+ break;
+ }
}
+ if (parsed <= 0)
+ goto parse_error;
+
err = pcistub_device_id_add(domain, bus, slot, func);
if (err)
goto out;
- /* if parsed<=0, we've reached the end of the string */
pos += parsed;
- } while (parsed > 0 && pci_devs_to_hide[pos]);
+ } while (pci_devs_to_hide[pos]);
}
/* If we're the first PCI Device Driver to register, we're the
@@ -1323,7 +1486,7 @@ out:
return err;
parse_error:
- printk(KERN_ERR DRV_NAME ": Error parsing pci_devs_to_hide at \"%s\"\n",
+ pr_err("Error parsing pci_devs_to_hide at \"%s\"\n",
pci_devs_to_hide + pos);
return -EINVAL;
}
@@ -1374,3 +1537,4 @@ module_init(xen_pcibk_init);
module_exit(xen_pcibk_cleanup);
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("xen-backend:pci");
diff --git a/drivers/xen/xen-pciback/pciback.h b/drivers/xen/xen-pciback/pciback.h
index a0e131a8150..f72af87640e 100644
--- a/drivers/xen/xen-pciback/pciback.h
+++ b/drivers/xen/xen-pciback/pciback.h
@@ -15,6 +15,8 @@
#include <linux/atomic.h>
#include <xen/interface/io/pciif.h>
+#define DRV_NAME "xen-pciback"
+
struct pci_dev_entry {
struct list_head list;
struct pci_dev *dev;
@@ -27,7 +29,7 @@ struct pci_dev_entry {
struct xen_pcibk_device {
void *pci_dev_data;
- spinlock_t dev_lock;
+ struct mutex dev_lock;
struct xenbus_device *xdev;
struct xenbus_watch be_watch;
u8 be_watching;
@@ -39,6 +41,7 @@ struct xen_pcibk_device {
struct xen_pcibk_dev_data {
struct list_head config_fields;
+ struct pci_saved_state *pci_saved_state;
unsigned int permissive:1;
unsigned int warned_on_write:1;
unsigned int enable_intx:1;
@@ -89,7 +92,7 @@ typedef int (*publish_pci_root_cb) (struct xen_pcibk_device *pdev,
* passthrough - BDFs are exactly like in the host.
*/
struct xen_pcibk_backend {
- char *name;
+ const char *name;
int (*init)(struct xen_pcibk_device *pdev);
void (*free)(struct xen_pcibk_device *pdev);
int (*find)(struct pci_dev *pcidev, struct xen_pcibk_device *pdev,
@@ -104,9 +107,9 @@ struct xen_pcibk_backend {
unsigned int devfn);
};
-extern struct xen_pcibk_backend xen_pcibk_vpci_backend;
-extern struct xen_pcibk_backend xen_pcibk_passthrough_backend;
-extern struct xen_pcibk_backend *xen_pcibk_backend;
+extern const struct xen_pcibk_backend xen_pcibk_vpci_backend;
+extern const struct xen_pcibk_backend xen_pcibk_passthrough_backend;
+extern const struct xen_pcibk_backend *xen_pcibk_backend;
static inline int xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
struct pci_dev *dev,
@@ -116,13 +119,14 @@ static inline int xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
if (xen_pcibk_backend && xen_pcibk_backend->add)
return xen_pcibk_backend->add(pdev, dev, devid, publish_cb);
return -1;
-};
+}
+
static inline void xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev,
struct pci_dev *dev)
{
- if (xen_pcibk_backend && xen_pcibk_backend->free)
+ if (xen_pcibk_backend && xen_pcibk_backend->release)
return xen_pcibk_backend->release(pdev, dev);
-};
+}
static inline struct pci_dev *
xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev, unsigned int domain,
@@ -131,7 +135,8 @@ xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev, unsigned int domain,
if (xen_pcibk_backend && xen_pcibk_backend->get)
return xen_pcibk_backend->get(pdev, domain, bus, devfn);
return NULL;
-};
+}
+
/**
* Add for domain0 PCIE-AER handling. Get guest domain/bus/devfn in xen_pcibk
* before sending aer request to pcifront, so that guest could identify
@@ -148,25 +153,29 @@ static inline int xen_pcibk_get_pcifront_dev(struct pci_dev *pcidev,
return xen_pcibk_backend->find(pcidev, pdev, domain, bus,
devfn);
return -1;
-};
+}
+
static inline int xen_pcibk_init_devices(struct xen_pcibk_device *pdev)
{
if (xen_pcibk_backend && xen_pcibk_backend->init)
return xen_pcibk_backend->init(pdev);
return -1;
-};
+}
+
static inline int xen_pcibk_publish_pci_roots(struct xen_pcibk_device *pdev,
publish_pci_root_cb cb)
{
if (xen_pcibk_backend && xen_pcibk_backend->publish)
return xen_pcibk_backend->publish(pdev, cb);
return -1;
-};
+}
+
static inline void xen_pcibk_release_devices(struct xen_pcibk_device *pdev)
{
if (xen_pcibk_backend && xen_pcibk_backend->free)
return xen_pcibk_backend->free(pdev);
-};
+}
+
/* Handles events from front-end */
irqreturn_t xen_pcibk_handle_event(int irq, void *dev_id);
void xen_pcibk_do_op(struct work_struct *data);
diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c
index 8c95c3415b7..c4a0666de6f 100644
--- a/drivers/xen/xen-pciback/pciback_ops.c
+++ b/drivers/xen/xen-pciback/pciback_ops.c
@@ -3,6 +3,9 @@
*
* Author: Ryan Wilson <hap9@epoch.ncsc.mil>
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/wait.h>
#include <linux/bitops.h>
@@ -10,7 +13,6 @@
#include <linux/sched.h>
#include "pciback.h"
-#define DRV_NAME "xen-pciback"
int verbose_request;
module_param(verbose_request, int, 0644);
@@ -114,7 +116,8 @@ void xen_pcibk_reset_device(struct pci_dev *dev)
if (dev->msi_enabled)
pci_disable_msi(dev);
#endif
- pci_disable_device(dev);
+ if (pci_is_enabled(dev))
+ pci_disable_device(dev);
pci_write_config_word(dev, PCI_COMMAND, 0);
@@ -136,7 +139,6 @@ int xen_pcibk_enable_msi(struct xen_pcibk_device *pdev,
struct pci_dev *dev, struct xen_pci_op *op)
{
struct xen_pcibk_dev_data *dev_data;
- int otherend = pdev->xdev->otherend_id;
int status;
if (unlikely(verbose_request))
@@ -145,8 +147,9 @@ int xen_pcibk_enable_msi(struct xen_pcibk_device *pdev,
status = pci_enable_msi(dev);
if (status) {
- printk(KERN_ERR "error enable msi for guest %x status %x\n",
- otherend, status);
+ pr_warn_ratelimited("%s: error enabling MSI for guest %u: err %d\n",
+ pci_name(dev), pdev->xdev->otherend_id,
+ status);
op->value = 0;
return XEN_PCI_ERR_op_failed;
}
@@ -210,12 +213,11 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev,
entries[i].vector = op->msix_entries[i].vector;
}
- result = pci_enable_msix(dev, entries, op->value);
-
+ result = pci_enable_msix_exact(dev, entries, op->value);
if (result == 0) {
for (i = 0; i < op->value; i++) {
op->msix_entries[i].entry = entries[i].entry;
- if (entries[i].vector)
+ if (entries[i].vector) {
op->msix_entries[i].vector =
xen_pirq_from_irq(entries[i].vector);
if (unlikely(verbose_request))
@@ -223,11 +225,12 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev,
"MSI-X[%d]: %d\n",
pci_name(dev), i,
op->msix_entries[i].vector);
+ }
}
- } else {
- printk(KERN_WARNING DRV_NAME ": %s: failed to enable MSI-X: err %d!\n",
- pci_name(dev), result);
- }
+ } else
+ pr_warn_ratelimited("%s: error enabling MSI-X for guest %u: err %d!\n",
+ pci_name(dev), pdev->xdev->otherend_id,
+ result);
kfree(entries);
op->value = result;
@@ -235,7 +238,7 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev,
if (dev_data)
dev_data->ack_intr = 0;
- return result;
+ return result > 0 ? 0 : result;
}
static
@@ -345,9 +348,9 @@ void xen_pcibk_do_op(struct work_struct *data)
notify_remote_via_irq(pdev->evtchn_irq);
/* Mark that we're done. */
- smp_mb__before_clear_bit(); /* /after/ clearing PCIF_active */
+ smp_mb__before_atomic(); /* /after/ clearing PCIF_active */
clear_bit(_PDEVF_op_active, &pdev->flags);
- smp_mb__after_clear_bit(); /* /before/ final check for work */
+ smp_mb__after_atomic(); /* /before/ final check for work */
/* Check to see if the driver domain tried to start another request in
* between clearing _XEN_PCIF_active and clearing _PDEVF_op_active.
@@ -372,7 +375,7 @@ static irqreturn_t xen_pcibk_guest_interrupt(int irq, void *dev_id)
dev_data->handled++;
if ((dev_data->handled % 1000) == 0) {
if (xen_test_irq_shared(irq)) {
- printk(KERN_INFO "%s IRQ line is not shared "
+ pr_info("%s IRQ line is not shared "
"with other domains. Turning ISR off\n",
dev_data->irq_name);
dev_data->ack_intr = 0;
diff --git a/drivers/xen/xen-pciback/vpci.c b/drivers/xen/xen-pciback/vpci.c
index 4a42cfb0959..51afff96c51 100644
--- a/drivers/xen/xen-pciback/vpci.c
+++ b/drivers/xen/xen-pciback/vpci.c
@@ -5,19 +5,20 @@
* Author: Ryan Wilson <hap9@epoch.ncsc.mil>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include "pciback.h"
#define PCI_SLOT_MAX 32
-#define DRV_NAME "xen-pciback"
struct vpci_dev_data {
/* Access to dev_list must be protected by lock */
struct list_head dev_list[PCI_SLOT_MAX];
- spinlock_t lock;
+ struct mutex lock;
};
static inline struct list_head *list_first(struct list_head *head)
@@ -33,13 +34,12 @@ static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev,
struct pci_dev_entry *entry;
struct pci_dev *dev = NULL;
struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;
- unsigned long flags;
if (domain != 0 || bus != 0)
return NULL;
if (PCI_SLOT(devfn) < PCI_SLOT_MAX) {
- spin_lock_irqsave(&vpci_dev->lock, flags);
+ mutex_lock(&vpci_dev->lock);
list_for_each_entry(entry,
&vpci_dev->dev_list[PCI_SLOT(devfn)],
@@ -50,7 +50,7 @@ static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev,
}
}
- spin_unlock_irqrestore(&vpci_dev->lock, flags);
+ mutex_unlock(&vpci_dev->lock);
}
return dev;
}
@@ -71,7 +71,6 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
int err = 0, slot, func = -1;
struct pci_dev_entry *t, *dev_entry;
struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;
- unsigned long flags;
if ((dev->class >> 24) == PCI_BASE_CLASS_BRIDGE) {
err = -EFAULT;
@@ -90,17 +89,22 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
dev_entry->dev = dev;
- spin_lock_irqsave(&vpci_dev->lock, flags);
+ mutex_lock(&vpci_dev->lock);
+
+ /*
+ * Keep multi-function devices together on the virtual PCI bus, except
+ * virtual functions.
+ */
+ if (!dev->is_virtfn) {
+ for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
+ if (list_empty(&vpci_dev->dev_list[slot]))
+ continue;
- /* Keep multi-function devices together on the virtual PCI bus */
- for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
- if (!list_empty(&vpci_dev->dev_list[slot])) {
t = list_entry(list_first(&vpci_dev->dev_list[slot]),
struct pci_dev_entry, list);
if (match_slot(dev, t->dev)) {
- pr_info(DRV_NAME ": vpci: %s: "
- "assign to virtual slot %d func %d\n",
+ pr_info("vpci: %s: assign to virtual slot %d func %d\n",
pci_name(dev), slot,
PCI_FUNC(dev->devfn));
list_add_tail(&dev_entry->list,
@@ -114,12 +118,11 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
/* Assign to a new slot on the virtual PCI bus */
for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
if (list_empty(&vpci_dev->dev_list[slot])) {
- printk(KERN_INFO DRV_NAME
- ": vpci: %s: assign to virtual slot %d\n",
- pci_name(dev), slot);
+ pr_info("vpci: %s: assign to virtual slot %d\n",
+ pci_name(dev), slot);
list_add_tail(&dev_entry->list,
&vpci_dev->dev_list[slot]);
- func = PCI_FUNC(dev->devfn);
+ func = dev->is_virtfn ? 0 : PCI_FUNC(dev->devfn);
goto unlock;
}
}
@@ -129,11 +132,13 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
"No more space on root virtual PCI bus");
unlock:
- spin_unlock_irqrestore(&vpci_dev->lock, flags);
+ mutex_unlock(&vpci_dev->lock);
/* Publish this device. */
if (!err)
err = publish_cb(pdev, 0, 0, PCI_DEVFN(slot, func), devid);
+ else
+ kfree(dev_entry);
out:
return err;
@@ -145,14 +150,13 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev,
int slot;
struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;
struct pci_dev *found_dev = NULL;
- unsigned long flags;
- spin_lock_irqsave(&vpci_dev->lock, flags);
+ mutex_lock(&vpci_dev->lock);
for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
- struct pci_dev_entry *e, *tmp;
- list_for_each_entry_safe(e, tmp, &vpci_dev->dev_list[slot],
- list) {
+ struct pci_dev_entry *e;
+
+ list_for_each_entry(e, &vpci_dev->dev_list[slot], list) {
if (e->dev == dev) {
list_del(&e->list);
found_dev = e->dev;
@@ -163,7 +167,7 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev,
}
out:
- spin_unlock_irqrestore(&vpci_dev->lock, flags);
+ mutex_unlock(&vpci_dev->lock);
if (found_dev)
pcistub_put_pci_dev(found_dev);
@@ -178,7 +182,7 @@ static int __xen_pcibk_init_devices(struct xen_pcibk_device *pdev)
if (!vpci_dev)
return -ENOMEM;
- spin_lock_init(&vpci_dev->lock);
+ mutex_init(&vpci_dev->lock);
for (slot = 0; slot < PCI_SLOT_MAX; slot++)
INIT_LIST_HEAD(&vpci_dev->dev_list[slot]);
@@ -222,10 +226,9 @@ static int __xen_pcibk_get_pcifront_dev(struct pci_dev *pcidev,
struct pci_dev_entry *entry;
struct pci_dev *dev = NULL;
struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;
- unsigned long flags;
int found = 0, slot;
- spin_lock_irqsave(&vpci_dev->lock, flags);
+ mutex_lock(&vpci_dev->lock);
for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
list_for_each_entry(entry,
&vpci_dev->dev_list[slot],
@@ -243,11 +246,11 @@ static int __xen_pcibk_get_pcifront_dev(struct pci_dev *pcidev,
}
}
}
- spin_unlock_irqrestore(&vpci_dev->lock, flags);
+ mutex_unlock(&vpci_dev->lock);
return found;
}
-struct xen_pcibk_backend xen_pcibk_vpci_backend = {
+const struct xen_pcibk_backend xen_pcibk_vpci_backend = {
.name = "vpci",
.init = __xen_pcibk_init_devices,
.free = __xen_pcibk_release_devices,
diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c
index 978d2c6f5dc..4a7e6e0a5f4 100644
--- a/drivers/xen/xen-pciback/xenbus.c
+++ b/drivers/xen/xen-pciback/xenbus.c
@@ -3,6 +3,9 @@
*
* Author: Ryan Wilson <hap9@epoch.ncsc.mil>
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
@@ -13,11 +16,10 @@
#include <asm/xen/pci.h>
#include "pciback.h"
-#define DRV_NAME "xen-pciback"
#define INVALID_EVTCHN_IRQ (-1)
struct workqueue_struct *xen_pcibk_wq;
-static int __read_mostly passthrough;
+static bool __read_mostly passthrough;
module_param(passthrough, bool, S_IRUGO);
MODULE_PARM_DESC(passthrough,
"Option to specify how to export PCI topology to guest:\n"\
@@ -44,7 +46,7 @@ static struct xen_pcibk_device *alloc_pdev(struct xenbus_device *xdev)
pdev->xdev = xdev;
dev_set_drvdata(&xdev->dev, pdev);
- spin_lock_init(&pdev->dev_lock);
+ mutex_init(&pdev->dev_lock);
pdev->sh_info = NULL;
pdev->evtchn_irq = INVALID_EVTCHN_IRQ;
@@ -62,14 +64,12 @@ out:
static void xen_pcibk_disconnect(struct xen_pcibk_device *pdev)
{
- spin_lock(&pdev->dev_lock);
-
+ mutex_lock(&pdev->dev_lock);
/* Ensure the guest can't trigger our handler before removing devices */
if (pdev->evtchn_irq != INVALID_EVTCHN_IRQ) {
unbind_from_irqhandler(pdev->evtchn_irq, pdev);
pdev->evtchn_irq = INVALID_EVTCHN_IRQ;
}
- spin_unlock(&pdev->dev_lock);
/* If the driver domain started an op, make sure we complete it
* before releasing the shared memory */
@@ -77,13 +77,11 @@ static void xen_pcibk_disconnect(struct xen_pcibk_device *pdev)
/* Note, the workqueue does not use spinlocks at all.*/
flush_workqueue(xen_pcibk_wq);
- spin_lock(&pdev->dev_lock);
if (pdev->sh_info != NULL) {
xenbus_unmap_ring_vfree(pdev->xdev, pdev->sh_info);
pdev->sh_info = NULL;
}
- spin_unlock(&pdev->dev_lock);
-
+ mutex_unlock(&pdev->dev_lock);
}
static void free_pdev(struct xen_pcibk_device *pdev)
@@ -95,6 +93,8 @@ static void free_pdev(struct xen_pcibk_device *pdev)
xen_pcibk_disconnect(pdev);
+ /* N.B. This calls pcistub_put_pci_dev which does the FLR on all
+ * of the PCIe devices. */
xen_pcibk_release_devices(pdev);
dev_set_drvdata(&pdev->xdev->dev, NULL);
@@ -120,9 +120,7 @@ static int xen_pcibk_do_attach(struct xen_pcibk_device *pdev, int gnt_ref,
goto out;
}
- spin_lock(&pdev->dev_lock);
pdev->sh_info = vaddr;
- spin_unlock(&pdev->dev_lock);
err = bind_interdomain_evtchn_to_irqhandler(
pdev->xdev->otherend_id, remote_evtchn, xen_pcibk_handle_event,
@@ -132,10 +130,7 @@ static int xen_pcibk_do_attach(struct xen_pcibk_device *pdev, int gnt_ref,
"Error binding event channel to IRQ");
goto out;
}
-
- spin_lock(&pdev->dev_lock);
pdev->evtchn_irq = err;
- spin_unlock(&pdev->dev_lock);
err = 0;
dev_dbg(&pdev->xdev->dev, "Attached!\n");
@@ -150,6 +145,7 @@ static int xen_pcibk_attach(struct xen_pcibk_device *pdev)
char *magic = NULL;
+ mutex_lock(&pdev->dev_lock);
/* Make sure we only do this setup once */
if (xenbus_read_driver_state(pdev->xdev->nodename) !=
XenbusStateInitialised)
@@ -176,7 +172,7 @@ static int xen_pcibk_attach(struct xen_pcibk_device *pdev)
if (magic == NULL || strcmp(magic, XEN_PCI_MAGIC) != 0) {
xenbus_dev_fatal(pdev->xdev, -EFAULT,
"version mismatch (%s/%s) with pcifront - "
- "halting xen_pcibk",
+ "halting " DRV_NAME,
magic, XEN_PCI_MAGIC);
goto out;
}
@@ -194,6 +190,7 @@ static int xen_pcibk_attach(struct xen_pcibk_device *pdev)
dev_dbg(&pdev->xdev->dev, "Connected? %d\n", err);
out:
+ mutex_unlock(&pdev->dev_lock);
kfree(magic);
@@ -214,6 +211,7 @@ static int xen_pcibk_publish_pci_dev(struct xen_pcibk_device *pdev,
goto out;
}
+ /* Note: The PV protocol uses %02x, don't change it */
err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str,
"%04x:%02x:%02x.%02x", domain, bus,
PCI_SLOT(devfn), PCI_FUNC(devfn));
@@ -237,7 +235,7 @@ static int xen_pcibk_export_device(struct xen_pcibk_device *pdev,
err = -EINVAL;
xenbus_dev_fatal(pdev->xdev, err,
"Couldn't locate PCI device "
- "(%04x:%02x:%02x.%01x)! "
+ "(%04x:%02x:%02x.%d)! "
"perhaps already in-use?",
domain, bus, slot, func);
goto out;
@@ -251,8 +249,8 @@ static int xen_pcibk_export_device(struct xen_pcibk_device *pdev,
dev_dbg(&dev->dev, "registering for %d\n", pdev->xdev->otherend_id);
if (xen_register_device_domain_owner(dev,
pdev->xdev->otherend_id) != 0) {
- dev_err(&dev->dev, "device has been assigned to another " \
- "domain! Over-writting the ownership, but beware.\n");
+ dev_err(&dev->dev, "Stealing ownership from dom%d.\n",
+ xen_find_device_domain_owner(dev));
xen_unregister_device_domain_owner(dev);
xen_register_device_domain_owner(dev, pdev->xdev->otherend_id);
}
@@ -282,7 +280,7 @@ static int xen_pcibk_remove_device(struct xen_pcibk_device *pdev,
if (!dev) {
err = -EINVAL;
dev_dbg(&pdev->xdev->dev, "Couldn't locate PCI device "
- "(%04x:%02x:%02x.%01x)! not owned by this domain\n",
+ "(%04x:%02x:%02x.%d)! not owned by this domain\n",
domain, bus, slot, func);
goto out;
}
@@ -290,6 +288,8 @@ static int xen_pcibk_remove_device(struct xen_pcibk_device *pdev,
dev_dbg(&dev->dev, "unregistering for %d\n", pdev->xdev->otherend_id);
xen_unregister_device_domain_owner(dev);
+ /* N.B. This ends up calling pcistub_put_pci_dev which ends up
+ * doing the FLR. */
xen_pcibk_release_pci_dev(pdev, dev);
out:
@@ -369,6 +369,7 @@ static int xen_pcibk_reconfigure(struct xen_pcibk_device *pdev)
dev_dbg(&pdev->xdev->dev, "Reconfiguring device ...\n");
+ mutex_lock(&pdev->dev_lock);
/* Make sure we only reconfigure once */
if (xenbus_read_driver_state(pdev->xdev->nodename) !=
XenbusStateReconfiguring)
@@ -506,6 +507,7 @@ static int xen_pcibk_reconfigure(struct xen_pcibk_device *pdev)
}
out:
+ mutex_unlock(&pdev->dev_lock);
return 0;
}
@@ -562,6 +564,7 @@ static int xen_pcibk_setup_backend(struct xen_pcibk_device *pdev)
char dev_str[64];
char state_str[64];
+ mutex_lock(&pdev->dev_lock);
/* It's possible we could get the call to setup twice, so make sure
* we're not already connected.
*/
@@ -642,10 +645,10 @@ static int xen_pcibk_setup_backend(struct xen_pcibk_device *pdev)
"Error switching to initialised state!");
out:
+ mutex_unlock(&pdev->dev_lock);
if (!err)
/* see if pcifront is already configured (if not, we'll wait) */
xen_pcibk_attach(pdev);
-
return err;
}
@@ -710,39 +713,35 @@ static int xen_pcibk_xenbus_remove(struct xenbus_device *dev)
return 0;
}
-static const struct xenbus_device_id xenpci_ids[] = {
+static const struct xenbus_device_id xen_pcibk_ids[] = {
{"pci"},
{""},
};
-static struct xenbus_driver xenbus_xen_pcibk_driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- .ids = xenpci_ids,
+static DEFINE_XENBUS_DRIVER(xen_pcibk, DRV_NAME,
.probe = xen_pcibk_xenbus_probe,
.remove = xen_pcibk_xenbus_remove,
.otherend_changed = xen_pcibk_frontend_changed,
-};
+);
-struct xen_pcibk_backend *xen_pcibk_backend;
+const struct xen_pcibk_backend *__read_mostly xen_pcibk_backend;
int __init xen_pcibk_xenbus_register(void)
{
xen_pcibk_wq = create_workqueue("xen_pciback_workqueue");
if (!xen_pcibk_wq) {
- printk(KERN_ERR "%s: create"
- "xen_pciback_workqueue failed\n", __func__);
+ pr_err("%s: create xen_pciback_workqueue failed\n", __func__);
return -EFAULT;
}
xen_pcibk_backend = &xen_pcibk_vpci_backend;
if (passthrough)
xen_pcibk_backend = &xen_pcibk_passthrough_backend;
- pr_info(DRV_NAME ": backend is %s\n", xen_pcibk_backend->name);
- return xenbus_register_backend(&xenbus_xen_pcibk_driver);
+ pr_info("backend is %s\n", xen_pcibk_backend->name);
+ return xenbus_register_backend(&xen_pcibk_driver);
}
void __exit xen_pcibk_xenbus_unregister(void)
{
destroy_workqueue(xen_pcibk_wq);
- xenbus_unregister_driver(&xenbus_xen_pcibk_driver);
+ xenbus_unregister_driver(&xen_pcibk_driver);
}