diff options
author | Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> | 2009-05-25 20:08:03 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2009-05-27 17:53:07 -0400 |
commit | dacd2549ca61ddbdd1ed62a76ca95dea3f0e02c6 (patch) | |
tree | e2369849d60890e3f5596b52346aefc74ce414c5 /drivers | |
parent | cd86a536c81e9300d984327517548ca0652eebf9 (diff) |
PCI/ACPI: fix wrong ref count handling in acpi_pci_bind()
The 'dev' field of struct acpi_pci_data is having a pointer to struct
pci_dev without incrementing the reference counter. Because of this, I
got the following kernel oops when I was doing some pci hotplug
operations. This patch fixes this bug by replacing wrong hand-made
pci_find_slot() with pci_get_slot() in acpi_pci_bind().
BUG: unable to handle kernel NULL pointer dereference at 00000000000000e8
IP: [<ffffffff803f0e9b>] acpi_pci_unbind+0xb1/0xdd
Call Trace:
[<ffffffff803ecee4>] acpi_bus_remove+0x54/0x68
[<ffffffff803ecf6d>] acpi_bus_trim+0x75/0xe3
[<ffffffffa0345ddd>] acpiphp_disable_slot+0x16d/0x1e0 [acpiphp]
[<ffffffffa03441f0>] disable_slot+0x20/0x60 [acpiphp]
[<ffffffff803cfc18>] power_write_file+0xc8/0x110
[<ffffffff803c6a54>] pci_slot_attr_store+0x24/0x30
[<ffffffff803469ce>] sysfs_write_file+0xce/0x140
[<ffffffff802e94e7>] vfs_write+0xc7/0x170
[<ffffffff802e9aa0>] sys_write+0x50/0x90
[<ffffffff8020bd6b>] system_call_fastpath+0x16/0x1b
Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Reviewed-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Reviewed-by: Alex Chiang <achiang@hp.com>
Tested-by: Alex Chiang <achiang@hp.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/pci_bind.c | 24 |
1 files changed, 6 insertions, 18 deletions
diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index 95650f83ce2e..bc46de3d967f 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c | |||
@@ -116,9 +116,6 @@ int acpi_pci_bind(struct acpi_device *device) | |||
116 | struct acpi_pci_data *pdata; | 116 | struct acpi_pci_data *pdata; |
117 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 117 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
118 | acpi_handle handle; | 118 | acpi_handle handle; |
119 | struct pci_dev *dev; | ||
120 | struct pci_bus *bus; | ||
121 | |||
122 | 119 | ||
123 | if (!device || !device->parent) | 120 | if (!device || !device->parent) |
124 | return -EINVAL; | 121 | return -EINVAL; |
@@ -176,20 +173,9 @@ int acpi_pci_bind(struct acpi_device *device) | |||
176 | * Locate matching device in PCI namespace. If it doesn't exist | 173 | * Locate matching device in PCI namespace. If it doesn't exist |
177 | * this typically means that the device isn't currently inserted | 174 | * this typically means that the device isn't currently inserted |
178 | * (e.g. docking station, port replicator, etc.). | 175 | * (e.g. docking station, port replicator, etc.). |
179 | * We cannot simply search the global pci device list, since | ||
180 | * PCI devices are added to the global pci list when the root | ||
181 | * bridge start ops are run, which may not have happened yet. | ||
182 | */ | 176 | */ |
183 | bus = pci_find_bus(data->id.segment, data->id.bus); | 177 | data->dev = pci_get_slot(pdata->bus, |
184 | if (bus) { | 178 | PCI_DEVFN(data->id.device, data->id.function)); |
185 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
186 | if (dev->devfn == PCI_DEVFN(data->id.device, | ||
187 | data->id.function)) { | ||
188 | data->dev = dev; | ||
189 | break; | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | if (!data->dev) { | 179 | if (!data->dev) { |
194 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 180 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
195 | "Device %04x:%02x:%02x.%d not present in PCI namespace\n", | 181 | "Device %04x:%02x:%02x.%d not present in PCI namespace\n", |
@@ -259,9 +245,10 @@ int acpi_pci_bind(struct acpi_device *device) | |||
259 | 245 | ||
260 | end: | 246 | end: |
261 | kfree(buffer.pointer); | 247 | kfree(buffer.pointer); |
262 | if (result) | 248 | if (result) { |
249 | pci_dev_put(data->dev); | ||
263 | kfree(data); | 250 | kfree(data); |
264 | 251 | } | |
265 | return result; | 252 | return result; |
266 | } | 253 | } |
267 | 254 | ||
@@ -303,6 +290,7 @@ static int acpi_pci_unbind(struct acpi_device *device) | |||
303 | if (data->dev->subordinate) { | 290 | if (data->dev->subordinate) { |
304 | acpi_pci_irq_del_prt(data->id.segment, data->bus->number); | 291 | acpi_pci_irq_del_prt(data->id.segment, data->bus->number); |
305 | } | 292 | } |
293 | pci_dev_put(data->dev); | ||
306 | kfree(data); | 294 | kfree(data); |
307 | 295 | ||
308 | end: | 296 | end: |