aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYinghai Lu <yinghai@kernel.org>2013-11-30 17:40:29 -0500
committerBjorn Helgaas <bhelgaas@google.com>2013-12-18 15:53:40 -0500
commitef83b0781a73f9efcb1228256bfdfb97fc9533a8 (patch)
tree01c8a9940789bfcef622006d9d8eb26fd4ab0ce6
parentef37702eb3cae2ec35d56ddd4f096cd47fec3f0d (diff)
PCI: Remove from bus_list and release resources in pci_release_dev()
Previously we removed the pci_dev from the bus_list and released its resources in pci_destroy_dev(). But that's too early: it's possible to call pci_destroy_dev() twice for the same device (e.g., via sysfs), and that will cause an oops when we try to remove it from bus_list the second time. We should remove it from the bus_list only when the last reference to the pci_dev has been released, i.e., in pci_release_dev(). [bhelgaas: changelog] Signed-off-by: Yinghai Lu <yinghai@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r--drivers/pci/probe.c21
-rw-r--r--drivers/pci/remove.c19
2 files changed, 19 insertions, 21 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 173a9cf4593e..12ec56c9a913 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1154,6 +1154,18 @@ static void pci_release_capabilities(struct pci_dev *dev)
1154 pci_free_cap_save_buffers(dev); 1154 pci_free_cap_save_buffers(dev);
1155} 1155}
1156 1156
1157static void pci_free_resources(struct pci_dev *dev)
1158{
1159 int i;
1160
1161 pci_cleanup_rom(dev);
1162 for (i = 0; i < PCI_NUM_RESOURCES; i++) {
1163 struct resource *res = dev->resource + i;
1164 if (res->parent)
1165 release_resource(res);
1166 }
1167}
1168
1157/** 1169/**
1158 * pci_release_dev - free a pci device structure when all users of it are finished. 1170 * pci_release_dev - free a pci device structure when all users of it are finished.
1159 * @dev: device that's been disconnected 1171 * @dev: device that's been disconnected
@@ -1163,9 +1175,14 @@ static void pci_release_capabilities(struct pci_dev *dev)
1163 */ 1175 */
1164static void pci_release_dev(struct device *dev) 1176static void pci_release_dev(struct device *dev)
1165{ 1177{
1166 struct pci_dev *pci_dev; 1178 struct pci_dev *pci_dev = to_pci_dev(dev);
1179
1180 down_write(&pci_bus_sem);
1181 list_del(&pci_dev->bus_list);
1182 up_write(&pci_bus_sem);
1183
1184 pci_free_resources(pci_dev);
1167 1185
1168 pci_dev = to_pci_dev(dev);
1169 pci_release_capabilities(pci_dev); 1186 pci_release_capabilities(pci_dev);
1170 pci_release_of_node(pci_dev); 1187 pci_release_of_node(pci_dev);
1171 pcibios_release_device(pci_dev); 1188 pcibios_release_device(pci_dev);
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 692f4c39ac48..f452148e6d55 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -3,20 +3,6 @@
3#include <linux/pci-aspm.h> 3#include <linux/pci-aspm.h>
4#include "pci.h" 4#include "pci.h"
5 5
6static void pci_free_resources(struct pci_dev *dev)
7{
8 int i;
9
10 msi_remove_pci_irq_vectors(dev);
11
12 pci_cleanup_rom(dev);
13 for (i = 0; i < PCI_NUM_RESOURCES; i++) {
14 struct resource *res = dev->resource + i;
15 if (res->parent)
16 release_resource(res);
17 }
18}
19
20static void pci_stop_dev(struct pci_dev *dev) 6static void pci_stop_dev(struct pci_dev *dev)
21{ 7{
22 pci_pme_active(dev, false); 8 pci_pme_active(dev, false);
@@ -36,11 +22,6 @@ static void pci_destroy_dev(struct pci_dev *dev)
36{ 22{
37 device_del(&dev->dev); 23 device_del(&dev->dev);
38 24
39 down_write(&pci_bus_sem);
40 list_del(&dev->bus_list);
41 up_write(&pci_bus_sem);
42
43 pci_free_resources(dev);
44 put_device(&dev->dev); 25 put_device(&dev->dev);
45} 26}
46 27