VMA는 주소 범위에 붙은 동작 계약입니다

/proc/PID/maps에서 두 이웃 range의 permission이 같아도 두 줄로 남을 수 있습니다. 반대로 여러 번 요청한 mapping이 하나의 range로 합쳐질 수도 있습니다. VMA가 단순한 allocation 영수증이 아니라 그 범위의 fault, backing, policy와 lifetime 조건을 묶은 객체이기 때문입니다.

한 VMA 안에서도 일부 page는 아직 없고 일부는 resident이며 다른 일부는 CoW 뒤의 anonymous page일 수 있습니다. '한 VMA는 한 physical allocation'이라는 모델부터 내려놓으면 field와 flag의 의미가 훨씬 명확해집니다. 아래 설명은 upstream v6.18을 기준으로 합니다.

범위와 배치 policy는 다릅니다

vm_start는 포함하는 시작이고 vm_end는 포함하지 않는 끝입니다. 둘은 mapping에 맞는 page alignment를 가집니다. 주소 addr가 이 VMA에 속하는 조건은 vm_start <= addr이고 addr < vm_end입니다. 끝 주소를 포함시켜 다음 VMA와 겹치게 해석하면 안 됩니다.

vm_mm은 소속 address space입니다. mm의 mmap_base, architecture의 unmapped-area 검색 정책, ASLR, stack의 위치와 resource limit은 새 mapping의 배치에 영향을 줍니다. top-down 검색에서 mmap_base를 단순히 '모든 mapping이 이 이상에서 시작하는 최소값'이라고 부를 수는 없습니다. 검색 방향과 legacy layout에 따라 의미가 달라집니다.

task_size와 TASK_SIZE는 user address-space의 한계와 관련되며 실제 할당된 길이나 RSS가 아닙니다. native x86-64에서는 4-level의 낮은 128 TiB, 5-level의 낮은 64 PiB 범위와 최상단 한 page를 제외하는 경계를 구별해야 합니다. 호환 task는 별도의 더 작은 한계를 사용합니다. 5-level을 지원하는 구성에서도 일반 mmap의 기본 window는 더 낮은 47-bit 구간을 사용할 수 있습니다. 'LA57이면 모든 새 pointer가 높은 주소로 간다'는 결론은 나오지 않습니다.

이런 placement 규칙은 이미 만들어진 VMA의 의미와도 다릅니다. fixed mapping, architecture 또는 file별 get_unmapped_area 처리, NUMA와 huge-page alignment 같은 조건을 함께 검토해야 합니다. base 하나가 모든 mapping의 위치를 결정하지 않습니다.

backing과 offset을 연결합니다

file-backed VMA의 vm_file은 열린 file 객체를 붙잡고 vm_pgoff는 file 안의 시작을 base-page 단위로 표현합니다. VMA 중간 주소의 file page index는 range 안의 page offset을 더해 구합니다. 예를 들어 4 KiB 환경에서 vm_pgoff = 10, addr = vm_start + 3 * 4096 + 17이면 page index는 13, page 내부 byte offset은 17입니다. byte 단위 file offset과 page 단위 pgoff를 섞지 않아야 합니다.

anonymous VMA에서도 pgoff는 reverse mapping의 좌표를 맞추는 데 쓰입니다. 최초 virtual page 위치를 바탕으로 정해진 값이 split이나 이동을 거쳐 관계를 유지하므로, 항상 현재 vm_start >> PAGE_SHIFT로 덮어써도 되는 field가 아닙니다. remap_pfn_range() 같은 special mapping에서는 또 다른 입력 계약이 적용될 수 있습니다.

vm_ops는 fault, huge fault와 VMA lifecycle 등에 대한 callback 집합입니다. ordinary file fault, device mapping과 anonymous fault를 하나의 data 공급 경로로 처리할 수 없는 이유가 여기 있습니다. vm_private_data는 driver 등의 private state이며 범용 kernel이 임의로 그 수명을 해석할 수 없습니다. vm_policy는 해당 범위의 NUMA allocation policy를 연결합니다.

file reverse mapping에는 VMA의 shared interval-tree linkage가 쓰이고, anonymous 쪽에는 anon_vma와 anon_vma_chain이 쓰입니다. private file mapping도 write 뒤 anonymous copy를 가질 수 있으므로 file 관계와 anonymous 관계가 단순히 서로 배타적인 두 종류라고 생각하면 안 됩니다. VMA에 붙인 anonymous name은 이름 객체의 reference와 접근 규칙을 통해 관리하며, 이름 자체가 data backing을 결정하지는 않습니다.

