freezer for cgroup v2
Subsystems (7)
v10 cover freezer for cgroup v2
This patchset implements freezer for cgroup v2.
It provides similar functionality as v1 freezer, but the interface conforms to the cgroup v2 interface design principles, and it provides a better user experience: tasks can be killed, ptrace works, there is no separate controller, which has to be enabled, etc.
Patches (1), (2) and (3) are some preparational work, patch (4) contains the implementation, patch (5) is a small cgroup kselftest fix, patch (6) covers freezer adds 6 new kselftests to cover the freezer functionality. Patchse (7) and (8) adding tracing points to simplify the debugging process. Patch (9) adds corresponding docs.
v10->v9:
- removed redundant fatal_signal_pending() check
- reworked vfork support
- minor cleanups
- rebase to cgroup/for-5.2
v9->v8:
- added support for vfork
- added a kselftest test for vfork case
- several tests fixes/improvements
- renamed stopped* into frozen* across the patchset
- other minor fixes
v8->v7:
- reworked/simplified cgroup frozen task accounting by merging nr_stopped and nr_frozen and removing nr_tasks_to_freeze
- don't notify the parent process if the child is going from the stopped to the frozen state
v7->v6:
- handle properly the case, when a task is both stopped and frozen
- check for CGRP_FREEZE instead of CGRP_FROZEN in cgroup_exit()
- minor cosmetic changes and rebase
v6->v5:
- reverted to clear TIF_SIGPENDING with additional checks before schedule(), as proposed by Oleg Nesterov
- made cgroup v2 freezer working with the system freezer (by using freezable_schedule())
- make freezer working with SIGSTOPped and PTRACEd tasks
- added tests to cover freezing a cgroup with SIGSTOPped and PTRACEd tasks
v5->v4:
- rewored cgroup state transition code (suggested by Tejun Heo)
- look at JOBCTL_TRAP_FREEZE instead of task->frozen in recalc_sigpending(), check for task->frozen and JOBCTL_TRAP_FREEZE in signal_pending_state() (suggested by Oleg Nesterov)
- some cosmetic changes in signal.c (suggested by Oleg Nesterov)
- cleaned up comments
v4->v3:
- reading nr_descendants doesn't require taking css_set_lock anymore
- fixed docs based on Mike Rapoport's feedback
- fixed double irq lock found by Dan Carpenter
v3->v2:
- dropped TASK_FROZEN for now, frozen tasks are put into TASK_INTERRUPTIBLE state; it's probably not the final version, but the API question can be discussed separately
- don't clear TIF_SIGPENDING before going to sleep, instead add task->frozen check in signal_pending_state() and recalc_sigpending()
- cgroup-level counter are now synchronized using css_set_lock, which simplified the whole code (e.g. per-cgroup works were removed)
- the amount of comments increased significantly
- many other improvements incorporating feedback from Tejun and Oleg
v2->v1:
- fixed locking aroung calling cgroup_freezer_leave()
- added docs
Roman Gushchin (9):
cgroup: rename freezer.c into legacy_freezer.c
cgroup: implement __cgroup_task_count() helper
cgroup: protect cgroup->nr_(dying_)descendants by css_set_lock
cgroup: cgroup v2 freezer
kselftests: cgroup: don't fail on cg_kill_all() error in cg_destroy()
kselftests: cgroup: add freezer controller self-tests
cgroup: make TRACE_CGROUP_PATH irq-safe
cgroup: add tracing points for cgroup v2 freezer
cgroup: document cgroup v2 freezer interface
Documentation/admin-guide/cgroup-v2.rst | 27 + include/linux/cgroup-defs.h | 33 + include/linux/cgroup.h | 43 + include/linux/sched.h | 2 + include/linux/sched/jobctl.h | 2 + include/trace/events/cgroup.h | 55 ++ kernel/cgroup/Makefile | 4 +- kernel/cgroup/cgroup-internal.h | 8 +- kernel/cgroup/cgroup-v1.c | 16 - kernel/cgroup/cgroup.c | 151 +++- kernel/cgroup/freezer.c | 647 +++++-------- kernel/cgroup/legacy_freezer.c | 481 ++++++++++ kernel/fork.c | 2 + kernel/signal.c | 70 +- tools/testing/selftests/cgroup/.gitignore | 1 + tools/testing/selftests/cgroup/Makefile | 2 + tools/testing/selftests/cgroup/cgroup_util.c | 58 +- tools/testing/selftests/cgroup/cgroup_util.h | 5 + tools/testing/selftests/cgroup/test_freezer.c | 851 ++++++++++++++++++ 19 files changed, 2026 insertions(+), 432 deletions(-) create mode 100644 kernel/cgroup/legacy_freezer.c create mode 100644 tools/testing/selftests/cgroup/test_freezer.c
Signature · 1 line
2.20.1
PATCH v10 1/9 cgroup: rename freezer.c into legacy_freezer.c
Freezer.c will contain an implementation of cgroup v2 freezer, so let's rename the v1 freezer to avoid naming conflicts.
---
kernel/cgroup/Makefile | 2 +-
kernel/cgroup/{freezer.c => legacy_freezer.c} | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename kernel/cgroup/{freezer.c => legacy_freezer.c} (100%)kernel/cgroup/Makefile +1 -1
@@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-y := cgroup.o rstat.o namespace.o cgroup-v1.o -obj-$(CONFIG_CGROUP_FREEZER) += freezer.o +obj-$(CONFIG_CGROUP_FREEZER) += legacy_freezer.o obj-$(CONFIG_CGROUP_PIDS) += pids.o obj-$(CONFIG_CGROUP_RDMA) += rdma.o obj-$(CONFIG_CPUSETS) += cpuset.o
kernel/cgroup/legacy_freezer.c renamed from kernel/cgroup/freezer.c +0 -0
Signature · 1 line
2.20.1
PATCH v10 2/9 cgroup: implement __cgroup_task_count() helper
In reply to Roman GushchinThe helper is identical to the existing cgroup_task_count() except it doesn't take the css_set_lock by itself, assuming that the caller does.
Also, move cgroup_task_count() implementation into kernel/cgroup/cgroup.c, as there is nothing specific to cgroup v1.
--- kernel/cgroup/cgroup-internal.h | 1 + kernel/cgroup/cgroup-v1.c | 16 ---------------- kernel/cgroup/cgroup.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 16 deletions(-)
kernel/cgroup/cgroup-internal.h +1 -0
@@ -240,6 +240,7 @@ int cgroup_rmdir(struct kernfs_node *kn);int cgroup_show_path (struct seq_file *sf,struct kernfs_node *kf_node,struct kernfs_root *kf_root); +int __cgroup_task_count (const struct cgroup *cgrp);int cgroup_task_count (const struct cgroup *cgrp);/*
kernel/cgroup/cgroup-v1.c +0 -16
@@ -342,22 +342,6 @@ static struct cgroup_pidlist *cgroup_pidlist_find_create(struct cgroup *cgrp,return l; } -/** -* cgroup_task_count - count the number of tasks in a cgroup. -* @cgrp: the cgroup in question -*/ -int cgroup_task_count (const struct cgroup *cgrp) -{ -int count =0 ; -struct cgrp_cset_link *link; - -spin_lock_irq (&css_set_lock); -list_for_each_entry (link, &cgrp->cset_links, cset_link) - count += link->cset->nr_tasks; -spin_unlock_irq (&css_set_lock); -return count; -} -/* * Load a cgroup's pidarray with either procs' tgids or tasks' pids */
kernel/cgroup/cgroup.c +33 -0
@@ -593,6 +593,39 @@ static void cgroup_get_live(struct cgroup *cgrp)css_get (&cgrp->self); } +/** +* __cgroup_task_count - count the number of tasks in a cgroup. The caller +* is responsible for taking the css_set_lock. +* @cgrp: the cgroup in question +*/ +int __cgroup_task_count (const struct cgroup *cgrp) +{ +int count =0 ; +struct cgrp_cset_link *link; + +lockdep_assert_held (&css_set_lock); + +list_for_each_entry (link, &cgrp->cset_links, cset_link) + count += link->cset->nr_tasks; + +return count; +} + +/** +* cgroup_task_count - count the number of tasks in a cgroup. +* @cgrp: the cgroup in question +*/ +int cgroup_task_count (const struct cgroup *cgrp) +{ +int count; + +spin_lock_irq (&css_set_lock); + count =__cgroup_task_count (cgrp); +spin_unlock_irq (&css_set_lock); + +return count; +} +struct cgroup_subsys_state *of_css (struct kernfs_open_file *of) {struct cgroup *cgrp = of->kn->parent->priv;
Signature · 1 line
2.20.1
PATCH v10 3/9 cgroup: protect cgroup->nr_(dying_)descendants by css_set_lock
In reply to Roman GushchinThe number of descendant cgroups and the number of dying descendant cgroups are currently synchronized using the cgroup_mutex.
The number of descendant cgroups will be required by the cgroup v2 freezer, which will use it to determine if a cgroup is frozen (depending on total number of descendants and number of frozen descendants). It's not always acceptable to grab the cgroup_mutex, especially from quite hot paths (e.g. exit()).
To avoid this, let's additionally synchronize these counters using the css_set_lock.
So, it's safe to read these counters with either cgroup_mutex or css_set_lock locked, and for changing both locks should be acquired.
--- include/linux/cgroup-defs.h | 5 +++++ kernel/cgroup/cgroup.c | 6 ++++++ 2 files changed, 11 insertions(+)
include/linux/cgroup-defs.h +5 -0
@@ -349,6 +349,11 @@ struct cgroup {* Dying cgroups are cgroups which were deleted by a user, * but are still existing because someone else is holding a reference. * max_descendants is a maximum allowed number of descent cgroups. +* +* nr_descendants and nr_dying_descendants are protected +* by cgroup_mutex and css_set_lock. It's fine to read them holding +* any of cgroup_mutex and css_set_lock; for writing both locks +* should be held. */ int nr_descendants;int nr_dying_descendants;
kernel/cgroup/cgroup.c +6 -0
@@ -4811,9 +4811,11 @@ static void css_release_work_fn(struct work_struct *work)if (cgroup_on_dfl (cgrp))cgroup_rstat_flush (cgrp); +spin_lock_irq (&css_set_lock);for (tcgrp =cgroup_parent (cgrp); tcgrp; tcgrp =cgroup_parent (tcgrp)) tcgrp->nr_dying_descendants--; +spin_unlock_irq (&css_set_lock);cgroup_idr_remove (&cgrp->root->cgroup_idr, cgrp->id); cgrp->id =-1 ;
@@ -5031,12 +5033,14 @@ static struct cgroup *cgroup_create(struct cgroup *parent)if (ret) goto out_psi_free; +spin_lock_irq (&css_set_lock);for (tcgrp = cgrp; tcgrp; tcgrp =cgroup_parent (tcgrp)) { cgrp->ancestor_ids[tcgrp->level] = tcgrp->id;if (tcgrp != cgrp) tcgrp->nr_descendants++; } +spin_unlock_irq (&css_set_lock);if (notify_on_release (parent))set_bit (CGRP_NOTIFY_ON_RELEASE , &cgrp->flags);
@@ -5321,10 +5325,12 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)if (parent &&cgroup_is_threaded (cgrp)) parent->nr_threaded_children--; +spin_lock_irq (&css_set_lock);for (tcgrp =cgroup_parent (cgrp); tcgrp; tcgrp =cgroup_parent (tcgrp)) { tcgrp->nr_descendants--; tcgrp->nr_dying_descendants++; } +spin_unlock_irq (&css_set_lock);cgroup1_check_for_release (parent);
Signature · 1 line
2.20.1
PATCH v10 4/9 cgroup: cgroup v2 freezer
In reply to Roman GushchinCgroup v1 implements the freezer controller, which provides an ability to stop the workload in a cgroup and temporarily free up some resources (cpu, io, network bandwidth and, potentially, memory) for some other tasks. Cgroup v2 lacks this functionality.
This patch implements freezer for cgroup v2.
Cgroup v2 freezer tries to put tasks into a state similar to jobctl stop. This means that tasks can be killed, ptraced (using PTRACE_SEIZE*), and interrupted. It is possible to attach to a frozen task, get some information (e.g. read registers) and detach. It's also possible to migrate a frozen tasks to another cgroup.
This differs cgroup v2 freezer from cgroup v1 freezer, which mostly tried to imitate the system-wide freezer. However uninterruptible sleep is fine when all tasks are going to be frozen (hibernation case), it's not the acceptable state for some subset of the system.
Cgroup v2 freezer is not supporting freezing kthreads.
If a non-root cgroup contains kthread, the cgroup still can be frozen, but the kthread will remain running, the cgroup will be shown as non-frozen, and the notification will not be delivered.
* PTRACE_ATTACH is not working because non-fatal signal delivery is blocked in frozen state.
There are some interface differences between cgroup v1 and cgroup v2 freezer too, which are required to conform the cgroup v2 interface design principles:
1) There is no separate controller, which has to be turned on:
the functionality is always available and is represented by cgroup.freeze and cgroup.events cgroup control files.
2) The desired state is defined by the cgroup.freeze control file. Any hierarchical configuration is allowed.
3) The interface is asynchronous. The actual state is available using cgroup.events control file ("frozen" field). There are no dedicated transitional states.
4) It's allowed to make any changes with the cgroup hierarchy (create new cgroups, remove old cgroups, move tasks between cgroups) no matter if some cgroups are frozen.
--- include/linux/cgroup-defs.h | 28 ++++ include/linux/cgroup.h | 43 +++++ include/linux/sched.h | 2 + include/linux/sched/jobctl.h | 2 + kernel/cgroup/Makefile | 2 +- kernel/cgroup/cgroup.c | 110 +++++++++++- kernel/cgroup/freezer.c | 317 +++++++++++++++++++++++++++++++++++ kernel/fork.c | 2 + kernel/signal.c | 70 +++++++- 9 files changed, 566 insertions(+), 10 deletions(-) create mode 100644 kernel/cgroup/freezer.c
include/linux/cgroup-defs.h +28 -0
@@ -65,6 +65,12 @@ enum {* specified at mount time and thus is implemented here. */ CGRP_CPUSET_CLONE_CHILDREN , + +/* Control group has to be frozen. */ +CGRP_FREEZE , + +/* Cgroup is frozen. */ + CGRP_FROZEN, };/* cgroup_root->flags */
@@ -317,6 +323,25 @@ struct cgroup_rstat_cpu {struct cgroup *updated_next;/* NULL iff not on the list */ }; +struct cgroup_freezer_state { +/* Should the cgroup and its descendants be frozen. */ +bool freeze; + +/* Should the cgroup actually be frozen? */ +int e_freeze; + +/* Fields below are protected by css_set_lock */ + +/* Number of frozen descendant cgroups */ +int nr_frozen_descendants; + +/* +* Number of tasks, which are counted as frozen: +* frozen, SIGSTOPped, and PTRACEd. +*/ +int nr_frozen_tasks; +}; +struct cgroup {/* self css with NULL ->ss, points back to this cgroup */ struct cgroup_subsys_state self;
@@ -453,6 +478,9 @@ struct cgroup {/* If there is block congestion on this cgroup. */ atomic_t congestion_count; +/* Used to store internal freezer state */ +struct cgroup_freezer_state freezer; +/* ids of the ancestors at each level including self */ int ancestor_ids[]; };
include/linux/cgroup.h +43 -0
47 lines not on this page · raw message
include/linux/sched.h +2 -0
@@ -726,6 +726,8 @@ struct task_struct {#ifdef CONFIG_CGROUPS /* disallow userland-initiated cgroup migration */ unsigned no_cgroup_migration:1; +/* task is frozen/stopped (used by the cgroup freezer) */ + unsigned frozen:1;#endif #ifdef CONFIG_BLK_CGROUP /* to be used once the psi infrastructure lands upstream. */
include/linux/sched/jobctl.h +2 -0
@@ -18,6 +18,7 @@ struct task_struct;#define JOBCTL_TRAP_NOTIFY_BIT 20/* trap for NOTIFY */ #define JOBCTL_TRAPPING_BIT 21/* switching to TRACED */ #define JOBCTL_LISTENING_BIT 22/* ptracer is listening for events */ +#define JOBCTL_TRAP_FREEZE_BIT 23/* trap for cgroup freezer */ #define JOBCTL_STOP_DEQUEUED (1UL << JOBCTL_STOP_DEQUEUED_BIT)#define JOBCTL_STOP_PENDING (1UL << JOBCTL_STOP_PENDING_BIT)
@@ -26,6 +27,7 @@ struct task_struct;#define JOBCTL_TRAP_NOTIFY (1UL << JOBCTL_TRAP_NOTIFY_BIT)#define JOBCTL_TRAPPING (1UL << JOBCTL_TRAPPING_BIT)#define JOBCTL_LISTENING (1UL << JOBCTL_LISTENING_BIT) +#define JOBCTL_TRAP_FREEZE (1UL << JOBCTL_TRAP_FREEZE_BIT)#define JOBCTL_TRAP_MASK (JOBCTL_TRAP_STOP | JOBCTL_TRAP_NOTIFY)#define JOBCTL_PENDING_MASK (JOBCTL_STOP_PENDING | JOBCTL_TRAP_MASK)
kernel/cgroup/Makefile +1 -1
@@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-y := cgroup.o rstat.o namespace.o cgroup-v1.o +obj-y := cgroup.o rstat.o namespace.o cgroup-v1.o freezer.o obj-$(CONFIG_CGROUP_FREEZER) += legacy_freezer.o obj-$(CONFIG_CGROUP_PIDS) += pids.o
kernel/cgroup/cgroup.c +106 -4
164 lines not on this page · raw message
kernel/cgroup/freezer.c new file +317 -0
317 lines not on this page · raw message
kernel/fork.c +2 -0
@@ -1222,7 +1222,9 @@ static int wait_for_vfork_done(struct task_struct *child,int killed;freezer_do_not_count (); +cgroup_enter_frozen (); killed =wait_for_completion_killable (vfork); +cgroup_leave_frozen (false);freezer_count ();if (killed) {
kernel/signal.c +65 -5
121 lines not on this page · raw message
Signature · 1 line
2.20.1
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 14 lines · Roman Gushchin
kernel/cgroup/freezer.c new file +13
-0+void cgroup_leave_frozen (bool always_leave) +{ +struct cgroup *cgrp; + +spin_lock_irq (&css_set_lock); + cgrp =task_dfl_cgroup (current); +if (always_leave || !test_bit (CGRP_FREEZE , &cgrp->flags)) { +cgroup_dec_frozen_cnt (cgrp); +cgroup_update_frozen (cgrp); +WARN_ON_ONCE (!current->frozen); + current->frozen = false; + } +spin_unlock_irq (&css_set_lock);
kernel/cgroup/freezer.c line 148
Quoted 10 lines
kernel/cgroup/freezer.c new file +10
-0+ +if (unlikely (current->frozen)) { +/* +* If the task remained in the frozen state, +* make sure it won't reach userspace without +* entering the signal handling loop. +*/ +spin_lock_irq (¤t->sighand->siglock); +recalc_sigpending (); +spin_unlock_irq (¤t->sighand->siglock);
I still can't understand this logic.
Once again, suppose we race with CGRP_FREEZE. If JOBCTL_TRAP_FREEZE is already set then signal_pending() must be already T and we do not need recalc_sigpending? If JOBCTL_TRAP_FREEZE is not set yet, how can recalc_sigpending() help?
Quoted 4 lines
kernel/cgroup/freezer.c new file +4
-0+static void cgroup_freeze_task (struct task_struct *task,bool freeze) +{ +unsigned long flags; +
kernel/cgroup/freezer.c line 169
Quoted 10 lines
kernel/cgroup/freezer.c new file +10
-0+/* If the task is about to die, don't bother with freezing it. */ +if (!lock_task_sighand (task, &flags)) +return ; + +if (freeze) { + task->jobctl |=JOBCTL_TRAP_FREEZE ; +signal_wake_up (task, false); + }else { + task->jobctl &= ~JOBCTL_TRAP_FREEZE ; +wake_up_process (task);
wake_up_interruptible() ?
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2489
Quoted 11 lines
static int
ptrace_signal(int signr, kernel_siginfo_t *info){kernel/signal.c +4
-0/* @@ -2442,6 +2483,10 @@ bool get_signal(struct ksignal *ksig) ksig->info.si_signo = signr =SIGKILL ;sigdelset (¤t->pending.signal,SIGKILL );recalc_sigpending (); + current->jobctl &= ~JOBCTL_TRAP_FREEZE ; +spin_unlock_irq (&sighand->siglock); +if (unlikely (cgroup_task_frozen (current))) +cgroup_leave_frozen (true);
Oh, and another leave_frozen below...
I feel this must be simplified somehow, but nothing comes to my mind right now.
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2519
Quoted 9 lines
kernel/signal.c +6
-0+/* +* If the task is leaving the frozen state, let's update +* cgroup counters and reset the frozen bit. +*/ +if (unlikely (cgroup_task_frozen (current))) {spin_unlock_irq (&sighand->siglock); +cgroup_leave_frozen (true); goto relock; }
afaics cgroup_leave_frozen(false) makes more sense here.
Oleg.
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Anyway, I do not want to delay this feature. Even if I am right we can cleanup this code later. I see nothing really wrong, so no objections from me. Sorry for delay.
Quoted 79 lines · Oleg Nesterov
Quoted 14 lines · Roman Gushchin
kernel/cgroup/freezer.c new file +13
-0+void cgroup_leave_frozen (bool always_leave) +{ +struct cgroup *cgrp; + +spin_lock_irq (&css_set_lock); + cgrp =task_dfl_cgroup (current); +if (always_leave || !test_bit (CGRP_FREEZE , &cgrp->flags)) { +cgroup_dec_frozen_cnt (cgrp); +cgroup_update_frozen (cgrp); +WARN_ON_ONCE (!current->frozen); + current->frozen = false; + } +spin_unlock_irq (&css_set_lock);kernel/cgroup/freezer.c line 148
Quoted 10 lines
kernel/cgroup/freezer.c new file +10
-0+ +if (unlikely (current->frozen)) { +/* +* If the task remained in the frozen state, +* make sure it won't reach userspace without +* entering the signal handling loop. +*/ +spin_lock_irq (¤t->sighand->siglock); +recalc_sigpending (); +spin_unlock_irq (¤t->sighand->siglock);I still can't understand this logic.
Once again, suppose we race with
CGRP_FREEZE. IfJOBCTL_TRAP_FREEZEis already set thensignal_pending()must be already T and we do not needrecalc_sigpending? IfJOBCTL_TRAP_FREEZEis not set yet, how canrecalc_sigpending()help?Quoted 4 lines
kernel/cgroup/freezer.c new file +4
-0+static void cgroup_freeze_task (struct task_struct *task,bool freeze) +{ +unsigned long flags; +kernel/cgroup/freezer.c line 169
Quoted 10 lines
kernel/cgroup/freezer.c new file +10
-0+/* If the task is about to die, don't bother with freezing it. */ +if (!lock_task_sighand (task, &flags)) +return ; + +if (freeze) { + task->jobctl |=JOBCTL_TRAP_FREEZE ; +signal_wake_up (task, false); + }else { + task->jobctl &= ~JOBCTL_TRAP_FREEZE ; +wake_up_process (task);
wake_up_interruptible()?kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2489
Quoted 11 lines
static int
ptrace_signal(int signr, kernel_siginfo_t *info){kernel/signal.c +4
-0/* @@ -2442,6 +2483,10 @@ bool get_signal(struct ksignal *ksig) ksig->info.si_signo = signr =SIGKILL ;sigdelset (¤t->pending.signal,SIGKILL );recalc_sigpending (); + current->jobctl &= ~JOBCTL_TRAP_FREEZE ; +spin_unlock_irq (&sighand->siglock); +if (unlikely (cgroup_task_frozen (current))) +cgroup_leave_frozen (true);Oh, and another
leave_frozenbelow...I feel this must be simplified somehow, but nothing comes to my mind right now.
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2519
Quoted 9 lines
kernel/signal.c +6
-0+/* +* If the task is leaving the frozen state, let's update +* cgroup counters and reset the frozen bit. +*/ +if (unlikely (cgroup_task_frozen (current))) {spin_unlock_irq (&sighand->siglock); +cgroup_leave_frozen (true); goto relock; }afaics
cgroup_leave_frozen(false)makes more sense here.Oleg.
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 3 lines · Oleg Nesterov
Anyway, I do not want to delay this feature. Even if I am right we can cleanup this code later. I see nothing really wrong, so no objections from me. Sorry for delay.
This is great!
Thank you so much for your time spent reviewing all these iterations! I hope the patchset is in a much better state now.
Roman
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
In reply to Oleg NesterovHi Oleg!
Quoted 28 lines · Oleg Nesterov
Quoted 14 lines · Roman Gushchin
kernel/cgroup/freezer.c new file +13
-0+void cgroup_leave_frozen (bool always_leave) +{ +struct cgroup *cgrp; + +spin_lock_irq (&css_set_lock); + cgrp =task_dfl_cgroup (current); +if (always_leave || !test_bit (CGRP_FREEZE , &cgrp->flags)) { +cgroup_dec_frozen_cnt (cgrp); +cgroup_update_frozen (cgrp); +WARN_ON_ONCE (!current->frozen); + current->frozen = false; + } +spin_unlock_irq (&css_set_lock);kernel/cgroup/freezer.c line 148
Quoted 10 lines
kernel/cgroup/freezer.c new file +10
-0+ +if (unlikely (current->frozen)) { +/* +* If the task remained in the frozen state, +* make sure it won't reach userspace without +* entering the signal handling loop. +*/ +spin_lock_irq (¤t->sighand->siglock); +recalc_sigpending (); +spin_unlock_irq (¤t->sighand->siglock);I still can't understand this logic.
Quoted 3 lines
Once again, suppose we race with
CGRP_FREEZE. IfJOBCTL_TRAP_FREEZEis already set thensignal_pending()must be already T and we do not needrecalc_sigpending? IfJOBCTL_TRAP_FREEZEis not set yet, how canrecalc_sigpending()help?
This is paired with cgroup_task_frozen() check in recalc_sigpending_tsk(). If the task is waking from waiting in vfork(), and it races with unfreezing of the cgroup, we should guarantee that the task won't return to userspace with task->frozen flag set, otherwise it would break accounting of frozen tasks.
We can't rely solely on JOBCTL_TRAP_FREEZE bit, as it can be cleared in parallel at any moment. So we backup it with the task->frozen check.
Quoted 17 lines
Quoted 5 lines
kernel/cgroup/freezer.c new file +4
-0+static void cgroup_freeze_task (struct task_struct *task,bool freeze) +{ +unsigned long flags; +kernel/cgroup/freezer.c line 169
Quoted 10 lines
kernel/cgroup/freezer.c new file +10
-0+/* If the task is about to die, don't bother with freezing it. */ +if (!lock_task_sighand (task, &flags)) +return ; + +if (freeze) { + task->jobctl |=JOBCTL_TRAP_FREEZE ; +signal_wake_up (task, false); + }else { + task->jobctl &= ~JOBCTL_TRAP_FREEZE ; +wake_up_process (task);
wake_up_interruptible()?
Wait_up_interruptible() is supposed to work with a workqueue,
but here there is nothing like this. Probably, I didn't understand your idea. Can you, please, elaborate a bit more?
Quoted 14 lines
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2489
Quoted 12 lines
static int
ptrace_signal(int signr, kernel_siginfo_t *info){kernel/signal.c +4
-0/* @@ -2442,6 +2483,10 @@ bool get_signal(struct ksignal *ksig) ksig->info.si_signo = signr =SIGKILL ;sigdelset (¤t->pending.signal,SIGKILL );recalc_sigpending (); + current->jobctl &= ~JOBCTL_TRAP_FREEZE ; +spin_unlock_irq (&sighand->siglock); +if (unlikely (cgroup_task_frozen (current))) +cgroup_leave_frozen (true);Oh, and another
leave_frozenbelow...
Yeah, because of this new "goto fatal" shortcut.
Quoted 14 lines
I feel this must be simplified somehow, but nothing comes to my mind right now.
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2519
Quoted 9 lines
kernel/signal.c +6
-0+/* +* If the task is leaving the frozen state, let's update +* cgroup counters and reset the frozen bit. +*/ +if (unlikely (cgroup_task_frozen (current))) {spin_unlock_irq (&sighand->siglock); +cgroup_leave_frozen (true); goto relock; }afaics
cgroup_leave_frozen(false)makes more sense here.
Why? I don't see any reasons why the task should remain in the frozen state after this point. Can you, please, provide an example?
Thank you for looking into it!
Roman
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 6 lines · Roman Gushchin
Quoted 4 lines
Once again, suppose we race with
CGRP_FREEZE. IfJOBCTL_TRAP_FREEZEis already set thensignal_pending()must be already T and we do not needrecalc_sigpending? IfJOBCTL_TRAP_FREEZEis not set yet, how canrecalc_sigpending()help?This is paired with
cgroup_task_frozen()check inrecalc_sigpending_tsk().
Ooh, I didn't notice this version added cgroup_task_frozen() into recalc_sigpending_tsk() ...
Honestly, I don't like this. But see another email I sent, we can cleanup this code later.
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
kernel/cgroup/freezer.c new file +8
-0+static void cgroup_freeze_task (struct task_struct *task,bool freeze) +{ +unsigned long flags; + +/* If the task is about to die, don't bother with freezing it. */ +if (!lock_task_sighand (task, &flags)) +return ; +Quoted 8 lines
kernel/cgroup/freezer.c line 169
Quoted 6 lines
kernel/cgroup/freezer.c new file +6
-0+if (freeze) { + task->jobctl |=JOBCTL_TRAP_FREEZE ; +signal_wake_up (task, false); + }else { + task->jobctl &= ~JOBCTL_TRAP_FREEZE ; +wake_up_process (task);
wake_up_interruptible()?
Quoted 3 lines
Wait_up_interruptible()is supposed to work with a workqueue,
but here there is nothing like this. Probably, I didn't understand your idea. Can you, please, elaborate a bit more?
Not sure I understand... We need to wake up the task if it sleeps in do_freezer_trap(), right? do_freezer_trap() uses TASK_INTERRUPTIBLE, so why can't wake_up_interruptible() == __wake_up(TASK_INTERRUPTIBLE) work?
Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
static int
ptrace_signal(int signr, kernel_siginfo_t *info){kernel/signal.c +0
-0/* @@ -2442,6 +2483,10 @@ bool get_signal(struct ksignal *ksig) ksig->info.si_signo = signr =SIGKILL ;Quoted 8 lines
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2489
Quoted 6 lines
kernel/signal.c +4
-0sigdelset (¤t->pending.signal,SIGKILL );recalc_sigpending (); + current->jobctl &= ~JOBCTL_TRAP_FREEZE ; +spin_unlock_irq (&sighand->siglock); +if (unlikely (cgroup_task_frozen (current))) +cgroup_leave_frozen (true);Oh, and another
leave_frozenbelow...
Quoted 1 line
Yeah, because of this new "goto fatal" shortcut.
I understand, but the code doesn't look nice... but again, I can't suggest anything better at least right now, so please forget.
Quoted 10 lines
Quoted 7 lines
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2519
Quoted 5 lines
kernel/signal.c +2
-0+if (unlikely (cgroup_task_frozen (current))) {spin_unlock_irq (&sighand->siglock); +cgroup_leave_frozen (true); goto relock; }afaics
cgroup_leave_frozen(false)makes more sense here.Why? I don't see any reasons why the task should remain in the frozen state after this point.
But cgroup_leave_frozen(false) will equally clear ->frozen if !CGRP_FREEZE ? OTOH, if CGRP_FREEZE is set again, why do we need to clear ->frozen?
Oleg.
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 6 lines · Oleg Nesterov
Quoted 5 lines · Roman Gushchin
Quoted 4 lines
Once again, suppose we race with
CGRP_FREEZE. IfJOBCTL_TRAP_FREEZEis already set thensignal_pending()must be already T and we do not needrecalc_sigpending? IfJOBCTL_TRAP_FREEZEis not set yet, how canrecalc_sigpending()help?
Quoted 7 lines
Quoted 1 line
This is paired with
cgroup_task_frozen()check inrecalc_sigpending_tsk().Ooh, I didn't notice this version added
cgroup_task_frozen()intorecalc_sigpending_tsk()...Honestly, I don't like this. But see another email I sent, we can cleanup this code later.
Yeah, totally agree: it's not pretty. But honestly I've no better ideas, so let's fix it later.
Quoted 18 lines
Quoted 18 lines
Quoted 17 lines
Quoted 5 lines
kernel/cgroup/freezer.c new file +4
-0+static void cgroup_freeze_task (struct task_struct *task,bool freeze) +{ +unsigned long flags; +kernel/cgroup/freezer.c line 169
Quoted 10 lines
kernel/cgroup/freezer.c new file +10
-0+/* If the task is about to die, don't bother with freezing it. */ +if (!lock_task_sighand (task, &flags)) +return ; + +if (freeze) { + task->jobctl |=JOBCTL_TRAP_FREEZE ; +signal_wake_up (task, false); + }else { + task->jobctl &= ~JOBCTL_TRAP_FREEZE ; +wake_up_process (task);
wake_up_interruptible()?
Quoted 7 lines
Quoted 3 lines
Wait_up_interruptible()is supposed to work with a workqueue,
but here there is nothing like this. Probably, I didn't understand your idea. Can you, please, elaborate a bit more?Not sure I understand... We need to wake up the task if it sleeps in
do_freezer_trap(), right?do_freezer_trap()usesTASK_INTERRUPTIBLE, so why can'twake_up_interruptible()==__wake_up(TASK_INTERRUPTIBLE)work?
Right, but __wake_up is supposed to wake threads blocked on a waitqueue:
/**
* __wake_up - wake up threads blocked on a waitqueue.
* @wq_head: the waitqueue
* @mode: which threads
* @nr_exclusive: how many wake-one or wake-many threads to wake up
* @key: is directly passed to the wakeup function
*
* If this function wakes up a task, it executes a full memory barrier before
* accessing the task state.
*/
void __wake_up(struct wait_queue_head *wq_head, unsigned int mode,
int nr_exclusive, void *key)
What should I pass as wq_head?
Quoted 28 lines
Quoted 15 lines
Quoted 14 lines
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2489
Quoted 12 lines
static int
ptrace_signal(int signr, kernel_siginfo_t *info){kernel/signal.c +4
-0/* @@ -2442,6 +2483,10 @@ bool get_signal(struct ksignal *ksig) ksig->info.si_signo = signr =SIGKILL ;sigdelset (¤t->pending.signal,SIGKILL );recalc_sigpending (); + current->jobctl &= ~JOBCTL_TRAP_FREEZE ; +spin_unlock_irq (&sighand->siglock); +if (unlikely (cgroup_task_frozen (current))) +cgroup_leave_frozen (true);Oh, and another
leave_frozenbelow...Quoted 1 line
Yeah, because of this new "goto fatal" shortcut.
I understand, but the code doesn't look nice... but again, I can't suggest anything better at least right now, so please forget.
Quoted 8 lines
Quoted 7 lines
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2519
Quoted 5 lines
kernel/signal.c +2
-0+if (unlikely (cgroup_task_frozen (current))) {spin_unlock_irq (&sighand->siglock); +cgroup_leave_frozen (true); goto relock; }afaics
cgroup_leave_frozen(false)makes more sense here.
Quoted 5 lines
Quoted 2 lines
Why? I don't see any reasons why the task should remain in the frozen state after this point.
But
cgroup_leave_frozen(false)will equally clear ->frozen if !CGRP_FREEZE ? OTOH, ifCGRP_FREEZEis set again, why do we need to clear ->frozen?
Hm, it might work too, but I'm not sure I like it more. IMO, the best option is to have a single cgroup_leave_frozen(true) in signal.c, it's just simpler. If a user changed the desired state of cgroup twice, there is no need to avoid state transitions. Or maybe I don't see it yet.
Thank you!
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 8 lines · Roman Gushchin
Quoted 8 lines
Quoted 7 lines
Quoted 3 lines
wake_up_interruptible()?
Wait_up_interruptible()is supposed to work with a workqueue,
but here there is nothing like this. Probably, I didn't understand your idea. Can you, please, elaborate a bit more?
Quoted 5 lines
Quoted 3 lines
Not sure I understand... We need to wake up the task if it sleeps in
do_freezer_trap(), right?do_freezer_trap()usesTASK_INTERRUPTIBLE, so why can'twake_up_interruptible()==__wake_up(TASK_INTERRUPTIBLE)work?Right, but
__wake_upis supposed to wake threads blocked on a waitqueue:
Ugh sorry ;) of course I meant wake_up_state(task, TASK_INTERRUPTIBLE).
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
Quoted 7 lines
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2519
Quoted 5 lines
kernel/signal.c +2
-0+if (unlikely (cgroup_task_frozen (current))) {spin_unlock_irq (&sighand->siglock); +cgroup_leave_frozen (true); goto relock; }afaics
cgroup_leave_frozen(false)makes more sense here.Why? I don't see any reasons why the task should remain in the frozen state after this point.
Quoted 7 lines
Quoted 2 lines
But
cgroup_leave_frozen(false)will equally clear ->frozen if !CGRP_FREEZE ? OTOH, ifCGRP_FREEZEis set again, why do we need to clear ->frozen?Hm, it might work too, but I'm not sure I like it more. IMO, the best option is to have a single
cgroup_leave_frozen(true)insignal.c, it's just simpler. If a user changed the desired state of cgroup twice, there is no need to avoid state transitions. Or maybe I don't see it yet.
Then why do we need cgroup_leave_frozen(false) in wait_for_vfork_done() ? How does it differ from get_signal() ?
If nothing else. Suppose that wait_for_vfork_done() calls leave(false) and this races with freezer, CGRP_FREEZE is already set but JOBCTL_TRAP_FREEZE is not.
This sets TIF_SIGPENDING to ensure the task won't return to user mode, thus it calls get_signal().
get_signal() doesn't see JOBCTL_TRAP_FREEZE, it notices ->frozen == T and does cgroup_leave_frozen(true) which clears ->frozen.
Then the task calls dequeue_signal(), clears TIF_SIGPENDING and returns to user mode?
Oleg.
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 9 lines · Oleg Nesterov
Quoted 8 lines · Roman Gushchin
Quoted 8 lines
Quoted 7 lines
Quoted 3 lines
wake_up_interruptible()?
Wait_up_interruptible()is supposed to work with a workqueue,
but here there is nothing like this. Probably, I didn't understand your idea. Can you, please, elaborate a bit more?
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
Not sure I understand... We need to wake up the task if it sleeps in
do_freezer_trap(), right?do_freezer_trap()usesTASK_INTERRUPTIBLE, so why can'twake_up_interruptible()==__wake_up(TASK_INTERRUPTIBLE)work?Right, but
__wake_upis supposed to wake threads blocked on a waitqueue:Ugh sorry ;) of course I meant
wake_up_state(task, TASK_INTERRUPTIBLE).
Agh, then it makes total sense to me. I'll master a follow-up patch.
Quoted 15 lines
Quoted 15 lines
Quoted 14 lines
Quoted 11 lines
Quoted 8 lines
kernel/signal.c bool get_signal(struct ksignal *ksig) · line 2519
Quoted 6 lines
kernel/signal.c +2
-0+if (unlikely (cgroup_task_frozen (current))) {spin_unlock_irq (&sighand->siglock); +cgroup_leave_frozen (true); goto relock; }afaics
cgroup_leave_frozen(false)makes more sense here.Why? I don't see any reasons why the task should remain in the frozen state after this point.
But
cgroup_leave_frozen(false)will equally clear ->frozen if !CGRP_FREEZE ? OTOH, ifCGRP_FREEZEis set again, why do we need to clear ->frozen?
Quoted 7 lines
Quoted 4 lines
Hm, it might work too, but I'm not sure I like it more. IMO, the best option is to have a single
cgroup_leave_frozen(true)insignal.c, it's just simpler. If a user changed the desired state of cgroup twice, there is no need to avoid state transitions. Or maybe I don't see it yet.Then why do we need
cgroup_leave_frozen(false)inwait_for_vfork_done()? How does it differ fromget_signal()?
We need it because sleeping in vfork is a special state which we want to account as frozen. And if the parent process wakes up while the cgroup is frozen (because of the child death, for example), we want to push it into the "proper" frozen state without changing the state of the cgroup.
Quoted 4 lines
If nothing else. Suppose that
wait_for_vfork_done()calls leave(false) and this races with freezer,CGRP_FREEZEis already set butJOBCTL_TRAP_FREEZEis not.
Quoted 8 lines
This sets
TIF_SIGPENDINGto ensure the task won't return to user mode, thus it callsget_signal().
get_signal()doesn't seeJOBCTL_TRAP_FREEZE, it notices ->frozen == T and doescgroup_leave_frozen(true)which clears ->frozen.Then the task calls
dequeue_signal(), clearsTIF_SIGPENDINGand returns to user mode?
Got it, a good catch! So if the freezer races with vfork() completion, we might have a spurious frozen->unfrozen->frozen transition of the cgroup state.
Switching to cgroup_leave_frozen(false) seems to solve it, but I'm slightly concerned that we're basically putting the task in a busy loop between the setting CGRP_FREEZE and setting TRAP_FREEZE. Do you think it's ok? I wonder if there are better solutions.
Thank you!
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 6 lines · Roman Gushchin
Quoted 6 lines
Quoted 5 lines
Hm, it might work too, but I'm not sure I like it more. IMO, the best option is to have a single
cgroup_leave_frozen(true)insignal.c, it's just simpler. If a user changed the desired state of cgroup twice, there is no need to avoid state transitions. Or maybe I don't see it yet.
Quoted 7 lines
Quoted 2 lines
Then why do we need
cgroup_leave_frozen(false)inwait_for_vfork_done()? How does it differ fromget_signal()?We need it because sleeping in vfork is a special state which we want to account as frozen. And if the parent process wakes up while the cgroup is frozen (because of the child death, for example), we want to push it into the "proper" frozen state without changing the state of the cgroup.
Again, I do not see how vfork() differs from get_signal() in this respect.
Let me provide another example. A TASK_STOPPED task reacts to SIGCONT and returns to get_signal(), current->frozen is true.
If this races with CGRP_FREEZE, the task should not return to user-space, just like vfork(). I see no difference.
They differ in that wait_for_vfork_done() should guarentee TIF_SIGPENDING in this case, but this is another story...
Quoted 13 lines
Quoted 4 lines
If nothing else. Suppose that
wait_for_vfork_done()calls leave(false) and this races with freezer,CGRP_FREEZEis already set butJOBCTL_TRAP_FREEZEis not.Quoted 8 lines
This sets
TIF_SIGPENDINGto ensure the task won't return to user mode, thus it callsget_signal().
get_signal()doesn't seeJOBCTL_TRAP_FREEZE, it notices ->frozen == T and doescgroup_leave_frozen(true)which clears ->frozen.Then the task calls
dequeue_signal(), clearsTIF_SIGPENDINGand returns to user mode?
Quoted 6 lines
Got it, a good catch! So if the freezer races with
vfork()completion, we might have a spuriousfrozen->unfrozen->frozentransition of the cgroup state.Switching to
cgroup_leave_frozen(false)seems to solve it, but I'm slightly concerned that we're basically putting the task in a busy loop between the settingCGRP_FREEZEand settingTRAP_FREEZE.
Yes, yes. Didn't I say I dislike the new ->frozen check in recalc() ? ;)
OK, how about the ABSOLUTELY UNTESTED patch below? For the start.
Oleg.
kernel/cgroup/freezer.c +7 -12
@@ -132,26 +132,21 @@ void cgroup_leave_frozen(bool always_leave) {struct cgroup *cgrp; +WARN_ON_ONCE (!current->frozen); +spin_lock_irq (&css_set_lock); cgrp =task_dfl_cgroup (current);if (always_leave || !test_bit (CGRP_FREEZE , &cgrp->flags)) {cgroup_dec_frozen_cnt (cgrp);cgroup_update_frozen (cgrp); -WARN_ON_ONCE (!current->frozen); current->frozen = false; + }else if (!(current->jobctl &JOBCTL_TRAP_FREEZE )) { +spin_lock (¤t->sighand->siglock); + current->jobctl |=JOBCTL_TRAP_FREEZE ; +set_thread_flag (TIF_SIGPENDING ); +spin_unlock (¤t->sighand->siglock); }spin_unlock_irq (&css_set_lock); - -if (unlikely (current->frozen)) { -/* -* If the task remained in the frozen state, -* make sure it won't reach userspace without -* entering the signal handling loop. -*/ -spin_lock_irq (¤t->sighand->siglock); -recalc_sigpending (); -spin_unlock_irq (¤t->sighand->siglock); - } }/*
kernel/signal.c +1 -1
@@ -2515,7 +2515,7 @@ bool get_signal(struct ksignal *ksig)*/ if (unlikely (cgroup_task_frozen (current))) {spin_unlock_irq (&sighand->siglock); -cgroup_leave_frozen (true); +cgroup_leave_frozen (false); goto relock; }
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 17 lines · Oleg Nesterov
Quoted 6 lines · Roman Gushchin
Quoted 6 lines
Quoted 5 lines
Hm, it might work too, but I'm not sure I like it more. IMO, the best option is to have a single
cgroup_leave_frozen(true)insignal.c, it's just simpler. If a user changed the desired state of cgroup twice, there is no need to avoid state transitions. Or maybe I don't see it yet.Quoted 7 lines
Quoted 2 lines
Then why do we need
cgroup_leave_frozen(false)inwait_for_vfork_done()? How does it differ fromget_signal()?We need it because sleeping in vfork is a special state which we want to account as frozen. And if the parent process wakes up while the cgroup is frozen (because of the child death, for example), we want to push it into the "proper" frozen state without changing the state of the cgroup.
Again, I do not see how
vfork()differs fromget_signal()in this respect.
Quoted 8 lines
Let me provide another example. A
TASK_STOPPEDtask reacts to SIGCONT and returns toget_signal(),current->frozenis true.If this races with
CGRP_FREEZE, the task should not return to user-space, just likevfork(). I see no difference.They differ in that
wait_for_vfork_done()should guarenteeTIF_SIGPENDINGin this case, but this is another story...
Right, I agree.
Quoted 17 lines
Quoted 17 lines
Quoted 5 lines
If nothing else. Suppose that
wait_for_vfork_done()calls leave(false) and this races with freezer,CGRP_FREEZEis already set butJOBCTL_TRAP_FREEZEis not.Quoted 8 lines
This sets
TIF_SIGPENDINGto ensure the task won't return to user mode, thus it callsget_signal().
get_signal()doesn't seeJOBCTL_TRAP_FREEZE, it notices ->frozen == T and doescgroup_leave_frozen(true)which clears ->frozen.Then the task calls
dequeue_signal(), clearsTIF_SIGPENDINGand returns to user mode?Got it, a good catch! So if the freezer races with
vfork()completion, we might have a spuriousfrozen->unfrozen->frozentransition of the cgroup state.
Quoted 7 lines
Quoted 3 lines
Switching to
cgroup_leave_frozen(false)seems to solve it, but I'm slightly concerned that we're basically putting the task in a busy loop between the settingCGRP_FREEZEand settingTRAP_FREEZE.Yes, yes. Didn't I say I dislike the new ->frozen check in
recalc()? ;)OK, how about the ABSOLUTELY UNTESTED patch below? For the start.
It looks good to me (and all freezer selftests pass).
Just to be sure, is it a solution to avoid the busy loop in the signal handling loop, right? Because it doesn't allow to drop the ->frozen check from recalc().
The JOBCTL_TRAP_FREEZE check without siglock initially looked dangerous to me, but after some thoughts I didn't find any case when it's wrong.
Do you prefer me to master a patch or to do it by yourself?
Thank you!
Roman
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 9 lines · Roman Gushchin
Quoted 2 lines · Oleg Nesterov
OK, how about the ABSOLUTELY UNTESTED patch below? For the start.
It looks good to me (and all freezer selftests pass).
Just to be sure, is it a solution to avoid the busy loop in the signal handling loop, right?
Yes,
Quoted 1 line
Because it doesn't allow to drop the ->frozen check from
recalc().
Yes. Because we can race with unfreeze after leave_frozen().
Quoted 2 lines
The
JOBCTL_TRAP_FREEZEcheck without siglock initially looked dangerous to me, but after some thoughts I didn't find any case when it's wrong.
I think this is fine... Yes, JOBCTL_TRAP_FREEZE can be already set when we take siglock, but I don't think we need to recheck this flag.
The only important thing (afaics) is that CGRP_FREEZE is stable under css_set_lock, so we can't wrongly set TRAP_FREEZE.
Quoted 1 line
Do you prefer me to master a patch
Yes please ;)
Oleg.
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
In reply to Roman GushchinReview void cgroup_exit(struct task_struct *tsk) · line 5934
Quoted 11 lines · Roman Gushchin
(quoted diff) +6
-0@@ -5830,6 +5926,12 @@ void cgroup_exit(struct task_struct *tsk) spin_lock_irq(&css_set_lock); css_set_move_task(tsk, cset, NULL, false); cset->nr_tasks--; + + if (unlikely(cgroup_task_frozen(tsk))) + cgroup_freezer_frozen_exit(tsk); + else if (unlikely(cgroup_task_freeze(tsk))) + cgroup_update_frozen(task_dfl_cgroup(tsk)); +
Now that I actually applied these patches, I can't understand how can cgroup_exit() hit cgroup_task_frozen(current)...
A cgroup_task_frozen() task must never return to user-mode or escape from get_signal() without leave_frozen() or we have a bug?
I must have missed something, could you explain why the patch below is wrong?
Oleg.
include/linux/cgroup.h +0 -1
@@ -889,7 +889,6 @@ void cgroup_update_frozen(struct cgroup *cgrp);void cgroup_freeze (struct cgroup *cgrp,bool freeze);void cgroup_freezer_migrate_task (struct task_struct *task,struct cgroup *src,struct cgroup *dst); -void cgroup_freezer_frozen_exit (struct task_struct *task);static inline bool cgroup_task_freeze (struct task_struct *task) {bool ret;
kernel/cgroup/cgroup.c +1 -4
@@ -6008,11 +6008,8 @@ void cgroup_exit(struct task_struct *tsk)css_set_move_task (tsk, cset,NULL , false); cset->nr_tasks--; -if (unlikely (cgroup_task_frozen (tsk))) -cgroup_freezer_frozen_exit (tsk); -else if (unlikely (cgroup_task_freeze (tsk))) +if (unlikely (cgroup_task_freeze (tsk)))cgroup_update_frozen (task_dfl_cgroup (tsk)); -spin_unlock_irq (&css_set_lock); }else {get_css_set (cset);
kernel/cgroup/freezer.c +0 -10
@@ -249,16 +249,6 @@ void cgroup_freezer_migrate_task(struct task_struct *task,cgroup_freeze_task (task,test_bit (CGRP_FREEZE , &dst->flags)); } -void cgroup_freezer_frozen_exit (struct task_struct *task) -{ -struct cgroup *cgrp =task_dfl_cgroup (task); - -lockdep_assert_held (&css_set_lock); - -cgroup_dec_frozen_cnt (cgrp); -cgroup_update_frozen (cgrp); -} -void cgroup_freeze (struct cgroup *cgrp,bool freeze) {struct cgroup_subsys_state *css;
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 12 lines · Oleg Nesterov
Review void cgroup_exit(struct task_struct *tsk) · line 5934
Quoted 11 lines · Roman Gushchin
(quoted diff) +6
-0@@ -5830,6 +5926,12 @@ void cgroup_exit(struct task_struct *tsk) spin_lock_irq(&css_set_lock); css_set_move_task(tsk, cset, NULL, false); cset->nr_tasks--; + + if (unlikely(cgroup_task_frozen(tsk))) + cgroup_freezer_frozen_exit(tsk); + else if (unlikely(cgroup_task_freeze(tsk))) + cgroup_update_frozen(task_dfl_cgroup(tsk)); +
Hello, Oleg!
A good catch! It's actually a leftover from one of the previous versions of the patchset. In the current iteration I'd replace it with:
WARN_ON_ONCE(cgroup_task_frozen(tsk));
just to make sure we're not leaking the frozen bit. Do you agree?
Thank you!
Roman
Re: [PATCH v10 4/9] cgroup: cgroup v2 freezer
Quoted 7 lines · Roman Gushchin
of the patchset. In the current iteration I'd replace it with:
WARN_ON_ONCE(cgroup_task_frozen(tsk));just to make sure we're not leaking the frozen bit. Do you agree?
Yes sure,
Oleg.
PATCH v10 5/9 kselftests: cgroup: don't fail on cg_kill_all() error in cg_destroy()
In reply to Roman GushchinIf the cgroup destruction races with an exit() of a belonging process(es), cg_kill_all() may fail. It's not a good reason to make cg_destroy() fail and leave the cgroup in place, potentially causing next test runs to fail.
--- tools/testing/selftests/cgroup/cgroup_util.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
tools/testing/selftests/cgroup/cgroup_util.c +1 -3
@@ -227,9 +227,7 @@ int cg_destroy(const char *cgroup) retry: ret =rmdir (cgroup);if (ret && errno ==EBUSY ) { - ret =cg_killall (cgroup); -if (ret) -return ret; +cg_killall (cgroup);usleep (100 ); goto retry; }
Signature · 1 line
2.20.1
PATCH v10 6/9 kselftests: cgroup: add freezer controller self-tests
In reply to Roman GushchinThis patch implements 9 tests for the freezer controller for cgroup v2:
1) a simple test, which aims to freeze and unfreeze a cgroup with 100 processes
2) a more complicated tree test, which creates a hierarchy of cgroups, puts some processes in some cgroups, and tries to freeze and unfreeze different parts of the subtree
3) a forkbomb test: the test aims to freeze a forkbomb running in a cgroup, kill all tasks in the cgroup and remove the cgroup without the unfreezing.
4) rmdir test: the test creates two nested cgroups, freezes the parent one, checks that the child can be successfully removed, and a new child can be created
5) migration tests: the test checks migration of a task between frozen cgroups: from a frozen to a running, from a running to a frozen, and from a frozen to a frozen.
6) ptrace test: the test checks that it's possible to attach to a process in a frozen cgroup, get some information and detach, and the cgroup will remain frozen.
7) stopped test: the test checks that it's possible to freeze a cgroup with a stopped task
8) ptraced test: the test checks that it's possible to freeze a cgroup with a ptraced task
9) vfork test: the test checks that it's possible to freeze a cgroup with a parent process waiting for the child process in vfork()
Expected output:
$ ./test_freezer ok 1 test_cgfreezer_simple ok 2 test_cgfreezer_tree ok 3 test_cgfreezer_forkbomb ok 4 test_cgrreezer_rmdir ok 5 test_cgfreezer_migrate ok 6 test_cgfreezer_ptrace ok 7 test_cgfreezer_stopped ok 8 test_cgfreezer_ptraced ok 9 test_cgfreezer_vfork
--- tools/testing/selftests/cgroup/.gitignore | 1 + tools/testing/selftests/cgroup/Makefile | 2 + tools/testing/selftests/cgroup/cgroup_util.c | 54 +- tools/testing/selftests/cgroup/cgroup_util.h | 5 + tools/testing/selftests/cgroup/test_freezer.c | 851 ++++++++++++++++++ 5 files changed, 912 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/cgroup/test_freezer.c
tools/testing/selftests/cgroup/.gitignore +1 -0
@@ -1,2 +1,3 @@ test_memcontrol test_core +test_freezer
tools/testing/selftests/cgroup/Makefile +2 -0
@@ -5,8 +5,10 @@ all: TEST_GEN_PROGS = test_memcontrol TEST_GEN_PROGS += test_core +TEST_GEN_PROGS += test_freezer include ../lib.mk $(OUTPUT)/test_memcontrol: cgroup_util.c $(OUTPUT)/test_core: cgroup_util.c +$(OUTPUT)/test_freezer: cgroup_util.c
tools/testing/selftests/cgroup/cgroup_util.c +53 -1
75 lines not on this page · raw message
tools/testing/selftests/cgroup/cgroup_util.h +5 -0
@@ -18,6 +18,7 @@ static inline int values_close(long a, long b, int err)extern int cg_find_unified_root (char *root,size_t len);extern char *cg_name (const char *root,const char *name);extern char *cg_name_indexed (const char *root,const char *name,int index); +extern char *cg_control (const char *cgroup,const char *control);extern int cg_create (const char *cgroup);extern int cg_destroy (const char *cgroup); extern int cg_read(const char *cgroup, const char *control,
@@ -32,6 +33,7 @@ extern int cg_write(const char *cgroup, const char *control, char *buf);extern int cg_run (const char *cgroup,int (*fn)(const char *cgroup,void *arg),void *arg); +extern int cg_enter (const char *cgroup,int pid);extern int cg_enter_current (const char *cgroup); extern int cg_run_nowait(const char *cgroup, int (*fn)(const char *cgroup, void *arg),
@@ -41,3 +43,6 @@ extern int alloc_pagecache(int fd, size_t size);extern int alloc_anon (const char *cgroup,void *arg);extern int is_swap_enabled (void );extern int set_oom_adj_score (int pid,int score); +extern int cg_wait_for_proc_count (const char *cgroup,int count); +extern int cg_killall (const char *cgroup); +extern char proc_read_text (int pid,const char *item,char *buf,size_t size);
tools/testing/selftests/cgroup/test_freezer.c new file +851 -0
851 lines not on this page · raw message
Signature · 1 line
2.20.1
Re: [PATCH v10 6/9] kselftests: cgroup: add freezer controller self-tests
Hi Roman,
Just want to share information here on what we notice on running this test case,
Quoted 3 lines · Roman Gushchin
This patch implements 9 tests for the freezer controller for cgroup v2:
...
Quoted 3 lines
6) ptrace test: the test checks that it's possible to attach to a process in a frozen cgroup, get some information and detach, and the cgroup will remain frozen.
selftests cgroup test_freezer failed because of the sys entry path not found.
Cgroup /sys/fs/cgroup/unified/cg_test_ptrace isn't frozen/sys/fs/cgroup/unified/cg_test_ptrace: isn't_frozen #
# not ok 6 test_cgfreezer_ptrace
This test case fails intermittently.
Test output:
-------------
# selftests cgroup test_freezer cgroup: test_freezer_ # # Cgroup /sys/fs/cgroup/unified/cg_test_ptrace isn't frozen /sys/fs/cgroup/unified/cg_test_ptrace: isn't_frozen # # ok 1 test_cgfreezer_simple 1: test_cgfreezer_simple_ # # ok 2 test_cgfreezer_tree 2: test_cgfreezer_tree_ # # ok 3 test_cgfreezer_forkbomb 3: test_cgfreezer_forkbomb_ # # ok 4 test_cgfreezer_rmdir 4: test_cgfreezer_rmdir_ # # ok 5 test_cgfreezer_migrate 5: test_cgfreezer_migrate_ # # not ok 6 test_cgfreezer_ptrace ok: 6_test_cgfreezer_ptrace # # ok 7 test_cgfreezer_stopped 7: test_cgfreezer_stopped_ # # ok 8 test_cgfreezer_ptraced 8: test_cgfreezer_ptraced_ # # ok 9 test_cgfreezer_vfork 9: test_cgfreezer_vfork_ # [FAIL] 3 selftests cgroup test_freezer selftests: cgroup_test_freezer [FAIL]
Test results link,
https://qa-reports.linaro.org/lkft/linux-mainline-oe/tests/kselftest/cgroup_test_freezer?page=1
- Naresh
Re: [PATCH v10 6/9] kselftests: cgroup: add freezer controller self-tests
Quoted 3 lines · Naresh Kamboju
Hi Roman,
Just want to share information here on what we notice on running this test case,
Hi Naresh!
Thank you for the report!
The interaction between ptrace and freezer is complicated (as ptrace in general),so there are known cases when spurious cgroup transitions (frozen <-> non frozen <-> frozen ) can happen.
Does it create any difficulties? If so, I'll send a diff to ignore the result of this test for now.
Thank you!
Quoted 11 lines · Roman Gushchin
Quoted 3 lines
This patch implements 9 tests for the freezer controller for cgroup v2:
...
Quoted 3 lines
6) ptrace test: the test checks that it's possible to attach to a process in a frozen cgroup, get some information and detach, and the cgroup will remain frozen.
selftests cgroup
test_freezerfailed because of the sys entry path not found.
Can you, please, elaborate on this?
Quoted 5 lines
Cgroup
/sys/fs/cgroup/unified/cg_test_ptraceisn't frozen/sys/fs/cgroup/unified/cg_test_ptrace: isn't_frozen ## not ok 6 test_cgfreezer_ptraceThis test case fails intermittently.
Thank you!
Re: [PATCH v10 6/9] kselftests: cgroup: add freezer controller self-tests
Hi Roman,
Quoted 3 lines
The interaction between ptrace and freezer is complicated (as ptrace in general),so there are known cases when spurious cgroup transitions (frozen <-> non frozen <-> frozen ) can happen.
When test ran for 8 times it got failed 5 times.
I have noticed intermittent failure on x86_64, arm64 hikey device and qemu_arm64.
Quoted 3 lines
Does it create any difficulties? If so, I'll send a diff to ignore the result of this test for now.
No difficulties. I will mark this test case as known failure.
Quoted 10 lines
Quoted 8 lines
Quoted 2 lines
This patch implements 9 tests for the freezer controller for cgroup v2:
...
Quoted 3 lines
6) ptrace test: the test checks that it's possible to attach to a process in a frozen cgroup, get some information and detach, and the cgroup will remain frozen.
selftests cgroup
test_freezerfailed because of the sys entry path not found.Can you, please, elaborate on this?
I see this failure output on screen and sharing log here.
Please find full test log here,
https://lkft.validation.linaro.org/scheduler/job/825346#L673
- Naresh
Re: [PATCH v10 6/9] kselftests: cgroup: add freezer controller self-tests
Quoted 9 lines · Naresh Kamboju
Hi Roman,
Quoted 3 lines
The interaction between ptrace and freezer is complicated (as ptrace in general),so there are known cases when spurious cgroup transitions (frozen <-> non frozen <-> frozen ) can happen.
When test ran for 8 times it got failed 5 times.
I have noticed intermittent failure onx86_64, arm64 hikey device andqemu_arm64.
Oh, it wasn't nearly as bad when I ran it. I'll take a look.
Quoted 6 lines
Quoted 4 lines
Does it create any difficulties? If so, I'll send a diff to ignore the result of this test for now.
No difficulties. I will mark this test case as known failure.
Sure.
Thanks!
PATCH v10 7/9 cgroup: make TRACE_CGROUP_PATH irq-safe
In reply to Roman GushchinTo use the TRACE_CGROUP_PATH() macro with css_set_lock locked, let's make the macro irq-safe.
It's necessary in order to trace cgroup freezer state transitions (frozen/not frozen), which are happening with css_set_lock locked.
--- kernel/cgroup/cgroup-internal.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
kernel/cgroup/cgroup-internal.h +5 -2
@@ -28,12 +28,15 @@ extern void __init enable_debug_cgroup(void);#define TRACE_CGROUP_PATH (type, cgrp, ...) \ do { \ if (trace_cgroup_##type##_enabled()) { \ - spin_lock(&trace_cgroup_path_lock); \ + unsigned long flags; \ + spin_lock_irqsave(&trace_cgroup_path_lock, \ + flags); \ cgroup_path(cgrp, trace_cgroup_path, \ TRACE_CGROUP_PATH_LEN); \ trace_cgroup_##type(cgrp, trace_cgroup_path, \ ##__VA_ARGS__); \ - spin_unlock(&trace_cgroup_path_lock); \ + spin_unlock_irqrestore(&trace_cgroup_path_lock, \ + flags); \ } \ } while (0)
Signature · 1 line
2.20.1
PATCH v10 8/9 cgroup: add tracing points for cgroup v2 freezer
In reply to Roman GushchinAdd cgroup:cgroup_freeze and cgroup:cgroup_unfreeze events, which are using the existing cgroup tracing infrastructure.
Add the cgroup_event event class, which is similar to the cgroup class, but contains an additional integer field to store a new value (the level field is dropped).
Also add two tracing events: cgroup_notify_populated and cgroup_notify_frozen, which are raised in a generic way using the TRACE_CGROUP_PATH() macro.
This allows to trace cgroup state transitions and is generally helpful for debugging the cgroup freezer code.
--- include/trace/events/cgroup.h | 55 +++++++++++++++++++++++++++++++++++ kernel/cgroup/cgroup.c | 2 ++ kernel/cgroup/freezer.c | 15 +++++++++- 3 files changed, 71 insertions(+), 1 deletion(-)
include/trace/events/cgroup.h +55 -0
@@ -103,6 +103,20 @@ DEFINE_EVENT(cgroup, cgroup_rename,TP_ARGS (cgrp, path) ); +DEFINE_EVENT (cgroup, cgroup_freeze, + + TP_PROTO(struct cgroup *cgrp, const char *path), + +TP_ARGS (cgrp, path) +); + +DEFINE_EVENT (cgroup, cgroup_unfreeze, + + TP_PROTO(struct cgroup *cgrp, const char *path), + +TP_ARGS (cgrp, path) +); + DECLARE_EVENT_CLASS(cgroup_migrate, TP_PROTO(struct cgroup *dst_cgrp, const char *path,
@@ -149,6 +163,47 @@ DEFINE_EVENT(cgroup_migrate, cgroup_transfer_tasks,TP_ARGS (dst_cgrp, path, task, threadgroup) ); +DECLARE_EVENT_CLASS (cgroup_event, + + TP_PROTO(struct cgroup *cgrp, const char *path, int val), + +TP_ARGS (cgrp, path, val), + +TP_STRUCT__entry ( + __field( int, root ) + __field( int, id ) + __field( int, level ) + __string( path, path ) +__field ( int, val ) + ), + +TP_fast_assign ( + __entry->root = cgrp->root->hierarchy_id; + __entry->id = cgrp->id; + __entry->level = cgrp->level; + __assign_str(path, path); + __entry->val = val; + ), + +TP_printk ("root=%d id=%d level=%d path=%s val=%d" , + __entry->root, __entry->id, __entry->level,__get_str (path), + __entry->val) +); + +DEFINE_EVENT (cgroup_event, cgroup_notify_populated, + + TP_PROTO(struct cgroup *cgrp, const char *path, int val), + +TP_ARGS (cgrp, path, val) +); + +DEFINE_EVENT (cgroup_event, cgroup_notify_frozen, + + TP_PROTO(struct cgroup *cgrp, const char *path, int val), + +TP_ARGS (cgrp, path, val) +); +#endif /* _TRACE_CGROUP_H */ /* This part must be outside protection */
kernel/cgroup/cgroup.c +2 -0
@@ -816,6 +816,8 @@ static void cgroup_update_populated(struct cgroup *cgrp, bool populated)break ;cgroup1_check_for_release (cgrp); +TRACE_CGROUP_PATH (notify_populated, cgrp, +cgroup_is_populated (cgrp));cgroup_file_notify (&cgrp->events_file); child = cgrp;
kernel/cgroup/freezer.c +14 -1
@@ -6,6 +6,8 @@#include "cgroup-internal.h" +#include <trace/events/cgroup.h> +/* * Propagate the cgroup frozen state upwards by the cgroup tree. */
@@ -28,6 +30,7 @@ static void cgroup_propagate_frozen(struct cgroup *cgrp, bool frozen) cgrp->nr_descendants) {set_bit (CGRP_FROZEN , &cgrp->flags);cgroup_file_notify (&cgrp->events_file); +TRACE_CGROUP_PATH (notify_frozen, cgrp,1 ); desc++; } }else {
@@ -35,6 +38,7 @@ static void cgroup_propagate_frozen(struct cgroup *cgrp, bool frozen)if (test_bit (CGRP_FROZEN , &cgrp->flags)) {clear_bit (CGRP_FROZEN , &cgrp->flags);cgroup_file_notify (&cgrp->events_file); +TRACE_CGROUP_PATH (notify_frozen, cgrp,0 ); desc++; } }
@@ -73,6 +77,7 @@ void cgroup_update_frozen(struct cgroup *cgrp)clear_bit (CGRP_FROZEN , &cgrp->flags); }cgroup_file_notify (&cgrp->events_file); +TRACE_CGROUP_PATH (notify_frozen, cgrp, frozen);/* Update the state of ancestor cgroups. */ cgroup_propagate_frozen (cgrp, frozen);
@@ -189,6 +194,11 @@ static void cgroup_do_freeze(struct cgroup *cgrp, bool freeze)clear_bit (CGRP_FREEZE , &cgrp->flags);spin_unlock_irq (&css_set_lock); +if (freeze) +TRACE_CGROUP_PATH (freeze, cgrp); +else +TRACE_CGROUP_PATH (unfreeze, cgrp); +css_task_iter_start (&cgrp->self,0 , &it);while ((task =css_task_iter_next (&it))) {/*
@@ -312,6 +322,9 @@ void cgroup_freeze(struct cgroup *cgrp, bool freeze)* In both cases it's better to notify a user, that there is * nothing to wait for. */ -if (!applied) +if (!applied) { +TRACE_CGROUP_PATH (notify_frozen, cgrp, +test_bit (CGRP_FROZEN , &cgrp->flags));cgroup_file_notify (&cgrp->events_file); + } }
Signature · 1 line
2.20.1
PATCH v10 9/9 cgroup: document cgroup v2 freezer interface
In reply to Roman GushchinDescribe cgroup v2 freezer interface in the cgroup v2 admin guide.
--- Documentation/admin-guide/cgroup-v2.rst | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
Documentation/admin-guide/cgroup-v2.rst +27 -0
@@ -864,6 +864,8 @@ All cgroup core files are prefixed with "cgroup." populated 1 if the cgroup or its descendants contains any live processes; otherwise, 0. + frozen + 1 if the cgroup is frozen; otherwise, 0. cgroup.max.descendants A read-write single value files. The default is "max".
@@ -897,6 +899,31 @@ All cgroup core files are prefixed with "cgroup." A dying cgroup can consume system resources not exceeding limits, which were active at the moment of cgroup deletion. + cgroup.freeze + A read-write single value file which exists on non-root cgroups. + Allowed values are "0" and "1". The default is "0". + + Writing "1" to the file causes freezing of the cgroup and all + descendant cgroups. This means that all belonging processes will + be stopped and will not run until the cgroup will be explicitly + unfrozen. Freezing of the cgroup may take some time; when this action + is completed, the "frozen" value in the cgroup.events control file + will be updated to "1" and the corresponding notification will be + issued. + + A cgroup can be frozen either by its own settings, or by settings + of any ancestor cgroups. If any of ancestor cgroups is frozen, the + cgroup will remain frozen. + + Processes in the frozen cgroup can be killed by a fatal signal. + They also can enter and leave a frozen cgroup: either by an explicit + move by a user, or if freezing of the cgroup races with fork(). + If a process is moved to a frozen cgroup, it stops. If a process is + moved out of a frozen cgroup, it becomes running. + + Frozen status of a cgroup doesn't affect any cgroup tree operations: + it's possible to delete a frozen (and empty) cgroup, as well as + create new sub-cgroups. Controllers ===========
Signature · 1 line
2.20.1
Tejun Heo
maintainerQuoted 7 lines · Roman Gushchin
This patchset implements freezer for cgroup v2.
It provides similar functionality as v1 freezer, but the interface conforms to the cgroup v2 interface design principles, and it provides a better user experience: tasks can be killed, ptrace works, there is no separate controller, which has to be enabled, etc.
Quoted 5 lines
Patches (1), (2) and (3) are some preparational work, patch (4) contains the implementation, patch (5) is a small cgroup kselftest fix, patch (6) covers freezer adds 6 new kselftests to cover the freezer functionality. Patchse (7) and (8) adding tracing points to simplify the debugging process. Patch (9) adds corresponding docs.
All patches applied to cgroup/for-5.2 w/ Oleg's
No-objection-from-me-by added to (4).
Thanks.
Signature · 1 line
tejun