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: Alexander Graf <agraf@suse.de> Signed-off-by: Anton Blanchard <anton@samba.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> CC: <stable@kernel.org> [v3.6]
This commit is contained in:
parent
c8adfeccee
commit
d900bd7366
@ -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 @@ again:
|
|||||||
* 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;
|
|||||||
|
|
||||||
#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 @@ retry:
|
|||||||
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 @@ wait_more:
|
|||||||
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user