diff --git a/articles/20220410-riscv-sparsemem.md b/articles/20220410-riscv-sparsemem.md index 78f24d61811155aa3ad4fdf2452dc0674327f394..3402a09d3821957bf035882a31e79b7f345ce025 100644 --- a/articles/20220410-riscv-sparsemem.md +++ b/articles/20220410-riscv-sparsemem.md @@ -1,4 +1,4 @@ -> Author: 尹天宇
+> Author: Jack Y.
> Date: 2022/04/10
> Revisor: lzufalcon
> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) @@ -28,7 +28,9 @@ Linux 最早采用的是简单直接的 FLATMEM 模型,从名字可以看出 ARCH_PFN_OFFSET) ``` -从 PFN 到 `struct page` 的地址,只需在 `struct page` 数组的基地址 `mem_map` 的基础上,加上 PFN(再减去体系结构定义的偏移量 `ARCH_PFN_OFFSET`,以适配不从 0x0 地址开始的物理空间)即可;而从 `struct page` 的地址到 PFN 也仅仅是把上述公式进行一下移项变换而已。 +从 PFN 到 `struct page` 的地址,只需在 `struct page` 数组的基地址 `mem_map` 的基础上,加上 PFN(再减去体系结构定义的偏移量 `ARCH_PFN_OFFSET`,以适配不从 0x0 地址开始的物理空间)即可;而从 `struct page` 的地址到 PFN 也仅仅是把上述公式进行一下移项变换而已。FLATMEM 模型的 `struct page` 结构如下图所示: + +![](images/riscv_sparsemem/FLATMEM.png) FLATMEM 模型的优点是结构简单,而且 `pfn_to_page()` 和 `page_to_pfn()` 只需进行两次加减法运算,十分高效。 @@ -81,7 +83,9 @@ struct mem_section mem_section[NR_SECTION_ROOTS][SECTIONS_PER_ROOT] ____cacheline_internodealigned_in_smp; ``` -由于在经典 SPARSEMEM 模型中, `SECTIONS_PER_ROOT` 被定义为 1,`mem_section` 二维数组实际上就是长度为 `NR_MEM_SECTIONS` 的一维数组。 +由于在经典 SPARSEMEM 模型中, `SECTIONS_PER_ROOT` 被定义为 1,`mem_section` 二维数组实际上就是长度为 `NR_MEM_SECTIONS` 的一维数组。经典 SPARSEMEM 模型中 `struct mem_section` 的组织结构如下: + +![](images/riscv_sparsemem/classic_sparsemem.png) 每一个 `struct mem_section` 都有一个编号,叫做 `section_nr`,定义方式为物理地址右移 `PA_SECTION_SHIFT` 位,可以轻易理解的是,`PA_SECTION_SHIFT` 的值就等于 `SECTION_SIZE_BITS`。 @@ -140,6 +144,16 @@ static unsigned long sparse_encode_mem_map(struct page *mem_map, unsigned long p } ``` +上述代码中的 `section_nr_to_pfn()` 也很直接,只要位移相应的位数即可。 + +```c +// include/linux/mmzone.h:1303 +static inline unsigned long section_nr_to_pfn(unsigned long sec) +{ + return sec << PFN_SECTION_SHIFT; +} +``` + 现在我们就可以顺理成章地得到经典 SPARSEMEM 模型的 `pfn_to_page()` 和 `page_to_pfn()` 了: 从 PFN 到 `struct page` 的步骤: @@ -273,6 +287,10 @@ static int __meminit sparse_index_init(unsigned long section_nr, int nid) } ``` +下图是 SPARSEMEM_EXTREME 扩展的 `struct mem_section` 组织结构,在图中下标为 $1$ 的 MEM_SECTION_ROOT 中无任何物理内存与其对应,即可不分配相应的 `struct mem_section` 结构体。 + +![](images/riscv_sparsemem/SPARSEMEM_EXTREME.png) + ## SPARSEMEM_VMEMMAP 扩展 SPARSEMEM_VMEMMAP 扩展是为了解决上文中提到的经典 SPARSEMEM 模型的第二个缺点,即 `pfn_to_page()` 和 `page_to_pfn()` 过程较复杂而出现的。在设计之初,增加 `SPARSEMEM_VMEMMAP` 的 Commit 的注释中描述其「可能使得 SPARSEMEM 成为绝大多数系统的默认(甚至唯一)选项」,足以体现出其重要性。 diff --git a/articles/images/riscv_sparsemem/FLATMEM.png b/articles/images/riscv_sparsemem/FLATMEM.png new file mode 100644 index 0000000000000000000000000000000000000000..9fe983a3df82b3d71f65769c4b616b5858ac979f Binary files /dev/null and b/articles/images/riscv_sparsemem/FLATMEM.png differ diff --git a/articles/images/riscv_sparsemem/SPARSEMEM_EXTREME.png b/articles/images/riscv_sparsemem/SPARSEMEM_EXTREME.png new file mode 100644 index 0000000000000000000000000000000000000000..f93dce633bf455113b1ab92ec26812e264a8505a Binary files /dev/null and b/articles/images/riscv_sparsemem/SPARSEMEM_EXTREME.png differ diff --git a/articles/images/riscv_sparsemem/classic_sparsemem.png b/articles/images/riscv_sparsemem/classic_sparsemem.png new file mode 100644 index 0000000000000000000000000000000000000000..7113fd385e14ee9a80117041b2838be0d7715f80 Binary files /dev/null and b/articles/images/riscv_sparsemem/classic_sparsemem.png differ