内存管理-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: 脏页面。

 

PG_active: 对于匿名页缓存,内核维护了两个链表,一个是最近常使用的 active-list 另一个是最近不常使用的 inactive-list。PG_active 标志位决定 page 在哪个链表,在 active-list 中的 pages 的 PG_active 都为 1,在 inactive-list 中的 pages 的 PG_active 都为 0。


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: 如果是页面缓存,则有 fs-private 数据.
如果 pagecache 页面包含文件系统特定数据(通常位于 page->private),则在页面缓存页面上设置 PG_private 位标志。私有分配可将其用于自己的用途。


PG_private_2: 如果是页面缓存,则有 fs 辅助数据.


PG_writeback: 页面正在写回.


PG_head: 首页.


PG_mappedtodisk: 已在磁盘上分配块.


PG_reclaim: 尽快回收.


PG_swapbacked: 页面由 RAM/swap 支持.


PG_unevictable: 页面unevictable“不可驱逐”.


PG_mlocked: 页面已 vma mlocked.


PG_uncached: 页面已被映射为未缓存.


PG_hwpoison: 硬件荼毒的页面,请勿触摸。指示页面在硬件中已损坏,并且包含具有错误 ECC 位的数据,从而触发了机器检查。访问并不安全,因为它可能会导致再一次机器检查。不要碰!

 

下面是别名:

PG_checked: 文件系统。
PG_swapcache: 基于swap的。
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  阅读(11)  评论(0编辑  收藏  举报

导航