← Documents Documentation/mm/memory-model.rst GitHub 원문 ↗

Linux 6.18.37 · Memory management

Physical Memory Model

PFN과 struct page의 관계, FLATMEM·SPARSEMEM·vmemmap, ZONE_DEVICE의 주소 범위 표현을 설명합니다.

Source pathDocumentation/mm/memory-model.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

memory-model.rst:1-175

Linux physical-memory model은 모든 PFN을 대응하는 `struct page`로 바꾸는 공통 계약을 유지하면서, memory가 연속적인지·hole이 많은지·hotplug가 필요한지에 따라 표현만 달리합니다. `FLATMEM`은 하나의 `mem_map`, `SPARSEMEM`은 section과 선택적 `vmemmap`, `ZONE_DEVICE`는 online이 아닌 device range까지 같은 page abstraction으로 확장합니다.

Physical memory model 비교
Model표현적합한 용도
`FLATMEM`전역 `mem_map` array연속적인 non-NUMA memory
Classic `SPARSEMEM`Section별 page arrayHole·NUMA·memory hotplug
Sparse `vmemmap`Virtual 연속 `struct page` array빠른 PFN 변환·대규모 system
`ZONE_DEVICE`Online이 아닌 subsection `mem_map`pmem·HMM·P2PDMA

System 형태와 필요한 기능에 따라 model을 선택합니다.

PFN에서 struct page로
PFN`FLATMEM` index`mem_map[PFN - ARCH_PFN_OFFSET]`
PFNClassic sparse sectionSection의 page array
PFNSparse vmemmap index`vmemmap[PFN]`

