summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2013-06-14 17:47:46 -0600
committerBjorn Helgaas <bhelgaas@google.com>2013-06-14 17:47:46 -0600
commitdf58f46c0f2a1d69268b734ac25c87ffb7aeb32a (patch)
tree46540d251f4f1c1e44af8d5ce339b37e0045ffe0
parent726246d2e6d0ed53ac22b6fec50d1345f25e6730 (diff)
parent050134864c1c76f49eb86c134a0e02fb3c196382 (diff)
Merge branch 'pci/jiang-bus-lock-v3' into next
* pci/jiang-bus-lock-v3: PCI: Return early on allocation failures to unindent mainline code PCI: Simplify IOV implementation and fix reference count races PCI: Drop redundant setting of bus->is_added in virtfn_add_bus() unicore32/PCI: Remove redundant call of pci_bus_add_devices() m68k/PCI: Remove redundant call of pci_bus_add_devices() PCI: Rename pci_release_bus_bridge_dev() to pci_release_host_bridge_dev() PCI: Fix refcount issue in pci_create_root_bus() error recovery path ia64/PCI: Clean up pci_scan_root_bus() usage PCI: Convert alloc_pci_dev(void) to pci_alloc_dev(bus) PCI: Introduce pci_alloc_dev(struct pci_bus*) to replace alloc_pci_dev() PCI: Introduce pci_bus_{get|put}() to manage PCI bus reference count Conflicts: drivers/pci/probe.c
-rw-r--r--arch/ia64/sn/kernel/io_init.c11
-rw-r--r--arch/m68k/platform/coldfire/pci.c1
-rw-r--r--arch/powerpc/kernel/pci_of_scan.c3
-rw-r--r--arch/sparc/kernel/pci.c3
-rw-r--r--arch/unicore32/kernel/pci.c5
-rw-r--r--drivers/char/agp/alpha-agp.c2
-rw-r--r--drivers/char/agp/parisc-agp.c2
-rw-r--r--drivers/pci/bus.c15
-rw-r--r--drivers/pci/iov.c60
-rw-r--r--drivers/pci/probe.c83
-rw-r--r--drivers/scsi/megaraid.c2
-rw-r--r--include/linux/pci.h5
12 files changed, 97 insertions, 95 deletions
diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c
index 238e2c511d9..e2c7733e18a 100644
--- a/arch/ia64/sn/kernel/io_init.c
+++ b/arch/ia64/sn/kernel/io_init.c
@@ -326,16 +326,7 @@ sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus)
bus = pci_scan_root_bus(NULL, busnum, &pci_root_ops, controller,
&resources);
if (bus == NULL)
- goto error_return; /* error, or bus already scanned */
-
- bus->sysdata = controller;
-
- return;
-
-error_return:
-
- kfree(controller);
- return;
+ kfree(controller);
}
/*
diff --git a/arch/m68k/platform/coldfire/pci.c b/arch/m68k/platform/coldfire/pci.c
index 8572246db84..b33f97a13e6 100644
--- a/arch/m68k/platform/coldfire/pci.c
+++ b/arch/m68k/platform/coldfire/pci.c
@@ -320,7 +320,6 @@ static int __init mcf_pci_init(void)
pci_bus_size_bridges(rootbus);
pci_bus_assign_resources(rootbus);
pci_enable_bridges(rootbus);
- pci_bus_add_devices(rootbus);
return 0;
}
diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c
index d2d407d6534..6b0ba5854d9 100644
--- a/arch/powerpc/kernel/pci_of_scan.c
+++ b/arch/powerpc/kernel/pci_of_scan.c
@@ -128,7 +128,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
const char *type;
struct pci_slot *slot;
- dev = alloc_pci_dev();
+ dev = pci_alloc_dev(bus);
if (!dev)
return NULL;
type = of_get_property(node, "device_type", NULL);
@@ -137,7 +137,6 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
pr_debug(" create device, devfn: %x, type: %s\n", devfn, type);
- dev->bus = bus;
dev->dev.of_node = of_node_get(node);
dev->dev.parent = bus->bridge;
dev->dev.bus = &pci_bus_type;
diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c
index 972892a0aa1..b16f624398a 100644
--- a/arch/sparc/kernel/pci.c
+++ b/arch/sparc/kernel/pci.c
@@ -254,7 +254,7 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
const char *type;
u32 class;
- dev = alloc_pci_dev();
+ dev = pci_alloc_dev(bus);
if (!dev)
return NULL;
@@ -281,7 +281,6 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
printk(" create device, devfn: %x, type: %s\n",
devfn, type);
- dev->bus = bus;
dev->sysdata = node;
dev->dev.parent = bus->bridge;
dev->dev.bus = &pci_bus_type;
diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c
index ef69c0c8282..374a055a8e6 100644
--- a/arch/unicore32/kernel/pci.c
+++ b/arch/unicore32/kernel/pci.c
@@ -277,11 +277,6 @@ static int __init pci_common_init(void)
pci_bus_assign_resources(puv3_bus);
}
- /*
- * Tell drivers about devices found.
- */
- pci_bus_add_devices(puv3_bus);
-
return 0;
}
subsys_initcall(pci_common_init);
diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c
index dd84af4d4f7..199b8e99f7d 100644
--- a/drivers/char/agp/alpha-agp.c
+++ b/drivers/char/agp/alpha-agp.c
@@ -174,7 +174,7 @@ alpha_core_agp_setup(void)
/*
* Build a fake pci_dev struct
*/
- pdev = alloc_pci_dev();
+ pdev = pci_alloc_dev(NULL);
if (!pdev)
return -ENOMEM;
pdev->vendor = 0xffff;
diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c
index 94821ab01c6..bf5d2477cb7 100644
--- a/drivers/char/agp/parisc-agp.c
+++ b/drivers/char/agp/parisc-agp.c
@@ -333,7 +333,7 @@ parisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa)
struct agp_bridge_data *bridge;
int error = 0;
- fake_bridge_dev = alloc_pci_dev();
+ fake_bridge_dev = pci_alloc_dev(NULL);
if (!fake_bridge_dev) {
error = -ENOMEM;
goto fail;
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 32e66a6f12d..b1ff02ab4f1 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -283,6 +283,21 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
}
EXPORT_SYMBOL_GPL(pci_walk_bus);
+struct pci_bus *pci_bus_get(struct pci_bus *bus)
+{
+ if (bus)
+ get_device(&bus->dev);
+ return bus;
+}
+EXPORT_SYMBOL(pci_bus_get);
+
+void pci_bus_put(struct pci_bus *bus)
+{
+ if (bus)
+ put_device(&bus->dev);
+}
+EXPORT_SYMBOL(pci_bus_put);
+
EXPORT_SYMBOL(pci_bus_alloc_resource);
EXPORT_SYMBOL_GPL(pci_bus_add_device);
EXPORT_SYMBOL(pci_bus_add_devices);
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index a971a6f6268..de8ffacf9c9 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -47,46 +47,36 @@ static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
return NULL;
pci_bus_insert_busn_res(child, busnr, busnr);
- bus->is_added = 1;
return child;
}
-static void virtfn_remove_bus(struct pci_bus *bus, int busnr)
+static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus)
{
- struct pci_bus *child;
-
- if (bus->number == busnr)
- return;
-
- child = pci_find_bus(pci_domain_nr(bus), busnr);
- BUG_ON(!child);
-
- if (list_empty(&child->devices))
- pci_remove_bus(child);
+ if (physbus != virtbus && list_empty(&virtbus->devices))
+ pci_remove_bus(virtbus);
}
static int virtfn_add(struct pci_dev *dev, int id, int reset)
{
int i;
- int rc;
+ int rc = -ENOMEM;
u64 size;
char buf[VIRTFN_ID_LEN];
struct pci_dev *virtfn;
struct resource *res;
struct pci_sriov *iov = dev->sriov;
+ struct pci_bus *bus;
- virtfn = alloc_pci_dev();
+ mutex_lock(&iov->dev->sriov->lock);
+ bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
+ if (!bus)
+ goto failed;
+
+ virtfn = pci_alloc_dev(bus);
if (!virtfn)
- return -ENOMEM;
+ goto failed0;
- mutex_lock(&iov->dev->sriov->lock);
- virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
- if (!virtfn->bus) {
- kfree(virtfn);
- mutex_unlock(&iov->dev->sriov->lock);
- return -ENOMEM;
- }
virtfn->devfn = virtfn_devfn(dev, id);
virtfn->vendor = dev->vendor;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
@@ -134,7 +124,9 @@ failed1:
pci_dev_put(dev);
mutex_lock(&iov->dev->sriov->lock);
pci_stop_and_remove_bus_device(virtfn);
- virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
+failed0:
+ virtfn_remove_bus(dev->bus, bus);
+failed:
mutex_unlock(&iov->dev->sriov->lock);
return rc;
@@ -143,20 +135,15 @@ failed1:
static void virtfn_remove(struct pci_dev *dev, int id, int reset)
{
char buf[VIRTFN_ID_LEN];
- struct pci_bus *bus;
struct pci_dev *virtfn;
struct pci_sriov *iov = dev->sriov;
- bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id));
- if (!bus)
- return;
-
- virtfn = pci_get_slot(bus, virtfn_devfn(dev, id));
+ virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
+ virtfn_bus(dev, id),
+ virtfn_devfn(dev, id));
if (!virtfn)
return;
- pci_dev_put(virtfn);
-
if (reset) {
device_release_driver(&virtfn->dev);
__pci_reset_function(virtfn);
@@ -174,9 +161,11 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
mutex_lock(&iov->dev->sriov->lock);
pci_stop_and_remove_bus_device(virtfn);
- virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
+ virtfn_remove_bus(dev->bus, virtfn->bus);
mutex_unlock(&iov->dev->sriov->lock);
+ /* balance pci_get_domain_bus_and_slot() */
+ pci_dev_put(virtfn);
pci_dev_put(dev);
}
@@ -333,13 +322,14 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
if (!pdev)
return -ENODEV;
- pci_dev_put(pdev);
-
- if (!pdev->is_physfn)
+ if (!pdev->is_physfn) {
+ pci_dev_put(pdev);
return -ENODEV;
+ }
rc = sysfs_create_link(&dev->dev.kobj,
&pdev->dev.kobj, "dep_link");
+ pci_dev_put(pdev);
if (rc)
return rc;
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index d0c33aac768..46ada5c098e 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -470,33 +470,46 @@ void pci_read_bridge_bases(struct pci_bus *child)
}
}
-static struct pci_bus * pci_alloc_bus(void)
+static struct pci_bus *pci_alloc_bus(void)
{
struct pci_bus *b;
b = kzalloc(sizeof(*b), GFP_KERNEL);
- if (b) {
- INIT_LIST_HEAD(&b->node);
- INIT_LIST_HEAD(&b->children);
- INIT_LIST_HEAD(&b->devices);
- INIT_LIST_HEAD(&b->slots);
- INIT_LIST_HEAD(&b->resources);
- b->max_bus_speed = PCI_SPEED_UNKNOWN;
- b->cur_bus_speed = PCI_SPEED_UNKNOWN;
- }
+ if (!b)
+ return NULL;
+
+ INIT_LIST_HEAD(&b->node);
+ INIT_LIST_HEAD(&b->children);
+ INIT_LIST_HEAD(&b->devices);
+ INIT_LIST_HEAD(&b->slots);
+ INIT_LIST_HEAD(&b->resources);
+ b->max_bus_speed = PCI_SPEED_UNKNOWN;
+ b->cur_bus_speed = PCI_SPEED_UNKNOWN;
return b;
}
+static void pci_release_host_bridge_dev(struct device *dev)
+{
+ struct pci_host_bridge *bridge = to_pci_host_bridge(dev);
+
+ if (bridge->release_fn)
+ bridge->release_fn(bridge);
+
+ pci_free_resource_list(&bridge->windows);
+
+ kfree(bridge);
+}
+
static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
{
struct pci_host_bridge *bridge;
bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
- if (bridge) {
- INIT_LIST_HEAD(&bridge->windows);
- bridge->bus = b;
- }
+ if (!bridge)
+ return NULL;
+ INIT_LIST_HEAD(&bridge->windows);
+ bridge->bus = b;
return bridge;
}
@@ -1152,6 +1165,7 @@ static void pci_release_dev(struct device *dev)
pci_release_capabilities(pci_dev);
pci_release_of_node(pci_dev);
pcibios_release_device(pci_dev);
+ pci_bus_put(pci_dev->bus);
kfree(pci_dev);
}
@@ -1208,19 +1222,7 @@ int pci_cfg_space_size(struct pci_dev *dev)
return PCI_CFG_SPACE_SIZE;
}
-static void pci_release_bus_bridge_dev(struct device *dev)
-{
- struct pci_host_bridge *bridge = to_pci_host_bridge(dev);
-
- if (bridge->release_fn)
- bridge->release_fn(bridge);
-
- pci_free_resource_list(&bridge->windows);
-
- kfree(bridge);
-}
-
-struct pci_dev *alloc_pci_dev(void)
+struct pci_dev *pci_alloc_dev(struct pci_bus *bus)
{
struct pci_dev *dev;
@@ -1230,9 +1232,16 @@ struct pci_dev *alloc_pci_dev(void)
INIT_LIST_HEAD(&dev->bus_list);
dev->dev.type = &pci_dev_type;
+ dev->bus = pci_bus_get(bus);
return dev;
}
+EXPORT_SYMBOL(pci_alloc_dev);
+
+struct pci_dev *alloc_pci_dev(void)
+{
+ return pci_alloc_dev(NULL);
+}
EXPORT_SYMBOL(alloc_pci_dev);
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
@@ -1283,11 +1292,10 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
return NULL;
- dev = alloc_pci_dev();
+ dev = pci_alloc_dev(bus);
if (!dev)
return NULL;
- dev->bus = bus;
dev->devfn = devfn;
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
@@ -1295,6 +1303,7 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
pci_set_of_node(dev);
if (pci_setup_device(dev)) {
+ pci_bus_put(dev->bus);
kfree(dev);
return NULL;
}
@@ -1720,15 +1729,19 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
goto err_out;
bridge->dev.parent = parent;
- bridge->dev.release = pci_release_bus_bridge_dev;
+ bridge->dev.release = pci_release_host_bridge_dev;
dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
error = pcibios_root_bridge_prepare(bridge);
- if (error)
- goto bridge_dev_reg_err;
+ if (error) {
+ kfree(bridge);
+ goto err_out;
+ }
error = device_register(&bridge->dev);
- if (error)
- goto bridge_dev_reg_err;
+ if (error) {
+ put_device(&bridge->dev);
+ goto err_out;
+ }
b->bridge = get_device(&bridge->dev);
device_enable_async_suspend(b->bridge);
pci_set_bus_of_node(b);
@@ -1784,8 +1797,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
class_dev_reg_err:
put_device(&bridge->dev);
device_unregister(&bridge->dev);
-bridge_dev_reg_err:
- kfree(bridge);
err_out:
kfree(b);
return NULL;
diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c
index 846f475f62c..90c95a3385d 100644
--- a/drivers/scsi/megaraid.c
+++ b/drivers/scsi/megaraid.c
@@ -2026,7 +2026,7 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor)
static inline int
make_local_pdev(adapter_t *adapter, struct pci_dev **pdev)
{
- *pdev = alloc_pci_dev();
+ *pdev = pci_alloc_dev(NULL);
if( *pdev == NULL ) return -1;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 8f170e9073a..0fd1f1582fa 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -364,7 +364,8 @@ static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
return dev;
}
-struct pci_dev *alloc_pci_dev(void);
+struct pci_dev *pci_alloc_dev(struct pci_bus *bus);
+struct pci_dev * __deprecated alloc_pci_dev(void);
#define to_pci_dev(n) container_of(n, struct pci_dev, dev)
#define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL)
@@ -1018,6 +1019,8 @@ int pci_request_selected_regions_exclusive(struct pci_dev *, int, const char *);
void pci_release_selected_regions(struct pci_dev *, int);
/* drivers/pci/bus.c */
+struct pci_bus *pci_bus_get(struct pci_bus *bus);
+void pci_bus_put(struct pci_bus *bus);
void pci_add_resource(struct list_head *resources, struct resource *res);
void pci_add_resource_offset(struct list_head *resources, struct resource *res,
resource_size_t offset);