현재 구조체를 전체로 보면 historical 설명의 변경점도 드러납니다. v6.18 vm_area_struct에는 process 주소순 list와 rb-tree node가 없고, per-VMA lock의 vm_refcnt·sequence 및 RCU-safe slab 재사용을 위한 field가 있습니다. file reverse mapping의 shared와 anon_name도 현재는 같은 union의 대체 용도가 아니라 별도 field입니다. 아래 GPL 정의는 configuration 분기를 삭제하지 않은 전체 구조체이며 code/GPL-2.0.txt와 출처 정보가 함께 있습니다.

v6_18_vm_area_struct.c

/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Official Linux v6.18: include/linux/mm_types.h
 * Complete struct; source lines 813-917.
 * Body unchanged. Kernel dependencies and configuration are required.
 * Source URL, SHA, commit, and license: provenance.json; GPL-2.0.txt.
 */

struct vm_area_struct {
	/* The first cache line has the info for VMA tree walking. */

	union {
		struct {
			/* VMA covers [vm_start; vm_end) addresses within mm */
			unsigned long vm_start;
			unsigned long vm_end;
		};
		freeptr_t vm_freeptr; /* Pointer used by SLAB_TYPESAFE_BY_RCU */
	};

	/*
	 * The address space we belong to.
	 * Unstable RCU readers are allowed to read this.
	 */
	struct mm_struct *vm_mm;
	pgprot_t vm_page_prot;          /* Access permissions of this VMA. */

	/*
	 * Flags, see mm.h.
	 * To modify use vm_flags_{init|reset|set|clear|mod} functions.
	 */
	union {
		const vm_flags_t vm_flags;
		vm_flags_t __private __vm_flags;
	};

#ifdef CONFIG_PER_VMA_LOCK
	/*
	 * Can only be written (using WRITE_ONCE()) while holding both:
	 *  - mmap_lock (in write mode)
	 *  - vm_refcnt bit at VMA_LOCK_OFFSET is set
	 * Can be read reliably while holding one of:
	 *  - mmap_lock (in read or write mode)
	 *  - vm_refcnt bit at VMA_LOCK_OFFSET is set or vm_refcnt > 1
	 * Can be read unreliably (using READ_ONCE()) for pessimistic bailout
	 * while holding nothing (except RCU to keep the VMA struct allocated).
	 *
	 * This sequence counter is explicitly allowed to overflow; sequence
	 * counter reuse can only lead to occasional unnecessary use of the
	 * slowpath.
	 */
	unsigned int vm_lock_seq;
#endif
	/*
	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
	 * list, after a COW of one of the file pages.	A MAP_SHARED vma
	 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
	 * or brk vma (with NULL file) can only be in an anon_vma list.
	 */
	struct list_head anon_vma_chain; /* Serialized by mmap_lock &
					  * page_table_lock */
	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */

	/* Function pointers to deal with this struct. */
	const struct vm_operations_struct *vm_ops;

	/* Information about our backing store: */
	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
					   units */
	struct file * vm_file;		/* File we map to (can be NULL). */
	void * vm_private_data;		/* was vm_pte (shared mem) */

#ifdef CONFIG_SWAP
	atomic_long_t swap_readahead_info;
#endif
#ifndef CONFIG_MMU
	struct vm_region *vm_region;	/* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
#endif
#ifdef CONFIG_NUMA_BALANCING
	struct vma_numab_state *numab_state;	/* NUMA Balancing state */
#endif
#ifdef CONFIG_PER_VMA_LOCK
	/* Unstable RCU readers are allowed to read this. */
	refcount_t vm_refcnt ____cacheline_aligned_in_smp;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map vmlock_dep_map;
#endif
#endif
	/*
	 * For areas with an address space and backing store,
	 * linkage into the address_space->i_mmap interval tree.
	 *
	 */
	struct {
		struct rb_node rb;
		unsigned long rb_subtree_last;
	} shared;
#ifdef CONFIG_ANON_VMA_NAME
	/*
	 * For private and shared anonymous mappings, a pointer to a null
	 * terminated string containing the name given to the vma, or NULL if
	 * unnamed. Serialized by mmap_lock. Use anon_vma_name to access.
	 */
	struct anon_vma_name *anon_name;
#endif
	struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
#ifdef __HAVE_PFNMAP_TRACKING
	struct pfnmap_track_ctx *pfnmap_track_ctx;
#endif
} __randomize_layout;

