ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

page_alloc __set_pfnblock_flags_mask

page_alloc __set_pfnblock_flags_mask __get_pfnblock_flags_mask()的写入对应物负责设置某个 pageblock 的一组属性位。一、函数签名static void __set_pfnblock_flags_mask(struct page *page, unsigned long pfn, unsigned long flags, unsigned long mask)参数参数类型含义pagestruct page *目标 pageblock 内的某一页用于定位 zone/位图pfnunsigned long目标页帧号flagsunsigned long要写入的位值已对齐到目标字段的低位maskunsigned long要更新哪些位的掩码只改这些位其余保持不变返回值void无返回值。static文件内私有不导出。对外由set_pfnblock_flags_mask()包装。与读取函数对比/* 读给 mask返回按 mask 过滤后的 flags */ static unsigned long __get_pfnblock_flags_mask(const struct page *page, unsigned long pfn, unsigned long mask); /* 写给 flags 和 mask把 mask 指定的位更新为 flags 对应值 */ static void __set_pfnblock_flags_mask(struct page *page, unsigned long pfn, unsigned long flags, unsigned long mask);读page, pfn, mask → flags写page, pfn, flags, mask → void二者参数对称mask 都是关心的位。二、典型实现static void __set_pfnblock_flags_mask(struct page *page, unsigned long pfn, unsigned long flags, unsigned long mask) { unsigned long *bitmap; unsigned long bitidx; unsigned long word; unsigned long current_flags; bitmap get_pageblock_bitmap(page, pfn); bitidx pfn_to_bitidx(page, pfn); /* * 读改写把该 pageblock 的位读出来 * 用 mask 保留旧值的其他位用 flags 填入新值。 * * 注意pageblock 的位可能跨 unsigned long 字 * 所以实现上要处理跨字情况或保证不跨字。 */ word bitmap[bitidx / BITS_PER_LONG]; current_flags (word (bitidx % BITS_PER_LONG)) ((1UL NR_PAGEBLOCK_BITS) - 1); current_flags ~mask; /* 清掉要改的位 */ current_flags | (flags mask); /* 填入新值 */ /* 写回…… */ }实际内核实现通常用get_pfnblock_bitmap_bitidx()拿到字指针 字内偏移再做读改写避免重复计算。示意static void __set_pfnblock_flags_mask(struct page *page, unsigned long pfn, unsigned long flags, unsigned long mask) { unsigned long *word; unsigned long bit; unsigned long old, new; get_pfnblock_bitmap_bitidx(page, pfn, word, bit); /* * 该 pageblock 的 NR_PAGEBLOCK_BITS 位位于 *word 的 bit 开始处。 * 读出、改、写回。 */ old (*word bit) ((1UL NR_PAGEBLOCK_BITS) - 1); new (old ~mask) | (flags mask); *word (*word ~(((1UL NR_PAGEBLOCK_BITS) - 1) bit)) | (new bit); }核心语义读-改-写read-modify-write这是关键新值 (旧值 ~mask) | (flags mask) ^^^^^^^^^^^ ^^^^^^^^^^^^^^^ 保留 mask 外的位 只更新 mask 内的位~maskmask 之外的位保留旧值flags mask只取 flags 中 mask 覆盖的位两者或起来得到更新后的值。这样只改 mask 指定的位不动其他位——这正是mask参数的意义。跨字问题一个 pageblock 占NR_PAGEBLOCK_BITS位4 或 8。BITS_PER_LONG是 32 或 64。若 pageblock 起始位bit使得bit NR_PAGEBLOCK_BITS BITS_PER_LONG则跨字。实际内核中pageblock 起始位通常对齐到 NR_PAGEBLOCK_BITS 的倍数且NR_PAGEBLOCK_BITS能整除BITS_PER_LONG4 整除 32/648 整除 32/64所以不会跨字。BUILD_BUG_ON前面get_pfnblock_bitmap_bitidx里的间接保证前提。因此实现可以放心地在一个字内完成读改写。三、与读取函数的对称性方面__get_pfnblock_flags_mask__set_pfnblock_flags_mask方向读写参数page, pfn, maskpage, pfn, flags, mask返回unsigned longvoidmask 含义只返回这些位只更新这些位底层依赖get_pageblock_bitmap/pfn_to_bitidx同上或get_pfnblock_bitmap_bitidx操作读 mask读-改-写两者是读/写配对mask 语义一致都表示调用方关心的位集合。四、典型上层封装1. 设置迁移类型void set_pfnblock_migratetype(struct page *page, enum migratetype migratetype, unsigned long pfn) { unsigned long flags; flags ((unsigned long)migratetype PB_migrate) PB_migrate_mask; __set_pfnblock_flags_mask(page, pfn, flags, PB_migrate_mask); }把migratetype编码到PB_migrate字段的位置用PB_migrate_mask指定只改迁移类型字段其他属性位如PB_migrate_skip保持不变。2. 设置独立位如PB_migrate_skipvoid set_pfnblock_bit(struct page *page, unsigned long pfn, enum pageblock_bits pb_bit) { VM_BUG_ON_PAGE(!is_standalone_pb_bit(pb_bit), page); __set_pfnblock_flags_mask(page, pfn, 1UL pb_bit, 1UL pb_bit); }flags 1UL pb_bit要置 1mask 1UL pb_bit只改这一位。3. 清除独立位void clear_pfnblock_bit(struct page *page, unsigned long pfn, enum pageblock_bits pb_bit) { VM_BUG_ON_PAGE(!is_standalone_pb_bit(pb_bit), page); __set_pfnblock_flags_mask(page, pfn, 0, 1UL pb_bit); }flags 0要置 0mask 1UL pb_bit只改这一位。4. 批量更新多个位/* 同时更新迁移类型和 skip 位 */ __set_pfnblock_flags_mask(page, pfn, (migratetype PB_migrate) | (skip PB_migrate_skip), PB_migrate_mask | (1UL PB_migrate_skip));五、并发与原子性这里有个重要问题__set_pfnblock_flags_mask()是非原子的读-改-写吗在多 CPU 并发修改同一个 pageblock 的不同位时非原子的读改写可能丢失更新lost update。内核的实际处理大多数 pageblock 属性修改是在持锁的情况下进行的如zone-lock、pgdat-node_size_lock等或者修改的是不同 pageblock不冲突某些场景如 compaction 标记 skip 位可能有额外的同步机制。所以这个函数本身不保证原子性正确性依赖调用方的同步。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进