Message ID | 20190718051139.74787-2-aik@ozlabs.ru (mailing list archive) |
---|---|
State | Accepted |
Commit | 56090a3902c80c296e822d11acdb6a101b322c52 |
Headers | show |
Series | powerpc/ioda2: Yet another attempt to allow DMA masks between 32 and 59 | expand |
Context | Check | Description |
---|---|---|
snowpatch_ozlabs/apply_patch | success | Successfully applied on branch next (f5c20693d8edcd665f1159dc941b9e7f87c17647) |
snowpatch_ozlabs/checkpatch | warning | total: 0 errors, 0 warnings, 3 checks, 38 lines checked |
On Thu, 2019-07-18 at 05:11:36 UTC, Alexey Kardashevskiy wrote: > pnv_tce() returns a pointer to a TCE entry and originally a TCE table > would be pre-allocated. For the default case of 2GB window the table > needs only a single level and that is fine. However if more levels are > requested, it is possible to get a race when 2 threads want a pointer > to a TCE entry from the same page of TCEs. > > This adds cmpxchg to handle the race. Note that once TCE is non-zero, > it cannot become zero again. > > CC: stable@vger.kernel.org # v4.19+ > Fixes: a68bd1267b72 ("powerpc/powernv/ioda: Allocate indirect TCE levels on demand") > Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru> Series applied to powerpc next, thanks. https://git.kernel.org/powerpc/c/56090a3902c80c296e822d11acdb6a101b322c52 cheers
diff --git a/arch/powerpc/platforms/powernv/pci-ioda-tce.c b/arch/powerpc/platforms/powernv/pci-ioda-tce.c index e28f03e1eb5e..8d6569590161 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda-tce.c +++ b/arch/powerpc/platforms/powernv/pci-ioda-tce.c @@ -48,6 +48,9 @@ static __be64 *pnv_alloc_tce_level(int nid, unsigned int shift) return addr; } +static void pnv_pci_ioda2_table_do_free_pages(__be64 *addr, + unsigned long size, unsigned int levels); + static __be64 *pnv_tce(struct iommu_table *tbl, bool user, long idx, bool alloc) { __be64 *tmp = user ? tbl->it_userspace : (__be64 *) tbl->it_base; @@ -57,9 +60,9 @@ static __be64 *pnv_tce(struct iommu_table *tbl, bool user, long idx, bool alloc) while (level) { int n = (idx & mask) >> (level * shift); - unsigned long tce; + unsigned long oldtce, tce = be64_to_cpu(READ_ONCE(tmp[n])); - if (tmp[n] == 0) { + if (!tce) { __be64 *tmp2; if (!alloc) @@ -70,10 +73,15 @@ static __be64 *pnv_tce(struct iommu_table *tbl, bool user, long idx, bool alloc) if (!tmp2) return NULL; - tmp[n] = cpu_to_be64(__pa(tmp2) | - TCE_PCI_READ | TCE_PCI_WRITE); + tce = __pa(tmp2) | TCE_PCI_READ | TCE_PCI_WRITE; + oldtce = be64_to_cpu(cmpxchg(&tmp[n], 0, + cpu_to_be64(tce))); + if (oldtce) { + pnv_pci_ioda2_table_do_free_pages(tmp2, + ilog2(tbl->it_level_size) + 3, 1); + tce = oldtce; + } } - tce = be64_to_cpu(tmp[n]); tmp = __va(tce & ~(TCE_PCI_READ | TCE_PCI_WRITE)); idx &= ~mask;
pnv_tce() returns a pointer to a TCE entry and originally a TCE table would be pre-allocated. For the default case of 2GB window the table needs only a single level and that is fine. However if more levels are requested, it is possible to get a race when 2 threads want a pointer to a TCE entry from the same page of TCEs. This adds cmpxchg to handle the race. Note that once TCE is non-zero, it cannot become zero again. CC: stable@vger.kernel.org # v4.19+ Fixes: a68bd1267b72 ("powerpc/powernv/ioda: Allocate indirect TCE levels on demand") Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru> --- The race occurs about 30 times in the first 3 minutes of copying files via rsync and that's about it. This fixes EEH's from https://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=110810 --- Changes: v2: * replaced spin_lock with cmpxchg+readonce --- arch/powerpc/platforms/powernv/pci-ioda-tce.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)