anon_name은 CONFIG_ANON_VMA_NAME에 따른 reference-counted 이름 객체입니다. alloc/get/put 관계를 통해 마지막 reference에서 해제하며, anon_vma_name() 같은 helper로 접근합니다. historical code의 vm_file == NULL 조건을 전제로 한 union access를 현재 구조체의 raw cast로 옮기면 안 됩니다. vm_policy도 vm_ops->get_policy()가 제공하는 주소별 shared policy보다 항상 우선하는 값이 아닙니다.

callback의 이름보다 반환 계약이 중요합니다

다음은 v6.18 vm_operations_struct의 전체 정의입니다. fault()는 새 backing을 찾는 진입점이고 page_mkwrite()는 기존 read-only file page를 writable하게 만들기 위한 통지입니다. 둘을 같은 event로 취급하면 dirtying·filesystem transaction·SIGBUS의 원인을 잘못 짚게 됩니다. special PFN에는 pfn_mkwrite()라는 별도 callback이 있습니다.

v6_18_vm_operations_struct.c

/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Official Linux v6.18: include/linux/mm.h
 * Complete struct; source lines 613-693.
 * Body unchanged. Kernel dependencies and configuration are required.
 * Source URL, SHA, commit, and license: provenance.json; GPL-2.0.txt.
 */

struct vm_operations_struct {
	void (*open)(struct vm_area_struct * area);
	/**
	 * @close: Called when the VMA is being removed from the MM.
	 * Context: User context.  May sleep.  Caller holds mmap_lock.
	 */
	void (*close)(struct vm_area_struct * area);
	/* Called any time before splitting to check if it's allowed */
	int (*may_split)(struct vm_area_struct *area, unsigned long addr);
	int (*mremap)(struct vm_area_struct *area);
	/*
	 * Called by mprotect() to make driver-specific permission
	 * checks before mprotect() is finalised.   The VMA must not
	 * be modified.  Returns 0 if mprotect() can proceed.
	 */
	int (*mprotect)(struct vm_area_struct *vma, unsigned long start,
			unsigned long end, unsigned long newflags);
	vm_fault_t (*fault)(struct vm_fault *vmf);
	vm_fault_t (*huge_fault)(struct vm_fault *vmf, unsigned int order);
	vm_fault_t (*map_pages)(struct vm_fault *vmf,
			pgoff_t start_pgoff, pgoff_t end_pgoff);
	unsigned long (*pagesize)(struct vm_area_struct * area);

	/* notification that a previously read-only page is about to become
	 * writable, if an error is returned it will cause a SIGBUS */
	vm_fault_t (*page_mkwrite)(struct vm_fault *vmf);

	/* same as page_mkwrite when using VM_PFNMAP|VM_MIXEDMAP */
	vm_fault_t (*pfn_mkwrite)(struct vm_fault *vmf);

	/* called by access_process_vm when get_user_pages() fails, typically
	 * for use by special VMAs. See also generic_access_phys() for a generic
	 * implementation useful for any iomem mapping.
	 */
	int (*access)(struct vm_area_struct *vma, unsigned long addr,
		      void *buf, int len, int write);

	/* Called by the /proc/PID/maps code to ask the vma whether it
	 * has a special name.  Returning non-NULL will also cause this
	 * vma to be dumped unconditionally. */
	const char *(*name)(struct vm_area_struct *vma);

#ifdef CONFIG_NUMA
	/*
	 * set_policy() op must add a reference to any non-NULL @new mempolicy
	 * to hold the policy upon return.  Caller should pass NULL @new to
	 * remove a policy and fall back to surrounding context--i.e. do not
	 * install a MPOL_DEFAULT policy, nor the task or system default
	 * mempolicy.
	 */
	int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new);

	/*
	 * get_policy() op must add reference [mpol_get()] to any policy at
	 * (vma,addr) marked as MPOL_SHARED.  The shared policy infrastructure
	 * in mm/mempolicy.c will do this automatically.
	 * get_policy() must NOT add a ref if the policy at (vma,addr) is not
	 * marked as MPOL_SHARED. vma policies are protected by the mmap_lock.
	 * If no [shared/vma] mempolicy exists at the addr, get_policy() op
	 * must return NULL--i.e., do not "fallback" to task or system default
	 * policy.
	 */
	struct mempolicy *(*get_policy)(struct vm_area_struct *vma,
					unsigned long addr, pgoff_t *ilx);
