sfc: Use a separate workqueue for resets
authorBen Hutchings <bhutchings@solarflare.com>
Fri, 18 Jul 2008 18:01:20 +0000 (19:01 +0100)
committerJeff Garzik <jgarzik@redhat.com>
Tue, 22 Jul 2008 23:44:13 +0000 (19:44 -0400)
This avoids deadlock in case a reset is triggered during self-test.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/net/sfc/efx.c
drivers/net/sfc/net_driver.h

index 7b2015f9e46994407c0fef9c0bcee02b3918ce49..7b2a8186623244da041ec8baa683d075f021c232 100644 (file)
@@ -1762,7 +1762,7 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
 
        efx->reset_pending = method;
 
-       queue_work(efx->workqueue, &efx->reset_work);
+       queue_work(efx->reset_workqueue, &efx->reset_work);
 }
 
 /**************************************************************************
@@ -1907,14 +1907,28 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
                goto fail1;
        }
 
+       efx->reset_workqueue = create_singlethread_workqueue("sfc_reset");
+       if (!efx->reset_workqueue) {
+               rc = -ENOMEM;
+               goto fail2;
+       }
+
        return 0;
 
+ fail2:
+       destroy_workqueue(efx->workqueue);
+       efx->workqueue = NULL;
+
  fail1:
        return rc;
 }
 
 static void efx_fini_struct(struct efx_nic *efx)
 {
+       if (efx->reset_workqueue) {
+               destroy_workqueue(efx->reset_workqueue);
+               efx->reset_workqueue = NULL;
+       }
        if (efx->workqueue) {
                destroy_workqueue(efx->workqueue);
                efx->workqueue = NULL;
@@ -1977,7 +1991,7 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
         * scheduled from this point because efx_stop_all() has been
         * called, we are no longer registered with driverlink, and
         * the net_device's have been removed. */
-       flush_workqueue(efx->workqueue);
+       flush_workqueue(efx->reset_workqueue);
 
        efx_pci_remove_main(efx);
 
@@ -2098,7 +2112,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
                 * scheduled since efx_stop_all() has been called, and we
                 * have not and never have been registered with either
                 * the rtnetlink or driverlink layers. */
-               cancel_work_sync(&efx->reset_work);
+               flush_workqueue(efx->reset_workqueue);
 
                /* Retry if a recoverably reset event has been scheduled */
                if ((efx->reset_pending != RESET_TYPE_INVISIBLE) &&
index d803b86c647cd64dba5cb512b6686656d6a540df..219c74a772c32deb333b62bef44b3dd2422f18ba 100644 (file)
@@ -616,7 +616,9 @@ union efx_multicast_hash {
  * @pci_dev: The PCI device
  * @type: Controller type attributes
  * @legacy_irq: IRQ number
- * @workqueue: Workqueue for resets, port reconfigures and the HW monitor
+ * @workqueue: Workqueue for port reconfigures and the HW monitor.
+ *     Work items do not hold and must not acquire RTNL.
+ * @reset_workqueue: Workqueue for resets.  Work item will acquire RTNL.
  * @reset_work: Scheduled reset workitem
  * @monitor_work: Hardware monitor workitem
  * @membase_phys: Memory BAR value as physical address
@@ -684,6 +686,7 @@ struct efx_nic {
        const struct efx_nic_type *type;
        int legacy_irq;
        struct workqueue_struct *workqueue;
+       struct workqueue_struct *reset_workqueue;
        struct work_struct reset_work;
        struct delayed_work monitor_work;
        resource_size_t membase_phys;