背景
DAMON 是内核的数据访问监控框架,基于它的 DAMOS 可以把监控结果变成动作,按规则对内存区域执行 reclaim、页迁移等操作。每个 scheme 可以携带 quota,限制单位时间内 action 作用的内存总量,避免优化动作本身拖累系统。
quota 手工很难调准,DAMOS 因此提供 aim-oriented 的 quota goal 自动调节。用户给定一个目标指标和它的 target_value,内核周期性采样指标的 current_value,与 target_value 比较后自动增减 quota,把指标推向目标。可选指标有 user_input、some_mem_psi_us、node_mem_used_bp、active_mem_bp 等,覆盖内存压力、节点利用率等常见调节目标。
quota goal 自动调节的反馈回路
┌──────────────────────────────────────┐
│ 每个调节周期采样目标指标的当前值 │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ 与 target value 比较得出差距 │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ 按差距调节 quota 的大小 │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ scheme 在 quota 约束下施加 action 并 │
│ 改变指标 │ 指标变化在下一周期被采样,回路闭合
└──────────────────────────────────────┘
这个回路能收敛有个隐含前提,指标要随 quota 正比变化。quota 调大,action 更激进,指标随之升高,反馈方向才正确。node_mem_used_bp 这类指标天然满足,利用率随迁移力度加大而上升。
反馈回路生效的前提是正比关系
┌────────────┐ ┌───────────────┐ ┌──────────────┐
│ quota 调大 ├──▶│ action 更激进 ├──▶│ 指标随之升高 │
└────────────┘ └───────────────┘ └──────────────┘
问题
- 自动调节要求指标随 quota 正比变化,反向目标须换成补值指标。
- 内存分层的 demotion 目标是 upper tier 空闲比例,与利用率互为补值。
- 每类补值场景新增一个指标会让 metric 数量无谓膨胀。
- hugepage_mem_bp 等指标已有可预见的补值需求,却均无补值版本。
方案
与其为每个反向场景新增指标,不如利用补值场景的规律,补值等于总量减原值。原值随 quota 反比变化时,补值恰好随 quota 正比变化,反馈方向被掰回来。patchset 给 struct damos_quota_goal 增加布尔字段 complement,置位后反馈回路拿到的 current_value 不再是指标原值,而是它的补值,调节目标随之反转。
换算分两处完成:some_mem_psi_us 的补值在采样时一并算出,其余指标集中在取值路径 damos_set_quota_goal_current_value() 里。取值路径对 user_input 与 some_mem_psi_us 都直接返回,前者由用户自己写补值,后者已在采样时换算过。
complement 置位后的取值换算
┌─────────────────────────────────────┐
│ 按 metric 类型采样指标原始值 │
└──────────────────┬──────────────────┘
▼
┌─────────────────────────────────────┐ complement 未置位时跳过换算,直接使用原
│ 用总量减去原始值得到补值 │ 始值
└──────────────────┬──────────────────┘
▼
┌─────────────────────────────────────┐
│ 换算结果写入 current value 参与调节 │
└─────────────────────────────────────┘
不同指标的总量含义不同,换算分三类。万分比指标的总量是 10000,原值小于 10000 时补值为二者之差,原值异常达到 10000 及以上时补值取零,避免无符号减法下溢。some_mem_psi_us 以微秒为单位,总量是 quota 重置周期换算出的微秒数,同样在差值达到或超过总量时把补值取零;首次采样时 last_psi_total 尚未初始化,差值无从算起,这一轮不更新取值也不做换算,不会把无效状态误当成补值。user_input 的语义由用户自定义,内核无从得知总量,置位后由用户自己写入补值,内核不做换算。
val = now_psi_total - last_psi_total;
if (goal->complement) {
if (val < s->quota.reset_interval * 1000)
val = s->quota.reset_interval * 1000 - val;
else
val = 0;
}
goal->current_value = val;
if (!goal->complement)
return;
if (goal->metric == DAMOS_QUOTA_USER_INPUT)
return;
if (goal->metric == DAMOS_QUOTA_SOME_MEM_PSI_US)
return;
if (goal->current_value < 10000)
goal->current_value = 10000 - goal->current_value;
else
goal->current_value = 0;
以内存分层为例看实际效果。提升 upper tier 利用率的经典做法 TPP(transparent page placement)使用两个 scheme,promotion scheme 把热页迁往 upper tier,目标是拉高利用率,demotion scheme 把冷页迁往 lower tier,目标是留出空闲。空闲比例正是利用率的补值,过去内核要为此同时提供 node_mem_used_bp 与 node_mem_free_bp 两个指标,现在 demotion scheme 复用利用率指标并置上 complement,效果等同空闲比例指标,active_mem_bp 置位后亦等效 inactive_mem_bp。
同一指标服务方向相反的两个 scheme
┌───────────────────────────┐
│ upper tier 内存利用率指标 │
└─────────────┬─────────────┘
┌────────────────────┴────────────────────┐
▼ ▼
┌───────────────────────────────────┐ ┌──────────────────────────────────┐
│ promotion scheme 用原值调节,激进 │ │ demotion scheme 置 complement 取 │
│ 迁移推高利用率 │ │ 补值,等效空闲比例指标 │
└───────────────────────────────────┘ └──────────────────────────────────┘
标志的入口有两处。内核代码经 damos_new_quota_goal() 创建 goal,构造函数新增 complement 参数,创建时一次说清,避免调用方手工赋值遗漏,reclaim、lru_sort 等现有调用方全部传 false,行为照旧。用户空间经 sysfs 配置,goal 目录下新增 complement 文件,写入 Y 或 N 即可开关,commit 时标志随 metric 与 target_value 一并同步进生效配置。
收益
作者未提供性能数据,从代码逻辑推断的预期收益:
- 反向调节场景直接复用现有指标,metric 数量不再随用例膨胀。
- 内存分层的双向调节共享同一指标,两个 scheme 配置语义统一。
- 未置位时取值逻辑与之前完全一致,现有配置不受影响。