#endif
#ifdef CONFIG_FIND_NORMAL_PAGE
	/*
	 * Called by vm_normal_page() for special PTEs in @vma at @addr. This
	 * allows for returning a "normal" page from vm_normal_page() even
	 * though the PTE indicates that the "struct page" either does not exist
	 * or should not be touched: "special".
	 *
	 * Do not add new users: this really only works when a "normal" page
	 * was mapped, but then the PTE got changed to something weird (+
	 * marked special) that would not make pte_pfn() identify the originally
	 * inserted page.
	 */
	struct page *(*find_normal_page)(struct vm_area_struct *vma,
					 unsigned long addr);
#endif /* CONFIG_FIND_NORMAL_PAGE */
};

open()·close()는 VMA 복제와 제거에 따른 private state 수명을 연결합니다. close()의 current 주석은 user context, sleep 가능, mmap lock 보유를 명시합니다. may_split()는 split 전에 허용 여부를 확인하고 mprotect() callback은 driver별 추가 검사를 수행하되 VMA를 직접 바꾸지 않아야 합니다. access()는 보통 GUP로 처리할 수 없는 special VMA 접근을 지원하는 예외이며, 일반 pointer를 무조건 RAM으로 바꾸는 API가 아닙니다.

NUMA callback에도 reference 규칙이 있습니다. set_policy()는 non-NULL policy를 보유하려면 reference를 얻고, NULL은 해당 VMA policy를 제거한다는 뜻입니다. get_policy()는 MPOL_SHARED policy에 reference를 추가하지만 일반 VMA policy에는 추가하지 않으며, 없으면 task default를 대신 반환하지 않고 NULL을 반환합니다. 이 차이는 caller가 나중에 무엇을 put해야 하는지 결정합니다. name()은 /proc/PID/maps의 special 이름 callback으로 user가 붙인 anonymous 이름과 다릅니다.

허용 permission과 현재 PTE

VM_READ, VM_WRITE, VM_EXEC는 현재 range의 허용 접근을 표현합니다. VM_MAYREAD, VM_MAYWRITE, VM_MAYEXEC는 이후 mprotect() 등으로 허용할 수 있는 상한과 관련됩니다. 지금 write가 금지되었다고 may-write까지 반드시 0은 아닙니다. 반대로 may-write가 없으면 현재 permission 변경으로 그 제한을 우회할 수 없습니다.

vm_page_prot는 architecture의 PTE protection template입니다. x86의 protection mapping은 VMA의 permission과 shared 성질을 바탕으로 기본 값을 고릅니다. 그러나 이 template을 모든 PTE의 현재 값이라고 읽으면 CoW와 write notification을 놓칩니다. writable VMA라도 write fault를 받기 위해 PTE는 read-only일 수 있습니다. vma_wants_writenotify() 같은 조건은 filesystem이 첫 write를 관찰할 수 있도록 기본 protection을 조정합니다.

VM_NONE은 독립된 bit가 아니라 0이라는 값입니다. access permission이 없다는 의미와 VMA 객체가 없다는 의미도 다릅니다. PROT_NONE mapping은 VMA로 남아 있을 수 있고, x86에서는 hardware present와 software의 PROTNONE 해석도 구분됩니다.

private CoW 성질은 일반적으로 VM_MAYWRITE가 있고 VM_SHARED가 없는지 보는 is_cow_mapping() 조건과 연결됩니다. 하지만 모든 private page가 처음부터 zero page를 가리킨다는 뜻은 아닙니다. file-backed page나 fork로 공유한 anonymous page도 있고, 첫 write에서 바로 backing을 마련할 수도 있습니다.

VM_MAYSHARE를 모든 MMU 구성에서 VM_SHARED와 동의어라고 외우는 것도 안전하지 않습니다. v6.18 do_mmap()의 file MAP_SHARED 처리에는 file이 write 가능한 방식으로 열리지 않았을 때 VM_SHARED와 VM_MAYWRITE는 지우지만 VM_MAYSHARE는 남기는 분기가 있습니다. 공유와 쓰기 가능성에 대한 원래 계약을 각 flag가 다르게 나타낼 수 있습니다.

residency와 특수 PFN mapping

VM_LOCKED는 mlock 대상 range임을 나타내고 reclaim에서 그 상태를 고려하게 합니다. 일반적인 mlock은 population도 시도하지만, VM_LOCKONFAULT를 함께 사용하면 아직 없는 page의 준비를 접근 때까지 미룹니다. 따라서 locked라는 range 속성만으로 모든 page가 현재 존재한다고 단정할 수 없습니다. MLOCK_ONFAULT와 MCL_ONFAULT는 각각 해당 API의 조건에 따라 사용합니다.

