NVIDIA: SAUCE: dmaengine: Fix system hang on PREEMPT_RT kernel

BugLink: https://bugs.launchpad.net/bugs/2080908

On RT kernel spin_lock() behavior changes. It becomes a sleeping lock
and spin_lock_irqsave() does not disable IRQs. Due to this, sometimes
the DMA IRQ comes while executing a section protected by
spin_lock_irqsave(). Since the ISR tries to lock the same spinlock
(which is now a sleeping lock) the kernel crashes (BUG: scheduling while
atomic: ksoftirqd/0/12/0x00010001).
Adding flag to use raw_spin_lock() in case of virtualized DMA drivers so
that it behaves like true spinlock even on RT kernel.

http://nvbugs/4610804
http://nvbugs/4326086

Signed-off-by: Hirak Biswas <hbiswas@nvidia.com>
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Acked-by: Noah Wager <noah.wager@canonical.com>
Acked-by: Jacob Martin <jacob.martin@canonical.com>
Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
Hirak Biswas
2024-04-10 10:47:48 +00:00
committed by Noah Wager
parent 872ad29e83
commit aa5d160643
3 changed files with 91 additions and 24 deletions
+14 -9
View File
@@ -415,17 +415,17 @@ static irqreturn_t tegra_adma_isr(int irq, void *dev_id)
struct tegra_adma_chan *tdc = dev_id;
unsigned long status;
spin_lock(&tdc->vc.lock);
dma_vchan_lock(&tdc->vc);
status = tegra_adma_irq_clear(tdc);
if (status == 0 || !tdc->desc) {
spin_unlock(&tdc->vc.lock);
dma_vchan_unlock(&tdc->vc);
return IRQ_NONE;
}
vchan_cyclic_callback(&tdc->desc->vd);
spin_unlock(&tdc->vc.lock);
dma_vchan_unlock(&tdc->vc);
return IRQ_HANDLED;
}
@@ -435,14 +435,14 @@ static void tegra_adma_issue_pending(struct dma_chan *dc)
struct tegra_adma_chan *tdc = to_tegra_adma_chan(dc);
unsigned long flags;
spin_lock_irqsave(&tdc->vc.lock, flags);
dma_vchan_lock_irqsave(&tdc->vc, flags);
if (vchan_issue_pending(&tdc->vc)) {
if (!tdc->desc)
tegra_adma_start(tdc);
}
spin_unlock_irqrestore(&tdc->vc.lock, flags);
dma_vchan_unlock_irqrestore(&tdc->vc, flags);
}
static bool tegra_adma_is_paused(struct tegra_adma_chan *tdc)
@@ -496,14 +496,14 @@ static int tegra_adma_terminate_all(struct dma_chan *dc)
unsigned long flags;
LIST_HEAD(head);
spin_lock_irqsave(&tdc->vc.lock, flags);
dma_vchan_lock_irqsave(&tdc->vc, flags);
if (tdc->desc)
tegra_adma_stop(tdc);
tegra_adma_request_free(tdc);
vchan_get_all_descriptors(&tdc->vc, &head);
spin_unlock_irqrestore(&tdc->vc.lock, flags);
dma_vchan_unlock_irqrestore(&tdc->vc, flags);
vchan_dma_desc_free_list(&tdc->vc, &head);
return 0;
@@ -524,7 +524,7 @@ static enum dma_status tegra_adma_tx_status(struct dma_chan *dc,
if (ret == DMA_COMPLETE || !txstate)
return ret;
spin_lock_irqsave(&tdc->vc.lock, flags);
dma_vchan_lock_irqsave(&tdc->vc, flags);
vd = vchan_find_desc(&tdc->vc, cookie);
if (vd) {
@@ -536,7 +536,7 @@ static enum dma_status tegra_adma_tx_status(struct dma_chan *dc,
residual = 0;
}
spin_unlock_irqrestore(&tdc->vc.lock, flags);
dma_vchan_unlock_irqrestore(&tdc->vc, flags);
dma_set_residue(txstate, residual);
@@ -936,6 +936,11 @@ static int tegra_adma_probe(struct platform_device *pdev)
goto irq_dispose;
}
if (tdma->is_virtualized)
tdc->vc.rt_spinlock_fix_enabled = DMA_RT_SPINLOCK_FIX_ENABLED;
else
tdc->vc.rt_spinlock_fix_enabled = 0;
vchan_init(&tdc->vc, &tdma->dma_dev);
tdc->vc.desc_free = tegra_adma_desc_free;
tdc->tdma = tdma;
+7 -7
View File
@@ -23,11 +23,11 @@ dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
unsigned long flags;
dma_cookie_t cookie;
spin_lock_irqsave(&vc->lock, flags);
dma_vchan_lock_irqsave(vc, flags);
cookie = dma_cookie_assign(tx);
list_move_tail(&vd->node, &vc->desc_submitted);
spin_unlock_irqrestore(&vc->lock, flags);
dma_vchan_unlock_irqrestore(vc, flags);
dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
vc, vd, cookie);
@@ -52,9 +52,9 @@ int vchan_tx_desc_free(struct dma_async_tx_descriptor *tx)
struct virt_dma_desc *vd = to_virt_desc(tx);
unsigned long flags;
spin_lock_irqsave(&vc->lock, flags);
dma_vchan_lock_irqsave(vc, flags);
list_del(&vd->node);
spin_unlock_irqrestore(&vc->lock, flags);
dma_vchan_unlock_irqrestore(vc, flags);
dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: freeing\n",
vc, vd, vd->tx.cookie);
@@ -87,7 +87,7 @@ static void vchan_complete(struct tasklet_struct *t)
struct dmaengine_desc_callback cb;
LIST_HEAD(head);
spin_lock_irq(&vc->lock);
dma_vchan_lock_irq(vc);
list_splice_tail_init(&vc->desc_completed, &head);
vd = vc->cyclic;
if (vd) {
@@ -96,7 +96,7 @@ static void vchan_complete(struct tasklet_struct *t)
} else {
memset(&cb, 0, sizeof(cb));
}
spin_unlock_irq(&vc->lock);
dma_vchan_unlock_irq(vc);
dmaengine_desc_callback_invoke(&cb, &vd->tx_result);
@@ -124,7 +124,7 @@ void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
{
dma_cookie_init(&vc->chan);
spin_lock_init(&vc->lock);
dma_vchan_lock_init(vc);
INIT_LIST_HEAD(&vc->desc_allocated);
INIT_LIST_HEAD(&vc->desc_submitted);
INIT_LIST_HEAD(&vc->desc_issued);
+70 -8
View File
@@ -34,13 +34,75 @@ struct virt_dma_chan {
struct list_head desc_terminated;
struct virt_dma_desc *cyclic;
/* To-do: Upstream review for this WAR */
raw_spinlock_t rawlock;
uint32_t rt_spinlock_fix_enabled;
};
#define DMA_RT_SPINLOCK_FIX_ENABLED 0xAF5FA2B1
static inline struct virt_dma_chan *to_virt_chan(struct dma_chan *chan)
{
return container_of(chan, struct virt_dma_chan, chan);
}
#define dma_vchan_lock_init(vc) \
do { \
if (DMA_RT_SPINLOCK_FIX_ENABLED == (vc)->rt_spinlock_fix_enabled) \
raw_spin_lock_init(&(vc)->rawlock); \
else \
spin_lock_init(&(vc)->lock); \
} while (0)
#define dma_vchan_lock(vc) \
do { \
if (DMA_RT_SPINLOCK_FIX_ENABLED == (vc)->rt_spinlock_fix_enabled) \
raw_spin_lock(&(vc)->rawlock); \
else \
spin_lock(&(vc)->lock); \
} while (0)
#define dma_vchan_unlock(vc) \
do { \
if (DMA_RT_SPINLOCK_FIX_ENABLED == (vc)->rt_spinlock_fix_enabled) \
raw_spin_unlock(&(vc)->rawlock); \
else \
spin_unlock(&(vc)->lock); \
} while (0)
#define dma_vchan_lock_irq(vc) \
do { \
if (DMA_RT_SPINLOCK_FIX_ENABLED == (vc)->rt_spinlock_fix_enabled) \
raw_spin_lock_irq(&(vc)->rawlock); \
else \
spin_lock_irq(&(vc)->lock); \
} while (0)
#define dma_vchan_unlock_irq(vc) \
do { \
if (DMA_RT_SPINLOCK_FIX_ENABLED == (vc)->rt_spinlock_fix_enabled) \
raw_spin_unlock_irq(&(vc)->rawlock); \
else \
spin_unlock_irq(&(vc)->lock); \
} while (0)
#define dma_vchan_lock_irqsave(vc, flags) \
do { \
if (DMA_RT_SPINLOCK_FIX_ENABLED == (vc)->rt_spinlock_fix_enabled) \
raw_spin_lock_irqsave(&(vc)->rawlock, flags); \
else \
spin_lock_irqsave(&(vc)->lock, flags); \
} while (0)
#define dma_vchan_unlock_irqrestore(vc, flags) \
do { \
if (DMA_RT_SPINLOCK_FIX_ENABLED == (vc)->rt_spinlock_fix_enabled) \
raw_spin_unlock_irqrestore(&(vc)->rawlock, flags); \
else \
spin_unlock_irqrestore(&(vc)->lock, flags); \
} while (0)
void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head);
void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev);
struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *, dma_cookie_t);
@@ -66,9 +128,9 @@ static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan
vd->tx_result.result = DMA_TRANS_NOERROR;
vd->tx_result.residue = 0;
spin_lock_irqsave(&vc->lock, flags);
dma_vchan_lock_irqsave(vc, flags);
list_add_tail(&vd->node, &vc->desc_allocated);
spin_unlock_irqrestore(&vc->lock, flags);
dma_vchan_unlock_irqrestore(vc, flags);
return &vd->tx;
}
@@ -116,9 +178,9 @@ static inline void vchan_vdesc_fini(struct virt_dma_desc *vd)
if (dmaengine_desc_test_reuse(&vd->tx)) {
unsigned long flags;
spin_lock_irqsave(&vc->lock, flags);
dma_vchan_lock_irqsave(vc, flags);
list_add(&vd->node, &vc->desc_allocated);
spin_unlock_irqrestore(&vc->lock, flags);
dma_vchan_unlock_irqrestore(vc, flags);
} else {
vc->desc_free(vd);
}
@@ -190,11 +252,11 @@ static inline void vchan_free_chan_resources(struct virt_dma_chan *vc)
unsigned long flags;
LIST_HEAD(head);
spin_lock_irqsave(&vc->lock, flags);
dma_vchan_lock_irqsave(vc, flags);
vchan_get_all_descriptors(vc, &head);
list_for_each_entry(vd, &head, node)
dmaengine_desc_clear_reuse(&vd->tx);
spin_unlock_irqrestore(&vc->lock, flags);
dma_vchan_unlock_irqrestore(vc, flags);
vchan_dma_desc_free_list(vc, &head);
}
@@ -215,11 +277,11 @@ static inline void vchan_synchronize(struct virt_dma_chan *vc)
tasklet_kill(&vc->task);
spin_lock_irqsave(&vc->lock, flags);
dma_vchan_lock_irqsave(vc, flags);
list_splice_tail_init(&vc->desc_terminated, &head);
spin_unlock_irqrestore(&vc->lock, flags);
dma_vchan_unlock_irqrestore(vc, flags);
vchan_dma_desc_free_list(vc, &head);
}