Skip to content

Commit

Permalink
firewire: ohci: Fix deadlock at bus reset
Browse files Browse the repository at this point in the history
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 dbf2576
"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>
  • Loading branch information
Stephan Gatzka authored and Stefan Richter committed Aug 29, 2013
1 parent 7a723c6 commit db9ae8f
Showing 1 changed file with 8 additions and 1 deletion.
9 changes: 8 additions & 1 deletion drivers/firewire/ohci.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ struct fw_ohci {
u32 self_id_buffer[512];
};

static struct workqueue_struct *selfid_workqueue;

static inline struct fw_ohci *fw_ohci(struct fw_card *card)
{
return container_of(card, struct fw_ohci, card);
Expand Down Expand Up @@ -2087,7 +2089,7 @@ static irqreturn_t irq_handler(int irq, void *data)
log_irqs(ohci, event);

if (event & OHCI1394_selfIDComplete)
queue_work(fw_workqueue, &ohci->bus_reset_work);
queue_work(selfid_workqueue, &ohci->bus_reset_work);

if (event & OHCI1394_RQPkt)
tasklet_schedule(&ohci->ar_request_ctx.tasklet);
Expand Down Expand Up @@ -3872,12 +3874,17 @@ static struct pci_driver fw_ohci_pci_driver = {

static int __init fw_ohci_init(void)
{
selfid_workqueue = alloc_workqueue(KBUILD_MODNAME, WQ_MEM_RECLAIM, 0);
if (!selfid_workqueue)
return -ENOMEM;

return pci_register_driver(&fw_ohci_pci_driver);
}

static void __exit fw_ohci_cleanup(void)
{
pci_unregister_driver(&fw_ohci_pci_driver);
destroy_workqueue(selfid_workqueue);
}

module_init(fw_ohci_init);
Expand Down

0 comments on commit db9ae8f

Please sign in to comment.