iommu/iova: avoid softlockup in fq_flush_timeout
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8ZE0I CVE: NA -------------------------------- There is a softlockup undering cpu pressure test. ... pc : _raw_spin_unlock_irqrestore+0x14/0x78 lr : fq_flush_timeout+0x94/0x118 ... Call trace: _raw_spin_unlock_irqrestore+0x14/0x78 call_timer_fn+0x3c/0x1d0 expire_timers+0xcc/0x190 run_timer_softirq+0xfc/0x268 __do_softirq+0x128/0x3dc ____do_softirq+0x18/0x30 call_on_irq_stack+0x24/0x30 do_softirq_own_stack+0x24/0x38 irq_exit_rcu+0xc0/0xe8 el1_interrupt+0x48/0xc0 el1h_64_irq_handler+0x18/0x28 el1h_64_irq+0x78/0x80 __schedule+0xf28/0x12a0 schedule+0x3c/0x108 schedule_timeout+0xa0/0x1d0 pktgen_thread_worker+0x1180/0x15d0 kthread+0x120/0x130 ret_from_fork+0x10/0x20 This is because the timer callback fq_flush_timeout may run more than 10ms, and timer may be processed continuously in the softirq so trigger softlockup. We can use work to deal with fq_ring_free for each cpu which may take long time, that to avoid triggering softlockup. Signed-off-by: Li Bin <huawei.libin@huawei.com> Signed-off-by: Zhang Zekun <zhangzekun11@huawei.com> Signed-off-by: Zihao Xue <xuezihao@huawei.com>
This commit is contained in:
parent
c441d1bac9
commit
a4ae0ae208
|
@ -83,6 +83,8 @@ struct iommu_dma_cookie {
|
|||
/* Options for dma-iommu use */
|
||||
struct iommu_dma_options options;
|
||||
struct mutex mutex;
|
||||
|
||||
struct work_struct free_iova_work;
|
||||
};
|
||||
|
||||
static DEFINE_STATIC_KEY_FALSE(iommu_deferred_attach_enabled);
|
||||
|
@ -179,17 +181,11 @@ static void fq_flush_iotlb(struct iommu_dma_cookie *cookie)
|
|||
static void fq_flush_timeout(struct timer_list *t)
|
||||
{
|
||||
struct iommu_dma_cookie *cookie = from_timer(cookie, t, fq_timer);
|
||||
int cpu;
|
||||
|
||||
atomic_set(&cookie->fq_timer_on, 0);
|
||||
fq_flush_iotlb(cookie);
|
||||
|
||||
if (cookie->options.qt == IOMMU_DMA_OPTS_SINGLE_QUEUE) {
|
||||
fq_ring_free(cookie, cookie->single_fq);
|
||||
} else {
|
||||
for_each_possible_cpu(cpu)
|
||||
fq_ring_free(cookie, per_cpu_ptr(cookie->percpu_fq, cpu));
|
||||
}
|
||||
schedule_work(&cookie->free_iova_work);
|
||||
}
|
||||
|
||||
static void queue_iova(struct iommu_dma_cookie *cookie,
|
||||
|
@ -274,6 +270,7 @@ static void iommu_dma_free_fq(struct iommu_dma_cookie *cookie)
|
|||
return;
|
||||
|
||||
del_timer_sync(&cookie->fq_timer);
|
||||
flush_work(&cookie->free_iova_work);
|
||||
if (cookie->options.qt == IOMMU_DMA_OPTS_SINGLE_QUEUE)
|
||||
iommu_dma_free_fq_single(cookie->single_fq);
|
||||
else
|
||||
|
@ -321,6 +318,21 @@ static int iommu_dma_init_fq_percpu(struct iommu_dma_cookie *cookie)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void free_iova_work_func(struct work_struct *work)
|
||||
{
|
||||
struct iommu_dma_cookie *cookie;
|
||||
int cpu;
|
||||
|
||||
cookie = container_of(work, struct iommu_dma_cookie, free_iova_work);
|
||||
if (cookie->options.qt == IOMMU_DMA_OPTS_SINGLE_QUEUE) {
|
||||
fq_ring_free(cookie, cookie->single_fq);
|
||||
} else {
|
||||
for_each_possible_cpu(cpu)
|
||||
fq_ring_free(cookie, per_cpu_ptr(cookie->percpu_fq, cpu));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* sysfs updates are serialised by the mutex of the group owning @domain */
|
||||
int iommu_dma_init_fq(struct iommu_domain *domain)
|
||||
{
|
||||
|
@ -343,6 +355,7 @@ int iommu_dma_init_fq(struct iommu_domain *domain)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_WORK(&cookie->free_iova_work, free_iova_work_func);
|
||||
timer_setup(&cookie->fq_timer, fq_flush_timeout, 0);
|
||||
atomic_set(&cookie->fq_timer_on, 0);
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue