aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/ohci.c
diff options
context:
space:
mode:
authorStephan Gatzka <stephan.gatzka@gmail.com>2013-08-26 14:50:05 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2013-08-29 16:35:05 -0400
commitdb9ae8fec7b19f0ac6c60d998cac968d801a998d (patch)
treeb8042b322264f0be3539e37a254f4f1f06b06e03 /drivers/firewire/ohci.c
parent7a723c6ed9e92bf91db5c65542c78106030afdbe (diff)
firewire: ohci: Fix deadlock at bus reset
Put bus_reset_work into its own workqueue. By doing this, forward progress of bus_reset_work() is guaranteed if the work is switched over to a rescuer thread. Switching work to a rescuer thread happens if a new worker thread could not be allocated in certain time (MAYDAY_INITIAL_TIMEOUT, typically 10 ms). This might not be possible under high memory pressure or even on a heavily loaded embedded system running a slow serial console. The former deadlock occured in the following situation: The rescuer thread ran fw_device_init->read_config_rom->read_rom->fw_run_transaction. fw_run_transaction blocked waiting for the completion object. This completion object would have been completed in bus_reset_work, but this work was never executed in the rescuer thread due to its strictly sequential behaviour. [Stefan R.: Removed WQ_NON_REENTRANT flag from allocation because it is no longer needed in current kernels. Add it back if you backport to kernels older than 3.7, i.e. one which does not contain dbf2576e37da "workqueue: make all workqueues non-reentrant". Swapped order of destroy_workqueue and pci_unregister_driver.] Signed-off-by: Stephan Gatzka <stephan.gatzka@gmail.com> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/ohci.c')
-rw-r--r--drivers/firewire/ohci.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 80830d6fe46b..6aa8a86cb83b 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -242,6 +242,8 @@ struct fw_ohci {
242 u32 self_id_buffer[512]; 242 u32 self_id_buffer[512];
243}; 243};
244 244
245static struct workqueue_struct *selfid_workqueue;
246
245static inline struct fw_ohci *fw_ohci(struct fw_card *card) 247static inline struct fw_ohci *fw_ohci(struct fw_card *card)
246{ 248{
247 return container_of(card, struct fw_ohci, card); 249 return container_of(card, struct fw_ohci, card);
@@ -2087,7 +2089,7 @@ static irqreturn_t irq_handler(int irq, void *data)
2087 log_irqs(ohci, event); 2089 log_irqs(ohci, event);
2088 2090
2089 if (event & OHCI1394_selfIDComplete) 2091 if (event & OHCI1394_selfIDComplete)
2090 queue_work(fw_workqueue, &ohci->bus_reset_work); 2092 queue_work(selfid_workqueue, &ohci->bus_reset_work);
2091 2093
2092 if (event & OHCI1394_RQPkt) 2094 if (event & OHCI1394_RQPkt)
2093 tasklet_schedule(&ohci->ar_request_ctx.tasklet); 2095 tasklet_schedule(&ohci->ar_request_ctx.tasklet);
@@ -3872,12 +3874,17 @@ static struct pci_driver fw_ohci_pci_driver = {
3872 3874
3873static int __init fw_ohci_init(void) 3875static int __init fw_ohci_init(void)
3874{ 3876{
3877 selfid_workqueue = alloc_workqueue(KBUILD_MODNAME, WQ_MEM_RECLAIM, 0);
3878 if (!selfid_workqueue)
3879 return -ENOMEM;
3880
3875 return pci_register_driver(&fw_ohci_pci_driver); 3881 return pci_register_driver(&fw_ohci_pci_driver);
3876} 3882}
3877 3883
3878static void __exit fw_ohci_cleanup(void) 3884static void __exit fw_ohci_cleanup(void)
3879{ 3885{
3880 pci_unregister_driver(&fw_ohci_pci_driver); 3886 pci_unregister_driver(&fw_ohci_pci_driver);
3887 destroy_workqueue(selfid_workqueue);
3881} 3888}
3882 3889
3883module_init(fw_ohci_init); 3890module_init(fw_ohci_init);