return;
pdsc_intr_free(pdsc, qcq->intx);
- qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED;
}
static int pdsc_qcq_intr_alloc(struct pdsc *pdsc, struct pdsc_qcq *qcq)
pdsc_qcq_intr_free(pdsc, qcq);
+ /* Drain any work queued by ISR before it was freed above */
+ if (qcq->work.func)
+ cancel_work_sync(&qcq->work);
+
+ qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED;
+
if (qcq->q_base)
dma_free_coherent(dev, qcq->q_size,
qcq->q_base, qcq->q_base_pa);
static void pdsc_core_uninit(struct pdsc *pdsc)
{
- pdsc_qcq_free(pdsc, &pdsc->notifyqcq);
+ /* Free adminqcq first: its work accesses notifyqcq, so we must
+ * disable its IRQ and drain its work before freeing notifyqcq.
+ */
pdsc_qcq_free(pdsc, &pdsc->adminqcq);
+ pdsc_qcq_free(pdsc, &pdsc->notifyqcq);
if (pdsc->kern_dbpage) {
iounmap(pdsc->kern_dbpage);
{
if (!pdsc->pdev->is_virtfn)
pdsc_devcmd_reset(pdsc);
- if (pdsc->adminqcq.work.func)
- cancel_work_sync(&pdsc->adminqcq.work);
pci_clear_master(pdsc->pdev);
pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev);
timer_shutdown_sync(&pdsc->wdtimer);
- if (pdsc->wq)
- destroy_workqueue(pdsc->wq);
mutex_lock(&pdsc->config_lock);
set_bit(PDSC_S_STOPPING_DRIVER, &pdsc->state);
pdsc_stop(pdsc);
pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING);
mutex_unlock(&pdsc->config_lock);
+
+ if (pdsc->wq)
+ destroy_workqueue(pdsc->wq);
mutex_destroy(&pdsc->config_lock);
mutex_destroy(&pdsc->devcmd_lock);