背景

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 配置语义统一。
  • 未置位时取值逻辑与之前完全一致,现有配置不受影响。