Memory Management

Almost everything a program believes about memory is a lie the MMU tells, maintained lazily by the page-fault handler. mm/ is the code that maintains the lie: it decides what is really in RAM, what gets evicted when RAM runs out, and who gets killed when eviction is not enough.

It is small — a few dozen files — and it is the second-hardest place in the kernel to get a patch merged, for the same reason as the scheduler: everything here is performance-sensitive, the effects are workload-dependent, and a mistake corrupts data rather than merely slowing things down.


Orient Yourself First

cd ~/kernel/linux

./scripts/get_maintainer.pl --scm --status -f mm/
git log --oneline --since="6 months ago" -- mm/ | head -30
git log --since="1 year ago" --format='%cN' -- mm/ | sort | uniq -c | sort -rn | head

ls mm/ | head -40
wc -l mm/*.c | sort -n | tail -12
ls Documentation/mm/ Documentation/admin-guide/mm/

Predict first: how many .c files are in mm/? How does that compare with drivers/gpu/drm/? The ratio of importance to size is the highest in the tree.


Why It Matters

BecauseConsequence
Every allocation in every subsystem ends hereA change here affects everything, always
It decides what is resident and what is evictedWhich is what "the machine is slow" usually means
It is the boundary between "you asked for memory" and "there is no memory"The OOM killer is a policy decision with no good answer
Devices see physical memory; programs see virtualEvery DMA bug is a disagreement about which
It is where the page cache livesWhich is most of your RAM, and most of your I/O performance

Where the Code Is

ls mm/
File / areaWhat lives there
memory.cThe fault handler. handle_mm_fault() — start here.
mmap.c, vma.cThe address space: VMAs, mmap, munmap, mprotect
page_alloc.cThe buddy allocator — the bottom of everything
slub.cThe slab allocator: kmalloc and named caches
vmalloc.cVirtually-contiguous allocation
filemap.cThe page cache: reads, writes, and the folio lookup
vmscan.cReclaim. LRU, MGLRU, kswapd, direct reclaim, shrinkers
oom_kill.cWhen reclaim is not enough
memcontrol.cMemory cgroups
compaction.c, migrate.cMaking high-order allocations possible again
huge_memory.c, hugetlb.cTransparent and explicit huge pages
rmap.cReverse mapping: given a page, who maps it?
swap*.c, page_io.cSwap
mmu_gather.c, tlb*TLB invalidation batching
$EDITOR Documentation/mm/index.rst
$EDITOR Documentation/core-api/memory-allocation.rst     # read this one twice
ls Documentation/admin-guide/mm/                          # the operator's view

The Structures

   struct mm_struct                 ONE ADDRESS SPACE
     ├── pgd                        the top of the page tables
     ├── mm_mt                      a MAPLE TREE of VMAs (was an rbtree)
     ├── mmap_lock                  the big per-mm lock
     ├── rss_stat                   resident set accounting
     └── owner, mm_users, mm_count  lifetime

   struct vm_area_struct (VMA)      ONE CONTIGUOUS MAPPING
     ├── vm_start, vm_end           [start, end)
     ├── vm_flags                   READ/WRITE/EXEC/SHARED/...
     ├── vm_file, vm_pgoff          file-backed? which file, what offset?
     ├── vm_ops                     ── fault(), map_pages(), ... ← the ops table
     └── anon_vma                   for reverse mapping of anonymous pages

   struct folio                     ONE OR MORE CONTIGUOUS PAGES, AS A UNIT
     ├── flags                      uptodate, dirty, locked, writeback, lru...
     ├── mapping ──▶ struct address_space   which file (or anon) owns it
     ├── index                      offset within that mapping
     └── _refcount, _mapcount       who holds it; how many page tables map it

   struct address_space             THE PAGE CACHE FOR ONE FILE
     ├── i_pages                    an XArray: file offset -> folio
     ├── a_ops                      ── read_folio(), writepages(), ... ← ops
     └── host ──▶ struct inode
rg -n "struct mm_struct \{" -A 40 include/linux/mm_types.h
rg -n "struct vm_area_struct \{" -A 40 include/linux/mm_types.h
rg -n "struct folio \{" -A 30 include/linux/mm_types.h
rg -n "struct vm_operations_struct \{" -A 30 include/linux/mm.h
rg -n "struct address_space_operations \{" -A 40 include/linux/fs.h

Note: struct page is being progressively replaced by struct folio and by type-specific descriptors. The conversion has been running for several years and is not finished, so you will find both in the tree and code that converts between them. New code should use folio APIs; if you are unsure which is current, git log --oneline -20 -- mm/filemap.c will show you what the maintainers are doing this month.


The Concepts

ChapterAnswers
Page Tables and FaultsHow does a virtual address become a physical one, and what happens when it cannot?
Allocators and FoliosWhere does memory come from, and what is the page cache?
Reclaim, cgroups, and OOMWhat happens when there is not enough?

Then Lab 11 traces one page fault from the instruction that caused it to the page that satisfied it.


How to Read It

 1. Documentation/core-api/memory-allocation.rst -- which allocator, which
    flags. Short, canonical, and it settles most questions.

 2. The structures above. Draw mm_struct -> VMA -> folio -> address_space
    before opening a .c file.

 3. ONE PATH: handle_mm_fault() in mm/memory.c.
       - the VMA lookup
       - the page-table walk: pgd -> p4d -> pud -> pmd -> pte
       - the four cases: anonymous, file-backed, swap, copy-on-write
    Follow it in GDB. It is the single most instructive path in mm/.

 4. THE SECOND PATH: filemap_read() in mm/filemap.c -- the page cache
    lookup, and what happens on a miss.

 5. Only then: vmscan.c. Reclaim is where the policy lives, and it will not
    make sense until you know what it is reclaiming.

Observing It

# The whole-system view
grep -E '^(MemTotal|MemFree|MemAvailable|Cached|Dirty|Writeback|Slab|AnonPages|Mapped)' /proc/meminfo
cat /proc/vmstat | grep -E 'pgfault|pgmajfault|pgalloc|pgsteal|pgscan|allocstall|compact'
cat /proc/buddyinfo                      # fragmentation, by order
sudo slabtop -o | head -20
cat /proc/pressure/memory 2>/dev/null    # PSI: time lost to memory stalls

# One process
cat /proc/self/maps
cat /proc/self/smaps_rollup              # the honest per-process number
grep -E 'VmRSS|VmSwap|RssAnon|RssFile' /proc/self/status

# Tracing
ls /sys/kernel/tracing/events/ | grep -E 'kmem|vmscan|filemap|compaction|oom'
sudo bpftrace -e 'kprobe:handle_mm_fault { @[comm] = count(); }'
sudo bpftrace -e 'tracepoint:vmscan:mm_vmscan_direct_reclaim_begin { @[comm] = count(); }'
sudo perf stat -e page-faults,minor-faults,major-faults -- <workload>

Tip: mm_vmscan_direct_reclaim_begin is one of the highest-value tracepoints in the kernel. Direct reclaim means an allocating task had to stop and free memory itself, synchronously, before its allocation could proceed. If that counter is nonzero on a latency-sensitive workload, you have found the problem.


What Is Moving

git log --oneline --since="6 months ago" -- mm/ | head -40
git log --oneline --grep="folio" -- mm/ | head -20
ChangeRoughlyWhy it matters
Folios5.16 onward, ongoingDisambiguates head/tail pages; the API surface is still converting
MGLRU (multi-generational LRU)6.1A different reclaim algorithm, selectable at runtime
Maple tree for VMAs6.1Replaced the rbtree + cached-vma arrangement
Per-VMA locks for faults6.4Fault handling without the per-mm mmap_lock in the common case
SLAB removed; SLUB only6.8One allocator. CONFIG_SLAB is gone.
DAMON, memory tiering, CXLongoingWhere "which memory" becomes a question with more than one answer

What a Good First Contribution Looks Like

mm/ has an extremely high review bar. Realistic entry points, in ascending difficulty:

TargetWhy it is plausible
Documentation in Documentation/mm/ and admin-guide/mm/The folio and MGLRU work left real staleness; verify a claim, find it wrong, patch it
Selftests in tools/testing/selftests/mm/Actively wanted, and writing one requires understanding rather than invention
A kmemleak/KASAN-found bug elsewhere in the treeYou use mm tooling to fix someone else's subsystem — much easier than patching mm itself
Folio conversions in a consumerA filesystem or driver still using deprecated page APIs. Mechanical, reviewable, wanted.
A tracepoint or a debugfs counterAdditive and low risk
Anything in the allocator or reclaimExpect months, several maintainers, and a demand for measurements across many workloads

Warning: The characteristic mm/ rejection is not "this is wrong". It is "what workload does this help, by how much, and what does it cost the workloads it does not help?" Reclaim and allocator heuristics are tuned against a decade of accumulated real-world cases, and a change that helps yours usually hurts one of those. Come with measurements from several workloads or do not come.


Common Misconceptions

MisconceptionReality
"Free memory should be high"Free memory is wasted memory. The page cache should be eating it. MemAvailable is the number that matters.
"malloc allocated memory"It extended a VMA. Pages appear on first touch — which is why a program can be OOM-killed while writing to memory it "already has".
"RSS is how much memory a process uses"Shared pages are counted in every process that maps them. smaps_rollup's PSS is the honest number.
"The OOM killer picks the biggest process"It scores by oom_score, adjustable via oom_score_adj, and a memcg OOM only considers that cgroup
"Swap is for when you run out of RAM"Swap lets the kernel evict cold anonymous pages so hot ones can stay. A machine with no swap must evict page cache instead — often the worse trade.
"drop_caches frees memory"It discards clean cache the kernel would have dropped anyway. It is a benchmarking tool, not a fix.
"vmalloc is just a bigger kmalloc"Virtually contiguous, physically scattered, slower, and not DMA-able
"Huge pages are always faster"Fewer TLB misses, but more internal fragmentation and expensive compaction to obtain them
"The kernel can move any page"Pinned, mlocked, and non-migratable pages are why high-order allocations fail on a machine with plenty of free memory

Validation / Self-check

  1. Name the four core structures and how they reference each other.
  2. What replaced the rbtree of VMAs, and roughly when?
  3. What is a folio, and which class of bug did it eliminate?
  4. Which single function is the most instructive path in mm/, and what are its four cases?
  5. Which tracepoint tells you an allocating task had to reclaim synchronously, and why does that matter more than most counters?
  6. Why is MemAvailable a better number than MemFree?
  7. Why is RSS a misleading measure of a process's memory use, and what is better?
  8. Why can a program be OOM-killed while writing to memory malloc already returned?
  9. What does swap actually buy you on a machine that is not out of RAM?
  10. Why do high-order allocations fail on a machine with plenty of free memory?
  11. What is the characteristic reason an mm/ patch is rejected, and what would you need to bring?
  12. Give three plausible first contributions here, and say why each is more plausible than patching page_alloc.c.

Next: Page Tables and Faults — how a virtual address becomes a physical one, and what happens when it cannot.