Commit d900bd73 authored by Anton Blanchard's avatar Anton Blanchard Committed by Benjamin Herrenschmidt

powerpc/iommu: Fix multiple issues with IOMMU pools code

There are a number of issues in the recent IOMMU pools code:

- On a preempt kernel we might switch CPUs in the middle of building
  a scatter gather list. When this happens the handle hint passed in
  no longer falls within the local CPU's pool. Check for this and
  fall back to the pool hint.

- We were missing a spin_unlock/spin_lock in one spot where we
  switch pools.

- We need to provide locking around dart_tlb_invalidate_all and
  dart_tlb_invalidate_one now that the global lock is gone.
Reported-by: default avatarAlexander Graf <agraf@suse.de>
Signed-off-by: default avatarAnton Blanchard <anton@samba.org>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
CC: <stable@kernel.org> [v3.6]
parent c8adfecc
...@@ -215,7 +215,8 @@ static unsigned long iommu_range_alloc(struct device *dev, ...@@ -215,7 +215,8 @@ static unsigned long iommu_range_alloc(struct device *dev,
spin_lock_irqsave(&(pool->lock), flags); spin_lock_irqsave(&(pool->lock), flags);
again: again:
if ((pass == 0) && handle && *handle) if ((pass == 0) && handle && *handle &&
(*handle >= pool->start) && (*handle < pool->end))
start = *handle; start = *handle;
else else
start = pool->hint; start = pool->hint;
...@@ -236,7 +237,9 @@ static unsigned long iommu_range_alloc(struct device *dev, ...@@ -236,7 +237,9 @@ static unsigned long iommu_range_alloc(struct device *dev,
* but on second pass, start at 0 in pool 0. * but on second pass, start at 0 in pool 0.
*/ */
if ((start & mask) >= limit || pass > 0) { if ((start & mask) >= limit || pass > 0) {
spin_unlock(&(pool->lock));
pool = &(tbl->pools[0]); pool = &(tbl->pools[0]);
spin_lock(&(pool->lock));
start = pool->start; start = pool->start;
} else { } else {
start &= mask; start &= mask;
......
...@@ -73,11 +73,16 @@ static int dart_is_u4; ...@@ -73,11 +73,16 @@ static int dart_is_u4;
#define DBG(...) #define DBG(...)
static DEFINE_SPINLOCK(invalidate_lock);
static inline void dart_tlb_invalidate_all(void) static inline void dart_tlb_invalidate_all(void)
{ {
unsigned long l = 0; unsigned long l = 0;
unsigned int reg, inv_bit; unsigned int reg, inv_bit;
unsigned long limit; unsigned long limit;
unsigned long flags;
spin_lock_irqsave(&invalidate_lock, flags);
DBG("dart: flush\n"); DBG("dart: flush\n");
...@@ -110,12 +115,17 @@ static inline void dart_tlb_invalidate_all(void) ...@@ -110,12 +115,17 @@ static inline void dart_tlb_invalidate_all(void)
panic("DART: TLB did not flush after waiting a long " panic("DART: TLB did not flush after waiting a long "
"time. Buggy U3 ?"); "time. Buggy U3 ?");
} }
spin_unlock_irqrestore(&invalidate_lock, flags);
} }
static inline void dart_tlb_invalidate_one(unsigned long bus_rpn) static inline void dart_tlb_invalidate_one(unsigned long bus_rpn)
{ {
unsigned int reg; unsigned int reg;
unsigned int l, limit; unsigned int l, limit;
unsigned long flags;
spin_lock_irqsave(&invalidate_lock, flags);
reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE | reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE |
(bus_rpn & DART_CNTL_U4_IONE_MASK); (bus_rpn & DART_CNTL_U4_IONE_MASK);
...@@ -137,6 +147,8 @@ static inline void dart_tlb_invalidate_one(unsigned long bus_rpn) ...@@ -137,6 +147,8 @@ static inline void dart_tlb_invalidate_one(unsigned long bus_rpn)
panic("DART: TLB did not flush after waiting a long " panic("DART: TLB did not flush after waiting a long "
"time. Buggy U4 ?"); "time. Buggy U4 ?");
} }
spin_unlock_irqrestore(&invalidate_lock, flags);
} }
static void dart_flush(struct iommu_table *tbl) static void dart_flush(struct iommu_table *tbl)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment