OOM キラーの "Memory cgroup out of memory: OOM victim *** (***) is already exiting. Skip killing the task" を調べた

業務中に Linux の OOM キラーの dmesg を見ていたら、見慣れない形式のログがあったので調べてみた

ログ

こんなログです

May 23 02:06:26 **** kernel: [40872.663481] Memory cgroup out of memory: OOM victim 46452 (nginx) is already exiting. Skip killing the task

〜 is already exiting. Skip killing the task というメッセージは見たことがなかったので調べました。

ソースを調べる

該当のメッセージを grep するとすぐに見つかりました。__oom_kill_process() で出力しています。

static void __oom_kill_process(struct task_struct *victim, const char *message)
{
    struct task_struct *p;
    struct mm_struct *mm;
    bool can_oom_reap = true;

    p = find_lock_task_mm(victim);
    if (!p) {
        pr_info("%s: OOM victim %d (%s) is already exiting. Skip killing the task\n",
            message, task_pid_nr(victim), victim->comm);
        put_task_struct(victim);
        return;
    } else if (victim != p) {
        get_task_struct(p);
        put_task_struct(victim);
        victim = p;
    }


... 略

OOM でプロセスを止める際、task_struct -> mm_struct が参照できない場合に出力されるログのようですね。プロセスが止まる処理と OOM キラーで止めるタイミングがかぶると出るんだろう。

コミットを探す

git blame ですぐに見つかりました。コミットで追加されている。

github.com

5.9 で入ったのかな

コミットログは下記のとおりです

When the OOM killer finds a victim and tryies to kill it, if the victim is already exiting, the task mm will be NULL and no process will be killed. But the dump_header() has been already executed, so it will be strange to dump so much information without killing a process. We'd better show some helpful information to indicate why this happens.

翻訳しておきます

OOMキラーが犠牲者を見つけ、それを殺そうとするとき、犠牲者がすでに終了していれば、タスクmmはNULLになり、プロセスは殺されない。 しかし、dump_header()はすでに実行されているので、プロセスを殺さずに多くの情報をダンプするのは奇妙である。 なぜこのようなことが起こるのかを示すために なぜこのようなことが起こるのかを示すために、有益な情報を示す必要がある。

DeepL での翻訳

なるほどなぁ。

dump_header()

下記のような実装です

static void dump_header(struct oom_control *oc, struct task_struct *p)
{
    pr_warn("%s invoked oom-killer: gfp_mask=%#x(%pGg), order=%d, oom_score_adj=%hd\n",
        current->comm, oc->gfp_mask, &oc->gfp_mask, oc->order,
            current->signal->oom_score_adj);
    if (!IS_ENABLED(CONFIG_COMPACTION) && oc->order)
        pr_warn("COMPACTION is disabled!!!\n");

    dump_stack();
    if (is_memcg_oom(oc))
        mem_cgroup_print_oom_meminfo(oc->memcg);
    else {
        __show_mem(SHOW_MEM_FILTER_NODES, oc->nodemask, gfp_zone(oc->gfp_mask));
        if (should_dump_unreclaim_slab())
            dump_unreclaimable_slab();
    }
    if (sysctl_oom_dump_tasks)
        dump_tasks(oc);
    if (p)
        dump_oom_summary(oc, p);
}

おそらくは、dmesg に invoked oom-killer のログが出ていたが、その後にプロセスの詳細が出ていなくて混乱を招いたケースがあったのでしょうね。