ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

page_alloc get_pageblock_bitmap

page_alloc get_pageblock_bitmap 涉及pageblock 标志位pageblock flags的存储位置计算和位索引计算以及一个判断独立位的辅助函数。它们是set_pageblock_migratetype()/get_pageblock_migratetype()等页块属性操作的底层基础。一、背景pageblock flags 是什么pageblock是伙伴系统之上、用于反碎片的页块粒度大小 2^pageblock_order页。每个 pageblock 有一些属性位enum pageblock_bits例如位含义PB_migrate~PB_migrate_end迁移类型用几位编码 MIGRATE_*PB_migrate_skip是否跳过规整其他__NR_PAGEBLOCK_BITS之内各种 pageblock 级标记这些位需要按 pageblock 存储。怎么存、存在哪就是get_pageblock_bitmap()和pfn_to_bitidx()要解决的。二、get_pageblock_bitmap()—— 找到存储位图的位图数组static inline unsigned long *get_pageblock_bitmap(const struct page *page, unsigned long pfn) { #ifdef CONFIG_SPARSEMEM return section_to_usemap(__pfn_to_section(pfn)); #else return page_zone(page)-pageblock_flags; #endif /* CONFIG_SPARSEMEM */ }作用返回存储影响某个 pageblock 的位的位图数组unsigned long *。两种内存模型两种存法1CONFIG_SPARSEMEM稀疏内存模型return section_to_usemap(__pfn_to_section(pfn));SPARSEMEM 把物理内存按section段组织每个 section 有struct mem_section。每个 section 关联一个usemapsection_to_usemap()取到用来存该 section 内的 pageblock flags。位图随 section 走__pfn_to_section(pfn)找到 pfn 所属 section再取它的 usemap。好处内存热插拔、稀疏内存下位图跟着 section 分配/释放管理灵活。典型在 64 位、支持热插拔的系统上使用。2非 SPARSEMEM如 FLATMEMreturn page_zone(page)-pageblock_flags;每个zone有一个pageblock_flags位图覆盖该 zone 的所有 pageblock。位图按 zone 存简单直接。典型在单节点、内存连续的嵌入式/小系统上使用。小结内存模型位图存储位置查找方式CONFIG_SPARSEMEM每个 section 的 usemapsection_to_usemap(__pfn_to_section(pfn))非 SPARSEMEM每个 zone 的pageblock_flagspage_zone(page)-pageblock_flags三、pfn_to_bitidx()—— 算出该 pageblock 在位图中的起始位索引static inline int pfn_to_bitidx(const struct page *page, unsigned long pfn) { #ifdef CONFIG_SPARSEMEM pfn (PAGES_PER_SECTION-1); #else pfn pfn - pageblock_start_pfn(page_zone(page)-zone_start_pfn); #endif /* CONFIG_SPARSEMEM */ return (pfn pageblock_order) * NR_PAGEBLOCK_BITS; }作用给定 pfn返回它在get_pageblock_bitmap()返回的位图中对应 pageblock 的起始位索引int。注意返回值是pageblock 的基址位索引即这个 pageblock 的NR_PAGEBLOCK_BITS个位从这里开始。具体访问哪个属性位还要加上pb_bit。分两步第一步把 pfn 归一化到位图覆盖范围的起点SPARSEMEMpfn (PAGES_PER_SECTION-1);PAGES_PER_SECTION是每个 section 的页数2 的幂。按位与(PAGES_PER_SECTION-1)等价于pfn % PAGES_PER_SECTION即pfn 在 section 内的偏移。因为位图是按 section 存的索引应相对 section 起点。非 SPARSEMEMpfn pfn - pageblock_start_pfn(page_zone(page)-zone_start_pfn);位图是按 zone 存的索引应相对zone 起点。page_zone(page)-zone_start_pfn是 zone 起始 pfn。pageblock_start_pfn(zone_start_pfn)把 zone 起点对齐到 pageblock 边界防止 zone 起点不在 pageblock 边界时算错。减去它得到pfn 相对 zone对齐后起点的偏移。第二步换算成位索引return (pfn pageblock_order) * NR_PAGEBLOCK_BITS;pfn pageblock_orderpfn / 2^pageblock_order这是第几个 pageblock相对归一化起点。乘以NR_PAGEBLOCK_BITS每个 pageblock 占NR_PAGEBLOCK_BITS个位。得到该 pageblock 在位图中的起始位索引。举例假设pageblock_order 9512 页/块NR_PAGEBLOCK_BITS 4pfn 在 section/zone 内偏移 1024 →1024 9 2→ 第 2 个 pageblock起始位索引 2 * 4 8。于是该 pageblock 的属性位在位图中是第 8、9、10、11 位。与get_pageblock_bitmap()的配合bitmap get_pageblock_bitmap(page, pfn); bitidx pfn_to_bitidx(page, pfn); /* 访问某个属性位 pb_bit */ test_bit(bitidx pb_bit, bitmap); set_bit(bitidx pb_bit, bitmap);bitmap定位到哪个位图数组bitidx定位到数组内的起始位 pb_bit定位到具体属性位。四、is_standalone_pb_bit()—— 判断是否为独立位static __always_inline bool is_standalone_pb_bit(enum pageblock_bits pb_bit) { return pb_bit PB_migrate_end pb_bit __NR_PAGEBLOCK_BITS; }背景pageblock bits 的分类enum pageblock_bits里的位分两类迁移类型字段PB_migrate到PB_migrate_end之间的若干位合起来编码一个迁移类型值。例如用 3 位编码 8 种迁移类型。这些位不能单独操作必须整体读写。独立位PB_migrate_end之后、__NR_PAGEBLOCK_BITS之前的位每一位独立表示一个布尔属性。例如PB_migrate_skip是否跳过规整。可以单独test_bit/set_bit。函数语义return pb_bit PB_migrate_end pb_bit __NR_PAGEBLOCK_BITS;pb_bit PB_migrate_end在迁移类型字段之后pb_bit __NR_PAGEBLOCK_BITS在有效位范围内两者都满足 → 是独立位。用途在set_pageblock_bit()/clear_pageblock_bit()/test_pageblock_bit()之类的通用函数里static inline void set_pageblock_bit(struct page *page, unsigned long pfn, enum pageblock_bits pb_bit) { if (is_standalone_pb_bit(pb_bit)) { /* 可以单独 set_bit */ set_bit(bitidx pb_bit, bitmap); } else { /* 属于迁移类型字段必须整体操作不能单独置位 */ WARN_ON_ONCE(1); // 或忽略 } }防止调用方错误地单独操作迁移类型字段中的某一位破坏迁移类型编码。__always_inline强制内联保证热路径零函数调用开销。边界PB_migrate_end本身不算独立位而非因为它是迁移字段的结束边界。__NR_PAGEBLOCK_BITS本身不算它是总数不是有效索引。五、三者的协作关系操作某个 pageblock 的某属性位 pb_bit 1. bitmap get_pageblock_bitmap(page, pfn) // 定位位图数组 2. bitidx pfn_to_bitidx(page, pfn) // 定位 pageblock 起始位 3. if (is_standalone_pb_bit(pb_bit)) // 判断能否单独操作 set_bit(bitidx pb_bit, bitmap) else /* 整体读改写迁移类型字段 */六、与前面内容的呼应前面出现过的这里的关联pageblock_orderpfn pageblock_order用它算第几个 pageblock迁移类型 /migratetype_names[]PB_migrate字段编码迁移类型set_pageblock_migratetype()底层用这三个函数定位和操作位反碎片 / compaction依赖 pageblock flags如PB_migrate_skipCONFIG_SPARSEMEM决定位图是按 section 还是按 zone 存七、小结表函数作用get_pageblock_bitmap(page, pfn)返回存储 pageblock flags 的位图数组SPARSEMEM 下按 section 取 usemap否则用 zone 的pageblock_flagspfn_to_bitidx(page, pfn)算出 pfn 对应 pageblock 在位图中的起始位索引先归一化到 section/zone 内偏移再乘NR_PAGEBLOCK_BITSis_standalone_pb_bit(pb_bit)判断某属性位是否为独立位在迁移类型字段之外可否单独操作__always_inline强制内联热路径零开销一句话总结get_pageblock_bitmap()和pfn_to_bitidx()共同定位某个 pfn 所在 pageblock 的属性位在哪种位图、哪个位索引上SPARSEMEM 按 section、否则按 zoneis_standalone_pb_bit()则区分可单独操作的独立位与必须整体读写的迁移类型字段防止错误地单独改动迁移类型编码——三者是 pageblock 属性操作尤其是迁移类型设置的底层基础。
返回列表