asm-generic路径下分页管理源码详解
asm-generic文件目录为/usr/src/linux-headers-6.8.0-45-generic/include/asm-generic,本次分析了其中三个头文件pgtable-nop4d.h,pgtable-nopud.h 和pgtable-nopmd.h 。
pgtable-nop4d.h 分析
该文件是为不支持四级页表(P4D)的架构设计的,它通过“折叠”P4D层级到PGD(Page Global Directory)层级来简化页表结构。以下是对代码的分析:
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PGTABLE_NOP4D_H
#define _PGTABLE_NOP4D_H
#ifndef __ASSEMBLY__
/* 定义了一个标志,表明P4D层级被折叠到了PGD层级 */
#define __PAGETABLE_P4D_FOLDED 1
/* 定义了一个p4d_t类型,其只包含一个pgd_t类型的成员,使得P4D的所有功能映射到PGD上。 */
typedef struct { pgd_t pgd; } p4d_t;
/* 这些宏定义了P4D层级应该具有的属性,但由于P4D被折叠到PGD,它们实际上使用了PGD的相应值 */
#define P4D_SHIFT PGDIR_SHIFT
#define PTRS_PER_P4D 1
#define P4D_SIZE (1UL << P4D_SHIFT)
#define P4D_MASK (~(P4D_SIZE-1))
/*
* 这里的 “pgd_xxx() ”函数对于折叠式两级设置来说是微不足道的:
* p4d 永远不会坏,p4d 永远存在(因为它被折叠到 pgd 条目中)
*/
/*
* 因为P4D总是存在且不会被记为坏(因为它被折叠到PGD中),
* 所以pgd_none和pgd_bad总是返回0,pgd_present总是返回1,
* 而pgd_clear则不执行任何操作
*/
static inline int pgd_none(pgd_t pgd) { return 0; }
static inline int pgd_bad(pgd_t pgd) { return 0; }
static inline int pgd_present(pgd_t pgd) { return 1; }
static inline void pgd_clear(pgd_t *pgd) { }
/* 由于折叠,pgd_ERROR函数实际检查的是PGD的错误状态 */
#define p4d_ERROR(p4d) (pgd_ERROR((p4d).pgd))
/* 这些宏在P4D被折叠到PGD的情况下没有实际作用,因此被定义为空操作 */
#define pgd_populate(mm, pgd, p4d) do { } while (0)
#define pgd_populate_safe(mm, pgd, p4d) do { } while (0)
/* (p4ds 被折叠到 pgds 中,因此不会被实际调用,但通用内联函数需要该定义) */
/*
* 这个宏使用set_p4d来设置PGD的值,由于P4D被折叠到PGD,
* 这里使用了p4d_t类型的强制转换来适配函数签名。
*/
#define set_pgd(pgdptr, pgdval) set_p4d((p4d_t *)(pgdptr), (p4d_t) { pgdval })
/* 该函数返回指向PGD的指针,也是P4D的指针,因为P4D和PGD在物理上相同 */
static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
{
return (p4d_t *)pgd;
}
/* 下两宏用于获取或设置p4d_t结构中的PGD值 */
#define p4d_val(x) (pgd_val((x).pgd))
#define __p4d(x) ((p4d_t) { __pgd(x) })
/*
* 这些宏被重定义以兼容P4D的概念,尽管它们实际上直接操作PGD条目。
* pgd_page返回与PGD条目相关联的页面结构,而pgd_page_vaddr返回PGD表的虚拟地址。
*/
#define pgd_page(pgd) (p4d_page((p4d_t){ pgd }))
#define pgd_page_vaddr(pgd) ((unsigned long)(p4d_pgtable((p4d_t){ pgd })))
/* 分配和释放p4d都很简单:1-entry p4d 位于pgd内,因此没有与之相关的额外内存 */
/* 由于P4D被折叠到PGD,因此不需要为P4D分配或释放额外的内存,因此这些宏被定义为空操作 */
#define p4d_alloc_one(mm, address) NULL
#define p4d_free(mm, x) do { } while (0)
#define p4d_free_tlb(tlb, x, a) do { } while (0)
/* 定义了P4D,即PGD的地址范围的结束点,由于P4D与PGD相同,因此直接返回给定的结束地址 */
#undef p4d_addr_end
#define p4d_addr_end(addr, end) (end)
#endif /* __ASSEMBLY__ */
#endif /* _PGTABLE_NOP4D_H */
pgtable-nopud.h 分析:
该头文件是 Linux 内核中用于处理没有物理页上级目录PUD(Page Upper Directory)的页表结构。在一些架构中,页表可能只包含页全局目录(PGD)、页上级目录(PUD)、页中间目录(PMD)和页表项(PTE)等中的几个层级,而不是全部。这个文件定义了在没有 PUD 层级的情况下的宏和函数。下面是对文件中各个宏和函数作用的详细分析:
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PGTABLE_NOPUD_H
#define _PGTABLE_NOPUD_H
#ifndef __ASSEMBLY__
#include <asm-generic/pgtable-nop4d.h>
/* 定义为 1,表示 PUD 层级被折叠了,在五级分页下直接由 P4D替代。 */
#define __PAGETABLE_PUD_FOLDED 1
/* 让pud类型由p4d组成,可以正确地确定大小,并允许访问p4d,而无需转换。*/
/* 定义pud_t结构体仅包含p4d_t类型的成员p4d,这表示在没有独立PUD层级的架构中,PUD的功能由P4D承担 */
typedef struct { p4d_t p4d; } pud_t;
/* 这些宏定义了被折叠层级PUD的属性,如页表项的偏移量PUD_SHIFT、每个PUD可指向的页中间目录的数量PTRS_PER_PUD,因为PUD被折叠为1,PUD管理的内存区域大小PUD_SIZE和地址掩码PUD_MASK */
#define PUD_SHIFT P4D_SHIFT
#define PTRS_PER_PUD 1
#define PUD_SIZE (1UL << PUD_SHIFT)
#define PUD_MASK (~(PUD_SIZE-1))
/*
* 这里的“p4d_xxx()”函数对于折叠的两级设置来说是微不足道的:
* pud永远不会坏并且永远存在(因为它被折叠到了p4d这条目中)
*/
/* 这些内联函数在PUD被折叠的上下文中是空的或返回固定值 */
static inline int p4d_none(p4d_t p4d) { return 0; }
static inline int p4d_bad(p4d_t p4d) { return 0; }
static inline int p4d_present(p4d_t p4d) { return 1; }
static inline void p4d_clear(p4d_t *p4d) { }
/* 通过P4D实现报告PUD相关的错误 */
#define pud_ERROR(pud) (p4d_ERROR((pud).p4d))
/* 因为PUD被折叠,故在没有独立PUD层级的系统中是空的 */
#define p4d_populate(mm, p4d, pud) do { } while (0)
#define p4d_populate_safe(mm, p4d, pud) do { } while (0)
/* (puds被折叠到p4ds中,因此不会被实际调用,但通用内联函数需要该定义) */
/* 设置P4D的值,但这里通过set_pud和类型转换来间接设置,以适配PUD被折叠的情况 */
#define set_p4d(p4dptr, p4dval) set_pud((pud_t *)(p4dptr), (pud_t) { p4dval })
/* 函数返回给定P4D指针作为PUD指针,因为PUD被折叠进P4D */
static inline pud_t *pud_offset(p4d_t *p4d, unsigned long address)
{
return (pud_t *)p4d;
}
#define pud_offset pud_offset
/* 用于获取PUD的值和构造PUD结构体 */
#define pud_val(x) (p4d_val((x).p4d))
#define __pud(x) ((pud_t) { __p4d(x) })
/* 通过内在为P4D的PUD获取页框和页表页 */
#define p4d_page(p4d) (pud_page((pud_t){ p4d }))
#define p4d_pgtable(p4d) ((pud_t *)(pud_pgtable((pud_t){ p4d })))
/* 分配和释放一个pud是微不足道的:1-entry pud 位于p4d内,因此没有额外内存 */
/* 由于PUD被折叠而不需要分配或释放内存,所以这些函数都是空的 */
#define pud_alloc_one(mm, address) NULL
#define pud_free(mm, x) do { } while (0)
#define pud_free_tlb(tlb, x, a) do { } while (0)
/* 用于计算给定地址范围内由实为P4D的PUD管理的结束地址 */
#undef pud_addr_end
#define pud_addr_end(addr, end) (end)
#endif /* __ASSEMBLY__ */
#endif /* _PGTABLE_NOPUD_H */
pgtable-nopmd.h 分析:
该文件是 Linux 内核中用于处理没有物理页上级目录PUD(Page Upper Directory)的页表结构头文件。在一些架构中,页表可能只包含页全局目录PGD、页上级目录PUD、页中间目录PMD和页表项PTE等中的几个层级,而不是全部。该文件定义了在没有 PUD 层级的情况下的宏和函数,下面是对源码的详细分析:
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PGTABLE_NOPMD_H
#define _PGTABLE_NOPMD_H
#ifndef __ASSEMBLY__
#include <asm-generic/pgtable-nopud.h>
struct mm_struct;
/* 定义为1,表示PMD层级被折叠了,直接由PUD或可能的其他层级替代 */
#define __PAGETABLE_PMD_FOLDED 1
/* pmd类型由一个pud组成,这样不仅大小合适,而且我们还可以访问该pmd折叠成的pud时而无需转换 */
/* 定义包含pud_t类型成员pud的结构体pmd_t,这表示在没有独立PMD层级的架构中,PMD的功能由PUD承担 */
typedef struct { pud_t pud; } pmd_t;
/* 这些宏定义了被折叠层级PMD的一些属性,如页表项的偏移量PMD_SHIFT、因为PMD被折叠为1的每个PMD可指向的页表项的数量PTRS_PER_PMD、PMD管理的内存区域大小PMD_SIZE和地址掩码PMD_MASK */
#define PMD_SHIFT PUD_SHIFT
#define PTRS_PER_PMD 1
#define PMD_SIZE (1UL << PMD_SHIFT)
#define PMD_MASK (~(PMD_SIZE-1))
/*
* 这里的 “pud_xxx() ”函数对于折叠式两级设置来说是微不足道的:
* pmd从不出错,pmd始终存在(因为它被折叠到了pud条目中)
*/
/* 这些函数在PMD被折叠的上下文中是空的或返回固定值,因为 PMD 不存在或总是有效的 */
static inline int pud_none(pud_t pud) { return 0; }
static inline int pud_bad(pud_t pud) { return 0; }
static inline int pud_present(pud_t pud) { return 1; }
static inline int pud_user(pud_t pud) { return 0; }
static inline int pud_leaf(pud_t pud) { return 0; }
static inline void pud_clear(pud_t *pud) { }
/* 通过 PUD 实现报告 PMD 相关的错误 */
#define pmd_ERROR(pmd) (pud_ERROR((pmd).pud))
/* 宏定义为空操作,因为在没有PMD的情况下无需填充PMD */
#define pud_populate(mm, pmd, pte) do { } while (0)
/* (pmds 被折叠到 puds 中,因此不会被实际调用,但通用内联函数需要定义。) */
/* 用于设置PUD的值,此处通过set_pmd和类型转换来间接设置,以适配PMD被折叠的情况 */
#define set_pud(pudptr, pudval) set_pmd((pmd_t *)(pudptr), (pmd_t) { pudval })
/* 因为PMD被折叠进PUD,故函数返回给定PUD指针作为PMD指针 */
static inline pmd_t * pmd_offset(pud_t * pud, unsigned long address)
{
return (pmd_t *)pud;
}
#define pmd_offset pmd_offset
/* 用于获取PMD的值和构造PMD结构体 */
#define pmd_val(x) (pud_val((x).pud))
#define __pmd(x) ((pmd_t) { __pud(x) } )
/* 通过实为PUD的PMD获取页框和页表页 */
#define pud_page(pud) (pmd_page((pmd_t){ pud }))
#define pud_pgtable(pud) ((pmd_t *)(pmd_page_vaddr((pmd_t){ pud })))
/*(分配和释放pmd非常简单:1-entry pmd位于pud内,因此没有额外的内存。) */
/* 由于PMD被折叠,这些函数都是空的或返回NULL */
#define pmd_alloc_one(mm, address) NULL
static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
{
}
#define pmd_free_tlb(tlb, x, a) do { } while (0)
/* 用于计算给定地址范围内由实为PUD的PMD管理的结束地址 */
#undef pmd_addr_end
#define pmd_addr_end(addr, end) (end)
#endif /* __ASSEMBLY__ */
#endif /* _PGTABLE_NOPMD_H */