summaryrefslogtreecommitdiffstats
path: root/drivers/pci/probe.c
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2013-06-15 01:47:46 +0200
committerBjorn Helgaas <bhelgaas@google.com>2013-06-15 01:47:46 +0200
commitdf58f46c0f2a1d69268b734ac25c87ffb7aeb32a (patch)
tree46540d251f4f1c1e44af8d5ce339b37e0045ffe0 /drivers/pci/probe.c
parentMerge branch 'pci/misc' into next (diff)
parentPCI: Return early on allocation failures to unindent mainline code (diff)
downloadlinux-df58f46c0f2a1d69268b734ac25c87ffb7aeb32a.tar.xz
linux-df58f46c0f2a1d69268b734ac25c87ffb7aeb32a.zip
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
Diffstat (limited to 'drivers/pci/probe.c')
-rw-r--r--drivers/pci/probe.c83
1 files changed, 47 insertions, 36 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index d0c33aac768e..46ada5c098eb 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;