Linux memcg_wb_domain脏页写回domain与cgroup关联
Linux memcg_wb_domain脏页写回domain与cgroup关联memcg_wb_domain是memory cgroup与writeback(回写)子系统之间的关键桥梁。在cgroup v2环境下每个memory cgroup拥有独立的脏页写回domain这使得内核可以基于cgroup粒度控制脏页比例、回写阈值和回写速率从而实现每cgroup级别的内存写回隔离。wb_domain结构扩展内核在wb_domain中嵌入memcg特定的字段使得每个memory cgroup都能拥有独立的回写domain而非全局共享一个domain。cstruct wb_domain {spinlock_t lock;/** 脏页限制和阈值dirty_limit是cgroup允许的脏页上限* dirty_thresh和bg_thresh分别是同步回写和后台回写阈值*/unsigned long dirty_limit;unsigned long dirty_thresh;unsigned long bg_thresh;/** 回写带宽估算用于计算回写速率* 基于周期性的脏页产生量和回写完成量*/struct dirty_throttle_control dtc;struct bdi_writeback_congested *congested;/** memcg特有字段指向所属memory cgroup的指针*/struct mem_cgroup *memcg;struct cgroup_subsys_state *css;};memcg_wb_domain的创建与初始化当memory cgroup被创建时内核调用memcg_wb_domain_init为其分配并初始化独立的写回domain。cint memcg_wb_domain_init(struct mem_cgroup *memcg, gfp_t gfp){struct wb_domain *dom;dom kzalloc(sizeof(*dom), gfp);if (!dom)return -ENOMEM;spin_lock_init(dom-lock);dom-memcg memcg;dom-css memcg-css;/** 初始化dirty阈值计算所需的时间戳和速率估算*/dom-dtc.thresh 0;dom-dtc.dirty 0;dom-dtc.time 0;dom-dtc.rate 0;dom-dtc.bw_time 0;dom-dtc.bw 0;/** 注册到全局domain链表用于per-domain的* 回写线程调度和阈值计算*/spin_lock(wb_domain_list_lock);list_add(dom-list, wb_domains);spin_unlock(wb_domain_list_lock);memcg-wb_domain dom;return 0;}脏页阈值计算中的cgroup关联当全局脏页阈值计算发生时memcg_wb_domain参与计算每个cgroup的独立脏页限制。核心函数memcg_wb_domain_dirty_limits根据cgroup memory限制和脏页比例计算阈值。cvoid memcg_wb_domain_dirty_limits(struct mem_cgroup *memcg,unsigned long *dirty_thresh,unsigned long *bg_thresh){struct wb_domain *dom memcg-wb_domain;unsigned long memcg_limit;unsigned long dirty_ratio;unsigned long bg_ratio;if (!dom)return;/** 获取cgroup的内存上限。* 如果memory.max为max(无限制)则回退到系统全局阈值*/memcg_limit memcg_page_state(memcg, MEMCG_LIMIT);if (memcg_limit PAGE_COUNTER_MAX) {*dirty_thresh global_dirty_limit;*bg_thresh global_background_dirty_limit;return;}/** 根据vm.dirty_ratio和vm.dirty_background_ratio计算阈值* dirty_thresh min(memcg_limit * dirty_ratio / 100, global_dirty_limit)* 同时确保阈值不超过cgroup限制本身*/dirty_ratio vm_dirty_ratio;bg_ratio vm_dirty_background_ratio;*dirty_thresh memcg_limit * dirty_ratio / 100;*bg_thresh memcg_limit * bg_ratio / 100;/** 硬件限制阈值不能低于PAGE_SIZE的倍数* 也不能超过系统总的可回收页*/*dirty_thresh min(*dirty_thresh, global_dirty_limit);*bg_thresh min(*bg_thresh, global_background_dirty_limit);dom-dirty_thresh *dirty_thresh;dom-bg_thresh *bg_thresh;}page dirtier追踪与写回domain绑定每个脏页(通过set_page_dirty标记)需要被关联到其所属memcg的写回domain。内核通过page-mem_cgroup和page-mapping-host的inode信息确定页面归属。cvoid memcg_wb_domain_register_page(struct page *page){struct mem_cgroup *memcg;struct wb_domain *dom;struct inode *inode;if (!page-mapping || !page-mapping-host)return;inode page-mapping-host;if (!inode-i_sb-s_bdi)return;/** 获取产生脏页的memcg通过page_cgroup关联查找*/memcg page_mem_cgroup(page);if (!memcg || !memcg-wb_domain)return;dom memcg-wb_domain;/** 将inode的回写关联到cgroup特定的bdi_writeback* 确保回写线程在刷脏页时考虑cgroup的domain信息*/if (inode-i_wb ! memcg-wb_domain-bdi-wb) {inode_attach_wb(inode, dom-bdi-wb);}}memcg_wb_domain在回写流程中的调度作用当内核触发回写操作时(wb_workfn或周期性回写)writeback代码根据页面所属cgroup的domain信息检查是否达到该cgroup的脏页阈值。cbool memcg_wb_domain_should_writeback(struct mem_cgroup *memcg){struct wb_domain *dom memcg-wb_domain;unsigned long dirty;if (!dom)return false;/** 统计该cgroup的当前脏页数量*/dirty memcg_page_state(memcg, MEMCG_DIRTY);/** 如果脏页数超过后台阈值触发回写* 如果超过同步阈值则需要阻塞dirtier*/if (dirty dom-bg_thresh)return true;return false;}通过memcg_wb_domain机制每个memory cgroup拥有了独立的脏页管理能力。这意味着一个cgroup内的大量脏页产生不会影响其他cgroup的写回行为从而在容器化环境中实现了真正的I/O资源隔离。当cgroup被销毁时memcg_wb_domain_destroy负责回收domain并确保所有挂起的回写操作在cgroup移除前完成。