Model마다 내부 경로는 달라도 PFN과 struct page는 일대일 대응합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 =====================
4 Physical Memory Model
5 =====================
6
7 Physical memory in a system may be addressed in different ways. The
8 simplest case is when the physical memory starts at address 0 and
9 spans a contiguous range up to the maximal address. It could be,
10 however, that this range contains small holes that are not accessible
11 for the CPU. Then there could be several contiguous ranges at
12 completely distinct addresses. And, don't forget about NUMA, where
13 different memory banks are attached to different CPUs.
14
15 Linux abstracts this diversity using one of the two memory models:
16 FLATMEM and SPARSEMEM. Each architecture defines what
17 memory models it supports, what the default memory model is and
18 whether it is possible to manually override that default.
19
20 All the memory models track the status of physical page frames using
21 struct page arranged in one or more arrays.
22
23 Regardless of the selected memory model, there exists one-to-one
24 mapping between the physical page frame number (PFN) and the
25 corresponding `struct page`.
26
27 Each memory model defines :c:func:`pfn_to_page` and :c:func:`page_to_pfn`
28 helpers that allow the conversion from PFN to `struct page` and vice
29 versa.
30
31 FLATMEM
32 =======
33
34 The simplest memory model is FLATMEM. This model is suitable for
35 non-NUMA systems with contiguous, or mostly contiguous, physical
36 memory.
37
38 In the FLATMEM memory model, there is a global `mem_map` array that
39 maps the entire physical memory. For most architectures, the holes
40 have entries in the `mem_map` array. The `struct page` objects
41 corresponding to the holes are never fully initialized.
42
43 To allocate the `mem_map` array, architecture specific setup code should
44 call :c:func:`free_area_init` function. Yet, the mappings array is not
45 usable until the call to :c:func:`memblock_free_all` that hands all the
46 memory to the page allocator.
47
48 An architecture may free parts of the `mem_map` array that do not cover the
49 actual physical pages. In such case, the architecture specific
50 :c:func:`pfn_valid` implementation should take the holes in the
51 `mem_map` into account.
52
53 With FLATMEM, the conversion between a PFN and the `struct page` is
54 straightforward: `PFN - ARCH_PFN_OFFSET` is an index to the
55 `mem_map` array.
56
57 The `ARCH_PFN_OFFSET` defines the first page frame number for
58 systems with physical memory starting at address different from 0.
59
60 SPARSEMEM
61 =========
62
63 SPARSEMEM is the most versatile memory model available in Linux and it
64 is the only memory model that supports several advanced features such
65 as hot-plug and hot-remove of the physical memory, alternative memory
66 maps for non-volatile memory devices and deferred initialization of
67 the memory map for larger systems.
68
69 The SPARSEMEM model presents the physical memory as a collection of
70 sections. A section is represented with struct mem_section
71 that contains `section_mem_map` that is, logically, a pointer to an
72 array of struct pages. However, it is stored with some other magic
73 that aids the sections management. The section size and maximal number
74 of section is specified using `SECTION_SIZE_BITS` and
75 `MAX_PHYSMEM_BITS` constants defined by each architecture that
76 supports SPARSEMEM. While `MAX_PHYSMEM_BITS` is an actual width of a
77 physical address that an architecture supports, the
78 `SECTION_SIZE_BITS` is an arbitrary value.
79
80 The maximal number of sections is denoted `NR_MEM_SECTIONS` and
81 defined as
82
83 .. math::
84
85 NR\_MEM\_SECTIONS = 2 ^ {(MAX\_PHYSMEM\_BITS - SECTION\_SIZE\_BITS)}
86
87 The `mem_section` objects are arranged in a two-dimensional array
88 called `mem_sections`. The size and placement of this array depend
89 on `CONFIG_SPARSEMEM_EXTREME` and the maximal possible number of
90 sections:
91
92 * When `CONFIG_SPARSEMEM_EXTREME` is disabled, the `mem_sections`
93 array is static and has `NR_MEM_SECTIONS` rows. Each row holds a
94 single `mem_section` object.
95 * When `CONFIG_SPARSEMEM_EXTREME` is enabled, the `mem_sections`
96 array is dynamically allocated. Each row contains PAGE_SIZE worth of
97 `mem_section` objects and the number of rows is calculated to fit
98 all the memory sections.
99
100 The architecture setup code should call sparse_init() to
101 initialize the memory sections and the memory maps.
102
103 With SPARSEMEM there are two possible ways to convert a PFN to the
104 corresponding `struct page` - a "classic sparse" and "sparse
105 vmemmap". The selection is made at build time and it is determined by
106 the value of `CONFIG_SPARSEMEM_VMEMMAP`.
107
108 The classic sparse encodes the section number of a page in page->flags
109 and uses high bits of a PFN to access the section that maps that page
110 frame. Inside a section, the PFN is the index to the array of pages.
111
112 The sparse vmemmap uses a virtually mapped memory map to optimize
113 pfn_to_page and page_to_pfn operations. There is a global `struct
114 page *vmemmap` pointer that points to a virtually contiguous array of
115 `struct page` objects. A PFN is an index to that array and the
116 offset of the `struct page` from `vmemmap` is the PFN of that
117 page.
118
119 To use vmemmap, an architecture has to reserve a range of virtual
120 addresses that will map the physical pages containing the memory
121 map and make sure that `vmemmap` points to that range. In addition,
122 the architecture should implement :c:func:`vmemmap_populate` method
123 that will allocate the physical memory and create page tables for the
124 virtual memory map. If an architecture does not have any special
125 requirements for the vmemmap mappings, it can use default
126 :c:func:`vmemmap_populate_basepages` provided by the generic memory
127 management.
128
129 The virtually mapped memory map allows storing `struct page` objects
130 for persistent memory devices in pre-allocated storage on those
131 devices. This storage is represented with struct vmem_altmap
132 that is eventually passed to vmemmap_populate() through a long chain
133 of function calls. The vmemmap_populate() implementation may use the
134 `vmem_altmap` along with :c:func:`vmemmap_alloc_block_buf` helper to
135 allocate memory map on the persistent memory device.
136
137 ZONE_DEVICE
138 ===========
139 The `ZONE_DEVICE` facility builds upon `SPARSEMEM_VMEMMAP` to offer
140 `struct page` `mem_map` services for device driver identified physical
141 address ranges. The "device" aspect of `ZONE_DEVICE` relates to the fact
142 that the page objects for these address ranges are never marked online,
143 and that a reference must be taken against the device, not just the page
144 to keep the memory pinned for active use. `ZONE_DEVICE`, via
145 :c:func:`devm_memremap_pages`, performs just enough memory hotplug to
146 turn on :c:func:`pfn_to_page`, :c:func:`page_to_pfn`, and
147 :c:func:`get_user_pages` service for the given range of pfns. Since the
148 page reference count never drops below 1 the page is never tracked as
149 free memory and the page's `struct list_head lru` space is repurposed
150 for back referencing to the host device / driver that mapped the memory.
151
152 While `SPARSEMEM` presents memory as a collection of sections,
153 optionally collected into memory blocks, `ZONE_DEVICE` users have a need
154 for smaller granularity of populating the `mem_map`. Given that
155 `ZONE_DEVICE` memory is never marked online it is subsequently never
156 subject to its memory ranges being exposed through the sysfs memory
157 hotplug api on memory block boundaries. The implementation relies on
158 this lack of user-api constraint to allow sub-section sized memory
159 ranges to be specified to :c:func:`arch_add_memory`, the top-half of
160 memory hotplug. Sub-section support allows for 2MB as the cross-arch
161 common alignment granularity for :c:func:`devm_memremap_pages`.
162
163 The users of `ZONE_DEVICE` are:
164
165 * pmem: Map platform persistent memory to be used as a direct-I/O target
166 via DAX mappings.
167
168 * hmm: Extend `ZONE_DEVICE` with `->page_fault()` and `->page_free()`
169 event callbacks to allow a device-driver to coordinate memory management
170 events related to device-memory, typically GPU memory. See
171 Documentation/mm/hmm.rst.
172
173 * p2pdma: Create `struct page` objects to allow peer devices in a
174 PCI/-E topology to coordinate direct-DMA operations between themselves,
175 i.e. bypass host memory.
176

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Physical memory model 개요

