• Will Deacon's avatar
    arm64: mm: always take dirty state from new pte in ptep_set_access_flags · 0106d456
    Will Deacon authored
    Commit 66dbd6e6 ("arm64: Implement ptep_set_access_flags() for
    hardware AF/DBM") ensured that pte flags are updated atomically in the
    face of potential concurrent, hardware-assisted updates. However, Alex
    reports that:
    
     | This patch breaks swapping for me.
     | In the broken case, you'll see either systemd cpu time spike (because
     | it's stuck in a page fault loop) or the system hang (because the
     | application owning the screen is stuck in a page fault loop).
    
    It turns out that this is because the 'dirty' argument to
    ptep_set_access_flags is always 0 for read faults, and so we can't use
    it to set PTE_RDONLY. The failing sequence is:
    
      1. We put down a PTE_WRITE | PTE_DIRTY | PTE_AF pte
      2. Memory pressure -> pte_mkold(pte) -> clear PTE_AF
      3. A read faults due to the missing access flag
      4. ptep_set_access_flags is called with dirty = 0, due to the read fault
      5. pte is then made PTE_WRITE | PTE_DIRTY | PTE_AF | PTE_RDONLY (!)
      6. A write faults, but pte_write is true so we get stuck
    
    The solution is to check the new page table entry (as would be done by
    the generic, non-atomic definition of ptep_set_access_flags that just
    calls set_pte_at) to establish the dirty state.
    
    Cc: <stable@vger.kernel.org> # 4.3+
    Fixes: 66dbd6e6 ("arm64: Implement ptep_set_access_flags() for hardware AF/DBM")
    Reviewed-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
    Reported-by: default avatarAlexander Graf <agraf@suse.de>
    Tested-by: default avatarAlexander Graf <agraf@suse.de>
    Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
    0106d456
fault.c 18.5 KB