arch/mm/fault: fix major fault accounting when retrying under per-VMA lock
authorSuren Baghdasaryan <surenb@google.com>
Tue, 26 Dec 2023 21:46:10 +0000 (13:46 -0800)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 29 Dec 2023 19:06:49 +0000 (11:06 -0800)
A test [1] in Android test suite started failing after [2] was merged.  It
turns out that after handling a major fault under per-VMA lock, the
process major fault counter does not register that fault as major.  Before
[2] read faults would be done under mmap_lock, in which case
FAULT_FLAG_TRIED flag is set before retrying.  That in turn causes
mm_account_fault() to account the fault as major once retry completes.
With per-VMA locks we often retry because a fault can't be handled without
locking the whole mm using mmap_lock.  Therefore such retries do not set
FAULT_FLAG_TRIED flag.  This logic does not work after [2] because we can
now handle read major faults under per-VMA lock and upon retry the fact
there was a major fault gets lost.  Fix this by setting FAULT_FLAG_TRIED
after retrying under per-VMA lock if VM_FAULT_MAJOR was returned.  Ideally
we would use an additional VM_FAULT bit to indicate the reason for the
retry (could not handle under per-VMA lock vs other reason) but this
simpler solution seems to work, so keeping it simple.

[1] https://cs.android.com/android/platform/superproject/+/master:test/vts-testcase/kernel/api/drop_caches_prop/drop_caches_test.cpp
[2] https://lore.kernel.org/all/20231006195318.4087158-6-willy@infradead.org/

Link: https://lkml.kernel.org/r/20231226214610.109282-1-surenb@google.com
Fixes: 12214eba1992 ("mm: handle read faults under the VMA lock")
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
arch/arm64/mm/fault.c
arch/powerpc/mm/fault.c
arch/riscv/mm/fault.c
arch/s390/mm/fault.c
arch/x86/mm/fault.c

index 460d799e12966aba2eb6be276cf3ca5ce419dafa..55f6455a828434bc4fb9b420c17fbcff31bb7d13 100644 (file)
@@ -607,6 +607,8 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
                goto done;
        }
        count_vm_vma_lock_event(VMA_LOCK_RETRY);
+       if (fault & VM_FAULT_MAJOR)
+               mm_flags |= FAULT_FLAG_TRIED;
 
        /* Quick path to respond to signals */
        if (fault_signal_pending(fault, regs)) {
index 9e49ede2bc1c018c07f2b1f9a62acfa9d4a89e48..53335ae21a40aec68a29006bf06ffc82786f5153 100644 (file)
@@ -497,6 +497,8 @@ static int ___do_page_fault(struct pt_regs *regs, unsigned long address,
                goto done;
        }
        count_vm_vma_lock_event(VMA_LOCK_RETRY);
+       if (fault & VM_FAULT_MAJOR)
+               flags |= FAULT_FLAG_TRIED;
 
        if (fault_signal_pending(fault, regs))
                return user_mode(regs) ? 0 : SIGBUS;
index 90d4ba36d1d0629e7b17aaf7cd9a23922b127d37..081339ddf47ef4bae95f02f80c227318b869cd6e 100644 (file)
@@ -304,6 +304,8 @@ void handle_page_fault(struct pt_regs *regs)
                goto done;
        }
        count_vm_vma_lock_event(VMA_LOCK_RETRY);
+       if (fault & VM_FAULT_MAJOR)
+               flags |= FAULT_FLAG_TRIED;
 
        if (fault_signal_pending(fault, regs)) {
                if (!user_mode(regs))
index 249aefcf7c4e6e1fe5290f3712b0e6fac3ca8e10..ab4098886e562f64cd54055d1a249ea09c61c6c9 100644 (file)
@@ -337,6 +337,9 @@ static void do_exception(struct pt_regs *regs, int access)
                return;
        }
        count_vm_vma_lock_event(VMA_LOCK_RETRY);
+       if (fault & VM_FAULT_MAJOR)
+               flags |= FAULT_FLAG_TRIED;
+
        /* Quick path to respond to signals */
        if (fault_signal_pending(fault, regs)) {
                if (!user_mode(regs))
index ab778eac195205714c3bd0f01c72fe390a34d62c..679b09cfe241c72e7f85bd7bbd406d59a259bf2a 100644 (file)
@@ -1370,6 +1370,8 @@ void do_user_addr_fault(struct pt_regs *regs,
                goto done;
        }
        count_vm_vma_lock_event(VMA_LOCK_RETRY);
+       if (fault & VM_FAULT_MAJOR)
+               flags |= FAULT_FLAG_TRIED;
 
        /* Quick path to respond to signals */
        if (fault_signal_pending(fault, regs)) {