1-30

System의 physical memory는 여러 방식으로 주소를 부여할 수 있습니다. 가장 단순한 경우 physical memory가 address 0에서 시작해 최대 주소까지 연속 범위를 이룹니다. 하지만 CPU가 접근할 수 없는 작은 hole이 범위 안에 있을 수도 있고, 완전히 다른 주소에 여러 연속 범위가 있을 수도 있습니다. 서로 다른 memory bank를 서로 다른 CPU에 연결하는 NUMA도 고려해야 합니다.

Linux는 이 다양성을 `FLATMEM`과 `SPARSEMEM` 두 memory model 중 하나로 추상화합니다. 각 architecture는 지원하는 model, 기본 model, 기본값을 수동으로 바꿀 수 있는지를 정의합니다.

모든 memory model은 하나 이상의 array에 배치된 `struct page`로 physical page frame 상태를 추적합니다.

어떤 memory model을 선택하더라도 physical page frame number, 즉 PFN과 해당 `struct page` 사이에는 일대일 mapping이 있습니다.

각 memory model은 PFN과 `struct page`를 양방향으로 변환하는 `pfn_to_page()`와 `page_to_pfn()` helper를 정의합니다.

.. SPDX-License-Identifier: GPL-2.0

=====================
Physical Memory Model
=====================

Physical memory in a system may be addressed in different ways. The
simplest case is when the physical memory starts at address 0 and
spans a contiguous range up to the maximal address. It could be,
however, that this range contains small holes that are not accessible
for the CPU. Then there could be several contiguous ranges at
completely distinct addresses. And, don't forget about NUMA, where
different memory banks are attached to different CPUs.

Linux abstracts this diversity using one of the two memory models:
FLATMEM and SPARSEMEM. Each architecture defines what
memory models it supports, what the default memory model is and
whether it is possible to manually override that default.

All the memory models track the status of physical page frames using
struct page arranged in one or more arrays.

Regardless of the selected memory model, there exists one-to-one
mapping between the physical page frame number (PFN) and the
corresponding `struct page`.

Each memory model defines :c:func:`pfn_to_page` and :c:func:`page_to_pfn`
helpers that allow the conversion from PFN to `struct page` and vice
versa.

FLATMEM

31-59

`FLATMEM`은 가장 단순한 memory model이며 physical memory가 연속적이거나 거의 연속적인 non-NUMA system에 적합합니다.

`FLATMEM`에는 전체 physical memory를 mapping하는 전역 `mem_map` array가 있습니다. 대부분의 architecture에서는 hole도 `mem_map` 항목을 가지지만 그 hole에 해당하는 `struct page` object는 완전히 초기화하지 않습니다.

`mem_map` array를 할당하려면 architecture별 setup code가 `free_area_init()`을 호출해야 합니다. 그러나 모든 memory를 page allocator에 넘기는 `memblock_free_all()`을 호출하기 전에는 mapping array를 사용할 수 없습니다.

Architecture는 실제 physical page를 포함하지 않는 `mem_map` 부분을 해제할 수 있습니다. 이 경우 architecture별 `pfn_valid()` 구현이 `mem_map`의 hole을 고려해야 합니다.

