On 11/19/25 05:21, Konstantin Khorenko wrote:
> 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 <[email protected]>
>>
>> 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)) {
> ?

Good point, I will look into that, maybe we should use u64 instead of (unsigned 
long) in this case, but it's definitely better to use a helper instead of open 
coding the comparison.

Note: I think time_after64(get_jiffies_64(), ...) here is not correct as it 
will check the opposite condition, probably time_is_after_jiffies64(...) is a 
better candidate.

> 
>> +        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);
>>   }
>>     /**
> 

-- 
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.

_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to