VM_PFNMAP은 ordinary page-cache/anonymous folio 계약으로 다룰 수 없는 PFN mapping을 나타냅니다. 흔한 예가 device memory지만, 이 flag가 있다고 모든 PFN에 대해 전역적으로 struct page가 없다고 단정하는 대신 해당 mapping이 ordinary refcounted-page 경로를 약속하지 않는다는 점에 주목해야 합니다. VM_MIXEDMAP은 struct page 기반 page와 special PFN을 함께 다룰 수 있는 mapping입니다.

VM_IO는 I/O 관련 mapping의 성질을 표시합니다. 평범한 RAM처럼 임의로 읽으면 register side effect 등이 발생할 수 있어 GUP, dump 등에서 별도로 취급합니다. kernel은 이런 주소를 절대 어떤 방식으로도 접근할 수 없다는 의미는 아니며, device/driver가 정한 접근 API를 따라야 한다는 경계입니다.

stack의 VM_GROWSDOWN은 자동 확장 후보라는 뜻이지 끝없이 늘어날 권한이 아닙니다. 현재 expansion은 write locking, 이웃과의 stack_guard_gap, 낮은 주소 제한과 RLIMIT_STACK 및 address-space accounting 등을 검토합니다. architecture가 지원하면 VM_GROWSUP인 별도 방향도 있습니다. guard를 실제 data VMA와 같은 방식으로 이미 할당된 page라고 생각해서는 안 됩니다.

fork와 reclaim에 건네는 의도

VM_DONTCOPY인 range는 fork 때 복제하지 않고, VM_WIPEONFORK인 private anonymous range는 자식에게 부모 data 대신 zero 내용이 보이도록 합니다. wipe는 fork 순간 모든 page를 즉시 memset한다는 구현 약속이 아닙니다. VM_DONTEXPAND는 무의미한 special mapping 확장을 막는 조건이며, VM_DONTDUMP는 일반적인 core dump range 선택에서 제외하도록 합니다. special kernel mapping의 우선 예외는 별도입니다.

user가 이 의도를 바꾸는 advice도 서로 짝이 있습니다. v6.18 madvise_vma_behavior()에서 MADV_DONTFORK·MADV_DOFORK는 VM_DONTCOPY를 설정·해제하고, MADV_WIPEONFORK·MADV_KEEPONFORK는 VM_WIPEONFORK를 설정·해제합니다. MADV_DONTDUMP·MADV_DODUMP는 VM_DONTDUMP에 대응합니다. 다만 반대 advice가 모든 mapping에서 허용되는 것은 아닙니다. 이 tag는 VM_IO의 DOFORK, file-backed 또는 shared range의 WIPEONFORK, VM_DROPPABLE의 KEEPONFORK를 거절합니다. DODUMP도 non-hugetlb special range나 droppable range에서 거절하므로, driver가 정한 특수 mapping의 제한을 무조건 되돌리는 API로 읽으면 안 됩니다.

VM_SEQ_READ와 VM_RAND_READ는 access pattern advice입니다. sequential은 readahead와 재사용 평가에, random은 불필요한 readahead 억제에 도움을 줍니다. 실제 data 접근 순서를 강제하는 permission이 아닙니다. VM_HUGEPAGE와 VM_NOHUGEPAGE도 THP에 대한 선호와 배제 조건입니다. positive advice는 madvise mode에서 특히 중요하지만 오직 그 mode에서만 의미가 있다고 일반화할 수 없고, advice만으로 큰 leaf나 physical allocation이 보장되지도 않습니다. VM_HUGETLB는 별도의 hugetlb mapping을 나타냅니다.

VM_MERGEABLE은 KSM에 참여할 수 있는 VMA 상태입니다. VM_UFFD_MISSING과 VM_UFFD_WP는 userfaultfd의 missing/write-protection 처리를 연결하고, VM_SYNC는 지원되는 DAX 등의 synchronous fault 계약과 관계됩니다. 이 flag들이 ordinary anonymous fault와 같은 동작을 한다고 가정하면 안 됩니다.

VM_NORESERVE와 VM_ACCOUNT는 commitment와 연결됩니다. current accountable_mapping()은 hugetlb의 별도 accounting을 제외하고 private writable이며 no-reserve가 아닌 조건을 검사합니다. 이것은 지금 확보한 RAM의 수량표가 아닙니다. VM_SOFTDIRTY도 range 수준의 변경 추적에 관계되며 새 mapping을 추적하는 의미가 있어 실제 모든 byte가 이미 write되었다는 증거가 아닙니다.