`FLATMEM`에서 PFN과 `struct page` 변환은 단순합니다. `PFN - ARCH_PFN_OFFSET`이 `mem_map` array의 index입니다.

`ARCH_PFN_OFFSET`은 physical memory가 address 0이 아닌 곳에서 시작하는 system의 첫 page-frame number를 정의합니다.

FLATMEM
=======

The simplest memory model is FLATMEM. This model is suitable for
non-NUMA systems with contiguous, or mostly contiguous, physical
memory.

In the FLATMEM memory model, there is a global `mem_map` array that
maps the entire physical memory. For most architectures, the holes
have entries in the `mem_map` array. The `struct page` objects
corresponding to the holes are never fully initialized.

To allocate the `mem_map` array, architecture specific setup code should
call :c:func:`free_area_init` function. Yet, the mappings array is not
usable until the call to :c:func:`memblock_free_all` that hands all the
memory to the page allocator.

An architecture may free parts of the `mem_map` array that do not cover the
actual physical pages. In such case, the architecture specific
:c:func:`pfn_valid` implementation should take the holes in the
`mem_map` into account.

With FLATMEM, the conversion between a PFN and the `struct page` is
straightforward: `PFN - ARCH_PFN_OFFSET` is an index to the
`mem_map` array.

The `ARCH_PFN_OFFSET` defines the first page frame number for
systems with physical memory starting at address different from 0.

SPARSEMEM과 vmemmap

60-136

`SPARSEMEM`은 Linux에서 가장 유연한 memory model입니다. Physical memory hot-plug와 hot-remove, non-volatile memory device용 대체 memory map, 큰 system의 memory-map 지연 초기화 같은 고급 기능을 지원하는 유일한 model입니다.

`SPARSEMEM`은 physical memory를 section 모음으로 표현합니다. Section은 논리적으로 `struct page` array pointer인 `section_mem_map`과 section 관리용 정보를 함께 담는 `struct mem_section`으로 표현합니다. Section 크기와 최대 section 수는 SPARSEMEM을 지원하는 각 architecture의 `SECTION_SIZE_BITS`와 `MAX_PHYSMEM_BITS` 상수로 정합니다. `MAX_PHYSMEM_BITS`는 architecture가 지원하는 실제 physical-address 폭이고 `SECTION_SIZE_BITS`는 임의로 선택한 값입니다.

최대 section 수 `NR_MEM_SECTIONS`는 다음과 같습니다.

NR_MEM_SECTIONS = 2 ^ (MAX_PHYSMEM_BITS - SECTION_SIZE_BITS)

`mem_section` object는 `mem_sections`라는 2차원 array에 배치합니다. Array 크기와 배치는 `CONFIG_SPARSEMEM_EXTREME` 및 가능한 최대 section 수에 따라 달라집니다.

  • `CONFIG_SPARSEMEM_EXTREME`을 끄면 `mem_sections`는 `NR_MEM_SECTIONS` row를 가진 static array이고 각 row가 `mem_section` object 하나를 담습니다.
  • `CONFIG_SPARSEMEM_EXTREME`을 켜면 `mem_sections`를 동적으로 할당합니다. 각 row가 `PAGE_SIZE`만큼의 `mem_section` object를 담고 모든 memory section에 맞도록 row 수를 계산합니다.

Architecture setup code는 `sparse_init()`을 호출해 memory section과 memory map을 초기화해야 합니다.

`SPARSEMEM`에서 PFN을 `struct page`로 바꾸는 방식은 classic sparse와 sparse vmemmap 두 가지입니다. Build 시 `CONFIG_SPARSEMEM_VMEMMAP` 값으로 선택합니다.

Classic sparse는 page의 section number를 `page->flags`에 encoding하고 PFN의 상위 bit로 해당 page frame을 mapping하는 section에 접근합니다. Section 안에서는 PFN이 page array의 index입니다.

Sparse vmemmap은 virtual mapping된 memory map으로 `pfn_to_page()`와 `page_to_pfn()` operation을 최적화합니다. 전역 `struct page *vmemmap` pointer가 virtual address상 연속인 `struct page` array를 가리킵니다. PFN이 이 array의 index이고 `vmemmap`에서 `struct page`까지의 offset이 해당 page의 PFN입니다.

