diff options
author | Yijing Wang <wangyijing@huawei.com> | 2015-07-17 05:16:31 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2015-07-30 16:49:10 -0400 |
commit | 67546762978f523749eac157903e0b01c18e083a (patch) | |
tree | 646a5a8749dae0c3192d9db23dc4eb1f63f90f75 /drivers/pci/slot.c | |
parent | ac10836b681289f7e430e52b106a209bbdcaa75e (diff) |
PCI: Protect pci_bus->slots with pci_slot_mutex, not pci_bus_sem
Rajat Jain reported a deadlock when PCIe hot-add and AER recovery happen at
the same time:
thread 1:
pciehp_enable_slot
pciehp_configure_device
pci_bus_add_devices
pci_bus_add_device
device_attach
device_lock(dev) # acquire device lock
...
pciehp_probe
init_slot
pci_hp_register
pci_create_slot
down_write(pci_bus_sem) # deadlock here
thread 2:
aer_isr_one_error
aer_process_err_device
do_recovery
broadcast_error_message(..., report_error_detected)
pci_walk_bus(..., cb=report_error_detected, ...)
down_read(&pci_bus_sem) # acquire pci_bus_sem
report_error_detected(dev) # cb()
device_lock(dev) # deadlock here
Previously, the bus->devices and bus->slots list were protected by
pci_bus_sem. In pci_create_slot(), we held it for writing so we could
add to the bus->slots list.
Add a new local pci_slot_mutex to protect bus->slots. Hold pci_bus_sem for
reading while searching the bus->devices list.
[bhelgaas: changelog]
Link: http://lkml.kernel.org/r/CAA93t1qpPqbih+UB0McA_d_+2rVaNkXsinAUxYzK9+JXSS+L-g@mail.gmail.com
Reported-by: Rajat Jain <rajatja@google.com>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci/slot.c')
-rw-r--r-- | drivers/pci/slot.c | 18 |
1 files changed, 11 insertions, 7 deletions
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 396c200b9ddb..4bd3fce93fa4 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c | |||
@@ -14,6 +14,7 @@ | |||
14 | 14 | ||
15 | struct kset *pci_slots_kset; | 15 | struct kset *pci_slots_kset; |
16 | EXPORT_SYMBOL_GPL(pci_slots_kset); | 16 | EXPORT_SYMBOL_GPL(pci_slots_kset); |
17 | static DEFINE_MUTEX(pci_slot_mutex); | ||
17 | 18 | ||
18 | static ssize_t pci_slot_attr_show(struct kobject *kobj, | 19 | static ssize_t pci_slot_attr_show(struct kobject *kobj, |
19 | struct attribute *attr, char *buf) | 20 | struct attribute *attr, char *buf) |
@@ -106,9 +107,11 @@ static void pci_slot_release(struct kobject *kobj) | |||
106 | dev_dbg(&slot->bus->dev, "dev %02x, released physical slot %s\n", | 107 | dev_dbg(&slot->bus->dev, "dev %02x, released physical slot %s\n", |
107 | slot->number, pci_slot_name(slot)); | 108 | slot->number, pci_slot_name(slot)); |
108 | 109 | ||
110 | down_read(&pci_bus_sem); | ||
109 | list_for_each_entry(dev, &slot->bus->devices, bus_list) | 111 | list_for_each_entry(dev, &slot->bus->devices, bus_list) |
110 | if (PCI_SLOT(dev->devfn) == slot->number) | 112 | if (PCI_SLOT(dev->devfn) == slot->number) |
111 | dev->slot = NULL; | 113 | dev->slot = NULL; |
114 | up_read(&pci_bus_sem); | ||
112 | 115 | ||
113 | list_del(&slot->list); | 116 | list_del(&slot->list); |
114 | 117 | ||
@@ -194,9 +197,8 @@ static int rename_slot(struct pci_slot *slot, const char *name) | |||
194 | static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) | 197 | static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) |
195 | { | 198 | { |
196 | struct pci_slot *slot; | 199 | struct pci_slot *slot; |
197 | /* | 200 | |
198 | * We already hold pci_bus_sem so don't worry | 201 | /* We already hold pci_slot_mutex */ |
199 | */ | ||
200 | list_for_each_entry(slot, &parent->slots, list) | 202 | list_for_each_entry(slot, &parent->slots, list) |
201 | if (slot->number == slot_nr) { | 203 | if (slot->number == slot_nr) { |
202 | kobject_get(&slot->kobj); | 204 | kobject_get(&slot->kobj); |
@@ -253,7 +255,7 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, | |||
253 | int err = 0; | 255 | int err = 0; |
254 | char *slot_name = NULL; | 256 | char *slot_name = NULL; |
255 | 257 | ||
256 | down_write(&pci_bus_sem); | 258 | mutex_lock(&pci_slot_mutex); |
257 | 259 | ||
258 | if (slot_nr == -1) | 260 | if (slot_nr == -1) |
259 | goto placeholder; | 261 | goto placeholder; |
@@ -301,16 +303,18 @@ placeholder: | |||
301 | INIT_LIST_HEAD(&slot->list); | 303 | INIT_LIST_HEAD(&slot->list); |
302 | list_add(&slot->list, &parent->slots); | 304 | list_add(&slot->list, &parent->slots); |
303 | 305 | ||
306 | down_read(&pci_bus_sem); | ||
304 | list_for_each_entry(dev, &parent->devices, bus_list) | 307 | list_for_each_entry(dev, &parent->devices, bus_list) |
305 | if (PCI_SLOT(dev->devfn) == slot_nr) | 308 | if (PCI_SLOT(dev->devfn) == slot_nr) |
306 | dev->slot = slot; | 309 | dev->slot = slot; |
310 | up_read(&pci_bus_sem); | ||
307 | 311 | ||
308 | dev_dbg(&parent->dev, "dev %02x, created physical slot %s\n", | 312 | dev_dbg(&parent->dev, "dev %02x, created physical slot %s\n", |
309 | slot_nr, pci_slot_name(slot)); | 313 | slot_nr, pci_slot_name(slot)); |
310 | 314 | ||
311 | out: | 315 | out: |
312 | kfree(slot_name); | 316 | kfree(slot_name); |
313 | up_write(&pci_bus_sem); | 317 | mutex_unlock(&pci_slot_mutex); |
314 | return slot; | 318 | return slot; |
315 | err: | 319 | err: |
316 | kfree(slot); | 320 | kfree(slot); |
@@ -332,9 +336,9 @@ void pci_destroy_slot(struct pci_slot *slot) | |||
332 | dev_dbg(&slot->bus->dev, "dev %02x, dec refcount to %d\n", | 336 | dev_dbg(&slot->bus->dev, "dev %02x, dec refcount to %d\n", |
333 | slot->number, atomic_read(&slot->kobj.kref.refcount) - 1); | 337 | slot->number, atomic_read(&slot->kobj.kref.refcount) - 1); |
334 | 338 | ||
335 | down_write(&pci_bus_sem); | 339 | mutex_lock(&pci_slot_mutex); |
336 | kobject_put(&slot->kobj); | 340 | kobject_put(&slot->kobj); |
337 | up_write(&pci_bus_sem); | 341 | mutex_unlock(&pci_slot_mutex); |
338 | } | 342 | } |
339 | EXPORT_SYMBOL_GPL(pci_destroy_slot); | 343 | EXPORT_SYMBOL_GPL(pci_destroy_slot); |
340 | 344 | ||