같은 속성만으로 합쳐지지는 않습니다

인접한 VMA를 병합하려면 permission만 같아서는 부족합니다. backing file, 이어지는 pgoff, NUMA policy, userfaultfd context, anonymous name과 reverse-mapping 호환성 등도 고려해야 합니다. v6.18 is_mergeable_vma()는 VM_SOFTDIRTY 차이는 별도로 다루지만 여러 다른 조건을 비교합니다. flag가 하나 다르면 무조건 분리, flag가 같으면 무조건 병합이라는 두 단순화 모두 맞지 않습니다.

VMA 객체 생성도 data allocation과 다릅니다. vm_area_alloc()은 metadata를 준비하고 vma_init()은 초기 field, dummy vm_ops와 anon_vma chain 등을 초기화합니다. 아직 user translation을 설치한 것이 아닙니다. 해제 역시 anonymous name과 NUMA policy 등 객체가 붙잡은 자원 및 안전한 수명 종료 조건을 따릅니다. index에서 제거한 즉시 아무 observer도 없다고 가정하여 임의로 free할 수는 없습니다.

VMA를 읽을 때에는 범위, permission, backing, fault policy와 lifecycle을 차례로 질문하는 편이 좋습니다. field들을 외우는 데 그치지 않고, 이 주소의 다음 access에서 kernel이 무엇을 해야 하는지를 설명할 수 있게 됩니다.

allocation과 initialization도 실제 정의로 구분해 보겠습니다. 아래는 v6.18 mm/vma_init.c와 mm.h의 해당 함수 전체입니다.

v6_18_vm_area_alloc.c

// SPDX-License-Identifier: GPL-2.0-or-later

/*
 * Functions for initializing, allocating, freeing and duplicating VMAs. Shared
 * between CONFIG_MMU and non-CONFIG_MMU kernel configurations.
 */
/*
 * Official Linux v6.18: mm/vma_init.c
 * Complete function; source lines 28-39.
 * Body unchanged. Kernel dependencies and configuration are required.
 * Source URL, SHA, commit, and license: provenance.json; GPL-2.0.txt.
 */

struct vm_area_struct *vm_area_alloc(struct mm_struct *mm)
{
	struct vm_area_struct *vma;

	vma = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
	if (!vma)
		return NULL;

	vma_init(vma, mm);

	return vma;
}

v6_18_vma_init.c

/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Official Linux v6.18: include/linux/mm.h
 * Complete function; source lines 773-780.
 * Body unchanged. Kernel dependencies and configuration are required.
 * Source URL, SHA, commit, and license: provenance.json; GPL-2.0.txt.
 */

static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm)
{
	memset(vma, 0, sizeof(*vma));
	vma->vm_mm = mm;
	vma->vm_ops = &vma_dummy_vm_ops;
	INIT_LIST_HEAD(&vma->anon_vma_chain);
	vma_lock_init(vma, false);
}

v6_18_vm_area_free.c

// SPDX-License-Identifier: GPL-2.0-or-later

/*
 * Functions for initializing, allocating, freeing and duplicating VMAs. Shared
 * between CONFIG_MMU and non-CONFIG_MMU kernel configurations.
 */
/*
 * Official Linux v6.18: mm/vma_init.c
 * Complete function; source lines 144-152.
 * Body unchanged. Kernel dependencies and configuration are required.
 * Source URL, SHA, commit, and license: provenance.json; GPL-2.0.txt.
 */

void vm_area_free(struct vm_area_struct *vma)
{
	/* The vma should be detached while being destroyed. */
	vma_assert_detached(vma);
	vma_numab_state_free(vma);
	free_anon_vma_name(vma);
	vma_pfnmap_track_ctx_release(vma);
	kmem_cache_free(vm_area_cachep, vma);
}

allocation 실패는 NULL입니다. 성공 시 초기화하지만 index 삽입이나 file reference 획득까지 수행하지는 않습니다. current free는 detached 상태를 검사하고 NUMA balancing state, anonymous name과 PFN tracking reference를 반환한 뒤 slab으로 돌려보냅니다. 전체 munmap()이 이 함수 하나로 끝난다는 뜻이 아니며, file put·policy put·callback·TLB 처리 등은 상위 teardown 계약과 함께 읽어야 합니다. historical kernel/fork.c의 짧은 free 함수와 현재 책임을 혼동하지 않습니다.

참고 자료