Vmemmap을 쓰려면 architecture가 memory map을 담은 physical page를 mapping할 virtual-address 범위를 예약하고 `vmemmap`이 그 범위를 가리키게 해야 합니다. 또한 physical memory를 할당하고 virtual memory map의 page table을 만드는 `vmemmap_populate()` method를 구현해야 합니다. 특별한 mapping 요구가 없다면 generic memory management의 기본 `vmemmap_populate_basepages()`를 사용할 수 있습니다.

Virtual mapping된 memory map은 persistent-memory device의 `struct page` object를 해당 device의 선할당 storage에 저장할 수 있게 합니다. 이 storage는 `struct vmem_altmap`으로 표현하며 긴 함수 호출 경로를 거쳐 `vmemmap_populate()`에 전달합니다. `vmemmap_populate()` 구현은 `vmem_altmap`과 `vmemmap_alloc_block_buf()` helper로 persistent-memory device에 memory map을 할당할 수 있습니다.

SPARSEMEM
=========

SPARSEMEM is the most versatile memory model available in Linux and it
is the only memory model that supports several advanced features such
as hot-plug and hot-remove of the physical memory, alternative memory
maps for non-volatile memory devices and deferred initialization of
the memory map for larger systems.

The SPARSEMEM model presents the physical memory as a collection of
sections. A section is represented with struct mem_section
that contains `section_mem_map` that is, logically, a pointer to an
array of struct pages. However, it is stored with some other magic
that aids the sections management. The section size and maximal number
of section is specified using `SECTION_SIZE_BITS` and
`MAX_PHYSMEM_BITS` constants defined by each architecture that
supports SPARSEMEM. While `MAX_PHYSMEM_BITS` is an actual width of a
physical address that an architecture supports, the
`SECTION_SIZE_BITS` is an arbitrary value.

The maximal number of sections is denoted `NR_MEM_SECTIONS` and
defined as

.. math::

   NR\_MEM\_SECTIONS = 2 ^ {(MAX\_PHYSMEM\_BITS - SECTION\_SIZE\_BITS)}

The `mem_section` objects are arranged in a two-dimensional array
called `mem_sections`. The size and placement of this array depend
on `CONFIG_SPARSEMEM_EXTREME` and the maximal possible number of
sections:

* When `CONFIG_SPARSEMEM_EXTREME` is disabled, the `mem_sections`
  array is static and has `NR_MEM_SECTIONS` rows. Each row holds a
  single `mem_section` object.
* When `CONFIG_SPARSEMEM_EXTREME` is enabled, the `mem_sections`
  array is dynamically allocated. Each row contains PAGE_SIZE worth of
  `mem_section` objects and the number of rows is calculated to fit
  all the memory sections.

The architecture setup code should call sparse_init() to
initialize the memory sections and the memory maps.

With SPARSEMEM there are two possible ways to convert a PFN to the
corresponding `struct page` - a "classic sparse" and "sparse
vmemmap". The selection is made at build time and it is determined by
the value of `CONFIG_SPARSEMEM_VMEMMAP`.

The classic sparse encodes the section number of a page in page->flags
and uses high bits of a PFN to access the section that maps that page
frame. Inside a section, the PFN is the index to the array of pages.

The sparse vmemmap uses a virtually mapped memory map to optimize
pfn_to_page and page_to_pfn operations. There is a global `struct
page *vmemmap` pointer that points to a virtually contiguous array of
`struct page` objects. A PFN is an index to that array and the
offset of the `struct page` from `vmemmap` is the PFN of that
page.

To use vmemmap, an architecture has to reserve a range of virtual
addresses that will map the physical pages containing the memory
map and make sure that `vmemmap` points to that range. In addition,
the architecture should implement :c:func:`vmemmap_populate` method
that will allocate the physical memory and create page tables for the
virtual memory map. If an architecture does not have any special
requirements for the vmemmap mappings, it can use default
:c:func:`vmemmap_populate_basepages` provided by the generic memory
management.

The virtually mapped memory map allows storing `struct page` objects
for persistent memory devices in pre-allocated storage on those
devices. This storage is represented with struct vmem_altmap
that is eventually passed to vmemmap_populate() through a long chain
of function calls. The vmemmap_populate() implementation may use the
`vmem_altmap` along with :c:func:`vmemmap_alloc_block_buf` helper to
allocate memory map on the persistent memory device.

