# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1203674763 0
# Node ID 4b9f2293d7507bab5cd6952c2c97e7b3d057641a
# Parent 5069c4f9481242082c52e58e1828c1a5a4bb9a65
Xen paravirtualised PCI hotplug.
Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@xxxxxxxxxxxxx>
---
drivers/xen/pciback/controller.c | 6
drivers/xen/pciback/passthrough.c | 5
drivers/xen/pciback/pciback.h | 6
drivers/xen/pciback/slot.c | 8
drivers/xen/pciback/vpci.c | 14 +
drivers/xen/pciback/xenbus.c | 380 ++++++++++++++++++++++++++++++--------
drivers/xen/pcifront/pci_op.c | 52 +++++
drivers/xen/pcifront/pcifront.h | 2
drivers/xen/pcifront/xenbus.c | 154 +++++++++++++++
include/xen/interface/io/xenbus.h | 9
10 files changed, 551 insertions(+), 85 deletions(-)
diff -r 5069c4f94812 -r 4b9f2293d750 drivers/xen/pciback/controller.c
--- a/drivers/xen/pciback/controller.c Thu Feb 21 10:22:27 2008 +0000
+++ b/drivers/xen/pciback/controller.c Fri Feb 22 10:06:03 2008 +0000
@@ -91,7 +91,8 @@ found:
return dev;
}
-int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev)
+int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev,
+ int devid, publish_pci_dev_cb publish_cb)
{
struct controller_dev_data *dev_data = pdev->pci_dev_data;
struct controller_dev_entry *dev_entry;
@@ -165,6 +166,9 @@ int pciback_add_pci_dev(struct pciback_d
out:
spin_unlock_irqrestore(&dev_data->lock, flags);
+
+ /* TODO: Publish virtual domain:bus:slot.func here. */
+
return ret;
}
diff -r 5069c4f94812 -r 4b9f2293d750 drivers/xen/pciback/passthrough.c
--- a/drivers/xen/pciback/passthrough.c Thu Feb 21 10:22:27 2008 +0000
+++ b/drivers/xen/pciback/passthrough.c Fri Feb 22 10:06:03 2008 +0000
@@ -41,7 +41,8 @@ struct pci_dev *pciback_get_pci_dev(stru
return dev;
}
-int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev)
+int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev,
+ int devid, publish_pci_dev_cb publish_cb)
{
struct passthrough_dev_data *dev_data = pdev->pci_dev_data;
struct pci_dev_entry *dev_entry;
@@ -55,6 +56,8 @@ int pciback_add_pci_dev(struct pciback_d
spin_lock_irqsave(&dev_data->lock, flags);
list_add_tail(&dev_entry->list, &dev_data->dev_list);
spin_unlock_irqrestore(&dev_data->lock, flags);
+
+ /* TODO: Publish virtual domain:bus:slot.func here. */
return 0;
}
diff -r 5069c4f94812 -r 4b9f2293d750 drivers/xen/pciback/pciback.h
--- a/drivers/xen/pciback/pciback.h Thu Feb 21 10:22:27 2008 +0000
+++ b/drivers/xen/pciback/pciback.h Fri Feb 22 10:06:03 2008 +0000
@@ -70,9 +70,13 @@ int pciback_config_write(struct pci_dev
int pciback_config_write(struct pci_dev *dev, int offset, int size, u32 value);
/* Handle requests for specific devices from the frontend */
+typedef int (*publish_pci_dev_cb) (struct pciback_device *pdev,
+ unsigned int domain, unsigned int bus,
+ unsigned int devfn, unsigned int devid);
typedef int (*publish_pci_root_cb) (struct pciback_device * pdev,
unsigned int domain, unsigned int bus);
-int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev);
+int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev,
+ int devid, publish_pci_dev_cb publish_cb);
void pciback_release_pci_dev(struct pciback_device *pdev, struct pci_dev *dev);
struct pci_dev *pciback_get_pci_dev(struct pciback_device *pdev,
unsigned int domain, unsigned int bus,
diff -r 5069c4f94812 -r 4b9f2293d750 drivers/xen/pciback/slot.c
--- a/drivers/xen/pciback/slot.c Thu Feb 21 10:22:27 2008 +0000
+++ b/drivers/xen/pciback/slot.c Fri Feb 22 10:06:03 2008 +0000
@@ -44,7 +44,8 @@ struct pci_dev *pciback_get_pci_dev(stru
return dev;
}
-int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev)
+int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev,
+ int devid, publish_pci_dev_cb publish_cb)
{
int err = 0, slot, bus;
struct slot_dev_data *slot_dev = pdev->pci_dev_data;
@@ -77,6 +78,11 @@ int pciback_add_pci_dev(struct pciback_d
unlock:
spin_unlock_irqrestore(&slot_dev->lock, flags);
+
+ /* Publish this device. */
+ if(!err)
+ err = publish_cb(pdev, 0, 0, PCI_DEVFN(slot, 0), devid);
+
out:
return err;
}
diff -r 5069c4f94812 -r 4b9f2293d750 drivers/xen/pciback/vpci.c
--- a/drivers/xen/pciback/vpci.c Thu Feb 21 10:22:27 2008 +0000
+++ b/drivers/xen/pciback/vpci.c Fri Feb 22 10:06:03 2008 +0000
@@ -62,9 +62,10 @@ static inline int match_slot(struct pci_
return 0;
}
-int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev)
-{
- int err = 0, slot;
+int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev,
+ int devid, publish_pci_dev_cb publish_cb)
+{
+ int err = 0, slot, func;
struct pci_dev_entry *t, *dev_entry;
struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;
unsigned long flags;
@@ -101,6 +102,7 @@ int pciback_add_pci_dev(struct pciback_d
PCI_FUNC(dev->devfn));
list_add_tail(&dev_entry->list,
&vpci_dev->dev_list[slot]);
+ func = PCI_FUNC(dev->devfn);
goto unlock;
}
}
@@ -114,6 +116,7 @@ int pciback_add_pci_dev(struct pciback_d
pci_name(dev), slot);
list_add_tail(&dev_entry->list,
&vpci_dev->dev_list[slot]);
+ func = PCI_FUNC(dev->devfn);
goto unlock;
}
}
@@ -124,6 +127,11 @@ int pciback_add_pci_dev(struct pciback_d
unlock:
spin_unlock_irqrestore(&vpci_dev->lock, flags);
+
+ /* Publish this device. */
+ if(!err)
+ err = publish_cb(pdev, 0, 0, PCI_DEVFN(slot, func), devid);
+
out:
return err;
}
diff -r 5069c4f94812 -r 4b9f2293d750 drivers/xen/pciback/xenbus.c
--- a/drivers/xen/pciback/xenbus.c Thu Feb 21 10:22:27 2008 +0000
+++ b/drivers/xen/pciback/xenbus.c Fri Feb 22 10:06:03 2008 +0000
@@ -161,95 +161,31 @@ static int pciback_attach(struct pciback
return err;
}
-static void pciback_frontend_changed(struct xenbus_device *xdev,
- enum xenbus_state fe_state)
-{
- struct pciback_device *pdev = xdev->dev.driver_data;
-
- dev_dbg(&xdev->dev, "fe state changed %d\n", fe_state);
-
- switch (fe_state) {
- case XenbusStateInitialised:
- pciback_attach(pdev);
- break;
-
- case XenbusStateClosing:
- xenbus_switch_state(xdev, XenbusStateClosing);
- break;
-
- case XenbusStateUnknown:
- case XenbusStateClosed:
- dev_dbg(&xdev->dev, "frontend is gone! unregister device\n");
- device_unregister(&xdev->dev);
- break;
-
- default:
- break;
- }
-}
-
-static int pciback_publish_pci_root(struct pciback_device *pdev,
- unsigned int domain, unsigned int bus)
-{
- unsigned int d, b;
- int i, root_num, len, err;
+static int pciback_publish_pci_dev(struct pciback_device *pdev,
+ unsigned int domain, unsigned int bus,
+ unsigned int devfn, unsigned int devid)
+{
+ int err;
+ int len;
char str[64];
- dev_dbg(&pdev->xdev->dev, "Publishing pci roots\n");
-
- err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename,
- "root_num", "%d", &root_num);
- if (err == 0 || err == -ENOENT)
- root_num = 0;
- else if (err < 0)
- goto out;
-
- /* Verify that we haven't already published this pci root */
- for (i = 0; i < root_num; i++) {
- len = snprintf(str, sizeof(str), "root-%d", i);
- if (unlikely(len >= (sizeof(str) - 1))) {
- err = -ENOMEM;
- goto out;
- }
-
- err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename,
- str, "%x:%x", &d, &b);
- if (err < 0)
- goto out;
- if (err != 2) {
- err = -EINVAL;
- goto out;
- }
-
- if (d == domain && b == bus) {
- err = 0;
- goto out;
- }
- }
-
- len = snprintf(str, sizeof(str), "root-%d", root_num);
+ len = snprintf(str, sizeof(str), "vdev-%d", devid);
if (unlikely(len >= (sizeof(str) - 1))) {
err = -ENOMEM;
goto out;
}
- dev_dbg(&pdev->xdev->dev, "writing root %d at %04x:%02x\n",
- root_num, domain, bus);
-
err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str,
- "%04x:%02x", domain, bus);
- if (err)
- goto out;
-
- err = xenbus_printf(XBT_NIL, pdev->xdev->nodename,
- "root_num", "%d", (root_num + 1));
+ "%04x:%02x:%02x.%02x", domain, bus,
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
out:
return err;
}
static int pciback_export_device(struct pciback_device *pdev,
- int domain, int bus, int slot, int func)
+ int domain, int bus, int slot, int func,
+ int devid)
{
struct pci_dev *dev;
int err = 0;
@@ -268,7 +204,7 @@ static int pciback_export_device(struct
goto out;
}
- err = pciback_add_pci_dev(pdev, dev);
+ err = pciback_add_pci_dev(pdev, dev, devid, pciback_publish_pci_dev);
if (err)
goto out;
@@ -284,6 +220,278 @@ static int pciback_export_device(struct
return err;
}
+static int pciback_remove_device(struct pciback_device *pdev,
+ int domain, int bus, int slot, int func)
+{
+ int err = 0;
+ struct pci_dev *dev;
+
+ dev_dbg(&pdev->xdev->dev, "removing dom %x bus %x slot %x func %x\n",
+ domain, bus, slot, func);
+
+ dev = pciback_get_pci_dev(pdev, domain, bus, PCI_DEVFN(slot, func));
+ if (!dev) {
+ err = -EINVAL;
+ dev_dbg(&pdev->xdev->dev, "Couldn't locate PCI device "
+ "(%04x:%02x:%02x.%01x)! not owned by this domain\n",
+ domain, bus, slot, func);
+ goto out;
+ }
+
+ pciback_release_pci_dev(pdev, dev);
+
+ out:
+ return err;
+}
+
+static int pciback_publish_pci_root(struct pciback_device *pdev,
+ unsigned int domain, unsigned int bus)
+{
+ unsigned int d, b;
+ int i, root_num, len, err;
+ char str[64];
+
+ dev_dbg(&pdev->xdev->dev, "Publishing pci roots\n");
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename,
+ "root_num", "%d", &root_num);
+ if (err == 0 || err == -ENOENT)
+ root_num = 0;
+ else if (err < 0)
+ goto out;
+
+ /* Verify that we haven't already published this pci root */
+ for (i = 0; i < root_num; i++) {
+ len = snprintf(str, sizeof(str), "root-%d", i);
+ if (unlikely(len >= (sizeof(str) - 1))) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename,
+ str, "%x:%x", &d, &b);
+ if (err < 0)
+ goto out;
+ if (err != 2) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (d == domain && b == bus) {
+ err = 0;
+ goto out;
+ }
+ }
+
+ len = snprintf(str, sizeof(str), "root-%d", root_num);
+ if (unlikely(len >= (sizeof(str) - 1))) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ dev_dbg(&pdev->xdev->dev, "writing root %d at %04x:%02x\n",
+ root_num, domain, bus);
+
+ err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str,
+ "%04x:%02x", domain, bus);
+ if (err)
+ goto out;
+
+ err = xenbus_printf(XBT_NIL, pdev->xdev->nodename,
+ "root_num", "%d", (root_num + 1));
+
+ out:
+ return err;
+}
+
+static int pciback_reconfigure(struct pciback_device *pdev)
+{
+ int err = 0;
+ int num_devs;
+ int domain, bus, slot, func;
+ int substate;
+ int i, len;
+ char state_str[64];
+ char dev_str[64];
+
+ spin_lock(&pdev->dev_lock);
+
+ dev_dbg(&pdev->xdev->dev, "Reconfiguring device ...\n");
+
+ /* Make sure we only reconfigure once */
+ if (xenbus_read_driver_state(pdev->xdev->nodename) !=
+ XenbusStateReconfiguring)
+ goto out;
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, "num_devs", "%d",
+ &num_devs);
+ if (err != 1) {
+ if (err >= 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading number of devices");
+ goto out;
+ }
+
+ for (i = 0; i < num_devs; i++) {
+ len = snprintf(state_str, sizeof(state_str), "state-%d", i);
+ if (unlikely(len >= (sizeof(state_str) - 1))) {
+ err = -ENOMEM;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "String overflow while reading "
+ "configuration");
+ goto out;
+ }
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, state_str,
+ "%d", &substate);
+ if (err != 1)
+ substate = XenbusStateUnknown;
+
+ switch (substate) {
+ /* case XenbusStateUnknown: */
+ case XenbusStateInitialising:
+ dev_dbg(&pdev->xdev->dev, "Attaching dev-%d ...\n", i);
+
+ len = snprintf(dev_str, sizeof(dev_str), "dev-%d", i);
+ if (unlikely(len >= (sizeof(dev_str) - 1))) {
+ err = -ENOMEM;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "String overflow while "
+ "reading configuration");
+ goto out;
+ }
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename,
+ dev_str, "%x:%x:%x.%x",
+ &domain, &bus, &slot, &func);
+ if (err < 0) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading device "
+ "configuration");
+ goto out;
+ }
+ if (err != 4) {
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error parsing pci device "
+ "configuration");
+ goto out;
+ }
+
+ err = pciback_export_device(pdev, domain, bus, slot,
+ func, i);
+ if (err)
+ goto out;
+
+ /* TODO: if we are to support multiple pci roots
+ * (CONFIG_XEN_PCIDEV_BACKEND_PASS), publish newly
+ * added root here.
+ */
+
+ err = xenbus_printf(XBT_NIL, pdev->xdev->nodename,
+ state_str, "%d",
+ XenbusStateInitialised);
+ if (err) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error switching substate of "
"dev-%d\n", i);
+ goto out;
+ }
+ break;
+
+ case XenbusStateClosing:
+ dev_dbg(&pdev->xdev->dev, "Detaching dev-%d ...\n", i);
+
+ len = snprintf(dev_str, sizeof(dev_str), "vdev-%d", i);
+ if (unlikely(len >= (sizeof(dev_str) - 1))) {
+ err = -ENOMEM;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "String overflow while "
+ "reading configuration");
+ goto out;
+ }
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename,
+ dev_str, "%x:%x:%x.%x",
+ &domain, &bus, &slot, &func);
+ if (err < 0) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading device "
+ "configuration");
+ goto out;
+ }
+ if (err != 4) {
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error parsing pci device "
+ "configuration");
+ goto out;
+ }
+
+ err = pciback_remove_device(pdev, domain, bus, slot,
+ func);
+ if(err)
+ goto out;
+
+ /* TODO: if we are to support multiple pci roots
+ * (CONFIG_XEN_PCIDEV_BACKEND_PASS), remove unnecessary
+ * root here.
+ */
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ err = xenbus_switch_state(pdev->xdev, XenbusStateReconfigured);
+ if (err) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error switching to reconfigured state!");
+ goto out;
+ }
+
+ out:
+ spin_unlock(&pdev->dev_lock);
+
+ return 0;
+}
+
+static void pciback_frontend_changed(struct xenbus_device *xdev,
+ enum xenbus_state fe_state)
+{
+ struct pciback_device *pdev = xdev->dev.driver_data;
+
+ dev_dbg(&xdev->dev, "fe state changed %d\n", fe_state);
+
+ switch (fe_state) {
+ case XenbusStateInitialised:
+ pciback_attach(pdev);
+ break;
+
+ case XenbusStateClosing:
+ xenbus_switch_state(xdev, XenbusStateClosed);
+ break;
+
+ case XenbusStateUnknown:
+ case XenbusStateClosed:
+ dev_dbg(&xdev->dev, "frontend is gone! unregister device\n");
+ device_unregister(&xdev->dev);
+ break;
+
+ case XenbusStateReconfiguring:
+ pciback_reconfigure(pdev);
+ break;
+
+ case XenbusStateConnected:
+ /* pcifront switched its state from reconfiguring to connected.
+ * Then switch to connected state.
+ */
+ xenbus_switch_state(xdev, XenbusStateConnected);
+ break;
+
+ default:
+ break;
+ }
+}
+
static int pciback_setup_backend(struct pciback_device *pdev)
{
/* Get configuration from xend (if available now) */
@@ -291,6 +499,7 @@ static int pciback_setup_backend(struct
int err = 0;
int i, num_devs;
char dev_str[64];
+ char state_str[64];
spin_lock(&pdev->dev_lock);
@@ -338,9 +547,26 @@ static int pciback_setup_backend(struct
goto out;
}
- err = pciback_export_device(pdev, domain, bus, slot, func);
+ err = pciback_export_device(pdev, domain, bus, slot, func, i);
if (err)
goto out;
+
+ /* Switch substate of this device. */
+ l = snprintf(state_str, sizeof(state_str), "state-%d", i);
+ if (unlikely(l >= (sizeof(state_str) - 1))) {
+ err = -ENOMEM;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "String overflow while reading "
+ "configuration");
+ goto out;
+ }
+ err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, state_str,
+ "%d", XenbusStateInitialised);
+ if (err) {
+ xenbus_dev_fatal(pdev->xdev, err, "Error switching "
+ "substate of dev-%d\n", i);
+ goto out;
+ }
}
err = pciback_publish_pci_roots(pdev, pciback_publish_pci_root);
diff -r 5069c4f94812 -r 4b9f2293d750 drivers/xen/pcifront/pci_op.c
--- a/drivers/xen/pcifront/pci_op.c Thu Feb 21 10:22:27 2008 +0000
+++ b/drivers/xen/pcifront/pci_op.c Fri Feb 22 10:06:03 2008 +0000
@@ -353,6 +353,58 @@ int pcifront_scan_root(struct pcifront_d
return err;
}
+int pcifront_rescan_root(struct pcifront_device *pdev,
+ unsigned int domain, unsigned int bus)
+{
+ struct pci_bus *b;
+ struct pci_dev *d;
+ unsigned int devfn;
+ int err = 0;
+
+#ifndef CONFIG_PCI_DOMAINS
+ if (domain != 0) {
+ dev_err(&pdev->xdev->dev,
+ "PCI Root in non-zero PCI Domain! domain=%d\n", domain);
+ dev_err(&pdev->xdev->dev,
+ "Please compile with CONFIG_PCI_DOMAINS\n");
+ err = -EINVAL;
+ goto err_out;
+ }
+#endif
+
+ dev_info(&pdev->xdev->dev, "Rescanning PCI Frontend Bus %04x:%02x\n",
+ domain, bus);
+
+ b = pci_find_bus(domain, bus);
+ if(!b)
+ /* If the bus is unknown, create it. */
+ return pcifront_scan_root(pdev, domain, bus);
+
+ /* Rescan the bus for newly attached functions and add.
+ * We omit handling of PCI bridge attachment because pciback prevents
+ * bridges from being exported.
+ */
+ for (devfn = 0; devfn < 0x100; devfn++) {
+ d = pci_get_slot(b, devfn);
+ if(d) {
+ /* Device is already known. */
+ pci_dev_put(d);
+ continue;
+ }
+
+ d = pci_scan_single_device(b, devfn);
+ if (d) {
+ dev_info(&pdev->xdev->dev, "New device on "
+ "%04x:%02x:%02x.%02x found.\n", domain, bus,
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
+ pci_bus_add_device(d);
+ }
+ }
+
+ err_out:
+ return err;
+}
+
static void free_root_bus_devs(struct pci_bus *bus)
{
struct pci_dev *dev;
diff -r 5069c4f94812 -r 4b9f2293d750 drivers/xen/pcifront/pcifront.h
--- a/drivers/xen/pcifront/pcifront.h Thu Feb 21 10:22:27 2008 +0000
+++ b/drivers/xen/pcifront/pcifront.h Fri Feb 22 10:06:03 2008 +0000
@@ -35,6 +35,8 @@ void pcifront_disconnect(struct pcifront
int pcifront_scan_root(struct pcifront_device *pdev,
unsigned int domain, unsigned int bus);
+int pcifront_rescan_root(struct pcifront_device *pdev,
+ unsigned int domain, unsigned int bus);
void pcifront_free_roots(struct pcifront_device *pdev);
#endif /* __XEN_PCIFRONT_H__ */
diff -r 5069c4f94812 -r 4b9f2293d750 drivers/xen/pcifront/xenbus.c
--- a/drivers/xen/pcifront/xenbus.c Thu Feb 21 10:22:27 2008 +0000
+++ b/drivers/xen/pcifront/xenbus.c Fri Feb 22 10:06:03 2008 +0000
@@ -214,6 +214,152 @@ static int pcifront_try_disconnect(struc
return err;
}
+static int pcifront_attach_devices(struct pcifront_device *pdev)
+{
+ int err = -EFAULT;
+ int i, num_roots, len;
+ unsigned int domain, bus;
+ char str[64];
+
+ spin_lock(&pdev->dev_lock);
+
+ if (xenbus_read_driver_state(pdev->xdev->nodename) !=
+ XenbusStateReconfiguring)
+ goto out;
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend,
+ "root_num", "%d", &num_roots);
+ if (err == -ENOENT) {
+ xenbus_dev_error(pdev->xdev, err,
+ "No PCI Roots found, trying 0000:00");
+ err = pcifront_rescan_root(pdev, 0, 0);
+ num_roots = 0;
+ } else if (err != 1) {
+ if (err == 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading number of PCI roots");
+ goto out;
+ }
+
+ for (i = 0; i < num_roots; i++) {
+ len = snprintf(str, sizeof(str), "root-%d", i);
+ if (unlikely(len >= (sizeof(str) - 1))) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str,
+ "%x:%x", &domain, &bus);
+ if (err != 2) {
+ if (err >= 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading PCI root %d", i);
+ goto out;
+ }
+
+ err = pcifront_rescan_root(pdev, domain, bus);
+ if (err) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error scanning PCI root %04x:%02x",
+ domain, bus);
+ goto out;
+ }
+ }
+
+ xenbus_switch_state(pdev->xdev, XenbusStateConnected);
+
+ out:
+ spin_unlock(&pdev->dev_lock);
+ return err;
+}
+
+static int pcifront_detach_devices(struct pcifront_device *pdev)
+{
+ int err;
+ int i, num_devs;
+ unsigned int domain, bus, slot, func;
+ struct pci_bus *pci_bus;
+ struct pci_dev *pci_dev;
+ char str[64];
+
+ spin_lock(&pdev->dev_lock);
+
+ if (xenbus_read_driver_state(pdev->xdev->nodename) !=
+ XenbusStateConnected)
+ goto out;
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, "num_devs", "%d",
+ &num_devs);
+ if (err != 1) {
+ if (err >= 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading number of PCI devices");
+ goto out;
+ }
+
+ /* Find devices being detached and remove them. */
+ for (i = 0; i < num_devs; i++) {
+ int l, state;
+ l = snprintf(str, sizeof(str), "state-%d", i);
+ if (unlikely(l >= (sizeof(str) - 1))) {
+ err = -ENOMEM;
+ goto out;
+ }
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, "%d",
+ &state);
+ if (err != 1)
+ state = XenbusStateUnknown;
+
+ if (state != XenbusStateClosing)
+ continue;
+
+ /* Remove device. */
+ l = snprintf(str, sizeof(str), "vdev-%d", i);
+ if (unlikely(l >= (sizeof(str) - 1))) {
+ err = -ENOMEM;
+ goto out;
+ }
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str,
+ "%x:%x:%x.%x", &domain, &bus, &slot, &func);
+ if (err != 4) {
+ if (err >= 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading PCI root %d", i);
+ goto out;
+ }
+
+ pci_bus = pci_find_bus(domain, bus);
+ if(!pci_bus) {
+ dev_dbg(&pdev->xdev->dev, "Cannot get bus %04x:%02x\n",
+ domain, bus);
+ continue;
+ }
+ pci_dev = pci_get_slot(pci_bus, PCI_DEVFN(slot, func));
+ if(!pci_dev) {
+ dev_dbg(&pdev->xdev->dev,
+ "Cannot get PCI device %04x:%02x:%02x.%02x\n",
+ domain, bus, slot, func);
+ continue;
+ }
+ pci_remove_bus_device(pci_dev);
+ pci_dev_put(pci_dev);
+
+ dev_dbg(&pdev->xdev->dev,
+ "PCI device %04x:%02x:%02x.%02x removed.\n",
+ domain, bus, slot, func);
+ }
+
+ err = xenbus_switch_state(pdev->xdev, XenbusStateReconfiguring);
+
+ out:
+ spin_unlock(&pdev->dev_lock);
+ return err;
+}
+
static void pcifront_backend_changed(struct xenbus_device *xdev,
enum xenbus_state be_state)
{
@@ -235,6 +381,14 @@ static void pcifront_backend_changed(str
case XenbusStateConnected:
pcifront_try_connect(pdev);
+ break;
+
+ case XenbusStateReconfiguring:
+ pcifront_detach_devices(pdev);
+ break;
+
+ case XenbusStateReconfigured:
+ pcifront_attach_devices(pdev);
break;
default:
diff -r 5069c4f94812 -r 4b9f2293d750 include/xen/interface/io/xenbus.h
--- a/include/xen/interface/io/xenbus.h Thu Feb 21 10:22:27 2008 +0000
+++ b/include/xen/interface/io/xenbus.h Fri Feb 22 10:06:03 2008 +0000
@@ -56,7 +56,14 @@ enum xenbus_state {
*/
XenbusStateClosing = 5,
- XenbusStateClosed = 6
+ XenbusStateClosed = 6,
+
+ /*
+ * Reconfiguring: The device is being reconfigured.
+ */
+ XenbusStateReconfiguring = 7,
+
+ XenbusStateReconfigured = 8
};
typedef enum xenbus_state XenbusState;
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|