[PATCH 09/46] mm/rmap: handle device-exclusive entries in folio_referenced_one()

Barry Song baohua at kernel.org
Tue Oct 27 14:22:56 PDT 2026


On Tue, Oct 27, 2026 at 7:20 AM David Hildenbrand (Arm)
<david at kernel.org> wrote:
>
> page_vma_mapped_walk() can return non-present device-exclusive entries,
> but folio_referenced_one() passes them to PTE young-bit accessors that
> require present PTEs.
>
> We must neither call clear_flush_young_ptes_notify() nor
> folio_pte_batch() with non-present entries. For example, clearing the
> young bit on non-present PTEs can corrupt the entry: on arm64
> PTE_AF overlaps the swap type, so clearing it turns a device-exclusive
> entry into an ordinary swap entry.
>
> folio_pte_batch() will VM_WARN_ON_FOLIO.
>
> Apply the same approach as the previous fixes to e.g., damon_ptep_mkold():
> treat these entries as old from the CPU's perspective and obtain their
> young state through the MMU notifier.
>
> Skip PTE batching and MGLRU look-around for non-present entries, and
> don't modify non-present PTEs.
>
> Fixes: b756a3b5e7ea ("mm: device exclusive memory access")
> Reviewed-by: Lorenzo Stoakes (ARM) <ljs at kernel.org>
> Cc: stable at vger.kernel.org
> Reported-by: Josef Bacik <josef at toxicpanda.com>
> Signed-off-by: David Hildenbrand (Arm) <david at kernel.org>
> ---

Reviewed-by: Barry Song <baohua at kernel.org>

[...]
> -               if (lru_gen_enabled() && !lru_gen_switching() && pvmw.pte) {
> +               if (lru_gen_enabled() && !lru_gen_switching() && pvmw.pte &&
> +                   pte_present(pteval)) {
>                         if (lru_gen_look_around(&pvmw, nr))

I was also a little concerned about the internals of
lru_gen_look_around(), since it visits a few neighboring
PTEs. But after checking, I found it is fine, by checking
get_pte_pfn() == -1:

bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
{
        ...
        for (i = 0, addr = start; addr != end;
             i += nr, pte += nr, addr += nr * PAGE_SIZE) {
                unsigned long pfn;
                pte_t ptent = ptep_get(pte);

                nr = 1;
                pfn = get_pte_pfn(ptent, vma, addr, pgdat);
                if (pfn == -1)
                        continue;
                ...
                if (!test_and_clear_young_ptes_notify(vma, addr, pte, nr))
                        continue;
                ...
        }
        ...
}

Thanks
Barry



More information about the kexec mailing list