summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-09 23:23:35 +0200
committerMark Brown <broonie@kernel.org>2026-10-09 23:23:35 +0200
commit6f73627fc3ffefcb6646fa2ebd18baf05a97f746 (patch)
tree1f8a796873e19de2daa8771b8557e5a9dab42e4d /kernel
parent0404392860ac0c23cbf61f6886178d193a6d2eb0 (diff)
parent75b768f035ccafe5c76ea10883474fdecccef56f (diff)
downloadlinux-next-6f73627fc3ffefcb6646fa2ebd18baf05a97f746.tar.gz
linux-next-6f73627fc3ffefcb6646fa2ebd18baf05a97f746.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git
Diffstat (limited to 'kernel')
-rw-r--r--kernel/cgroup/cgroup-internal.h2
-rw-r--r--kernel/cgroup/cgroup-v1.c26
-rw-r--r--kernel/cgroup/cgroup.c181
-rw-r--r--kernel/cgroup/cpuset-internal.h8
-rw-r--r--kernel/cgroup/cpuset-v1.c59
-rw-r--r--kernel/cgroup/cpuset.c145
-rw-r--r--kernel/cgroup/debug.c18
-rw-r--r--kernel/cgroup/dmem.c4
-rw-r--r--kernel/cgroup/freezer.c5
-rw-r--r--kernel/cgroup/namespace.c2
10 files changed, 216 insertions, 234 deletions
diff --git a/kernel/cgroup/cgroup-internal.h b/kernel/cgroup/cgroup-internal.h
index 7c367c8d0cbe..233fb8b45d2c 100644
--- a/kernel/cgroup/cgroup-internal.h
+++ b/kernel/cgroup/cgroup-internal.h
@@ -254,7 +254,7 @@ void cgroup_procs_write_finish(struct task_struct *task,
enum cgroup_attach_lock_mode lock_mode)
__releases(&cgroup_threadgroup_rwsem);
-void cgroup_lock_and_drain_offline(struct cgroup *cgrp);
+int cgroup_lock_and_drain_offline(struct cgroup *cgrp);
int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode);
int cgroup_rmdir(struct kernfs_node *kn);
diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c
index cbcde5c28b18..7f55a7bc8402 100644
--- a/kernel/cgroup/cgroup-v1.c
+++ b/kernel/cgroup/cgroup-v1.c
@@ -506,8 +506,8 @@ static ssize_t __cgroup1_procs_write(struct kernfs_open_file *of,
enum cgroup_attach_lock_mode lock_mode;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENODEV;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
task = cgroup_procs_write_start(buf, threadgroup, &lock_mode);
ret = PTR_ERR_OR_ZERO(task);
@@ -569,8 +569,8 @@ static ssize_t cgroup_release_agent_write(struct kernfs_open_file *of,
return -EPERM;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENODEV;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
spin_lock(&release_agent_path_lock);
strscpy(cgrp->root->release_agent_path, strstrip(buf),
sizeof(cgrp->root->release_agent_path));
@@ -605,10 +605,7 @@ static u64 cgroup_read_notify_on_release(struct cgroup_subsys_state *css,
static int cgroup_write_notify_on_release(struct cgroup_subsys_state *css,
struct cftype *cft, u64 val)
{
- if (val)
- set_bit(CGRP_NOTIFY_ON_RELEASE, &css->cgroup->flags);
- else
- clear_bit(CGRP_NOTIFY_ON_RELEASE, &css->cgroup->flags);
+ assign_bit(CGRP_NOTIFY_ON_RELEASE, &css->cgroup->flags, val);
return 0;
}
@@ -621,10 +618,7 @@ static u64 cgroup_clone_children_read(struct cgroup_subsys_state *css,
static int cgroup_clone_children_write(struct cgroup_subsys_state *css,
struct cftype *cft, u64 val)
{
- if (val)
- set_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags);
- else
- clear_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags);
+ assign_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags, val);
return 0;
}
@@ -1097,7 +1091,9 @@ int cgroup1_reconfigure(struct fs_context *fc)
int ret = 0;
u32 added_mask, removed_mask;
- cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
+ ret = cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
+ if (unlikely(ret))
+ return ret;
/* See what subsystems are wanted */
ret = check_cgroupfs_options(fc);
@@ -1262,7 +1258,9 @@ int cgroup1_get_tree(struct fs_context *fc)
if (!ns_capable(ctx->ns->user_ns, CAP_SYS_ADMIN))
return -EPERM;
- cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
+ ret = cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
+ if (unlikely(ret))
+ return ret;
ret = cgroup1_root_to_use(fc);
if (!ret && !percpu_ref_tryget_live(&ctx->root->cgrp.self.refcnt))
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 227d09704ca5..14634246a666 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -548,20 +548,11 @@ static struct cgroup_subsys_state *cgroup_e_css_by_mask(struct cgroup *cgrp,
struct cgroup_subsys_state *cgroup_e_css(struct cgroup *cgrp,
struct cgroup_subsys *ss)
{
- struct cgroup_subsys_state *css;
-
if (!CGROUP_HAS_SUBSYS_CONFIG)
return NULL;
- do {
- css = cgroup_css(cgrp, ss);
-
- if (css)
- return css;
- cgrp = cgroup_parent(cgrp);
- } while (cgrp);
-
- return init_css_set.subsys[ss->id];
+ return rcu_dereference_check(cgrp->e_css[ss->id],
+ lockdep_is_held(&cgroup_mutex));
}
/**
@@ -585,17 +576,10 @@ struct cgroup_subsys_state *cgroup_get_e_css(struct cgroup *cgrp,
rcu_read_lock();
- do {
- css = cgroup_css(cgrp, ss);
-
- if (css && css_tryget_online(css))
- goto out_unlock;
- cgrp = cgroup_parent(cgrp);
- } while (cgrp);
+ css = cgroup_e_css(cgrp, ss);
+ while (!css_tryget_online(css))
+ css = cgroup_e_css(cgroup_parent(css->cgroup), ss);
- css = init_css_set.subsys[ss->id];
- css_get(css);
-out_unlock:
rcu_read_unlock();
return css;
}
@@ -1379,7 +1363,10 @@ static void cgroup_destroy_root(struct cgroup_root *root)
trace_cgroup_destroy_root(root);
- cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
+ /* runs off a workqueue, no signal can interrupt the drain */
+ ret = cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
+ if (WARN_ON_ONCE(ret))
+ cgroup_lock();
BUG_ON(atomic_read(&root->nr_cgrps));
BUG_ON(!list_empty(&cgrp->self.children));
@@ -1685,9 +1672,10 @@ void cgroup_kn_unlock(struct kernfs_node *kn)
* This helper is to be used by a cgroup kernfs method currently servicing
* @kn. It breaks the active protection, performs cgroup locking and
* verifies that the associated cgroup is alive. Returns the cgroup if
- * alive; otherwise, %NULL. A successful return should be undone by a
- * matching cgroup_kn_unlock() invocation. If @drain_offline is %true, the
- * cgroup is drained of offlining csses before return.
+ * alive; otherwise, an ERR_PTR value. A successful return should be undone by
+ * a matching cgroup_kn_unlock() invocation. If @drain_offline is %true, the
+ * cgroup is drained of offlining csses before return, and an interrupted drain
+ * fails with -ERESTARTSYS.
*
* Any cgroup kernfs method implementation which requires locking the
* associated cgroup should use this helper. It avoids nesting cgroup
@@ -1697,6 +1685,7 @@ void cgroup_kn_unlock(struct kernfs_node *kn)
struct cgroup *cgroup_kn_lock_live(struct kernfs_node *kn, bool drain_offline)
{
struct cgroup *cgrp;
+ int ret;
if (kernfs_type(kn) == KERNFS_DIR)
cgrp = kn->priv;
@@ -1710,19 +1699,25 @@ struct cgroup *cgroup_kn_lock_live(struct kernfs_node *kn, bool drain_offline)
* break the active_ref protection.
*/
if (!cgroup_tryget(cgrp))
- return NULL;
+ return ERR_PTR(-ENODEV);
kernfs_break_active_protection(kn);
- if (drain_offline)
- cgroup_lock_and_drain_offline(cgrp);
- else
+ if (drain_offline) {
+ ret = cgroup_lock_and_drain_offline(cgrp);
+ if (unlikely(ret)) {
+ kernfs_unbreak_active_protection(kn);
+ cgroup_put(cgrp);
+ return ERR_PTR(ret);
+ }
+ } else {
cgroup_lock();
+ }
if (!cgroup_is_dead(cgrp))
return cgrp;
cgroup_kn_unlock(kn);
- return NULL;
+ return ERR_PTR(-ENODEV);
}
static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
@@ -2131,12 +2126,24 @@ void init_cgroup_root(struct cgroup_fs_context *ctx)
{
struct cgroup_root *root = ctx->root;
struct cgroup *cgrp = &root->cgrp;
+ struct cgroup_subsys *ss;
+ int ssid;
INIT_LIST_HEAD_RCU(&root->root_list);
atomic_set(&root->nr_cgrps, 1);
cgrp->root = root;
init_cgroup_housekeeping(cgrp);
+ /*
+ * A root cgroup's effective css is always the root css in
+ * init_css_set.subsys[], whichever hierarchy the subsystem is bound
+ * to, so rebind_subsystems() doesn't need to update e_css[]. For
+ * cgrp_dfl_root this runs before the root csses exist, and
+ * online_css() sets the entries when they come online.
+ */
+ for_each_subsys(ss, ssid)
+ RCU_INIT_POINTER(cgrp->e_css[ssid], init_css_set.subsys[ssid]);
+
/* DYNMODS must be modified through cgroup_favor_dynmods() */
root->flags = ctx->flags & ~CGRP_ROOT_FAVOR_DYNMODS;
if (ctx->release_agent)
@@ -3320,16 +3327,20 @@ out_finish:
* @cgrp: root of the target subtree
*
* Because css offlining is asynchronous, userland may try to re-enable a
- * controller while the previous css is still around. This function grabs
- * cgroup_mutex and drains the previous css instances of @cgrp's subtree.
+ * controller while the previous css is still around. This function grabs
+ * cgroup_mutex and waits until no css in @cgrp's subtree is dying. A dying css
+ * offlines only after every task that still pins it has finished exiting, which
+ * can take arbitrarily long, so the wait is interruptible.
+ *
+ * Returns 0 with cgroup_mutex held once the subtree is drained, or -ERESTARTSYS
+ * without it if interrupted by a signal.
*/
-void cgroup_lock_and_drain_offline(struct cgroup *cgrp)
- __acquires(&cgroup_mutex)
+int cgroup_lock_and_drain_offline(struct cgroup *cgrp)
{
struct cgroup *dsct;
struct cgroup_subsys_state *d_css;
struct cgroup_subsys *ss;
- int ssid;
+ int ssid, ret;
restart:
cgroup_lock();
@@ -3343,17 +3354,20 @@ restart:
continue;
cgroup_get_live(dsct);
- prepare_to_wait(&dsct->offline_waitq, &wait,
- TASK_UNINTERRUPTIBLE);
-
+ ret = prepare_to_wait_event(&dsct->offline_waitq, &wait,
+ TASK_INTERRUPTIBLE);
cgroup_unlock();
- schedule();
+ if (!ret)
+ schedule();
finish_wait(&dsct->offline_waitq, &wait);
-
cgroup_put(dsct);
+ if (unlikely(ret))
+ return ret;
goto restart;
}
}
+
+ return 0;
}
/**
@@ -3650,8 +3664,8 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of,
}
cgrp = cgroup_kn_lock_live(of->kn, true);
- if (!cgrp)
- return -ENODEV;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
for_each_subsys(ss, ssid) {
if (enable & (1 << ssid)) {
@@ -3790,8 +3804,8 @@ static ssize_t cgroup_type_write(struct kernfs_open_file *of, char *buf,
/* drain dying csses before we re-apply (threaded) subtree control */
cgrp = cgroup_kn_lock_live(of->kn, true);
- if (!cgrp)
- return -ENOENT;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
/* threaded can only be enabled */
ret = cgroup_enable_threaded(cgrp);
@@ -3833,8 +3847,8 @@ static ssize_t cgroup_max_descendants_write(struct kernfs_open_file *of,
return -ERANGE;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENOENT;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
WRITE_ONCE(cgrp->max_descendants, descendants);
@@ -3876,8 +3890,8 @@ static ssize_t cgroup_max_depth_write(struct kernfs_open_file *of,
return -ERANGE;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENOENT;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
WRITE_ONCE(cgrp->max_depth, depth);
@@ -4075,8 +4089,8 @@ static ssize_t pressure_write(struct kernfs_open_file *of, char *buf,
ssize_t ret = 0;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENODEV;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
ctx = of->priv;
if (!ctx) {
@@ -4192,8 +4206,8 @@ static ssize_t cgroup_pressure_write(struct kernfs_open_file *of,
return -ERANGE;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENOENT;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
psi = cgroup_psi(cgrp);
if (psi->enabled != enable) {
@@ -4268,8 +4282,8 @@ static ssize_t cgroup_freeze_write(struct kernfs_open_file *of,
return -ERANGE;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENOENT;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
cgroup_freeze(cgrp, freeze);
@@ -4330,8 +4344,8 @@ static ssize_t cgroup_kill_write(struct kernfs_open_file *of, char *buf,
return -ERANGE;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENOENT;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
/*
* Killing is a process directed operation, i.e. the whole thread-group
@@ -5488,8 +5502,8 @@ static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf,
enum cgroup_attach_lock_mode lock_mode;
dst_cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!dst_cgrp)
- return -ENODEV;
+ if (IS_ERR(dst_cgrp))
+ return PTR_ERR(dst_cgrp);
task = cgroup_procs_write_start(buf, threadgroup, &lock_mode);
ret = PTR_ERR_OR_ZERO(task);
@@ -5859,6 +5873,22 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
BUG_ON(cgroup_css(cgrp, ss));
}
+static void cgroup_update_e_css(struct cgroup *cgrp, struct cgroup_subsys *ss)
+{
+ struct cgroup_subsys_state *d_css;
+
+ lockdep_assert_held(&cgroup_mutex);
+
+ css_for_each_descendant_pre(d_css, &cgrp->self) {
+ struct cgroup *dsct = d_css->cgroup;
+ struct cgroup_subsys_state *css = cgroup_css(dsct, ss);
+
+ if (!css)
+ css = cgroup_e_css(cgroup_parent(dsct), ss);
+ rcu_assign_pointer(dsct->e_css[ss->id], css);
+ }
+}
+
/* invoke ->css_online() on a new CSS and mark it online if successful */
static int online_css(struct cgroup_subsys_state *css)
{
@@ -5872,6 +5902,7 @@ static int online_css(struct cgroup_subsys_state *css)
if (!ret) {
css->flags |= CSS_ONLINE;
rcu_assign_pointer(css->cgroup->subsys[ss->id], css);
+ cgroup_update_e_css(css->cgroup, ss);
atomic_inc(&css->online_cnt);
if (css->parent) {
@@ -5898,6 +5929,7 @@ static void offline_css(struct cgroup_subsys_state *css)
css->flags &= ~CSS_ONLINE;
RCU_INIT_POINTER(css->cgroup->subsys[ss->id], NULL);
+ cgroup_update_e_css(css->cgroup, ss);
wake_up_all(&css->cgroup->offline_waitq);
}
@@ -5969,6 +6001,7 @@ static struct cgroup *cgroup_create(struct cgroup *parent, const char *name,
{
struct cgroup_root *root = parent->root;
struct cgroup *cgrp, *tcgrp;
+ struct cgroup_subsys *ss;
struct kernfs_node *kn;
int i, level = parent->level + 1;
int ret;
@@ -6013,6 +6046,9 @@ static struct cgroup *cgroup_create(struct cgroup *parent, const char *name,
for (tcgrp = cgrp; tcgrp; tcgrp = cgroup_parent(tcgrp))
cgrp->ancestors[tcgrp->level] = tcgrp;
+ for_each_subsys(ss, i)
+ RCU_INIT_POINTER(cgrp->e_css[i], cgroup_e_css(parent, ss));
+
/*
* New cgroup inherits effective freeze counter, and
* if the parent has to be frozen, the child has too.
@@ -6123,8 +6159,8 @@ int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode)
return -EINVAL;
parent = cgroup_kn_lock_live(parent_kn, false);
- if (!parent)
- return -ENODEV;
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
if (!cgroup_check_hierarchy_limits(parent)) {
ret = -EAGAIN;
@@ -6400,7 +6436,7 @@ int cgroup_rmdir(struct kernfs_node *kn)
int ret = 0;
cgrp = cgroup_kn_lock_live(kn, false);
- if (!cgrp)
+ if (IS_ERR(cgrp))
return 0;
ret = cgroup_destroy_locked(cgrp);
@@ -7197,15 +7233,19 @@ static void do_cgroup_task_dead(struct task_struct *tsk)
* the cgroup and task_struct can be pinned indefinitely. Bounce through lazy
* irq_work to allow batching while ensuring timely completion.
*/
-static DEFINE_PER_CPU(struct llist_head, cgrp_dead_tasks);
-static DEFINE_PER_CPU(struct irq_work, cgrp_dead_tasks_iwork);
+struct cgroup_dead {
+ struct irq_work iwork;
+ struct llist_head tasks;
+};
+static DEFINE_PER_CPU(struct cgroup_dead, cgroup_dead);
static void cgrp_dead_tasks_iwork_fn(struct irq_work *iwork)
{
+ struct cgroup_dead *cgrp_dead = container_of(iwork, struct cgroup_dead, iwork);
struct llist_node *lnode;
struct task_struct *task, *next;
- lnode = llist_del_all(this_cpu_ptr(&cgrp_dead_tasks));
+ lnode = llist_del_all(&cgrp_dead->tasks);
llist_for_each_entry_safe(task, next, lnode, cg_dead_lnode) {
do_cgroup_task_dead(task);
put_task_struct(task);
@@ -7217,17 +7257,20 @@ static void __init cgroup_rt_init(void)
int cpu;
for_each_possible_cpu(cpu) {
- init_llist_head(per_cpu_ptr(&cgrp_dead_tasks, cpu));
- per_cpu(cgrp_dead_tasks_iwork, cpu) =
- IRQ_WORK_INIT_LAZY(cgrp_dead_tasks_iwork_fn);
+ struct cgroup_dead *cgrp_dead = per_cpu_ptr(&cgroup_dead, cpu);
+
+ init_llist_head(&cgrp_dead->tasks);
+ cgrp_dead->iwork = IRQ_WORK_INIT_LAZY(cgrp_dead_tasks_iwork_fn);
}
}
void cgroup_task_dead(struct task_struct *task)
{
+ struct cgroup_dead *cgrp_dead = this_cpu_ptr(&cgroup_dead);
+
get_task_struct(task);
- llist_add(&task->cg_dead_lnode, this_cpu_ptr(&cgrp_dead_tasks));
- irq_work_queue(this_cpu_ptr(&cgrp_dead_tasks_iwork));
+ llist_add(&task->cg_dead_lnode, &cgrp_dead->tasks);
+ irq_work_queue(&cgrp_dead->iwork);
}
#else /* CONFIG_PREEMPT_RT */
static void __init cgroup_rt_init(void) {}
diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-internal.h
index e7d010661fd3..5da9c91295b0 100644
--- a/kernel/cgroup/cpuset-internal.h
+++ b/kernel/cgroup/cpuset-internal.h
@@ -304,7 +304,9 @@ void cpuset_callback_lock_irq(void);
void cpuset_callback_unlock_irq(void);
void cpuset_update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus);
void cpuset_update_tasks_nodemask(struct cpuset *cs);
-int cpuset_update_flag(cpuset_flagbits_t bit, struct cpuset *cs, int turning_on);
+struct cpuset *dup_or_alloc_cpuset(struct cpuset *cs);
+void free_cpuset(struct cpuset *cs);
+int validate_change(struct cpuset *cur, struct cpuset *trial);
ssize_t cpuset_write_resmask(struct kernfs_open_file *of,
char *buf, size_t nbytes, loff_t off);
int cpuset_common_seq_show(struct seq_file *sf, void *v);
@@ -318,7 +320,6 @@ void cpuset_full_unlock(void);
extern struct cftype cpuset1_files[];
void cpuset1_update_task_spread_flags(struct cpuset *cs,
struct task_struct *tsk);
-void cpuset1_update_tasks_flags(struct cpuset *cs);
void cpuset1_hotplug_update_tasks(struct cpuset *cs,
struct cpumask *new_cpus, nodemask_t *new_mems,
bool cpus_updated, bool mems_updated);
@@ -328,11 +329,11 @@ void cpuset1_init(struct cpuset *cs);
void cpuset1_online_css(struct cgroup_subsys_state *css);
int cpuset1_generate_sched_domains(cpumask_var_t **domains,
struct sched_domain_attr **attributes);
+void cpuset1_offline_css(struct cpuset *cs);
#else
static inline void cpuset1_update_task_spread_flags(struct cpuset *cs,
struct task_struct *tsk) {}
-static inline void cpuset1_update_tasks_flags(struct cpuset *cs) {}
static inline void cpuset1_hotplug_update_tasks(struct cpuset *cs,
struct cpumask *new_cpus, nodemask_t *new_mems,
bool cpus_updated, bool mems_updated) {}
@@ -344,6 +345,7 @@ static inline void cpuset1_init(struct cpuset *cs) {}
static inline void cpuset1_online_css(struct cgroup_subsys_state *css) {}
static inline int cpuset1_generate_sched_domains(cpumask_var_t **domains,
struct sched_domain_attr **attributes) { return 0; };
+static inline void cpuset1_offline_css(struct cpuset *cs) {}
#endif /* CONFIG_CPUSETS_V1 */
diff --git a/kernel/cgroup/cpuset-v1.c b/kernel/cgroup/cpuset-v1.c
index 562ad35f00d0..7c7f2887dfaf 100644
--- a/kernel/cgroup/cpuset-v1.c
+++ b/kernel/cgroup/cpuset-v1.c
@@ -229,7 +229,7 @@ void cpuset1_update_task_spread_flags(struct cpuset *cs,
* function is called with cpuset_mutex held, cpuset membership stays
* stable.
*/
-void cpuset1_update_tasks_flags(struct cpuset *cs)
+static void cpuset1_update_tasks_flags(struct cpuset *cs)
{
struct css_task_iter it;
struct task_struct *task;
@@ -473,6 +473,51 @@ static u64 cpuset_read_u64(struct cgroup_subsys_state *css, struct cftype *cft)
return 0;
}
+/*
+ * cpuset_update_flag - read a 0 or a 1 in a file and update associated flag
+ * bit: the bit to update (see cpuset_flagbits_t)
+ * cs: the cpuset to update
+ * turning_on: whether the flag is being set or cleared
+ *
+ * Call with cpuset_mutex held.
+ */
+static int cpuset_update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
+ int turning_on)
+{
+ struct cpuset *trialcs;
+ int balance_flag_changed;
+ int spread_page_changed;
+ int err;
+
+ trialcs = dup_or_alloc_cpuset(cs);
+ if (!trialcs)
+ return -ENOMEM;
+
+ assign_bit(bit, &trialcs->flags, turning_on);
+
+ err = validate_change(cs, trialcs);
+ if (err < 0)
+ goto out;
+
+ balance_flag_changed = (is_sched_load_balance(cs) !=
+ is_sched_load_balance(trialcs));
+
+ spread_page_changed = is_spread_page(cs) != is_spread_page(trialcs);
+
+ cpuset_callback_lock_irq();
+ cs->flags = trialcs->flags;
+ cpuset_callback_unlock_irq();
+
+ if (!cpumask_empty(trialcs->cpus_allowed) && balance_flag_changed)
+ rebuild_sched_domains_locked();
+
+ if (spread_page_changed)
+ cpuset1_update_tasks_flags(cs);
+out:
+ free_cpuset(trialcs);
+ return err;
+}
+
static int cpuset_write_u64(struct cgroup_subsys_state *css, struct cftype *cft,
u64 val)
{
@@ -581,6 +626,18 @@ void cpuset1_online_css(struct cgroup_subsys_state *css)
cpuset_callback_unlock_irq();
}
+/*
+ * Legacy-hierarchy handling when a cpuset is taken offline: if it had
+ * sched_load_balance enabled, turn it off so the scheduler domains are
+ * rebuilt. A no-op on the default hierarchy.
+ */
+void cpuset1_offline_css(struct cpuset *cs)
+{
+ if (!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) &&
+ is_sched_load_balance(cs))
+ cpuset_update_flag(CS_SCHED_LOAD_BALANCE, cs, 0);
+}
+
static void
update_domain_attr(struct sched_domain_attr *dattr, struct cpuset *c)
{
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 8b220e9df91e..0ebba2d646c3 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -168,7 +168,6 @@ static cpumask_var_t isolated_hk_cpus; /* T */
* It can be set in
* - update_partition_sd_lb()
* - update_cpumasks_hier()
- * - cpuset_update_flag()
* - cpuset_hotplug_update_tasks()
* - cpuset_handle_hotplug()
*
@@ -629,7 +628,7 @@ static inline void free_tmpmasks(struct tmpmasks *tmp)
*
* Return: Pointer to newly allocated cpuset on success, NULL on failure
*/
-static struct cpuset *dup_or_alloc_cpuset(struct cpuset *cs)
+struct cpuset *dup_or_alloc_cpuset(struct cpuset *cs)
{
struct cpuset *trial;
@@ -670,7 +669,7 @@ static struct cpuset *dup_or_alloc_cpuset(struct cpuset *cs)
* free_cpuset - free the cpuset
* @cs: the cpuset to be freed
*/
-static inline void free_cpuset(struct cpuset *cs)
+void free_cpuset(struct cpuset *cs)
{
free_cpumask_var(cs->cpus_allowed);
free_cpumask_var(cs->effective_cpus);
@@ -764,7 +763,7 @@ static inline bool mems_excl_conflict(struct cpuset *cs1, struct cpuset *cs2)
* Return 0 if valid, -errno if not.
*/
-static int validate_change(struct cpuset *cur, struct cpuset *trial)
+int validate_change(struct cpuset *cur, struct cpuset *trial)
{
struct cgroup_subsys_state *css;
struct cpuset *c, *par;
@@ -792,8 +791,8 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial)
* For v1, effective_cpus == cpus_allowed & user_xcpus() returns
* cpus_allowed.
*
- * For v2, is_cpu_exclusive() & is_sched_load_balance() are true only
- * for non-isolated partition root. At this point, the target
+ * For v2, is_partition_valid(cur) & is_sched_load_balance() are true
+ * only for non-isolated partition root. At this point, the target
* effective_cpus isn't computed yet. user_xcpus() is the best
* approximation.
*
@@ -802,7 +801,8 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial)
* becomes an issue.
*/
ret = -EBUSY;
- if (is_cpu_exclusive(cur) && is_sched_load_balance(cur) &&
+ if ((is_partition_valid(cur) || (!cpuset_v2() && is_cpu_exclusive(cur))) &&
+ is_sched_load_balance(cur) &&
!cpuset_cpumask_can_shrink(cur->effective_cpus, user_xcpus(trial)))
goto out;
@@ -1195,25 +1195,6 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs,
struct tmpmasks *tmp);
/*
- * Update partition exclusive flag
- *
- * Return: 0 if successful, an error code otherwise
- */
-static int update_partition_exclusive_flag(struct cpuset *cs, int new_prs)
-{
- bool exclusive = (new_prs > PRS_MEMBER);
-
- if (exclusive && !is_cpu_exclusive(cs)) {
- if (cpuset_update_flag(CS_CPU_EXCLUSIVE, cs, 1))
- return PERR_NOTEXCL;
- } else if (!exclusive && is_cpu_exclusive(cs)) {
- /* Turning off CS_CPU_EXCLUSIVE will not return error */
- cpuset_update_flag(CS_CPU_EXCLUSIVE, cs, 0);
- }
- return 0;
-}
-
-/*
* Update partition load balance flag and/or rebuild sched domain
*
* Changing load balance flag will automatically call
@@ -1237,10 +1218,7 @@ static void update_partition_sd_lb(struct cpuset *cs, int old_prs)
}
if (new_lb != !!is_sched_load_balance(cs)) {
rebuild_domains = true;
- if (new_lb)
- set_bit(CS_SCHED_LOAD_BALANCE, &cs->flags);
- else
- clear_bit(CS_SCHED_LOAD_BALANCE, &cs->flags);
+ assign_bit(CS_SCHED_LOAD_BALANCE, &cs->flags, new_lb);
}
if (rebuild_domains)
@@ -1271,11 +1249,9 @@ static void reset_partition_data(struct cpuset *cs)
lockdep_assert_held(&callback_lock);
- if (cpumask_empty(cs->exclusive_cpus)) {
+ if (cpumask_empty(cs->exclusive_cpus))
cpumask_clear(cs->effective_xcpus);
- if (is_cpu_exclusive(cs))
- clear_bit(CS_CPU_EXCLUSIVE, &cs->flags);
- }
+
if (!cpumask_and(cs->effective_cpus, parent->effective_cpus, cs->cpus_allowed))
cpumask_copy(cs->effective_cpus, parent->effective_cpus);
}
@@ -2053,19 +2029,6 @@ write_error:
return 0;
/*
- * Transitioning between invalid to valid or vice versa may require
- * changing CS_CPU_EXCLUSIVE. In the case of partcmd_update,
- * validate_change() has already been successfully called and
- * CPU lists in cs haven't been updated yet. So defer it to later.
- */
- if ((old_prs != new_prs) && (cmd != partcmd_update)) {
- int err = update_partition_exclusive_flag(cs, new_prs);
-
- if (err)
- return err;
- }
-
- /*
* Change the parent's effective_cpus & effective_xcpus (top cpuset
* only).
*
@@ -2087,9 +2050,6 @@ write_error:
spin_unlock_irq(&callback_lock);
- if ((old_prs != new_prs) && (cmd == partcmd_update))
- update_partition_exclusive_flag(cs, new_prs);
-
if (adding || deleting) {
cpuset_update_tasks_cpumask(parent, tmp->addmask);
update_sibling_cpumasks(parent, cs, tmp);
@@ -2330,10 +2290,8 @@ get_css:
*/
if (cpuset_v2() && !is_partition_valid(cp) &&
(is_sched_load_balance(parent) != is_sched_load_balance(cp))) {
- if (is_sched_load_balance(parent))
- set_bit(CS_SCHED_LOAD_BALANCE, &cp->flags);
- else
- clear_bit(CS_SCHED_LOAD_BALANCE, &cp->flags);
+ assign_bit(CS_SCHED_LOAD_BALANCE, &cp->flags,
+ is_sched_load_balance(parent));
}
/*
@@ -2891,59 +2849,6 @@ bool current_cpuset_is_being_rebound(void)
return ret;
}
-/*
- * cpuset_update_flag - read a 0 or a 1 in a file and update associated flag
- * bit: the bit to update (see cpuset_flagbits_t)
- * cs: the cpuset to update
- * turning_on: whether the flag is being set or cleared
- *
- * Call with cpuset_mutex held.
- */
-
-int cpuset_update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
- int turning_on)
-{
- struct cpuset *trialcs;
- int balance_flag_changed;
- int spread_page_changed;
- int err;
-
- trialcs = dup_or_alloc_cpuset(cs);
- if (!trialcs)
- return -ENOMEM;
-
- if (turning_on)
- set_bit(bit, &trialcs->flags);
- else
- clear_bit(bit, &trialcs->flags);
-
- err = validate_change(cs, trialcs);
- if (err < 0)
- goto out;
-
- balance_flag_changed = (is_sched_load_balance(cs) !=
- is_sched_load_balance(trialcs));
-
- spread_page_changed = is_spread_page(cs) != is_spread_page(trialcs);
-
- spin_lock_irq(&callback_lock);
- cs->flags = trialcs->flags;
- spin_unlock_irq(&callback_lock);
-
- if (!cpumask_empty(trialcs->cpus_allowed) && balance_flag_changed) {
- if (cpuset_v2())
- cpuset_force_rebuild();
- else
- rebuild_sched_domains_locked();
- }
-
- if (spread_page_changed)
- cpuset1_update_tasks_flags(cs);
-out:
- free_cpuset(trialcs);
- return err;
-}
-
/**
* update_prstate - update partition_root_state
* @cs: the cpuset to update
@@ -2971,10 +2876,6 @@ static int update_prstate(struct cpuset *cs, int new_prs)
if (alloc_tmpmasks(&tmpmask))
return -ENOMEM;
- err = update_partition_exclusive_flag(cs, new_prs);
- if (err)
- goto out;
-
if (!old_prs) {
/*
* cpus_allowed and exclusive_cpus cannot be both empty.
@@ -3038,13 +2939,10 @@ static int update_prstate(struct cpuset *cs, int new_prs)
}
out:
/*
- * Make partition invalid & disable CS_CPU_EXCLUSIVE if an error
- * happens.
+ * Make partition invalid if an error happens.
*/
- if (err) {
+ if (err)
new_prs = -new_prs;
- update_partition_exclusive_flag(cs, new_prs);
- }
spin_lock_irq(&callback_lock);
cs->partition_root_state = new_prs;
@@ -3066,8 +2964,6 @@ out:
update_partition_sd_lb(cs, old_prs);
notify_partition_change(cs, old_prs);
- if (force_sd_rebuild)
- rebuild_sched_domains_locked();
free_tmpmasks(&tmpmask);
return 0;
}
@@ -3706,21 +3602,12 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)
return 0;
}
-/*
- * If the cpuset being removed has its flag 'sched_load_balance'
- * enabled, then simulate turning sched_load_balance off, which
- * will call rebuild_sched_domains_locked(). That is not needed
- * in the default hierarchy where only changes in partition
- * will cause repartitioning.
- */
static void cpuset_css_offline(struct cgroup_subsys_state *css)
{
struct cpuset *cs = css_cs(css);
cpuset_full_lock();
- if (!cpuset_v2() && is_sched_load_balance(cs))
- cpuset_update_flag(CS_SCHED_LOAD_BALANCE, cs, 0);
-
+ cpuset1_offline_css(cs);
cpuset_dec();
cpuset_full_unlock();
}
@@ -4336,7 +4223,7 @@ int cpuset_num_cpus(struct cgroup *cgrp)
int nr = num_online_cpus();
struct cpuset *cs;
- if (is_in_v2_mode()) {
+ if (cpuset_v2()) {
guard(rcu)();
cs = css_cs(cgroup_e_css(cgrp, &cpuset_cgrp_subsys));
if (cs)
diff --git a/kernel/cgroup/debug.c b/kernel/cgroup/debug.c
index 883347b87842..aba243b5fd5f 100644
--- a/kernel/cgroup/debug.c
+++ b/kernel/cgroup/debug.c
@@ -45,13 +45,13 @@ static int current_css_set_read(struct seq_file *seq, void *v)
struct cgroup_subsys_state *css;
int i, refcnt;
- if (!cgroup_kn_lock_live(of->kn, false))
+ if (IS_ERR(cgroup_kn_lock_live(of->kn, false)))
return -ENODEV;
spin_lock_irq(&css_set_lock);
cset = task_css_set(current);
refcnt = refcount_read(&cset->refcount);
- seq_printf(seq, "css_set %pK %d", cset, refcnt);
+ seq_printf(seq, "css_set %p %d", cset, refcnt);
if (refcnt > cset->nr_tasks)
seq_printf(seq, " +%d", refcnt - cset->nr_tasks);
seq_puts(seq, "\n");
@@ -125,10 +125,10 @@ static int cgroup_css_links_read(struct seq_file *seq, void *v)
* Print out the proc_cset and threaded_cset relationship
* and highlight difference between refcount and task_count.
*/
- seq_printf(seq, "css_set %pK", cset);
+ seq_printf(seq, "css_set %p", cset);
if (rcu_dereference_protected(cset->dom_cset, 1) != cset) {
threaded_csets++;
- seq_printf(seq, "=>%pK", cset->dom_cset);
+ seq_printf(seq, "=>%p", cset->dom_cset);
}
if (!list_empty(&cset->threaded_csets)) {
struct css_set *tcset;
@@ -137,7 +137,7 @@ static int cgroup_css_links_read(struct seq_file *seq, void *v)
list_for_each_entry(tcset, &cset->threaded_csets,
threaded_csets_node) {
seq_puts(seq, idx ? "," : "<=");
- seq_printf(seq, "%pK", tcset);
+ seq_printf(seq, "%p", tcset);
idx++;
}
} else {
@@ -206,8 +206,8 @@ static int cgroup_subsys_states_read(struct seq_file *seq, void *v)
int i;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENODEV;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
for_each_subsys(ss, i) {
css = rcu_dereference_check(cgrp->subsys[ss->id], true);
@@ -254,8 +254,8 @@ static int cgroup_masks_read(struct seq_file *seq, void *v)
struct cgroup *cgrp;
cgrp = cgroup_kn_lock_live(of->kn, false);
- if (!cgrp)
- return -ENODEV;
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
cgroup_masks_read_one(seq, "subtree_control", cgrp->subtree_control);
cgroup_masks_read_one(seq, "subtree_ss_mask", cgrp->subtree_ss_mask);
diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c
index 47a41646de4b..d49ed5d4a923 100644
--- a/kernel/cgroup/dmem.c
+++ b/kernel/cgroup/dmem.c
@@ -706,8 +706,8 @@ EXPORT_SYMBOL_GPL(dmem_cgroup_uncharge);
*
* When this function fails with -EAGAIN and @ret_limit_pool is non-null, it
* will be set to the pool for which the limit is hit. This can be used for
- * eviction as argument to dmem_cgroup_evict_valuable(). This reference must be freed
- * with @dmem_cgroup_pool_state_put().
+ * eviction as argument to dmem_cgroup_state_evict_valuable(). This reference
+ * must be freed with @dmem_cgroup_pool_state_put().
*
* Return: 0 on success, -EAGAIN on hitting a limit, or a negative errno on failure.
*/
diff --git a/kernel/cgroup/freezer.c b/kernel/cgroup/freezer.c
index 6c18854bff34..35112d62318a 100644
--- a/kernel/cgroup/freezer.c
+++ b/kernel/cgroup/freezer.c
@@ -20,10 +20,7 @@ static bool cgroup_update_frozen_flag(struct cgroup *cgrp, bool frozen)
if (test_bit(CGRP_FROZEN, &cgrp->flags) == frozen)
return false;
- if (frozen)
- set_bit(CGRP_FROZEN, &cgrp->flags);
- else
- clear_bit(CGRP_FROZEN, &cgrp->flags);
+ assign_bit(CGRP_FROZEN, &cgrp->flags, frozen);
cgroup_file_notify(&cgrp->events_file);
TRACE_CGROUP_PATH(notify_frozen, cgrp, frozen);
diff --git a/kernel/cgroup/namespace.c b/kernel/cgroup/namespace.c
index ea4ee13936be..76660332ec64 100644
--- a/kernel/cgroup/namespace.c
+++ b/kernel/cgroup/namespace.c
@@ -53,8 +53,6 @@ struct cgroup_namespace *copy_cgroup_ns(u64 flags,
struct ucounts *ucounts;
struct css_set *cset;
- BUG_ON(!old_ns);
-
if (!(flags & CLONE_NEWCGROUP)) {
get_cgroup_ns(old_ns);
return old_ns;