内存管理-38-页标志-1-pageflags
基于msm-5.4
一、相关结构
1. enum pageflags
enum pageflags { //include/linux/page-flags.h PG_locked, PG_referenced, PG_uptodate, PG_dirty, PG_lru, PG_active, PG_workingset, PG_waiters, PG_error, PG_slab, PG_owner_priv_1, PG_arch_1, PG_reserved, PG_private, PG_private_2, PG_writeback, PG_head, PG_mappedtodisk, PG_reclaim, PG_swapbacked, PG_unevictable, PG_mlocked, #ifdef CONFIG_ARCH_USES_PG_UNCACHED //默认不使能 PG_uncached, #endif #ifdef CONFIG_MEMORY_FAILURE //默认不使能 PG_hwpoison, #endif #if defined(CONFIG_IDLE_PAGE_TRACKING) && defined(CONFIG_64BIT) //前者默认不使能 PG_young, PG_idle, #endif __NR_PAGEFLAGS, //到这就结束了,后面是别名 /* Filesystems */ PG_checked = PG_owner_priv_1, /* SwapBacked */ PG_swapcache = PG_owner_priv_1, PG_fscache = PG_private_2, /* XEN */ PG_pinned = PG_owner_priv_1, PG_savepinned = PG_dirty, PG_foreign = PG_owner_priv_1, PG_xen_remapped = PG_owner_priv_1, /* SLOB */ PG_slob_free = PG_private, /* Compound pages. Stored in first tail page's flags */ PG_double_map = PG_private_2, /* non-lru isolated movable page */ PG_isolated = PG_reclaim, };
本结构列出 page->flags 中的各种标志位.
成员解释:
PG_locked: 页面已锁定,请勿触摸。
在启动磁盘 I/O 期间,设置 PG_locked。此位在 I/O 之前设置,并在回写开始或读取完成时清除。PG_writeback 在回写开始之前设置,并在回写完成时清除。
PG_locked 还会将页面固定在页面缓存中,并阻止文件截断。
page_waitqueue(page) 是所有等待页面解锁的任务的等待队列。
PG_referenced/PG_reclaim: 用于匿名的和文件支持的页面缓存的页面回收(请参阅 mm/vmscan.c)。
PG_referenced 标志位表明 page 最近是否被访问过(只有文件页使用)。对于 mmap 的页面,需要在回收扫描时,通过 reverse mapping 检查 PTE 的 Access 位来判断,而对于通过页面在文件的 offset 来作为索引的(比如 read/write),则可以在 page 被访问后,立即使用 mark_page_accessed() 进行 PG_referenced 的标记。
PG_uptodate: 指示页面的内容是否有效。读取完成后,页面将变为最新,除非发生磁盘 I/O 错误。
PG_dirty: 页为脏页,文件页被修改,以及非文件页加入到swap cache后,就会被标记为脏页。在此页回写前会被清除,但是回写失败时又会被置位。
PG_lru:表示页在lru链表中。
PG_active: 对于匿名页缓存,内核维护了两个链表,一个是最近常使用的 active-list 另一个是最近不常使用的 inactive-list。PG_active 标志位决定 page 在哪个链表,在 active-list 中的 pages 的 PG_active 都为 1,在 inactive-list 中的 pages 的 PG_active 都为 0。配合 PG_lru 就可以得出页是处于非活动页lru链表还是活动页lru链表。
PG_waiters: 页面有等待者,请检查其等待队列。必须是 bit#7,并且与“PG_locked”位于同一字节
PG_error: 指示此页面上发生了 I/O 错误。
PG_owner_priv_1: 所有者使用,如果是页面缓存,则 fs 可以使用。
PG_arch_1: 是特定于体系结构的页面状态位。通用代码保证在页面首次进入页面缓存时清除此位。
PG_reserved: 是为特殊页面设置的。此类页面的“struct page”通常除了其所有者外不应被触碰(例如,设置为 dirty)。标记为 PG_reserved 的页面包括:
(1) 内核映像(包括 vDSO)和类似内容(例如 BIOS、initrd、HW tables)的页面。
(2) 在启动早期(在页面分配器初始化之前)保留或分配的页面。这包括(取决于架构)初始 vmemmap、初始页表、crashkernel、elfcorehdr 等等。一旦(如果有的话)释放,PG_reserved 将被清除,并将它们提供给页面分配器。
(3) 页面落入物理内存间隙 - 不是 IORESOURCE_SYSRAM。尝试读取/写入这些页面可能会导致糟糕的结果,不要碰!
(4) 零页
(5) 在online section时未添加到页面分配器的页面,因为它们已通过 online_page_callback() 排除,或者因为它们是 PG_hwpoison。
(6) 在 kexec/kdump 上下文中分配的页面(已加载的内核映像、控制页面、vmcoreinfo)
(7) MMIO/DMA 页面,某些架构不允许对未标记为 PG_reserved 的页面进行 ioremap(因为它们可能被不遵守缓存策略的其他人使用)。
(8) 离线section的页面(离线部分的结构页面不应被信任,因为它们将在首次联机时初始化)。
(9) ia64 上的 MCA 页面
(10) 保存 POWER 固件辅助转储的 CPU 注释的页面
(11)设备内存(例如 PMEM、DAX、HMM)
一些 PG_reserved 页面将被排除在休眠映像之外。PG_reserved 通常不会妨碍任何人转储或交换,并且不再需要 remap_pfn_range()。ioremap 可能需要它。因此,映射到用户空间的页面的 PG_reserved 可以指示零页面、vDSO、MMIO 页面或设备内存。
PG_private: 页描述符中的 page->private 保存有数据。如果是页面缓存,则有 fs-private 数据。如果 pagecache 页面包含文件系统特定数据(通常位于 page->private),则在页面缓存页面上设置 PG_private 位标志。私有分配可将其用于自己的用途。
PG_private_2: 如果是页面缓存,则有 fs 辅助数据.
PG_writeback: 页面正在进行回写。
PG_head: 首页.
PG_mappedtodisk: 已在磁盘上分配块.
PG_reclaim: 尽快回收; 页正在进行回收,只有在内存回收时才会对需要回收的页进行此标记。
PG_swapbacked: 页面由 RAM/swap 支持。此页可写入swap分区,一般用于表示此页是非文件页。
PG_unevictable: 页面unevictable“不可驱逐”.
PG_mlocked: 页面已 vma mlocked。页被锁在内存中。
PG_uncached: 页面已被映射为未缓存.
PG_hwpoison: 硬件荼毒的页面,请勿触摸。指示页面在硬件中已损坏,并且包含具有错误 ECC 位的数据,从而触发了机器检查。访问并不安全,因为它可能会导致再一次机器检查。不要碰!
下面是别名:
PG_checked: 文件系统。
PG_swapcache: 基于swap的。页已经加入到了swap cache中(只有非文件页使用)。
PG_fscache: 缓存支持的页面。FS-Cache 占用两个页面位来维护本地缓存状态。当这些 inode 正在本地缓存时,这些位会在属于 netfs 的 inode 的页面上设置。
PG_pinned: 在 Xen 中固定为只读分页表页面。
PG_savepinned: 固定作为域保存的一部分(参见 xen_mm_pin_all())。
PG_foreign: 具有另一个(外部)域的页面的授权映射。
PG_xen_remapped: 由 swiotlb-xen 重新映射。
2. 其它宏
//include/linux/page-flags.h #define PG_buddy 0x00000080 #define PG_offline 0x00000100 #define PG_kmemcg 0x00000200 #define PG_table 0x00000400 #define PG_guard 0x00000800
二、相关操作宏
1. 基础宏
//PG_##uname() 函数 #define TESTPAGEFLAG(uname, lname, policy) \ static __always_inline int Page##uname(struct page *page) \ { return test_bit(PG_##lname, &policy(page, 0)->flags); } //SetPage##uname() 函数 #define SETPAGEFLAG(uname, lname, policy) \ static __always_inline void SetPage##uname(struct page *page) \ { set_bit(PG_##lname, &policy(page, 1)->flags); } //ClearPage##uname() 函数 #define CLEARPAGEFLAG(uname, lname, policy) \ static __always_inline void ClearPage##uname(struct page *page) \ { clear_bit(PG_##lname, &policy(page, 1)->flags); } //__SetPage##uname 函数 #define __SETPAGEFLAG(uname, lname, policy) \ static __always_inline void __SetPage##uname(struct page *page) \ { __set_bit(PG_##lname, &policy(page, 1)->flags); } //__ClearPage##uname 函数 #define __CLEARPAGEFLAG(uname, lname, policy) \ static __always_inline void __ClearPage##uname(struct page *page) \ { __clear_bit(PG_##lname, &policy(page, 1)->flags); }
SETPAGEFLAG 宏与 __SETPAGEFLAG 宏的区别是,前者调用的是 set_bit() 原子地进行设置,后者调用的是 __set_bit() 非原子地进行设置。
2. 测试并操作宏
//TestSetPage##uname() 测试并设置 #define TESTSETFLAG(uname, lname, policy) \ static __always_inline int TestSetPage##uname(struct page *page) \ { return test_and_set_bit(PG_##lname, &policy(page, 1)->flags); } //TestClearPage##uname() 测试并清除 #define TESTCLEARFLAG(uname, lname, policy) \ static __always_inline int TestClearPage##uname(struct page *page) \ { return test_and_clear_bit(PG_##lname, &policy(page, 1)->flags); }
使用的是非原子的版本。
3. 组合使用宏
/* 同时定义 PG_##uname() SetPage##uname() ClearPage##uname() 函数, 使用原子版本 */ #define PAGEFLAG(uname, lname, policy) \ TESTPAGEFLAG(uname, lname, policy) \ SETPAGEFLAG(uname, lname, policy) \ CLEARPAGEFLAG(uname, lname, policy) /* 同时定义 PG_##uname() __SetPage##uname() __ClearPage##uname() 函数, 使用非原子版本 */ #define __PAGEFLAG(uname, lname, policy) \ TESTPAGEFLAG(uname, lname, policy) \ __SETPAGEFLAG(uname, lname, policy) \ __CLEARPAGEFLAG(uname, lname, policy) /* 同时定义 TestSetPage##uname() TestClearPage##uname() 函数 */ #define TESTSCFLAG(uname, lname, policy) \ TESTSETFLAG(uname, lname, policy) \ TESTCLEARFLAG(uname, lname, policy)
4. 上面提到的policy有
4.1 PF_POISONED_CHECK 类
#define PF_POISONED_CHECK(page) ({ VM_BUG_ON_PGFLAGS(PagePoisoned(page), page); page; }) #define PF_ANY(page, enforce) PF_POISONED_CHECK(page) #define PF_HEAD(page, enforce) PF_POISONED_CHECK(compound_head(page))
检查此结构页面是否中毒/未初始化。若使能了 CONFIG_DEBUG_VM_PGFLAGS ,页面若被标记为“中毒”(PagePoisoned),则进行断言触发内核BUG()死机。若没使能此宏则可忽略。
4.2 PF_ONLY_HEAD
#define PF_ONLY_HEAD(page, enforce) ({ \ VM_BUG_ON_PGFLAGS(PageTail(page), page); \ PF_POISONED_CHECK(page); })
若使能 CONFIG_DEBUG_VM_PGFLAGS 多了一个若page是尾页则触发BUG(),从 compound_head()/PageTail() 的实现上看,复合页面只有首页是head,其它页都是tail。对于复合页面,调用者只能在头页面上操作。
4.3 PF_NO_TAIL
#define PF_NO_TAIL(page, enforce) ({ \ VM_BUG_ON_PGFLAGS(enforce && PageTail(page), page); \ PF_POISONED_CHECK(compound_head(page)); })
若使能 CONFIG_DEBUG_VM_PGFLAGS 多了一个若 enforce 为真且 page是尾页则触发BUG()。断言必须在小页面或头页面上修改页面标志。
4.4 PF_NO_COMPOUND
#define PF_NO_COMPOUND(page, enforce) ({ \ VM_BUG_ON_PGFLAGS(enforce && PageCompound(page), page); \ PF_POISONED_CHECK(page); })
若使能 CONFIG_DEBUG_VM_PGFLAGS 多了一个若 enforce 为真且 page 是复合页则触发BUG()。断言要求页面标志与复合页面无关。
posted on 2024-11-05 15:33 Hello-World3 阅读(26) 评论(0) 编辑 收藏 举报