Skip to content

Commit c9b732a

Browse files
zhangl6gregkh
authored andcommitted
dmaengine: idxd: Convert spinlock to mutex to lock evl workqueue
[ Upstream commit d5638de ] drain_workqueue() cannot be called safely in a spinlocked context due to possible task rescheduling. In the multi-task scenario, calling queue_work() while drain_workqueue() will lead to a Call Trace as pushing a work on a draining workqueue is not permitted in spinlocked context. Call Trace: <TASK> ? __warn+0x7d/0x140 ? __queue_work+0x2b2/0x440 ? report_bug+0x1f8/0x200 ? handle_bug+0x3c/0x70 ? exc_invalid_op+0x18/0x70 ? asm_exc_invalid_op+0x1a/0x20 ? __queue_work+0x2b2/0x440 queue_work_on+0x28/0x30 idxd_misc_thread+0x303/0x5a0 [idxd] ? __schedule+0x369/0xb40 ? __pfx_irq_thread_fn+0x10/0x10 ? irq_thread+0xbc/0x1b0 irq_thread_fn+0x21/0x70 irq_thread+0x102/0x1b0 ? preempt_count_add+0x74/0xa0 ? __pfx_irq_thread_dtor+0x10/0x10 ? __pfx_irq_thread+0x10/0x10 kthread+0x103/0x140 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x31/0x50 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1b/0x30 </TASK> The current implementation uses a spinlock to protect event log workqueue and will lead to the Call Trace due to potential task rescheduling. To address the locking issue, convert the spinlock to mutex, allowing the drain_workqueue() to be called in a safe mutex-locked context. This change ensures proper synchronization when accessing the event log workqueue, preventing potential Call Trace and improving the overall robustness of the code. Fixes: c40bd7d ("dmaengine: idxd: process user page faults for completion record") Signed-off-by: Rex Zhang <[email protected]> Reviewed-by: Dave Jiang <[email protected]> Reviewed-by: Fenghua Yu <[email protected]> Reviewed-by: Lijun Pan <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vinod Koul <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 2232eb3 commit c9b732a

File tree

6 files changed

+12
-13
lines changed

6 files changed

+12
-13
lines changed

drivers/dma/idxd/cdev.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ static void idxd_cdev_evl_drain_pasid(struct idxd_wq *wq, u32 pasid)
342342
if (!evl)
343343
return;
344344

345-
spin_lock(&evl->lock);
345+
mutex_lock(&evl->lock);
346346
status.bits = ioread64(idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
347347
t = status.tail;
348348
h = status.head;
@@ -354,9 +354,8 @@ static void idxd_cdev_evl_drain_pasid(struct idxd_wq *wq, u32 pasid)
354354
set_bit(h, evl->bmap);
355355
h = (h + 1) % size;
356356
}
357-
spin_unlock(&evl->lock);
358-
359357
drain_workqueue(wq->wq);
358+
mutex_unlock(&evl->lock);
360359
}
361360

362361
static int idxd_cdev_release(struct inode *node, struct file *filep)

drivers/dma/idxd/debugfs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ static int debugfs_evl_show(struct seq_file *s, void *d)
6666
if (!evl || !evl->log)
6767
return 0;
6868

69-
spin_lock(&evl->lock);
69+
mutex_lock(&evl->lock);
7070

7171
evl_status.bits = ioread64(idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
7272
t = evl_status.tail;
@@ -87,7 +87,7 @@ static int debugfs_evl_show(struct seq_file *s, void *d)
8787
dump_event_entry(idxd, s, i, &count, processed);
8888
}
8989

90-
spin_unlock(&evl->lock);
90+
mutex_unlock(&evl->lock);
9191
return 0;
9292
}
9393

drivers/dma/idxd/device.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ static int idxd_device_evl_setup(struct idxd_device *idxd)
775775
goto err_alloc;
776776
}
777777

778-
spin_lock(&evl->lock);
778+
mutex_lock(&evl->lock);
779779
evl->log = addr;
780780
evl->dma = dma_addr;
781781
evl->log_size = size;
@@ -796,7 +796,7 @@ static int idxd_device_evl_setup(struct idxd_device *idxd)
796796
gencfg.evl_en = 1;
797797
iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
798798

799-
spin_unlock(&evl->lock);
799+
mutex_unlock(&evl->lock);
800800
return 0;
801801

802802
err_alloc:
@@ -819,7 +819,7 @@ static void idxd_device_evl_free(struct idxd_device *idxd)
819819
if (!gencfg.evl_en)
820820
return;
821821

822-
spin_lock(&evl->lock);
822+
mutex_lock(&evl->lock);
823823
gencfg.evl_en = 0;
824824
iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
825825

@@ -836,7 +836,7 @@ static void idxd_device_evl_free(struct idxd_device *idxd)
836836
evl_dma = evl->dma;
837837
evl->log = NULL;
838838
evl->size = IDXD_EVL_SIZE_MIN;
839-
spin_unlock(&evl->lock);
839+
mutex_unlock(&evl->lock);
840840

841841
dma_free_coherent(dev, evl_log_size, evl_log, evl_dma);
842842
}

drivers/dma/idxd/idxd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ struct idxd_driver_data {
293293

294294
struct idxd_evl {
295295
/* Lock to protect event log access. */
296-
spinlock_t lock;
296+
struct mutex lock;
297297
void *log;
298298
dma_addr_t dma;
299299
/* Total size of event log = number of entries * entry size. */

drivers/dma/idxd/init.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ static int idxd_init_evl(struct idxd_device *idxd)
354354
if (!evl)
355355
return -ENOMEM;
356356

357-
spin_lock_init(&evl->lock);
357+
mutex_init(&evl->lock);
358358
evl->size = IDXD_EVL_SIZE_MIN;
359359

360360
idxd_name = dev_name(idxd_confdev(idxd));

drivers/dma/idxd/irq.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ static void process_evl_entries(struct idxd_device *idxd)
363363
evl_status.bits = 0;
364364
evl_status.int_pending = 1;
365365

366-
spin_lock(&evl->lock);
366+
mutex_lock(&evl->lock);
367367
/* Clear interrupt pending bit */
368368
iowrite32(evl_status.bits_upper32,
369369
idxd->reg_base + IDXD_EVLSTATUS_OFFSET + sizeof(u32));
@@ -380,7 +380,7 @@ static void process_evl_entries(struct idxd_device *idxd)
380380

381381
evl_status.head = h;
382382
iowrite32(evl_status.bits_lower32, idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
383-
spin_unlock(&evl->lock);
383+
mutex_unlock(&evl->lock);
384384
}
385385

386386
irqreturn_t idxd_misc_thread(int vec, void *data)

0 commit comments

Comments
 (0)