__vm_normal_page(9)

πŸ“¦ version 7.3.1, in nixpkgs unstable

πŸ’– __vm_normal_page πŸ’–

okay so bestie, welcome to the kernel API manual for __vm_normal_page (section 9) β€” and yes, this is exactly as iconic as it sounds πŸ’…


✨ NAME ✨

__vm_normal_page β€” get the "struct page" associated with a page table entry. that's it. that's the whole vibe. 🫢


✨ SYNOPSIS ✨

struct page * __vm_normal_page (struct vm_area_struct *vma , unsigned
 long addr , unsigned long pfn , bool special , const void *entry ,
 size_t entry_size , enum pgtable_level level );

like, look at all those arguments. main character function energy honestly πŸ‘‘


✨ ARGUMENTS ✨

  • vma β€” the VMA that's mapping the page table entry. it's giving "this is where the memory lives" 🌸
  • addr β€” the address where the page table entry is mapped. literally the spot ✨
  • pfn β€” the PFN stored in the page table entry. the page frame number, no cap πŸ”₯
  • special β€” whether the page table entry is marked "special". yes it's in quotes in the original, that's just the lore πŸ’€
  • entry β€” the page table entry value. and listen β€” it's for error reporting purposes only. don't use it for anything else bestie πŸ™ˆ
  • entry_size β€” the size of entry. small but important πŸ’œ
  • level β€” the page table level. also for error reporting purposes only. don't get creative with it 😭

✨ DESCRIPTION ✨

okay so here's the tea πŸ“ "Special" mappings do not wish to be associated with a "struct page" β€” either it doesn't exist, or it exists but they don't wanna touch it. rude but valid. in that case, NULL is returned here. "Normal" mappings do have a struct page and are ordinarily refcounted. that's the whole distinction, periodt πŸŽ€

page mappings of the shared zero folios are always considered "special", because they are not ordinarily refcounted: neither the refcount nor the mapcount of these folios is adjusted when mapping them into user page tables. but β€” and this is lowkey important β€” selected page table walkers (such as GUP) can still identify mappings of the shared zero folios and work with the underlying "struct page". so they're special but not invisible, iykyk πŸ’…

there are 2 broad cases, bestie:

  1. an architecture may define a "special" page table entry bit, such as pte_special, in which case this function is trivial. easy. slay. ✨
  2. an architecture may not have a spare page table entry bit, which requires a more complicated scheme, described below. buckle up 🌈

with CONFIG_FIND_NORMAL_PAGE, we might have the "special" bit set on page table entries that actually map "normal" pages: however, that page cannot be looked up through the PFN stored in the page table entry, but instead will be looked up through vm_ops->find_normal_page. so far, this only applies to PTEs. noted. πŸ’–

a raw VM_PFNMAP mapping (ie. one that is not COWed) is always considered a special mapping (even if there are underlying and valid "struct pages"). COWed pages of a VM_PFNMAP are always normal. that's just how it is ✨

the way we recognize COWed pages within VM_PFNMAP mappings is through the rules set up by "remap_pfn_range": the vma will have the VM_PFNMAP bit set, and the vm_pgoff will point to the first PFN mapped: thus every special mapping will always honor the rule

pfn_of_page == vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT)

and for normal mappings this is false. simple as that πŸ¦‹

this restricts such mappings to be a linear translation from virtual address to pfn. to get around this restriction, we allow arbitrary mappings so long as the vma is not a COW mapping; in that case, we know that all ptes are special (because none can have been COWed). big brain move honestly 🧚

in order to support COW of arbitrary special mappings, we have VM_MIXEDMAP. yes, another flag, keep up bestie πŸ’€

VM_MIXEDMAP mappings can likewise contain memory with or without "struct page" backing, however the difference is that all pages with a struct page (that is, those where pfn_valid is true, except the shared zero folios) are refcounted and considered normal pages by the VM. all of them. not some. all. πŸ‘‘

the disadvantage is that pages are refcounted (which can be slower and simply not an option for some PFNMAP users). the advantage is that we don't have to follow the strict linearity rule of PFNMAP mappings in order to support COWable mappings. trade-offs, bestie, it's giving balance 🌸


✨ RETURN ✨

returns the "struct page" if this is a "normal" mapping. returns NULL if this is a "special" mapping. that's the whole return value situation πŸ’–


✨ SEE ALSO ✨

kernel file ./mm/memory.c plus: free_pgd_range(9), free_pgtables(9), vm_normal_page(9), vm_normal_folio(9), vm_normal_page_pmd(9), vm_normal_folio_pmd(9), vm_normal_page_pud(9), restore_exclusive_pte(9), cond_install_uffd_wp_ptes(9), zap_vma_for_reaping(9), unmap_vmas(9), zap_vma_range_batched(9), zap_vma_range(9), zap_special_vma_range(9), vm_insert_pages(9), vm_insert_page(9), vm_map_pages(9), vm_map_pages_zero(9), vmf_insert_pfn_prot(9), vmf_insert_pfn(9), remap_pfn_range(9), vm_iomap_memory(9), __vmf_anon_prepare(9), finish_mkwrite_fault(9), unmap_mapping_folio(9), unmap_mapping_pages(9), unmap_mapping_range(9), set_pte_range(9), finish_fault(9), mm_account_fault(9), follow_pfnmap_start(9), follow_pfnmap_end(9), generic_access_phys(9), access_remote_vm(9), copy_remote_vm_str(9), folio_zero_user(9)

like, that's a whole friend group right there 🫢


and that's the page bestie!! this masterpiece lives in ./mm, dated October 2026 β€” go read it, it's literally iconic πŸ’œβœ¨πŸŽ€

πŸ“¦ where this page comes from