[Devel] [PATCH VZ10 6/6] cgroup-v2/freezer: Print information about unfreezable process
Konstantin Khorenko
khorenko at virtuozzo.com
Wed Nov 19 00:21:34 MSK 2025
LGTM, a small remark below.
On 11/14/25 11:59, Pavel Tikhomirov wrote:
> We already had this feature in cgroup-v1 freezer, so let's also have it
> for cgroup-v2. Difference in cgroup-v2 freezer is that there is no loop
> over tasks like in freezer_read() -> update_if_frozen(),
> cgroup_events_show() only checks CGRP_FROZEN flag, so we have to
> add our own loop over each descendant css and each task in it.
>
> Note: freeze_jiffies is protected with css_set_lock together with
> CGRP_FREEZE bit.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-118578
> Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
>
> Feature: cgroup/freeze: enhance logging
> ---
> include/linux/cgroup-defs.h | 5 ++
> include/linux/cgroup.h | 3 ++
> kernel/cgroup/cgroup.c | 97 ++++++++++++++++++++++++++++++++++
> kernel/cgroup/freezer.c | 8 ++-
> kernel/cgroup/legacy_freezer.c | 36 +------------
> 5 files changed, 112 insertions(+), 37 deletions(-)
>
> diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
> index d68fca31e6e65..85a918ae4c5a9 100644
> --- a/include/linux/cgroup-defs.h
> +++ b/include/linux/cgroup-defs.h
> @@ -424,6 +424,11 @@ struct cgroup_freezer_state {
> * Number of kernel threads in this cgroup
> */
> int nr_kthreads;
> +
> + /*
> + * Jiffies of last freeze
> + */
> + unsigned long freeze_jiffies;
> };
>
> struct cgroup {
> diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
> index 86ee6604a5207..2e01e484cbac7 100644
> --- a/include/linux/cgroup.h
> +++ b/include/linux/cgroup.h
> @@ -876,4 +876,7 @@ struct cgroup *task_get_cgroup1(struct task_struct *tsk, int hierarchy_id);
>
> struct cgroup_of_peak *of_peak(struct kernfs_open_file *of);
>
> +void warn_freeze_timeout_task(struct cgroup *cgrp, int timeout,
> + struct task_struct *task);
> +
> #endif /* _LINUX_CGROUP_H */
> diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
> index 2f7db20f8a460..6df9ab9944036 100644
> --- a/kernel/cgroup/cgroup.c
> +++ b/kernel/cgroup/cgroup.c
> @@ -61,6 +61,9 @@
> #include <linux/psi.h>
> #include <net/sock.h>
> #include <linux/task_work.h>
> +#include <linux/ratelimit.h>
> +#include <linux/stacktrace.h>
> +#include <linux/sysctl.h>
> #include <linux/ve.h>
>
> #define CREATE_TRACE_POINTS
> @@ -4099,6 +4102,98 @@ static ssize_t cgroup_max_depth_write(struct kernfs_open_file *of,
> return nbytes;
> }
>
> +#define MAX_STACK_TRACE_DEPTH 64
> +
> +void warn_freeze_timeout_task(struct cgroup *cgrp, int timeout,
> + struct task_struct *task)
> +{
> + char *buf __free(kfree) = NULL;
> + unsigned long *entries __free(kfree) = NULL;
> + unsigned long nr_entries, i;
> + pid_t tgid;
> +
> + buf = kmalloc(PATH_MAX, GFP_KERNEL);
> + if (!buf)
> + return;
> +
> + if (cgroup_path(cgrp, buf, PATH_MAX) < 0)
> + return;
> +
> + tgid = task_pid_nr_ns(task, &init_pid_ns);
> + printk(KERN_WARNING "Freeze of %s took %d sec, "
> + "due to unfreezable process %d:%s, stack:\n",
> + buf, timeout/HZ, tgid, task->comm);
> +
> + entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries),
> + GFP_KERNEL);
> + if (!entries)
> + return;
> + nr_entries = stack_trace_save_tsk(task, entries,
> + MAX_STACK_TRACE_DEPTH, 0);
> + for (i = 0; i < nr_entries; i++)
> + printk(KERN_WARNING "[<%pK>] %pB\n",
> + (void *)entries[i], (void *)entries[i]);
> +}
> +
> +static void warn_freeze_timeout(struct cgroup *cgrp, int timeout)
> +{
> + struct cgroup_subsys_state *css;
> + char *buf __free(kfree) = NULL;
> +
> + guard(rcu)();
> + css_for_each_descendant_post(css, &cgrp->self) {
> + struct task_struct *task;
> + struct css_task_iter it;
> +
> + css_task_iter_start(css, 0, &it);
> + while ((task = css_task_iter_next(&it))) {
> + if (task->flags & PF_KTHREAD)
> + continue;
> + if (task->frozen)
> + continue;
> +
> + warn_freeze_timeout_task(cgrp, timeout, task);
> + css_task_iter_end(&it);
> + return;
> + }
> + css_task_iter_end(&it);
> + }
> +
> + buf = kmalloc(PATH_MAX, GFP_KERNEL);
> + if (!buf)
> + return;
> +
> + if (cgroup_path(cgrp, buf, PATH_MAX) < 0)
> + return;
> +
> + printk(KERN_WARNING "Freeze of %s took %d sec, "
> + "but no unfreezable process detected\n", buf, timeout/HZ);
> +}
> +
> +static void check_freeze_timeout(struct cgroup *cgrp)
> +{
> + static DEFINE_RATELIMIT_STATE(freeze_timeout_rs,
> + DEFAULT_FREEZE_TIMEOUT, 1);
> + unsigned long freeze_jiffies;
> + int timeout;
> +
> + scoped_guard(spinlock, &css_set_lock) {
> + if (!test_bit(CGRP_FREEZE, &cgrp->flags) ||
> + test_bit(CGRP_FROZEN, &cgrp->flags))
> + return;
> + freeze_jiffies = READ_ONCE(cgrp->freezer.freeze_jiffies);
> + }
> +
> + timeout = READ_ONCE(sysctl_freeze_timeout);
> + if (!freeze_jiffies || freeze_jiffies + timeout > get_jiffies_64())
if (!freeze_jiffies || time_after64(get_jiffies_64(), freeze_jiffies + timeout)) {
?
> + return;
> +
> + if (!__ratelimit(&freeze_timeout_rs))
> + return;
> +
> + warn_freeze_timeout(cgrp, timeout);
> +}
> +
> static int cgroup_events_show(struct seq_file *seq, void *v)
> {
> struct cgroup *cgrp = seq_css(seq)->cgroup;
> @@ -4106,6 +4201,8 @@ static int cgroup_events_show(struct seq_file *seq, void *v)
> seq_printf(seq, "populated %d\n", cgroup_is_populated(cgrp));
> seq_printf(seq, "frozen %d\n", test_bit(CGRP_FROZEN, &cgrp->flags));
>
> + check_freeze_timeout(cgrp);
> +
> return 0;
> }
>
> diff --git a/kernel/cgroup/freezer.c b/kernel/cgroup/freezer.c
> index a2d9e55257bcc..e6ef44d0ab4a8 100644
> --- a/kernel/cgroup/freezer.c
> +++ b/kernel/cgroup/freezer.c
> @@ -3,6 +3,7 @@
> #include <linux/sched.h>
> #include <linux/sched/task.h>
> #include <linux/sched/signal.h>
> +#include <linux/jiffies.h>
>
> #include "cgroup-internal.h"
>
> @@ -183,10 +184,13 @@ static void cgroup_do_freeze(struct cgroup *cgrp, bool freeze)
> lockdep_assert_held(&cgroup_mutex);
>
> spin_lock_irq(&css_set_lock);
> - if (freeze)
> + if (freeze) {
> set_bit(CGRP_FREEZE, &cgrp->flags);
> - else
> + cgrp->freezer.freeze_jiffies = get_jiffies_64();
> + } else {
> clear_bit(CGRP_FREEZE, &cgrp->flags);
> + cgrp->freezer.freeze_jiffies = 0;
> + }
> spin_unlock_irq(&css_set_lock);
>
> if (freeze)
> diff --git a/kernel/cgroup/legacy_freezer.c b/kernel/cgroup/legacy_freezer.c
> index b048f6b530688..a59e5a14f45ed 100644
> --- a/kernel/cgroup/legacy_freezer.c
> +++ b/kernel/cgroup/legacy_freezer.c
> @@ -25,7 +25,6 @@
> #include <linux/cpu.h>
> #include <linux/jiffies.h>
> #include <linux/ratelimit.h>
> -#include <linux/stacktrace.h>
> #include <linux/sysctl.h>
>
> /*
> @@ -244,11 +243,6 @@ static void check_freezer_timeout(struct cgroup_subsys_state *css,
> DEFAULT_FREEZE_TIMEOUT, 1);
> int __freeze_timeout = READ_ONCE(sysctl_freeze_timeout);
> struct freezer *freezer = css_freezer(css);
> - unsigned long nr_entries;
> - unsigned long *entries;
> - char *freezer_cg_name;
> - pid_t tgid;
> - int i;
>
> if (!freezer->freeze_jiffies ||
> freezer->freeze_jiffies + __freeze_timeout > get_jiffies_64())
> @@ -257,35 +251,7 @@ static void check_freezer_timeout(struct cgroup_subsys_state *css,
> if (!__ratelimit(&freeze_timeout_rs))
> return;
>
> - freezer_cg_name = kmalloc(PATH_MAX, GFP_KERNEL);
> - if (!freezer_cg_name)
> - return;
> -
> - if (cgroup_path(css->cgroup, freezer_cg_name, PATH_MAX) < 0)
> - goto free_cg_name;
> -
> - tgid = task_pid_nr_ns(task, &init_pid_ns);
> -
> - printk(KERN_WARNING "Freeze of %s took %d sec, "
> - "due to unfreezable process %d:%s, stack:\n",
> - freezer_cg_name, __freeze_timeout/HZ, tgid, task->comm);
> -
> - entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries),
> - GFP_KERNEL);
> - if (!entries)
> - goto free_cg_name;
> -
> - nr_entries = stack_trace_save_tsk(task, entries,
> - MAX_STACK_TRACE_DEPTH, 0);
> -
> - for (i = 0; i < nr_entries; i++) {
> - printk(KERN_WARNING "[<%pK>] %pB\n",
> - (void *)entries[i], (void *)entries[i]);
> - }
> -
> - kfree(entries);
> -free_cg_name:
> - kfree(freezer_cg_name);
> + warn_freeze_timeout_task(css->cgroup, __freeze_timeout, task);
> }
>
> /**
More information about the Devel
mailing list