dmaengine: idxd: fix descriptor flushing locking
authorDave Jiang <dave.jiang@intel.com>
Mon, 13 Dec 2021 18:51:29 +0000 (11:51 -0700)
committerVinod Koul <vkoul@kernel.org>
Wed, 5 Jan 2022 07:41:22 +0000 (13:11 +0530)
The descriptor flushing for shutdown is not holding the irq_entry list
lock. If there's ongoing interrupt completion handling, this can corrupt
the list. Add locking to protect list walking. Also refactor the code so
it's more compact.

Fixes: 8f47d1a5e545 ("dmaengine: idxd: connect idxd to dmaengine subsystem")
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Link: https://lore.kernel.org/r/163942148935.2412839.18282664745572777280.stgit@djiang5-desk3.ch.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/idxd/init.c

index 29c732a94027afa6aa5d41cdfebe78bcd0d4fd3c..03c735727f68dd6cabb2c24772d46ab475fdb3a2 100644 (file)
@@ -689,26 +689,28 @@ static int idxd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        return rc;
 }
 
-static void idxd_flush_pending_llist(struct idxd_irq_entry *ie)
+static void idxd_flush_pending_descs(struct idxd_irq_entry *ie)
 {
        struct idxd_desc *desc, *itr;
        struct llist_node *head;
+       LIST_HEAD(flist);
+       enum idxd_complete_type ctype;
 
+       spin_lock(&ie->list_lock);
        head = llist_del_all(&ie->pending_llist);
-       if (!head)
-               return;
-
-       llist_for_each_entry_safe(desc, itr, head, llnode)
-               idxd_dma_complete_txd(desc, IDXD_COMPLETE_ABORT, true);
-}
+       if (head) {
+               llist_for_each_entry_safe(desc, itr, head, llnode)
+                       list_add_tail(&desc->list, &ie->work_list);
+       }
 
-static void idxd_flush_work_list(struct idxd_irq_entry *ie)
-{
-       struct idxd_desc *desc, *iter;
+       list_for_each_entry_safe(desc, itr, &ie->work_list, list)
+               list_move_tail(&desc->list, &flist);
+       spin_unlock(&ie->list_lock);
 
-       list_for_each_entry_safe(desc, iter, &ie->work_list, list) {
+       list_for_each_entry_safe(desc, itr, &flist, list) {
                list_del(&desc->list);
-               idxd_dma_complete_txd(desc, IDXD_COMPLETE_ABORT, true);
+               ctype = desc->completion->status ? IDXD_COMPLETE_NORMAL : IDXD_COMPLETE_ABORT;
+               idxd_dma_complete_txd(desc, ctype, true);
        }
 }
 
@@ -762,8 +764,7 @@ static void idxd_shutdown(struct pci_dev *pdev)
                synchronize_irq(irq_entry->vector);
                if (i == 0)
                        continue;
-               idxd_flush_pending_llist(irq_entry);
-               idxd_flush_work_list(irq_entry);
+               idxd_flush_pending_descs(irq_entry);
        }
        flush_workqueue(idxd->wq);
 }