| From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| Date: Tue, 26 Mar 2019 18:31:54 +0100 |
| Subject: [PATCH] powerpc/pseries/iommu: Use a locallock instead |
| local_irq_save() |
| |
| The locallock protects the per-CPU variable tce_page. The function |
| attempts to allocate memory while tce_page is protected (by disabling |
| interrupts). |
| |
| Use local_irq_save() instead of local_irq_disable(). |
| |
| Cc: stable-rt@vger.kernel.org |
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| --- |
| arch/powerpc/platforms/pseries/iommu.c | 16 ++++++++++------ |
| 1 file changed, 10 insertions(+), 6 deletions(-) |
| |
| --- a/arch/powerpc/platforms/pseries/iommu.c |
| +++ b/arch/powerpc/platforms/pseries/iommu.c |
| @@ -24,6 +24,7 @@ |
| #include <linux/of.h> |
| #include <linux/iommu.h> |
| #include <linux/rculist.h> |
| +#include <linux/locallock.h> |
| #include <asm/io.h> |
| #include <asm/prom.h> |
| #include <asm/rtas.h> |
| @@ -177,6 +178,7 @@ static int tce_build_pSeriesLP(unsigned |
| } |
| |
| static DEFINE_PER_CPU(__be64 *, tce_page); |
| +static DEFINE_LOCAL_IRQ_LOCK(tcp_page_lock); |
| |
| static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, |
| long npages, unsigned long uaddr, |
| @@ -198,7 +200,8 @@ static int tce_buildmulti_pSeriesLP(stru |
| direction, attrs); |
| } |
| |
| - local_irq_save(flags); /* to protect tcep and the page behind it */ |
| + /* to protect tcep and the page behind it */ |
| + local_lock_irqsave(tcp_page_lock, flags); |
| |
| tcep = __this_cpu_read(tce_page); |
| |
| @@ -209,7 +212,7 @@ static int tce_buildmulti_pSeriesLP(stru |
| tcep = (__be64 *)__get_free_page(GFP_ATOMIC); |
| /* If allocation fails, fall back to the loop implementation */ |
| if (!tcep) { |
| - local_irq_restore(flags); |
| + local_unlock_irqrestore(tcp_page_lock, flags); |
| return tce_build_pSeriesLP(tbl->it_index, tcenum, |
| tbl->it_page_shift, |
| npages, uaddr, direction, attrs); |
| @@ -244,7 +247,7 @@ static int tce_buildmulti_pSeriesLP(stru |
| tcenum += limit; |
| } while (npages > 0 && !rc); |
| |
| - local_irq_restore(flags); |
| + local_unlock_irqrestore(tcp_page_lock, flags); |
| |
| if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) { |
| ret = (int)rc; |
| @@ -415,13 +418,14 @@ static int tce_setrange_multi_pSeriesLP( |
| DMA_BIDIRECTIONAL, 0); |
| } |
| |
| - local_irq_disable(); /* to protect tcep and the page behind it */ |
| + /* to protect tcep and the page behind it */ |
| + local_lock_irq(tcp_page_lock); |
| tcep = __this_cpu_read(tce_page); |
| |
| if (!tcep) { |
| tcep = (__be64 *)__get_free_page(GFP_ATOMIC); |
| if (!tcep) { |
| - local_irq_enable(); |
| + local_unlock_irq(tcp_page_lock); |
| return -ENOMEM; |
| } |
| __this_cpu_write(tce_page, tcep); |
| @@ -467,7 +471,7 @@ static int tce_setrange_multi_pSeriesLP( |
| |
| /* error cleanup: caller will clear whole range */ |
| |
| - local_irq_enable(); |
| + local_unlock_irq(tcp_page_lock); |
| return rc; |
| } |
| |