aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/slot.c
diff options
context:
space:
mode:
authorYijing Wang <wangyijing@huawei.com>2015-07-17 05:16:31 -0400
committerBjorn Helgaas <bhelgaas@google.com>2015-07-30 16:49:10 -0400
commit67546762978f523749eac157903e0b01c18e083a (patch)
tree646a5a8749dae0c3192d9db23dc4eb1f63f90f75 /drivers/pci/slot.c
parentac10836b681289f7e430e52b106a209bbdcaa75e (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.c18
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
15struct kset *pci_slots_kset; 15struct kset *pci_slots_kset;
16EXPORT_SYMBOL_GPL(pci_slots_kset); 16EXPORT_SYMBOL_GPL(pci_slots_kset);
17static DEFINE_MUTEX(pci_slot_mutex);
17 18
18static ssize_t pci_slot_attr_show(struct kobject *kobj, 19static 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)
194static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) 197static 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
311out: 315out:
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;
315err: 319err:
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}
339EXPORT_SYMBOL_GPL(pci_destroy_slot); 343EXPORT_SYMBOL_GPL(pci_destroy_slot);
340 344