Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 308886
b: refs/heads/master
c: 26c1917
h: refs/heads/master
v: v3
  • Loading branch information
Andrea Arcangeli authored and Linus Torvalds committed May 29, 2012
1 parent d117c45 commit 6ebdc11
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 3 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a7f638f999ff42310e9582273b1fe25ea6e469ba
refs/heads/master: 26c191788f18129af0eb32a358cdaea0c7479626
50 changes: 50 additions & 0 deletions trunk/arch/x86/include/asm/pgtable-3level.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,56 @@ static inline void native_set_pte(pte_t *ptep, pte_t pte)
ptep->pte_low = pte.pte_low;
}

#define pmd_read_atomic pmd_read_atomic
/*
* pte_offset_map_lock on 32bit PAE kernels was reading the pmd_t with
* a "*pmdp" dereference done by gcc. Problem is, in certain places
* where pte_offset_map_lock is called, concurrent page faults are
* allowed, if the mmap_sem is hold for reading. An example is mincore
* vs page faults vs MADV_DONTNEED. On the page fault side
* pmd_populate rightfully does a set_64bit, but if we're reading the
* pmd_t with a "*pmdp" on the mincore side, a SMP race can happen
* because gcc will not read the 64bit of the pmd atomically. To fix
* this all places running pmd_offset_map_lock() while holding the
* mmap_sem in read mode, shall read the pmdp pointer using this
* function to know if the pmd is null nor not, and in turn to know if
* they can run pmd_offset_map_lock or pmd_trans_huge or other pmd
* operations.
*
* Without THP if the mmap_sem is hold for reading, the
* pmd can only transition from null to not null while pmd_read_atomic runs.
* So there's no need of literally reading it atomically.
*
* With THP if the mmap_sem is hold for reading, the pmd can become
* THP or null or point to a pte (and in turn become "stable") at any
* time under pmd_read_atomic, so it's mandatory to read it atomically
* with cmpxchg8b.
*/
#ifndef CONFIG_TRANSPARENT_HUGEPAGE
static inline pmd_t pmd_read_atomic(pmd_t *pmdp)
{
pmdval_t ret;
u32 *tmp = (u32 *)pmdp;

ret = (pmdval_t) (*tmp);
if (ret) {
/*
* If the low part is null, we must not read the high part
* or we can end up with a partial pmd.
*/
smp_rmb();
ret |= ((pmdval_t)*(tmp + 1)) << 32;
}

return (pmd_t) { ret };
}
#else /* CONFIG_TRANSPARENT_HUGEPAGE */
static inline pmd_t pmd_read_atomic(pmd_t *pmdp)
{
return (pmd_t) { atomic64_read((atomic64_t *)pmdp) };
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */

static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte)
{
set_64bit((unsigned long long *)(ptep), native_pte_val(pte));
Expand Down
22 changes: 20 additions & 2 deletions trunk/include/asm-generic/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,18 @@ static inline int pmd_write(pmd_t pmd)
#endif /* __HAVE_ARCH_PMD_WRITE */
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */

#ifndef pmd_read_atomic
static inline pmd_t pmd_read_atomic(pmd_t *pmdp)
{
/*
* Depend on compiler for an atomic pmd read. NOTE: this is
* only going to work, if the pmdval_t isn't larger than
* an unsigned long.
*/
return *pmdp;
}
#endif

/*
* This function is meant to be used by sites walking pagetables with
* the mmap_sem hold in read mode to protect against MADV_DONTNEED and
Expand All @@ -458,11 +470,17 @@ static inline int pmd_write(pmd_t pmd)
* undefined so behaving like if the pmd was none is safe (because it
* can return none anyway). The compiler level barrier() is critically
* important to compute the two checks atomically on the same pmdval.
*
* For 32bit kernels with a 64bit large pmd_t this automatically takes
* care of reading the pmd atomically to avoid SMP race conditions
* against pmd_populate() when the mmap_sem is hold for reading by the
* caller (a special atomic read not done by "gcc" as in the generic
* version above, is also needed when THP is disabled because the page
* fault can populate the pmd from under us).
*/
static inline int pmd_none_or_trans_huge_or_clear_bad(pmd_t *pmd)
{
/* depend on compiler for an atomic pmd read */
pmd_t pmdval = *pmd;
pmd_t pmdval = pmd_read_atomic(pmd);
/*
* The barrier will stabilize the pmdval in a register or on
* the stack so that it will stop changing under the code.
Expand Down

0 comments on commit 6ebdc11

Please sign in to comment.