ZONE_DEVICE

137-175

`ZONE_DEVICE`는 `SPARSEMEM_VMEMMAP` 위에 구축되며 device driver가 지정한 physical-address 범위에 `struct page`와 `mem_map` 서비스를 제공합니다. 여기서 device라는 특성은 이 주소 범위의 page object를 online으로 표시하지 않고, memory를 active use 상태로 pin하려면 page뿐 아니라 device에도 reference를 잡아야 한다는 뜻입니다. `ZONE_DEVICE`는 `devm_memremap_pages()`를 통해 필요한 만큼만 memory hotplug를 수행해 주어진 PFN 범위에 `pfn_to_page()`, `page_to_pfn()`, `get_user_pages()` 서비스를 활성화합니다. Page reference count는 1 아래로 내려가지 않으므로 free memory로 추적하지 않으며 page의 `struct list_head lru` 공간은 memory를 mapping한 host device 또는 driver를 역참조하는 데 다시 사용합니다.

`SPARSEMEM`은 memory를 section 모음, 선택적으로 memory block 모음으로 표현하지만 `ZONE_DEVICE` 사용자는 더 작은 단위로 `mem_map`을 populate해야 합니다. `ZONE_DEVICE` memory는 online으로 표시하지 않으므로 sysfs memory-hotplug API에 memory-block 경계로 노출되지 않습니다. 구현은 이런 user-API 제약이 없다는 점을 이용해 memory-hotplug 상위 절반인 `arch_add_memory()`에 subsection 크기 범위를 지정할 수 있게 합니다. Subsection 지원은 `devm_memremap_pages()`에 architecture 공통 2MB alignment 단위를 제공합니다.

`ZONE_DEVICE` 사용자는 다음과 같습니다.

  • `pmem`: Platform persistent memory를 DAX mapping의 direct-I/O target으로 mapping합니다.
  • `hmm`: `ZONE_DEVICE`에 `->page_fault()`와 `->page_free()` event callback을 추가해 device driver가 보통 GPU memory인 device-memory 관련 memory-management event를 조정하게 합니다. `Documentation/mm/hmm.rst`를 참조하십시오.
  • `p2pdma`: PCI/PCIe topology의 peer device들이 host memory를 우회해 서로 direct-DMA operation을 조정할 수 있도록 `struct page` object를 만듭니다.
ZONE_DEVICE
===========
The `ZONE_DEVICE` facility builds upon `SPARSEMEM_VMEMMAP` to offer
`struct page` `mem_map` services for device driver identified physical
address ranges. The "device" aspect of `ZONE_DEVICE` relates to the fact
that the page objects for these address ranges are never marked online,
and that a reference must be taken against the device, not just the page
to keep the memory pinned for active use. `ZONE_DEVICE`, via
:c:func:`devm_memremap_pages`, performs just enough memory hotplug to
turn on :c:func:`pfn_to_page`, :c:func:`page_to_pfn`, and
:c:func:`get_user_pages` service for the given range of pfns. Since the
page reference count never drops below 1 the page is never tracked as
free memory and the page's `struct list_head lru` space is repurposed
for back referencing to the host device / driver that mapped the memory.

While `SPARSEMEM` presents memory as a collection of sections,
optionally collected into memory blocks, `ZONE_DEVICE` users have a need
for smaller granularity of populating the `mem_map`. Given that
`ZONE_DEVICE` memory is never marked online it is subsequently never
subject to its memory ranges being exposed through the sysfs memory
hotplug api on memory block boundaries. The implementation relies on
this lack of user-api constraint to allow sub-section sized memory
ranges to be specified to :c:func:`arch_add_memory`, the top-half of
memory hotplug. Sub-section support allows for 2MB as the cross-arch
common alignment granularity for :c:func:`devm_memremap_pages`.

The users of `ZONE_DEVICE` are:

* pmem: Map platform persistent memory to be used as a direct-I/O target
  via DAX mappings.

* hmm: Extend `ZONE_DEVICE` with `->page_fault()` and `->page_free()`
  event callbacks to allow a device-driver to coordinate memory management
  events related to device-memory, typically GPU memory. See
  Documentation/mm/hmm.rst.

* p2pdma: Create `struct page` objects to allow peer devices in a
  PCI/-E topology to coordinate direct-DMA operations between themselves,
  i.e. bypass host memory.