History

Merge tag 'cgroup-for-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup

Pull cgroup updates from Tejun Heo: - Per-cpu cpu usage stats are now tracked This currently isn't printed out in the cgroupfs interface and can only be accessed through e.g. BPF. Should decide on a not-too-ugly way to show per-cpu stats in cgroupfs - cpuset received some cleanups and prepatory patches for the pending cpus.exclusive patchset which will allow cpuset partitions to be created below non-partition parents, which should ease the management of partition cpusets - A lot of code and documentation cleanup patches - tools/testing/selftests/cgroup/test_cpuset.c added * tag 'cgroup-for-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup: (32 commits) cgroup: Avoid -Wstringop-overflow warnings cgroup:namespace: Remove unused cgroup_namespaces_init() cgroup/rstat: Record the cumulative per-cpu time of cgroup and its descendants cgroup: clean up if condition in cgroup_pidlist_start() cgroup: fix obsolete function name in cgroup_destroy_locked() Documentation: cgroup-v2.rst: Correct number of stats entries cgroup: fix obsolete function name above css_free_rwork_fn() cgroup/cpuset: fix kernel-doc cgroup: clean up printk() cgroup: fix obsolete comment above cgroup_create() docs: cgroup-v1: fix typo docs: cgroup-v1: correct the term of Page Cache organization in inode cgroup/misc: Store atomic64_t reads to u64 cgroup/misc: Change counters to be explicit 64bit types cgroup/misc: update struct members descriptions cgroup: remove cgrp->kn check in css_populate_dir() cgroup: fix obsolete function name cgroup: use cached local variable parent in for loop cgroup: remove obsolete comment above struct cgroupstats cgroup: put cgroup_tryget_css() inside CONFIG_CGROUP_SCHED ...

Linus Torvalds committed 3 years ago

Commit 7716f382 parents e987af4 78d44b819 files · +560 −204Browse files

Unified · Split

19 files
  1. Documentation/admin-guide/cgroup-v1/memory.rst +3 −3
  2. Documentation/admin-guide/cgroup-v2.rst +1 −1
  3. MAINTAINERS +2 −0
  4. include/linux/cgroup-defs.h +14 −0
  5. include/linux/misc_cgroup.h +13 −15
  6. include/uapi/linux/cgroupstats.h +0 −2
  7. kernel/cgroup/cgroup-v1.c +1 −1
  8. kernel/cgroup/cgroup.c +41 −44
  9. kernel/cgroup/cpuset.c +164 −100
  10. kernel/cgroup/misc.c +27 −28
  11. kernel/cgroup/namespace.c +0 −6
  12. kernel/cgroup/rstat.c +10 −2
  13. tools/testing/selftests/cgroup/.gitignore +1 −0
  14. tools/testing/selftests/cgroup/Makefile +2 −0
  15. tools/testing/selftests/cgroup/cgroup_util.c +2 −0
  16. tools/testing/selftests/cgroup/cgroup_util.h +2 −0
  17. tools/testing/selftests/cgroup/test_core.c +1 −1
  18. tools/testing/selftests/cgroup/test_cpuset.c +275 −0
  19. tools/testing/selftests/cgroup/test_cpuset_prs.sh +1 −1
Documentation/admin-guide/cgroup-v1/memory.rst+3−3
@@ -195,11 +195,11 @@ are not accounted. We just account pages under usual VM management. · 194 unmodified lines
195 195
196RSS pages are accounted at page_fault unless they've already been accounted196RSS pages are accounted at page_fault unless they've already been accounted
197for earlier. A file page will be accounted for as Page Cache when it's197for earlier. A file page will be accounted for as Page Cache when it's
198inserted into inode (radix-tree). While it's mapped into the page tables of198inserted into inode (xarray). While it's mapped into the page tables of
199processes, duplicate accounting is carefully avoided.199processes, duplicate accounting is carefully avoided.
200 200
201An RSS page is unaccounted when it's fully unmapped. A PageCache page is201An RSS page is unaccounted when it's fully unmapped. A PageCache page is
202unaccounted when it's removed from radix-tree. Even if RSS pages are fully202unaccounted when it's removed from xarray. Even if RSS pages are fully
203unmapped (by kswapd), they may exist as SwapCache in the system until they203unmapped (by kswapd), they may exist as SwapCache in the system until they
204are really freed. Such SwapCaches are also accounted.204are really freed. Such SwapCaches are also accounted.
205A swapped-in page is accounted after adding into swapcache.205A swapped-in page is accounted after adding into swapcache.
@@ -907,7 +907,7 @@ experiences some pressure. In this situation, only group C will receive the · 701 unmodified lines
907notification, i.e. groups A and B will not receive it. This is done to avoid907notification, i.e. groups A and B will not receive it. This is done to avoid
908excessive "broadcasting" of messages, which disturbs the system and which is908excessive "broadcasting" of messages, which disturbs the system and which is
909especially bad if we are low on memory or thrashing. Group B, will receive909especially bad if we are low on memory or thrashing. Group B, will receive
910notification only if there are no event listers for group C.910notification only if there are no event listeners for group C.
911 911
912There are three optional modes that specify different propagation behavior:912There are three optional modes that specify different propagation behavior:
913 913
Documentation/admin-guide/cgroup-v2.rst+1−1
@@ -1045,7 +1045,7 @@ All time durations are in microseconds. · 1044 unmodified lines
1045 - user_usec1045 - user_usec
1046 - system_usec1046 - system_usec
1047 1047
1048 and the following three when the controller is enabled:1048 and the following five when the controller is enabled:
1049 1049
1050 - nr_periods1050 - nr_periods
1051 - nr_throttled1051 - nr_throttled
MAINTAINERS+2−0
@@ -5255,6 +5255,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git · 5254 unmodified lines
5255F: Documentation/admin-guide/cgroup-v1/cpusets.rst5255F: Documentation/admin-guide/cgroup-v1/cpusets.rst
5256F: include/linux/cpuset.h5256F: include/linux/cpuset.h
5257F: kernel/cgroup/cpuset.c5257F: kernel/cgroup/cpuset.c
5258F: tools/testing/selftests/cgroup/test_cpuset.c
5259F: tools/testing/selftests/cgroup/test_cpuset_prs.sh
5258 5260
5259CONTROL GROUP - MEMORY RESOURCE CONTROLLER (MEMCG)5261CONTROL GROUP - MEMORY RESOURCE CONTROLLER (MEMCG)
5260M: Johannes Weiner <hannes@cmpxchg.org>5262M: Johannes Weiner <hannes@cmpxchg.org>
include/linux/cgroup-defs.h+14−0
@@ -341,6 +341,20 @@ struct cgroup_rstat_cpu { · 340 unmodified lines
341 */341 */
342 struct cgroup_base_stat last_bstat;342 struct cgroup_base_stat last_bstat;
343 343
344 /*
345 * This field is used to record the cumulative per-cpu time of
346 * the cgroup and its descendants. Currently it can be read via
347 * eBPF/drgn etc, and we are still trying to determine how to
348 * expose it in the cgroupfs interface.
349 */
350 struct cgroup_base_stat subtree_bstat;
351
352 /*
353 * Snapshots at the last reading. These are used to calculate the
354 * deltas to propagate to the per-cpu subtree_bstat.
355 */
356 struct cgroup_base_stat last_subtree_bstat;
357
344 /*358 /*
345 * Child cgroups with stat updates on this cpu since the last read359 * Child cgroups with stat updates on this cpu since the last read
346 * are linked on the parent's ->updated_children through360 * are linked on the parent's ->updated_children through
include/linux/misc_cgroup.h+13−15
@@ -31,17 +31,18 @@ struct misc_cg; · 30 unmodified lines
31 * struct misc_res: Per cgroup per misc type resource31 * struct misc_res: Per cgroup per misc type resource
32 * @max: Maximum limit on the resource.32 * @max: Maximum limit on the resource.
33 * @usage: Current usage of the resource.33 * @usage: Current usage of the resource.
34 * @failed: True if charged failed for the resource in a cgroup.34 * @events: Number of times, the resource limit exceeded.
35 */35 */
36struct misc_res {36struct misc_res {
37 unsigned long max;37 u64 max;
38 atomic_long_t usage;38 atomic64_t usage;
39 atomic_long_t events;39 atomic64_t events;
40};40};
41 41
42/**42/**
43 * struct misc_cg - Miscellaneous controller's cgroup structure.43 * struct misc_cg - Miscellaneous controller's cgroup structure.
44 * @css: cgroup subsys state object.44 * @css: cgroup subsys state object.
45 * @events_file: Handle for the misc resources events file.
45 * @res: Array of misc resources usage in the cgroup.46 * @res: Array of misc resources usage in the cgroup.
46 */47 */
47struct misc_cg {48struct misc_cg {
@@ -53,12 +54,10 @@ struct misc_cg { · 5 unmodified lines
53 struct misc_res res[MISC_CG_RES_TYPES];54 struct misc_res res[MISC_CG_RES_TYPES];
54};55};
55 56
56unsigned long misc_cg_res_total_usage(enum misc_res_type type);57u64 misc_cg_res_total_usage(enum misc_res_type type);
57int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity);58int misc_cg_set_capacity(enum misc_res_type type, u64 capacity);
58int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg,59int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, u64 amount);
59 unsigned long amount);60void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, u64 amount);
60void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg,
61 unsigned long amount);
62 61
63/**62/**
64 * css_misc() - Get misc cgroup from the css.63 * css_misc() - Get misc cgroup from the css.
@@ -99,27 +98,26 @@ static inline void put_misc_cg(struct misc_cg *cg) · 34 unmodified lines
99 98
100#else /* !CONFIG_CGROUP_MISC */99#else /* !CONFIG_CGROUP_MISC */
101 100
102static inline unsigned long misc_cg_res_total_usage(enum misc_res_type type)101static inline u64 misc_cg_res_total_usage(enum misc_res_type type)
103{102{
104 return 0;103 return 0;
105}104}
106 105
107static inline int misc_cg_set_capacity(enum misc_res_type type,106static inline int misc_cg_set_capacity(enum misc_res_type type, u64 capacity)
108 unsigned long capacity)
109{107{
110 return 0;108 return 0;
111}109}
112 110
113static inline int misc_cg_try_charge(enum misc_res_type type,111static inline int misc_cg_try_charge(enum misc_res_type type,
114 struct misc_cg *cg,112 struct misc_cg *cg,
115 unsigned long amount)113 u64 amount)
116{114{
117 return 0;115 return 0;
118}116}
119 117
120static inline void misc_cg_uncharge(enum misc_res_type type,118static inline void misc_cg_uncharge(enum misc_res_type type,
121 struct misc_cg *cg,119 struct misc_cg *cg,
122 unsigned long amount)120 u64 amount)
123{121{
124}122}
125 123
include/uapi/linux/cgroupstats.h+0−2
@@ -24,8 +24,6 @@ · 23 unmodified lines
24 * basis. This data is shared using taskstats.24 * basis. This data is shared using taskstats.
25 *25 *
26 * Most of these states are derived by looking at the task->state value26 * Most of these states are derived by looking at the task->state value
27 * For the nr_io_wait state, a flag in the delay accounting structure
28 * indicates that the task is waiting on IO
29 *27 *
30 * Each member is aligned to a 8 byte boundary.28 * Each member is aligned to a 8 byte boundary.
31 */29 */
kernel/cgroup/cgroup-v1.c+1−1
@@ -431,7 +431,7 @@ static void *cgroup_pidlist_start(struct seq_file *s, loff_t *pos) · 430 unmodified lines
431 if (l->list[mid] == pid) {431 if (l->list[mid] == pid) {
432 index = mid;432 index = mid;
433 break;433 break;
434 } else if (l->list[mid] <= pid)434 } else if (l->list[mid] < pid)
435 index = mid + 1;435 index = mid + 1;
436 else436 else
437 end = mid;437 end = mid;
kernel/cgroup/cgroup.c+41−44
@@ -492,28 +492,6 @@ static struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp, · 491 unmodified lines
492 return &cgrp->self;492 return &cgrp->self;
493}493}
494 494
495/**
496 * cgroup_tryget_css - try to get a cgroup's css for the specified subsystem
497 * @cgrp: the cgroup of interest
498 * @ss: the subsystem of interest
499 *
500 * Find and get @cgrp's css associated with @ss. If the css doesn't exist
501 * or is offline, %NULL is returned.
502 */
503static struct cgroup_subsys_state *cgroup_tryget_css(struct cgroup *cgrp,
504 struct cgroup_subsys *ss)
505{
506 struct cgroup_subsys_state *css;
507
508 rcu_read_lock();
509 css = cgroup_css(cgrp, ss);
510 if (css && !css_tryget_online(css))
511 css = NULL;
512 rcu_read_unlock();
513
514 return css;
515}
516
517/**495/**
518 * cgroup_e_css_by_mask - obtain a cgroup's effective css for the specified ss496 * cgroup_e_css_by_mask - obtain a cgroup's effective css for the specified ss
519 * @cgrp: the cgroup of interest497 * @cgrp: the cgroup of interest
@@ -679,7 +657,7 @@ EXPORT_SYMBOL_GPL(of_css); · 159 unmodified lines
679 * @ssid: the index of the subsystem, CGROUP_SUBSYS_COUNT after reaching the end657 * @ssid: the index of the subsystem, CGROUP_SUBSYS_COUNT after reaching the end
680 * @cgrp: the target cgroup to iterate css's of658 * @cgrp: the target cgroup to iterate css's of
681 *659 *
682 * Should be called under cgroup_[tree_]mutex.660 * Should be called under cgroup_mutex.
683 */661 */
684#define for_each_css(css, ssid, cgrp) \662#define for_each_css(css, ssid, cgrp) \
685 for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT; (ssid)++) \663 for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT; (ssid)++) \
@@ -929,7 +907,7 @@ static void css_set_move_task(struct task_struct *task, · 243 unmodified lines
929#define CSS_SET_HASH_BITS 7907#define CSS_SET_HASH_BITS 7
930static DEFINE_HASHTABLE(css_set_table, CSS_SET_HASH_BITS);908static DEFINE_HASHTABLE(css_set_table, CSS_SET_HASH_BITS);
931 909
932static unsigned long css_set_hash(struct cgroup_subsys_state *css[])910static unsigned long css_set_hash(struct cgroup_subsys_state **css)
933{911{
934 unsigned long key = 0UL;912 unsigned long key = 0UL;
935 struct cgroup_subsys *ss;913 struct cgroup_subsys *ss;
@@ -1070,7 +1048,7 @@ static bool compare_css_sets(struct css_set *cset, · 134 unmodified lines
1070 */1048 */
1071static struct css_set *find_existing_css_set(struct css_set *old_cset,1049static struct css_set *find_existing_css_set(struct css_set *old_cset,
1072 struct cgroup *cgrp,1050 struct cgroup *cgrp,
1073 struct cgroup_subsys_state *template[])1051 struct cgroup_subsys_state **template)
1074{1052{
1075 struct cgroup_root *root = cgrp->root;1053 struct cgroup_root *root = cgrp->root;
1076 struct cgroup_subsys *ss;1054 struct cgroup_subsys *ss;
@@ -1736,7 +1714,7 @@ static int css_populate_dir(struct cgroup_subsys_state *css) · 659 unmodified lines
1736 struct cftype *cfts, *failed_cfts;1714 struct cftype *cfts, *failed_cfts;
1737 int ret;1715 int ret;
1738 1716
1739 if ((css->flags & CSS_VISIBLE) || !cgrp->kn)1717 if (css->flags & CSS_VISIBLE)
1740 return 0;1718 return 0;
1741 1719
1742 if (!css->ss) {1720 if (!css->ss) {
@@ -2499,7 +2477,7 @@ struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset, · 756 unmodified lines
2499 2477
2500 /*2478 /*
2501 * This function may be called both before and2479 * This function may be called both before and
2502 * after cgroup_taskset_migrate(). The two cases2480 * after cgroup_migrate_execute(). The two cases
2503 * can be distinguished by looking at whether @cset2481 * can be distinguished by looking at whether @cset
2504 * has its ->mg_dst_cset set.2482 * has its ->mg_dst_cset set.
2505 */2483 */
@@ -3654,9 +3632,32 @@ static int cgroup_stat_show(struct seq_file *seq, void *v) · 1148 unmodified lines
3654 return 0;3632 return 0;
3655}3633}
3656 3634
3657static int __maybe_unused cgroup_extra_stat_show(struct seq_file *seq,3635#ifdef CONFIG_CGROUP_SCHED
3658 struct cgroup *cgrp, int ssid)3636/**
3637 * cgroup_tryget_css - try to get a cgroup's css for the specified subsystem
3638 * @cgrp: the cgroup of interest
3639 * @ss: the subsystem of interest
3640 *
3641 * Find and get @cgrp's css associated with @ss. If the css doesn't exist
3642 * or is offline, %NULL is returned.
3643 */
3644static struct cgroup_subsys_state *cgroup_tryget_css(struct cgroup *cgrp,
3645 struct cgroup_subsys *ss)
3659{3646{
3647 struct cgroup_subsys_state *css;
3648
3649 rcu_read_lock();
3650 css = cgroup_css(cgrp, ss);
3651 if (css && !css_tryget_online(css))
3652 css = NULL;
3653 rcu_read_unlock();
3654
3655 return css;
3656}
3657
3658static int cgroup_extra_stat_show(struct seq_file *seq, int ssid)
3659{
3660 struct cgroup *cgrp = seq_css(seq)->cgroup;
3660 struct cgroup_subsys *ss = cgroup_subsys[ssid];3661 struct cgroup_subsys *ss = cgroup_subsys[ssid];
3661 struct cgroup_subsys_state *css;3662 struct cgroup_subsys_state *css;
3662 int ret;3663 int ret;
@@ -3672,15 +3673,15 @@ static int __maybe_unused cgroup_extra_stat_show(struct seq_file *seq, · 9 unmodified lines
3672 css_put(css);3673 css_put(css);
3673 return ret;3674 return ret;
3674}3675}
3676#endif
3675 3677
3676static int cpu_stat_show(struct seq_file *seq, void *v)3678static int cpu_stat_show(struct seq_file *seq, void *v)
3677{3679{
3678 struct cgroup __maybe_unused *cgrp = seq_css(seq)->cgroup;
3679 int ret = 0;3680 int ret = 0;
3680 3681
3681 cgroup_base_stat_cputime_show(seq);3682 cgroup_base_stat_cputime_show(seq);
3682#ifdef CONFIG_CGROUP_SCHED3683#ifdef CONFIG_CGROUP_SCHED
3683 ret = cgroup_extra_stat_show(seq, cgrp, cpu_cgrp_id);3684 ret = cgroup_extra_stat_show(seq, cpu_cgrp_id);
3684#endif3685#endif
3685 return ret;3686 return ret;
3686}3687}
@@ -4350,14 +4351,13 @@ static int cgroup_init_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) · 663 unmodified lines
4350 return ret;4351 return ret;
4351}4352}
4352 4353
4353static int cgroup_rm_cftypes_locked(struct cftype *cfts)4354static void cgroup_rm_cftypes_locked(struct cftype *cfts)
4354{4355{
4355 lockdep_assert_held(&cgroup_mutex);4356 lockdep_assert_held(&cgroup_mutex);
4356 4357
4357 list_del(&cfts->node);4358 list_del(&cfts->node);
4358 cgroup_apply_cftypes(cfts, false);4359 cgroup_apply_cftypes(cfts, false);
4359 cgroup_exit_cftypes(cfts);4360 cgroup_exit_cftypes(cfts);
4360 return 0;
4361}4361}
4362 4362
4363/**4363/**
@@ -4373,8 +4373,6 @@ static int cgroup_rm_cftypes_locked(struct cftype *cfts) · 9 unmodified lines
4373 */4373 */
4374int cgroup_rm_cftypes(struct cftype *cfts)4374int cgroup_rm_cftypes(struct cftype *cfts)
4375{4375{
4376 int ret;
4377
4378 if (!cfts || cfts[0].name[0] == '\0')4376 if (!cfts || cfts[0].name[0] == '\0')
4379 return 0;4377 return 0;
4380 4378
@@ -4382,9 +4380,9 @@ int cgroup_rm_cftypes(struct cftype *cfts) · 1 unmodified line
4382 return -ENOENT;4380 return -ENOENT;
4383 4381
4384 cgroup_lock();4382 cgroup_lock();
4385 ret = cgroup_rm_cftypes_locked(cfts);4383 cgroup_rm_cftypes_locked(cfts);
4386 cgroup_unlock();4384 cgroup_unlock();
4387 return ret;4385 return 0;
4388}4386}
4389 4387
4390/**4388/**
@@ -5337,7 +5335,7 @@ static struct cftype cgroup_psi_files[] = { · 946 unmodified lines
5337 * RCU callback.5335 * RCU callback.
5338 *5336 *
5339 * 4. After the grace period, the css can be freed. Implemented in5337 * 4. After the grace period, the css can be freed. Implemented in
5340 * css_free_work_fn().5338 * css_free_rwork_fn().
5341 *5339 *
5342 * It is actually hairier because both step 2 and 4 require process context5340 * It is actually hairier because both step 2 and 4 require process context
5343 * and thus involve punting to css->destroy_work adding two additional5341 * and thus involve punting to css->destroy_work adding two additional
@@ -5581,8 +5579,7 @@ err_free_css: · 237 unmodified lines
5581 5579
5582/*5580/*
5583 * The returned cgroup is fully initialized including its control mask, but5581 * The returned cgroup is fully initialized including its control mask, but
5584 * it isn't associated with its kernfs_node and doesn't have the control5582 * it doesn't have the control mask applied.
5585 * mask applied.
5586 */5583 */
5587static struct cgroup *cgroup_create(struct cgroup *parent, const char *name,5584static struct cgroup *cgroup_create(struct cgroup *parent, const char *name,
5588 umode_t mode)5585 umode_t mode)
@@ -5908,7 +5905,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) · 319 unmodified lines
5908 /*5905 /*
5909 * Mark @cgrp and the associated csets dead. The former prevents5906 * Mark @cgrp and the associated csets dead. The former prevents
5910 * further task migration and child creation by disabling5907 * further task migration and child creation by disabling
5911 * cgroup_lock_live_group(). The latter makes the csets ignored by5908 * cgroup_kn_lock_live(). The latter makes the csets ignored by
5912 * the migration path.5909 * the migration path.
5913 */5910 */
5914 cgrp->self.flags &= ~CSS_ONLINE;5911 cgrp->self.flags &= ~CSS_ONLINE;
@@ -5930,7 +5927,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) · 15 unmodified lines
5930 parent->nr_threaded_children--;5927 parent->nr_threaded_children--;
5931 5928
5932 spin_lock_irq(&css_set_lock);5929 spin_lock_irq(&css_set_lock);
5933 for (tcgrp = cgroup_parent(cgrp); tcgrp; tcgrp = cgroup_parent(tcgrp)) {5930 for (tcgrp = parent; tcgrp; tcgrp = cgroup_parent(tcgrp)) {
5934 tcgrp->nr_descendants--;5931 tcgrp->nr_descendants--;
5935 tcgrp->nr_dying_descendants++;5932 tcgrp->nr_dying_descendants++;
5936 /*5933 /*
@@ -6123,8 +6120,8 @@ int __init cgroup_init(void) · 186 unmodified lines
6123 continue;6120 continue;
6124 6121
6125 if (cgroup1_ssid_disabled(ssid))6122 if (cgroup1_ssid_disabled(ssid))
6126 printk(KERN_INFO "Disabling %s control group subsystem in v1 mounts\n",6123 pr_info("Disabling %s control group subsystem in v1 mounts\n",
6127 ss->name);6124 ss->name);
6128 6125
6129 cgrp_dfl_root.subsys_mask |= 1 << ss->id;6126 cgrp_dfl_root.subsys_mask |= 1 << ss->id;
6130 6127
kernel/cgroup/cpuset.c+164−100
@@ -1230,7 +1230,7 @@ static void update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus) · 1229 unmodified lines
1230 /*1230 /*
1231 * Percpu kthreads in top_cpuset are ignored1231 * Percpu kthreads in top_cpuset are ignored
1232 */1232 */
1233 if ((task->flags & PF_KTHREAD) && kthread_is_per_cpu(task))1233 if (kthread_is_per_cpu(task))
1234 continue;1234 continue;
1235 cpumask_andnot(new_cpus, possible_mask, cs->subparts_cpus);1235 cpumask_andnot(new_cpus, possible_mask, cs->subparts_cpus);
1236 } else {1236 } else {
@@ -1255,7 +1255,7 @@ static void update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus) · 18 unmodified lines
1255static void compute_effective_cpumask(struct cpumask *new_cpus,1255static void compute_effective_cpumask(struct cpumask *new_cpus,
1256 struct cpuset *cs, struct cpuset *parent)1256 struct cpuset *cs, struct cpuset *parent)
1257{1257{
1258 if (parent->nr_subparts_cpus) {1258 if (parent->nr_subparts_cpus && is_partition_valid(cs)) {
1259 cpumask_or(new_cpus, parent->effective_cpus,1259 cpumask_or(new_cpus, parent->effective_cpus,
1260 parent->subparts_cpus);1260 parent->subparts_cpus);
1261 cpumask_and(new_cpus, new_cpus, cs->cpus_allowed);1261 cpumask_and(new_cpus, new_cpus, cs->cpus_allowed);
@@ -1277,6 +1277,52 @@ enum subparts_cmd { · 15 unmodified lines
1277 1277
1278static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,1278static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
1279 int turning_on);1279 int turning_on);
1280static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs,
1281 struct tmpmasks *tmp);
1282
1283/*
1284 * Update partition exclusive flag
1285 *
1286 * Return: 0 if successful, an error code otherwise
1287 */
1288static int update_partition_exclusive(struct cpuset *cs, int new_prs)
1289{
1290 bool exclusive = (new_prs > 0);
1291
1292 if (exclusive && !is_cpu_exclusive(cs)) {
1293 if (update_flag(CS_CPU_EXCLUSIVE, cs, 1))
1294 return PERR_NOTEXCL;
1295 } else if (!exclusive && is_cpu_exclusive(cs)) {
1296 /* Turning off CS_CPU_EXCLUSIVE will not return error */
1297 update_flag(CS_CPU_EXCLUSIVE, cs, 0);
1298 }
1299 return 0;
1300}
1301
1302/*
1303 * Update partition load balance flag and/or rebuild sched domain
1304 *
1305 * Changing load balance flag will automatically call
1306 * rebuild_sched_domains_locked().
1307 */
1308static void update_partition_sd_lb(struct cpuset *cs, int old_prs)
1309{
1310 int new_prs = cs->partition_root_state;
1311 bool new_lb = (new_prs != PRS_ISOLATED);
1312 bool rebuild_domains = (new_prs > 0) || (old_prs > 0);
1313
1314 if (new_lb != !!is_sched_load_balance(cs)) {
1315 rebuild_domains = true;
1316 if (new_lb)
1317 set_bit(CS_SCHED_LOAD_BALANCE, &cs->flags);
1318 else
1319 clear_bit(CS_SCHED_LOAD_BALANCE, &cs->flags);
1320 }
1321
1322 if (rebuild_domains)
1323 rebuild_sched_domains_locked();
1324}
1325
1280/**1326/**
1281 * update_parent_subparts_cpumask - update subparts_cpus mask of parent cpuset1327 * update_parent_subparts_cpumask - update subparts_cpus mask of parent cpuset
1282 * @cs: The cpuset that requests change in partition root state1328 * @cs: The cpuset that requests change in partition root state
@@ -1336,8 +1382,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, · 53 unmodified lines
1336 return is_partition_invalid(parent)1382 return is_partition_invalid(parent)
1337 ? PERR_INVPARENT : PERR_NOTPART;1383 ? PERR_INVPARENT : PERR_NOTPART;
1338 }1384 }
1339 if ((newmask && cpumask_empty(newmask)) ||1385 if (!newmask && cpumask_empty(cs->cpus_allowed))
1340 (!newmask && cpumask_empty(cs->cpus_allowed)))
1341 return PERR_CPUSEMPTY;1386 return PERR_CPUSEMPTY;
1342 1387
1343 /*1388 /*
@@ -1403,11 +1448,16 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, · 59 unmodified lines
1403 cpumask_and(tmp->addmask, newmask, parent->cpus_allowed);1448 cpumask_and(tmp->addmask, newmask, parent->cpus_allowed);
1404 adding = cpumask_andnot(tmp->addmask, tmp->addmask,1449 adding = cpumask_andnot(tmp->addmask, tmp->addmask,
1405 parent->subparts_cpus);1450 parent->subparts_cpus);
1451 /*
1452 * Empty cpumask is not allowed
1453 */
1454 if (cpumask_empty(newmask)) {
1455 part_error = PERR_CPUSEMPTY;
1406 /*1456 /*
1407 * Make partition invalid if parent's effective_cpus could1457 * Make partition invalid if parent's effective_cpus could
1408 * become empty and there are tasks in the parent.1458 * become empty and there are tasks in the parent.
1409 */1459 */
1410 if (adding &&1460 } else if (adding &&
1411 cpumask_subset(parent->effective_cpus, tmp->addmask) &&1461 cpumask_subset(parent->effective_cpus, tmp->addmask) &&
1412 !cpumask_intersects(tmp->delmask, cpu_active_mask) &&1462 !cpumask_intersects(tmp->delmask, cpu_active_mask) &&
1413 partition_is_populated(parent, cs)) {1463 partition_is_populated(parent, cs)) {
@@ -1480,14 +1530,13 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, · 66 unmodified lines
1480 1530
1481 /*1531 /*
1482 * Transitioning between invalid to valid or vice versa may require1532 * Transitioning between invalid to valid or vice versa may require
1483 * changing CS_CPU_EXCLUSIVE and CS_SCHED_LOAD_BALANCE.1533 * changing CS_CPU_EXCLUSIVE.
1484 */1534 */
1485 if (old_prs != new_prs) {1535 if (old_prs != new_prs) {
1486 if (is_prs_invalid(old_prs) && !is_cpu_exclusive(cs) &&1536 int err = update_partition_exclusive(cs, new_prs);
1487 (update_flag(CS_CPU_EXCLUSIVE, cs, 1) < 0))1537
1488 return PERR_NOTEXCL;1538 if (err)
1489 if (is_prs_invalid(new_prs) && is_cpu_exclusive(cs))1539 return err;
1490 update_flag(CS_CPU_EXCLUSIVE, cs, 0);
1491 }1540 }
1492 1541
1493 /*1542 /*
@@ -1520,23 +1569,33 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, · 26 unmodified lines
1520 1569
1521 spin_unlock_irq(&callback_lock);1570 spin_unlock_irq(&callback_lock);
1522 1571
1523 if (adding || deleting)1572 if (adding || deleting) {
1524 update_tasks_cpumask(parent, tmp->addmask);1573 update_tasks_cpumask(parent, tmp->addmask);
1574 if (parent->child_ecpus_count)
1575 update_sibling_cpumasks(parent, cs, tmp);
1576 }
1525 1577
1526 /*1578 /*
1527 * Set or clear CS_SCHED_LOAD_BALANCE when partcmd_update, if necessary.1579 * For partcmd_update without newmask, it is being called from
1528 * rebuild_sched_domains_locked() may be called.1580 * cpuset_hotplug_workfn() where cpus_read_lock() wasn't taken.
1581 * Update the load balance flag and scheduling domain if
1582 * cpus_read_trylock() is successful.
1529 */1583 */
1530 if (old_prs != new_prs) {1584 if ((cmd == partcmd_update) && !newmask && cpus_read_trylock()) {
1531 if (old_prs == PRS_ISOLATED)1585 update_partition_sd_lb(cs, old_prs);
1532 update_flag(CS_SCHED_LOAD_BALANCE, cs, 1);1586 cpus_read_unlock();
1533 else if (new_prs == PRS_ISOLATED)
1534 update_flag(CS_SCHED_LOAD_BALANCE, cs, 0);
1535 }1587 }
1588
1536 notify_partition_change(cs, old_prs);1589 notify_partition_change(cs, old_prs);
1537 return 0;1590 return 0;
1538}1591}
1539 1592
1593/*
1594 * update_cpumasks_hier() flags
1595 */
1596#define HIER_CHECKALL 0x01 /* Check all cpusets with no skipping */
1597#define HIER_NO_SD_REBUILD 0x02 /* Don't rebuild sched domains */
1598
1540/*1599/*
1541 * update_cpumasks_hier - Update effective cpumasks and tasks in the subtree1600 * update_cpumasks_hier - Update effective cpumasks and tasks in the subtree
1542 * @cs: the cpuset to consider1601 * @cs: the cpuset to consider
@@ -1551,7 +1610,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, · 8 unmodified lines
1551 * Called with cpuset_mutex held1610 * Called with cpuset_mutex held
1552 */1611 */
1553static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,1612static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
1554 bool force)1613 int flags)
1555{1614{
1556 struct cpuset *cp;1615 struct cpuset *cp;
1557 struct cgroup_subsys_state *pos_css;1616 struct cgroup_subsys_state *pos_css;
@@ -1588,11 +1647,16 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp, · 30 unmodified lines
1588 }1647 }
1589 1648
1590 /*1649 /*
1591 * Skip the whole subtree if the cpumask remains the same1650 * Skip the whole subtree if
1592 * and has no partition root state and force flag not set.1651 * 1) the cpumask remains the same,
1652 * 2) has no partition root state,
1653 * 3) HIER_CHECKALL flag not set, and
1654 * 4) for v2 load balance state same as its parent.
1593 */1655 */
1594 if (!cp->partition_root_state && !force &&1656 if (!cp->partition_root_state && !(flags & HIER_CHECKALL) &&
1595 cpumask_equal(tmp->new_cpus, cp->effective_cpus)) {1657 cpumask_equal(tmp->new_cpus, cp->effective_cpus) &&
1658 (!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) ||
1659 (is_sched_load_balance(parent) == is_sched_load_balance(cp)))) {
1596 pos_css = css_rightmost_descendant(pos_css);1660 pos_css = css_rightmost_descendant(pos_css);
1597 continue;1661 continue;
1598 }1662 }
@@ -1675,6 +1739,20 @@ update_parent_subparts: · 76 unmodified lines
1675 1739
1676 update_tasks_cpumask(cp, tmp->new_cpus);1740 update_tasks_cpumask(cp, tmp->new_cpus);
1677 1741
1742 /*
1743 * On default hierarchy, inherit the CS_SCHED_LOAD_BALANCE
1744 * from parent if current cpuset isn't a valid partition root
1745 * and their load balance states differ.
1746 */
1747 if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys) &&
1748 !is_partition_valid(cp) &&
1749 (is_sched_load_balance(parent) != is_sched_load_balance(cp))) {
1750 if (is_sched_load_balance(parent))
1751 set_bit(CS_SCHED_LOAD_BALANCE, &cp->flags);
1752 else
1753 clear_bit(CS_SCHED_LOAD_BALANCE, &cp->flags);
1754 }
1755
1678 /*1756 /*
1679 * On legacy hierarchy, if the effective cpumask of any non-1757 * On legacy hierarchy, if the effective cpumask of any non-
1680 * empty cpuset is changed, we need to rebuild sched domains.1758 * empty cpuset is changed, we need to rebuild sched domains.
@@ -1692,7 +1770,7 @@ update_parent_subparts: · 11 unmodified lines
1692 }1770 }
1693 rcu_read_unlock();1771 rcu_read_unlock();
1694 1772
1695 if (need_rebuild_sched_domains)1773 if (need_rebuild_sched_domains && !(flags & HIER_NO_SD_REBUILD))
1696 rebuild_sched_domains_locked();1774 rebuild_sched_domains_locked();
1697}1775}
1698 1776
@@ -1716,7 +1794,9 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, · 17 unmodified lines
1716 * to use the right effective_cpus value.1794 * to use the right effective_cpus value.
1717 *1795 *
1718 * The update_cpumasks_hier() function may sleep. So we have to1796 * The update_cpumasks_hier() function may sleep. So we have to
1719 * release the RCU read lock before calling it.1797 * release the RCU read lock before calling it. HIER_NO_SD_REBUILD
1798 * flag is used to suppress rebuild of sched domains as the callers
1799 * will take care of that.
1720 */1800 */
1721 rcu_read_lock();1801 rcu_read_lock();
1722 cpuset_for_each_child(sibling, pos_css, parent) {1802 cpuset_for_each_child(sibling, pos_css, parent) {
@@ -1728,7 +1808,7 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, · 5 unmodified lines
1728 continue;1808 continue;
1729 1809
1730 rcu_read_unlock();1810 rcu_read_unlock();
1731 update_cpumasks_hier(sibling, tmp, false);1811 update_cpumasks_hier(sibling, tmp, HIER_NO_SD_REBUILD);
1732 rcu_read_lock();1812 rcu_read_lock();
1733 css_put(&sibling->css);1813 css_put(&sibling->css);
1734 }1814 }
@@ -1747,6 +1827,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, · 12 unmodified lines
1747 int retval;1827 int retval;
1748 struct tmpmasks tmp;1828 struct tmpmasks tmp;
1749 bool invalidate = false;1829 bool invalidate = false;
1830 int old_prs = cs->partition_root_state;
1750 1831
1751 /* top_cpuset.cpus_allowed tracks cpu_online_mask; it's read-only */1832 /* top_cpuset.cpus_allowed tracks cpu_online_mask; it's read-only */
1752 if (cs == &top_cpuset)1833 if (cs == &top_cpuset)
@@ -1774,18 +1855,8 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, · 21 unmodified lines
1774 if (cpumask_equal(cs->cpus_allowed, trialcs->cpus_allowed))1855 if (cpumask_equal(cs->cpus_allowed, trialcs->cpus_allowed))
1775 return 0;1856 return 0;
1776 1857
1777#ifdef CONFIG_CPUMASK_OFFSTACK1858 if (alloc_cpumasks(NULL, &tmp))
1778 /*1859 return -ENOMEM;
1779 * Use the cpumasks in trialcs for tmpmasks when they are pointers
1780 * to allocated cpumasks.
1781 *
1782 * Note that update_parent_subparts_cpumask() uses only addmask &
1783 * delmask, but not new_cpus.
1784 */
1785 tmp.addmask = trialcs->subparts_cpus;
1786 tmp.delmask = trialcs->effective_cpus;
1787 tmp.new_cpus = NULL;
1788#endif
1789 1860
1790 retval = validate_change(cs, trialcs);1861 retval = validate_change(cs, trialcs);
1791 1862
@@ -1814,7 +1885,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, · 22 unmodified lines
1814 retval = 0;1885 retval = 0;
1815 }1886 }
1816 if (retval < 0)1887 if (retval < 0)
1817 return retval;1888 goto out_free;
1818 1889
1819 if (cs->partition_root_state) {1890 if (cs->partition_root_state) {
1820 if (invalidate)1891 if (invalidate)
@@ -1849,13 +1920,8 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, · 28 unmodified lines
1849 }1920 }
1850 spin_unlock_irq(&callback_lock);1921 spin_unlock_irq(&callback_lock);
1851 1922
1852#ifdef CONFIG_CPUMASK_OFFSTACK
1853 /* Now trialcs->cpus_allowed is available */
1854 tmp.new_cpus = trialcs->cpus_allowed;
1855#endif
1856
1857 /* effective_cpus will be updated here */1923 /* effective_cpus will be updated here */
1858 update_cpumasks_hier(cs, &tmp, false);1924 update_cpumasks_hier(cs, &tmp, 0);
1859 1925
1860 if (cs->partition_root_state) {1926 if (cs->partition_root_state) {
1861 struct cpuset *parent = parent_cs(cs);1927 struct cpuset *parent = parent_cs(cs);
@@ -1866,7 +1932,12 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, · 4 unmodified lines
1866 */1932 */
1867 if (parent->child_ecpus_count)1933 if (parent->child_ecpus_count)
1868 update_sibling_cpumasks(parent, cs, &tmp);1934 update_sibling_cpumasks(parent, cs, &tmp);
1935
1936 /* Update CS_SCHED_LOAD_BALANCE and/or sched_domains */
1937 update_partition_sd_lb(cs, old_prs);
1869 }1938 }
1939out_free:
1940 free_cpumasks(NULL, &tmp);
1870 return 0;1941 return 0;
1871}1942}
1872 1943
@@ -2242,7 +2313,6 @@ out: · 369 unmodified lines
2242static int update_prstate(struct cpuset *cs, int new_prs)2313static int update_prstate(struct cpuset *cs, int new_prs)
2243{2314{
2244 int err = PERR_NONE, old_prs = cs->partition_root_state;2315 int err = PERR_NONE, old_prs = cs->partition_root_state;
2245 bool sched_domain_rebuilt = false;
2246 struct cpuset *parent = parent_cs(cs);2316 struct cpuset *parent = parent_cs(cs);
2247 struct tmpmasks tmpmask;2317 struct tmpmasks tmpmask;
2248 2318
@@ -2261,45 +2331,26 @@ static int update_prstate(struct cpuset *cs, int new_prs) · 12 unmodified lines
2261 if (alloc_cpumasks(NULL, &tmpmask))2331 if (alloc_cpumasks(NULL, &tmpmask))
2262 return -ENOMEM;2332 return -ENOMEM;
2263 2333
2334 err = update_partition_exclusive(cs, new_prs);
2335 if (err)
2336 goto out;
2337
2264 if (!old_prs) {2338 if (!old_prs) {
2265 /*2339 /*
2266 * Turning on partition root requires setting the2340 * cpus_allowed cannot be empty.
2267 * CS_CPU_EXCLUSIVE bit implicitly as well and cpus_allowed
2268 * cannot be empty.
2269 */2341 */
2270 if (cpumask_empty(cs->cpus_allowed)) {2342 if (cpumask_empty(cs->cpus_allowed)) {
2271 err = PERR_CPUSEMPTY;2343 err = PERR_CPUSEMPTY;
2272 goto out;2344 goto out;
2273 }2345 }
2274 2346
2275 err = update_flag(CS_CPU_EXCLUSIVE, cs, 1);
2276 if (err) {
2277 err = PERR_NOTEXCL;
2278 goto out;
2279 }
2280
2281 err = update_parent_subparts_cpumask(cs, partcmd_enable,2347 err = update_parent_subparts_cpumask(cs, partcmd_enable,
2282 NULL, &tmpmask);2348 NULL, &tmpmask);
2283 if (err) {
2284 update_flag(CS_CPU_EXCLUSIVE, cs, 0);
2285 goto out;
2286 }
2287
2288 if (new_prs == PRS_ISOLATED) {
2289 /*
2290 * Disable the load balance flag should not return an
2291 * error unless the system is running out of memory.
2292 */
2293 update_flag(CS_SCHED_LOAD_BALANCE, cs, 0);
2294 sched_domain_rebuilt = true;
2295 }
2296 } else if (old_prs && new_prs) {2349 } else if (old_prs && new_prs) {
2297 /*2350 /*
2298 * A change in load balance state only, no change in cpumasks.2351 * A change in load balance state only, no change in cpumasks.
2299 */2352 */
2300 update_flag(CS_SCHED_LOAD_BALANCE, cs, (new_prs != PRS_ISOLATED));2353 ;
2301 sched_domain_rebuilt = true;
2302 goto out; /* Sched domain is rebuilt in update_flag() */
2303 } else {2354 } else {
2304 /*2355 /*
2305 * Switching back to member is always allowed even if it2356 * Switching back to member is always allowed even if it
@@ -2318,40 +2369,31 @@ static int update_prstate(struct cpuset *cs, int new_prs) · 12 unmodified lines
2318 compute_effective_cpumask(cs->effective_cpus, cs, parent);2369 compute_effective_cpumask(cs->effective_cpus, cs, parent);
2319 spin_unlock_irq(&callback_lock);2370 spin_unlock_irq(&callback_lock);
2320 }2371 }
2321
2322 /* Turning off CS_CPU_EXCLUSIVE will not return error */
2323 update_flag(CS_CPU_EXCLUSIVE, cs, 0);
2324
2325 if (!is_sched_load_balance(cs)) {
2326 /* Make sure load balance is on */
2327 update_flag(CS_SCHED_LOAD_BALANCE, cs, 1);
2328 sched_domain_rebuilt = true;
2329 }
2330 }2372 }
2331
2332 update_tasks_cpumask(parent, tmpmask.new_cpus);
2333
2334 if (parent->child_ecpus_count)
2335 update_sibling_cpumasks(parent, cs, &tmpmask);
2336
2337 if (!sched_domain_rebuilt)
2338 rebuild_sched_domains_locked();
2339out:2373out:
2340 /*2374 /*
2341 * Make partition invalid if an error happen2375 * Make partition invalid & disable CS_CPU_EXCLUSIVE if an error
2376 * happens.
2342 */2377 */
2343 if (err)2378 if (err) {
2344 new_prs = -new_prs;2379 new_prs = -new_prs;
2380 update_partition_exclusive(cs, new_prs);
2381 }
2382
2345 spin_lock_irq(&callback_lock);2383 spin_lock_irq(&callback_lock);
2346 cs->partition_root_state = new_prs;2384 cs->partition_root_state = new_prs;
2347 WRITE_ONCE(cs->prs_err, err);2385 WRITE_ONCE(cs->prs_err, err);
2348 spin_unlock_irq(&callback_lock);2386 spin_unlock_irq(&callback_lock);
2387
2349 /*2388 /*
2350 * Update child cpusets, if present.2389 * Update child cpusets, if present.
2351 * Force update if switching back to member.2390 * Force update if switching back to member.
2352 */2391 */
2353 if (!list_empty(&cs->css.children))2392 if (!list_empty(&cs->css.children))
2354 update_cpumasks_hier(cs, &tmpmask, !new_prs);2393 update_cpumasks_hier(cs, &tmpmask, !new_prs ? HIER_CHECKALL : 0);
2394
2395 /* Update sched domains and load balance flag */
2396 update_partition_sd_lb(cs, old_prs);
2355 2397
2356 notify_partition_change(cs, old_prs);2398 notify_partition_change(cs, old_prs);
2357 free_cpumasks(NULL, &tmpmask);2399 free_cpumasks(NULL, &tmpmask);
@@ -2487,6 +2529,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) · 129 unmodified lines
2487 struct cgroup_subsys_state *css;2529 struct cgroup_subsys_state *css;
2488 struct cpuset *cs, *oldcs;2530 struct cpuset *cs, *oldcs;
2489 struct task_struct *task;2531 struct task_struct *task;
2532 bool cpus_updated, mems_updated;
2490 int ret;2533 int ret;
2491 2534
2492 /* used later by cpuset_attach() */2535 /* used later by cpuset_attach() */
@@ -2501,13 +2544,25 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) · 8 unmodified lines
2501 if (ret)2544 if (ret)
2502 goto out_unlock;2545 goto out_unlock;
2503 2546
2547 cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus);
2548 mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
2549
2504 cgroup_taskset_for_each(task, css, tset) {2550 cgroup_taskset_for_each(task, css, tset) {
2505 ret = task_can_attach(task);2551 ret = task_can_attach(task);
2506 if (ret)2552 if (ret)
2507 goto out_unlock;2553 goto out_unlock;
2508 ret = security_task_setscheduler(task);2554
2509 if (ret)2555 /*
2510 goto out_unlock;2556 * Skip rights over task check in v2 when nothing changes,
2557 * migration permission derives from hierarchy ownership in
2558 * cgroup_procs_write_permission()).
2559 */
2560 if (!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) ||
2561 (cpus_updated || mems_updated)) {
2562 ret = security_task_setscheduler(task);
2563 if (ret)
2564 goto out_unlock;
2565 }
2511 2566
2512 if (dl_task(task)) {2567 if (dl_task(task)) {
2513 cs->nr_migrate_dl_tasks++;2568 cs->nr_migrate_dl_tasks++;
@@ -3222,6 +3277,14 @@ static int cpuset_css_online(struct cgroup_subsys_state *css) · 708 unmodified lines
3222 cs->use_parent_ecpus = true;3277 cs->use_parent_ecpus = true;
3223 parent->child_ecpus_count++;3278 parent->child_ecpus_count++;
3224 }3279 }
3280
3281 /*
3282 * For v2, clear CS_SCHED_LOAD_BALANCE if parent is isolated
3283 */
3284 if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys) &&
3285 !is_sched_load_balance(parent))
3286 clear_bit(CS_SCHED_LOAD_BALANCE, &cs->flags);
3287
3225 spin_unlock_irq(&callback_lock);3288 spin_unlock_irq(&callback_lock);
3226 3289
3227 if (!test_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags))3290 if (!test_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags))
@@ -3521,17 +3584,16 @@ hotplug_update_tasks_legacy(struct cpuset *cs, · 293 unmodified lines
3521 is_empty = cpumask_empty(cs->cpus_allowed) ||3584 is_empty = cpumask_empty(cs->cpus_allowed) ||
3522 nodes_empty(cs->mems_allowed);3585 nodes_empty(cs->mems_allowed);
3523 3586
3524 mutex_unlock(&cpuset_mutex);
3525
3526 /*3587 /*
3527 * Move tasks to the nearest ancestor with execution resources,3588 * Move tasks to the nearest ancestor with execution resources,
3528 * This is full cgroup operation which will also call back into3589 * This is full cgroup operation which will also call back into
3529 * cpuset. Should be done outside any lock.3590 * cpuset. Should be done outside any lock.
3530 */3591 */
3531 if (is_empty)3592 if (is_empty) {
3593 mutex_unlock(&cpuset_mutex);
3532 remove_tasks_in_empty_cpuset(cs);3594 remove_tasks_in_empty_cpuset(cs);
3533 3595 mutex_lock(&cpuset_mutex);
3534 mutex_lock(&cpuset_mutex);3596 }
3535}3597}
3536 3598
3537static void3599static void
@@ -3691,6 +3753,7 @@ unlock: · 153 unmodified lines
3691 3753
3692/**3754/**
3693 * cpuset_hotplug_workfn - handle CPU/memory hotunplug for a cpuset3755 * cpuset_hotplug_workfn - handle CPU/memory hotunplug for a cpuset
3756 * @work: unused
3694 *3757 *
3695 * This function is called after either CPU or memory configuration has3758 * This function is called after either CPU or memory configuration has
3696 * changed and updates cpuset accordingly. The top_cpuset is always3759 * changed and updates cpuset accordingly. The top_cpuset is always
@@ -4073,6 +4136,7 @@ bool cpuset_node_allowed(int node, gfp_t gfp_mask) · 376 unmodified lines
4073 4136
4074/**4137/**
4075 * cpuset_spread_node() - On which node to begin search for a page4138 * cpuset_spread_node() - On which node to begin search for a page
4139 * @rotor: round robin rotor
4076 *4140 *
4077 * If a task is marked PF_SPREAD_PAGE or PF_SPREAD_SLAB (as for4141 * If a task is marked PF_SPREAD_PAGE or PF_SPREAD_SLAB (as for
4078 * tasks in a cpuset with is_spread_page or is_spread_slab set),4142 * tasks in a cpuset with is_spread_page or is_spread_slab set),
kernel/cgroup/misc.c+27−28
@@ -14,7 +14,7 @@ · 13 unmodified lines
14#include <linux/misc_cgroup.h>14#include <linux/misc_cgroup.h>
15 15
16#define MAX_STR "max"16#define MAX_STR "max"
17#define MAX_NUM ULONG_MAX17#define MAX_NUM U64_MAX
18 18
19/* Miscellaneous res name, keep it in sync with enum misc_res_type */19/* Miscellaneous res name, keep it in sync with enum misc_res_type */
20static const char *const misc_res_name[] = {20static const char *const misc_res_name[] = {
@@ -37,7 +37,7 @@ static struct misc_cg root_cg; · 16 unmodified lines
37 * more than the actual capacity. We are using Limits resource distribution37 * more than the actual capacity. We are using Limits resource distribution
38 * model of cgroup for miscellaneous controller.38 * model of cgroup for miscellaneous controller.
39 */39 */
40static unsigned long misc_res_capacity[MISC_CG_RES_TYPES];40static u64 misc_res_capacity[MISC_CG_RES_TYPES];
41 41
42/**42/**
43 * parent_misc() - Get the parent of the passed misc cgroup.43 * parent_misc() - Get the parent of the passed misc cgroup.
@@ -74,10 +74,10 @@ static inline bool valid_type(enum misc_res_type type) · 30 unmodified lines
74 * Context: Any context.74 * Context: Any context.
75 * Return: Current total usage of the resource.75 * Return: Current total usage of the resource.
76 */76 */
77unsigned long misc_cg_res_total_usage(enum misc_res_type type)77u64 misc_cg_res_total_usage(enum misc_res_type type)
78{78{
79 if (valid_type(type))79 if (valid_type(type))
80 return atomic_long_read(&root_cg.res[type].usage);80 return atomic64_read(&root_cg.res[type].usage);
81 81
82 return 0;82 return 0;
83}83}
@@ -95,7 +95,7 @@ EXPORT_SYMBOL_GPL(misc_cg_res_total_usage); · 11 unmodified lines
95 * * %0 - Successfully registered the capacity.95 * * %0 - Successfully registered the capacity.
96 * * %-EINVAL - If @type is invalid.96 * * %-EINVAL - If @type is invalid.
97 */97 */
98int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity)98int misc_cg_set_capacity(enum misc_res_type type, u64 capacity)
99{99{
100 if (!valid_type(type))100 if (!valid_type(type))
101 return -EINVAL;101 return -EINVAL;
@@ -114,9 +114,9 @@ EXPORT_SYMBOL_GPL(misc_cg_set_capacity); · 12 unmodified lines
114 * Context: Any context.114 * Context: Any context.
115 */115 */
116static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg,116static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg,
117 unsigned long amount)117 u64 amount)
118{118{
119 WARN_ONCE(atomic_long_add_negative(-amount, &cg->res[type].usage),119 WARN_ONCE(atomic64_add_negative(-amount, &cg->res[type].usage),
120 "misc cgroup resource %s became less than 0",120 "misc cgroup resource %s became less than 0",
121 misc_res_name[type]);121 misc_res_name[type]);
122}122}
@@ -137,13 +137,12 @@ static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg, · 14 unmodified lines
137 * * -EBUSY - If max limit will be crossed or total usage will be more than the137 * * -EBUSY - If max limit will be crossed or total usage will be more than the
138 * capacity.138 * capacity.
139 */139 */
140int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg,140int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, u64 amount)
141 unsigned long amount)
142{141{
143 struct misc_cg *i, *j;142 struct misc_cg *i, *j;
144 int ret;143 int ret;
145 struct misc_res *res;144 struct misc_res *res;
146 int new_usage;145 u64 new_usage;
147 146
148 if (!(valid_type(type) && cg && READ_ONCE(misc_res_capacity[type])))147 if (!(valid_type(type) && cg && READ_ONCE(misc_res_capacity[type])))
149 return -EINVAL;148 return -EINVAL;
@@ -154,7 +153,7 @@ int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, · 4 unmodified lines
154 for (i = cg; i; i = parent_misc(i)) {153 for (i = cg; i; i = parent_misc(i)) {
155 res = &i->res[type];154 res = &i->res[type];
156 155
157 new_usage = atomic_long_add_return(amount, &res->usage);156 new_usage = atomic64_add_return(amount, &res->usage);
158 if (new_usage > READ_ONCE(res->max) ||157 if (new_usage > READ_ONCE(res->max) ||
159 new_usage > READ_ONCE(misc_res_capacity[type])) {158 new_usage > READ_ONCE(misc_res_capacity[type])) {
160 ret = -EBUSY;159 ret = -EBUSY;
@@ -165,7 +164,7 @@ int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, · 4 unmodified lines
165 164
166err_charge:165err_charge:
167 for (j = i; j; j = parent_misc(j)) {166 for (j = i; j; j = parent_misc(j)) {
168 atomic_long_inc(&j->res[type].events);167 atomic64_inc(&j->res[type].events);
169 cgroup_file_notify(&j->events_file);168 cgroup_file_notify(&j->events_file);
170 }169 }
171 170
@@ -184,8 +183,7 @@ EXPORT_SYMBOL_GPL(misc_cg_try_charge); · 12 unmodified lines
184 *183 *
185 * Context: Any context.184 * Context: Any context.
186 */185 */
187void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg,186void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, u64 amount)
188 unsigned long amount)
189{187{
190 struct misc_cg *i;188 struct misc_cg *i;
191 189
@@ -209,7 +207,7 @@ static int misc_cg_max_show(struct seq_file *sf, void *v) · 17 unmodified lines
209{207{
210 int i;208 int i;
211 struct misc_cg *cg = css_misc(seq_css(sf));209 struct misc_cg *cg = css_misc(seq_css(sf));
212 unsigned long max;210 u64 max;
213 211
214 for (i = 0; i < MISC_CG_RES_TYPES; i++) {212 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
215 if (READ_ONCE(misc_res_capacity[i])) {213 if (READ_ONCE(misc_res_capacity[i])) {
@@ -217,7 +215,7 @@ static int misc_cg_max_show(struct seq_file *sf, void *v) · 1 unmodified line
217 if (max == MAX_NUM)215 if (max == MAX_NUM)
218 seq_printf(sf, "%s max\n", misc_res_name[i]);216 seq_printf(sf, "%s max\n", misc_res_name[i]);
219 else217 else
220 seq_printf(sf, "%s %lu\n", misc_res_name[i],218 seq_printf(sf, "%s %llu\n", misc_res_name[i],
221 max);219 max);
222 }220 }
223 }221 }
@@ -241,13 +239,13 @@ static int misc_cg_max_show(struct seq_file *sf, void *v) · 17 unmodified lines
241 * Return:239 * Return:
242 * * >= 0 - Number of bytes processed in the input.240 * * >= 0 - Number of bytes processed in the input.
243 * * -EINVAL - If buf is not valid.241 * * -EINVAL - If buf is not valid.
244 * * -ERANGE - If number is bigger than the unsigned long capacity.242 * * -ERANGE - If number is bigger than the u64 capacity.
245 */243 */
246static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf,244static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf,
247 size_t nbytes, loff_t off)245 size_t nbytes, loff_t off)
248{246{
249 struct misc_cg *cg;247 struct misc_cg *cg;
250 unsigned long max;248 u64 max;
251 int ret = 0, i;249 int ret = 0, i;
252 enum misc_res_type type = MISC_CG_RES_TYPES;250 enum misc_res_type type = MISC_CG_RES_TYPES;
253 char *token;251 char *token;
@@ -271,7 +269,7 @@ static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf, · 17 unmodified lines
271 if (!strcmp(MAX_STR, buf)) {269 if (!strcmp(MAX_STR, buf)) {
272 max = MAX_NUM;270 max = MAX_NUM;
273 } else {271 } else {
274 ret = kstrtoul(buf, 0, &max);272 ret = kstrtou64(buf, 0, &max);
275 if (ret)273 if (ret)
276 return ret;274 return ret;
277 }275 }
@@ -297,13 +295,13 @@ static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf, · 19 unmodified lines
297static int misc_cg_current_show(struct seq_file *sf, void *v)295static int misc_cg_current_show(struct seq_file *sf, void *v)
298{296{
299 int i;297 int i;
300 unsigned long usage;298 u64 usage;
301 struct misc_cg *cg = css_misc(seq_css(sf));299 struct misc_cg *cg = css_misc(seq_css(sf));
302 300
303 for (i = 0; i < MISC_CG_RES_TYPES; i++) {301 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
304 usage = atomic_long_read(&cg->res[i].usage);302 usage = atomic64_read(&cg->res[i].usage);
305 if (READ_ONCE(misc_res_capacity[i]) || usage)303 if (READ_ONCE(misc_res_capacity[i]) || usage)
306 seq_printf(sf, "%s %lu\n", misc_res_name[i], usage);304 seq_printf(sf, "%s %llu\n", misc_res_name[i], usage);
307 }305 }
308 306
309 return 0;307 return 0;
@@ -322,12 +320,12 @@ static int misc_cg_current_show(struct seq_file *sf, void *v) · 12 unmodified lines
322static int misc_cg_capacity_show(struct seq_file *sf, void *v)320static int misc_cg_capacity_show(struct seq_file *sf, void *v)
323{321{
324 int i;322 int i;
325 unsigned long cap;323 u64 cap;
326 324
327 for (i = 0; i < MISC_CG_RES_TYPES; i++) {325 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
328 cap = READ_ONCE(misc_res_capacity[i]);326 cap = READ_ONCE(misc_res_capacity[i]);
329 if (cap)327 if (cap)
330 seq_printf(sf, "%s %lu\n", misc_res_name[i], cap);328 seq_printf(sf, "%s %llu\n", misc_res_name[i], cap);
331 }329 }
332 330
333 return 0;331 return 0;
@@ -336,12 +334,13 @@ static int misc_cg_capacity_show(struct seq_file *sf, void *v) · 2 unmodified lines
336static int misc_events_show(struct seq_file *sf, void *v)334static int misc_events_show(struct seq_file *sf, void *v)
337{335{
338 struct misc_cg *cg = css_misc(seq_css(sf));336 struct misc_cg *cg = css_misc(seq_css(sf));
339 unsigned long events, i;337 u64 events;
338 int i;
340 339
341 for (i = 0; i < MISC_CG_RES_TYPES; i++) {340 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
342 events = atomic_long_read(&cg->res[i].events);341 events = atomic64_read(&cg->res[i].events);
343 if (READ_ONCE(misc_res_capacity[i]) || events)342 if (READ_ONCE(misc_res_capacity[i]) || events)
344 seq_printf(sf, "%s.max %lu\n", misc_res_name[i], events);343 seq_printf(sf, "%s.max %llu\n", misc_res_name[i], events);
345 }344 }
346 return 0;345 return 0;
347}346}
@@ -397,7 +396,7 @@ misc_cg_alloc(struct cgroup_subsys_state *parent_css) · 49 unmodified lines
397 396
398 for (i = 0; i < MISC_CG_RES_TYPES; i++) {397 for (i = 0; i < MISC_CG_RES_TYPES; i++) {
399 WRITE_ONCE(cg->res[i].max, MAX_NUM);398 WRITE_ONCE(cg->res[i].max, MAX_NUM);
400 atomic_long_set(&cg->res[i].usage, 0);399 atomic64_set(&cg->res[i].usage, 0);
401 }400 }
402 401
403 return &cg->css;402 return &cg->css;
kernel/cgroup/namespace.c+0−6
@@ -149,9 +149,3 @@ const struct proc_ns_operations cgroupns_operations = { · 148 unmodified lines
149 .install = cgroupns_install,149 .install = cgroupns_install,
150 .owner = cgroupns_owner,150 .owner = cgroupns_owner,
151};151};
152
153static __init int cgroup_namespaces_init(void)
154{
155 return 0;
156}
157subsys_initcall(cgroup_namespaces_init);
kernel/cgroup/rstat.c+10−2
@@ -344,6 +344,7 @@ static void cgroup_base_stat_flush(struct cgroup *cgrp, int cpu) · 343 unmodified lines
344{344{
345 struct cgroup_rstat_cpu *rstatc = cgroup_rstat_cpu(cgrp, cpu);345 struct cgroup_rstat_cpu *rstatc = cgroup_rstat_cpu(cgrp, cpu);
346 struct cgroup *parent = cgroup_parent(cgrp);346 struct cgroup *parent = cgroup_parent(cgrp);
347 struct cgroup_rstat_cpu *prstatc;
347 struct cgroup_base_stat delta;348 struct cgroup_base_stat delta;
348 unsigned seq;349 unsigned seq;
349 350
@@ -357,17 +358,24 @@ static void cgroup_base_stat_flush(struct cgroup *cgrp, int cpu) · 7 unmodified lines
357 delta = rstatc->bstat;358 delta = rstatc->bstat;
358 } while (__u64_stats_fetch_retry(&rstatc->bsync, seq));359 } while (__u64_stats_fetch_retry(&rstatc->bsync, seq));
359 360
360 /* propagate percpu delta to global */361 /* propagate per-cpu delta to cgroup and per-cpu global statistics */
361 cgroup_base_stat_sub(&delta, &rstatc->last_bstat);362 cgroup_base_stat_sub(&delta, &rstatc->last_bstat);
362 cgroup_base_stat_add(&cgrp->bstat, &delta);363 cgroup_base_stat_add(&cgrp->bstat, &delta);
363 cgroup_base_stat_add(&rstatc->last_bstat, &delta);364 cgroup_base_stat_add(&rstatc->last_bstat, &delta);
365 cgroup_base_stat_add(&rstatc->subtree_bstat, &delta);
364 366
365 /* propagate global delta to parent (unless that's root) */367 /* propagate cgroup and per-cpu global delta to parent (unless that's root) */
366 if (cgroup_parent(parent)) {368 if (cgroup_parent(parent)) {
367 delta = cgrp->bstat;369 delta = cgrp->bstat;
368 cgroup_base_stat_sub(&delta, &cgrp->last_bstat);370 cgroup_base_stat_sub(&delta, &cgrp->last_bstat);
369 cgroup_base_stat_add(&parent->bstat, &delta);371 cgroup_base_stat_add(&parent->bstat, &delta);
370 cgroup_base_stat_add(&cgrp->last_bstat, &delta);372 cgroup_base_stat_add(&cgrp->last_bstat, &delta);
373
374 delta = rstatc->subtree_bstat;
375 prstatc = cgroup_rstat_cpu(parent, cpu);
376 cgroup_base_stat_sub(&delta, &rstatc->last_subtree_bstat);
377 cgroup_base_stat_add(&prstatc->subtree_bstat, &delta);
378 cgroup_base_stat_add(&rstatc->last_subtree_bstat, &delta);
371 }379 }
372}380}
373 381
tools/testing/selftests/cgroup/.gitignore+1−0
@@ -5,5 +5,6 @@ test_freezer · 4 unmodified lines
5test_kmem5test_kmem
6test_kill6test_kill
7test_cpu7test_cpu
8test_cpuset
8test_zswap9test_zswap
9wait_inotify10wait_inotify
tools/testing/selftests/cgroup/Makefile+2−0
@@ -12,6 +12,7 @@ TEST_GEN_PROGS += test_core · 11 unmodified lines
12TEST_GEN_PROGS += test_freezer12TEST_GEN_PROGS += test_freezer
13TEST_GEN_PROGS += test_kill13TEST_GEN_PROGS += test_kill
14TEST_GEN_PROGS += test_cpu14TEST_GEN_PROGS += test_cpu
15TEST_GEN_PROGS += test_cpuset
15TEST_GEN_PROGS += test_zswap16TEST_GEN_PROGS += test_zswap
16 17
17LOCAL_HDRS += $(selfdir)/clone3/clone3_selftests.h $(selfdir)/pidfd/pidfd.h18LOCAL_HDRS += $(selfdir)/clone3/clone3_selftests.h $(selfdir)/pidfd/pidfd.h
@@ -24,4 +25,5 @@ $(OUTPUT)/test_core: cgroup_util.c · 6 unmodified lines
24$(OUTPUT)/test_freezer: cgroup_util.c25$(OUTPUT)/test_freezer: cgroup_util.c
25$(OUTPUT)/test_kill: cgroup_util.c26$(OUTPUT)/test_kill: cgroup_util.c
26$(OUTPUT)/test_cpu: cgroup_util.c27$(OUTPUT)/test_cpu: cgroup_util.c
28$(OUTPUT)/test_cpuset: cgroup_util.c
27$(OUTPUT)/test_zswap: cgroup_util.c29$(OUTPUT)/test_zswap: cgroup_util.c
tools/testing/selftests/cgroup/cgroup_util.c+2−0
@@ -286,6 +286,8 @@ int cg_destroy(const char *cgroup) · 285 unmodified lines
286{286{
287 int ret;287 int ret;
288 288
289 if (!cgroup)
290 return 0;
289retry:291retry:
290 ret = rmdir(cgroup);292 ret = rmdir(cgroup);
291 if (ret && errno == EBUSY) {293 if (ret && errno == EBUSY) {
tools/testing/selftests/cgroup/cgroup_util.h+2−0
@@ -11,6 +11,8 @@ · 10 unmodified lines
11#define USEC_PER_SEC 1000000L11#define USEC_PER_SEC 1000000L
12#define NSEC_PER_SEC 1000000000L12#define NSEC_PER_SEC 1000000000L
13 13
14#define TEST_UID 65534 /* usually nobody, any !root is fine */
15
14/*16/*
15 * Checks if two given values differ by less than err% of their sum.17 * Checks if two given values differ by less than err% of their sum.
16 */18 */
tools/testing/selftests/cgroup/test_core.c+1−1
@@ -683,7 +683,7 @@ cleanup: · 682 unmodified lines
683 */683 */
684static int test_cgcore_lesser_euid_open(const char *root)684static int test_cgcore_lesser_euid_open(const char *root)
685{685{
686 const uid_t test_euid = 65534; /* usually nobody, any !root is fine */686 const uid_t test_euid = TEST_UID;
687 int ret = KSFT_FAIL;687 int ret = KSFT_FAIL;
688 char *cg_test_a = NULL, *cg_test_b = NULL;688 char *cg_test_a = NULL, *cg_test_b = NULL;
689 char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL;689 char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL;
tools/testing/selftests/cgroup/test_cpuset.c+275−0
@@ -0,0 +1,275 @@
1// SPDX-License-Identifier: GPL-2.0
2
3#include <linux/limits.h>
4#include <signal.h>
5
6#include "../kselftest.h"
7#include "cgroup_util.h"
8
9static int idle_process_fn(const char *cgroup, void *arg)
10{
11 (void)pause();
12 return 0;
13}
14
15static int do_migration_fn(const char *cgroup, void *arg)
16{
17 int object_pid = (int)(size_t)arg;
18
19 if (setuid(TEST_UID))
20 return EXIT_FAILURE;
21
22 // XXX checking /proc/$pid/cgroup would be quicker than wait
23 if (cg_enter(cgroup, object_pid) ||
24 cg_wait_for_proc_count(cgroup, 1))
25 return EXIT_FAILURE;
26
27 return EXIT_SUCCESS;
28}
29
30static int do_controller_fn(const char *cgroup, void *arg)
31{
32 const char *child = cgroup;
33 const char *parent = arg;
34
35 if (setuid(TEST_UID))
36 return EXIT_FAILURE;
37
38 if (!cg_read_strstr(child, "cgroup.controllers", "cpuset"))
39 return EXIT_FAILURE;
40
41 if (cg_write(parent, "cgroup.subtree_control", "+cpuset"))
42 return EXIT_FAILURE;
43
44 if (cg_read_strstr(child, "cgroup.controllers", "cpuset"))
45 return EXIT_FAILURE;
46
47 if (cg_write(parent, "cgroup.subtree_control", "-cpuset"))
48 return EXIT_FAILURE;
49
50 if (!cg_read_strstr(child, "cgroup.controllers", "cpuset"))
51 return EXIT_FAILURE;
52
53 return EXIT_SUCCESS;
54}
55
56/*
57 * Migrate a process between two sibling cgroups.
58 * The success should only depend on the parent cgroup permissions and not the
59 * migrated process itself (cpuset controller is in place because it uses
60 * security_task_setscheduler() in cgroup v1).
61 *
62 * Deliberately don't set cpuset.cpus in children to avoid definining migration
63 * permissions between two different cpusets.
64 */
65static int test_cpuset_perms_object(const char *root, bool allow)
66{
67 char *parent = NULL, *child_src = NULL, *child_dst = NULL;
68 char *parent_procs = NULL, *child_src_procs = NULL, *child_dst_procs = NULL;
69 const uid_t test_euid = TEST_UID;
70 int object_pid = 0;
71 int ret = KSFT_FAIL;
72
73 parent = cg_name(root, "cpuset_test_0");
74 if (!parent)
75 goto cleanup;
76 parent_procs = cg_name(parent, "cgroup.procs");
77 if (!parent_procs)
78 goto cleanup;
79 if (cg_create(parent))
80 goto cleanup;
81
82 child_src = cg_name(parent, "cpuset_test_1");
83 if (!child_src)
84 goto cleanup;
85 child_src_procs = cg_name(child_src, "cgroup.procs");
86 if (!child_src_procs)
87 goto cleanup;
88 if (cg_create(child_src))
89 goto cleanup;
90
91 child_dst = cg_name(parent, "cpuset_test_2");
92 if (!child_dst)
93 goto cleanup;
94 child_dst_procs = cg_name(child_dst, "cgroup.procs");
95 if (!child_dst_procs)
96 goto cleanup;
97 if (cg_create(child_dst))
98 goto cleanup;
99
100 if (cg_write(parent, "cgroup.subtree_control", "+cpuset"))
101 goto cleanup;
102
103 if (cg_read_strstr(child_src, "cgroup.controllers", "cpuset") ||
104 cg_read_strstr(child_dst, "cgroup.controllers", "cpuset"))
105 goto cleanup;
106
107 /* Enable permissions along src->dst tree path */
108 if (chown(child_src_procs, test_euid, -1) ||
109 chown(child_dst_procs, test_euid, -1))
110 goto cleanup;
111
112 if (allow && chown(parent_procs, test_euid, -1))
113 goto cleanup;
114
115 /* Fork a privileged child as a test object */
116 object_pid = cg_run_nowait(child_src, idle_process_fn, NULL);
117 if (object_pid < 0)
118 goto cleanup;
119
120 /* Carry out migration in a child process that can drop all privileges
121 * (including capabilities), the main process must remain privileged for
122 * cleanup.
123 * Child process's cgroup is irrelevant but we place it into child_dst
124 * as hacky way to pass information about migration target to the child.
125 */
126 if (allow ^ (cg_run(child_dst, do_migration_fn, (void *)(size_t)object_pid) == EXIT_SUCCESS))
127 goto cleanup;
128
129 ret = KSFT_PASS;
130
131cleanup:
132 if (object_pid > 0) {
133 (void)kill(object_pid, SIGTERM);
134 (void)clone_reap(object_pid, WEXITED);
135 }
136
137 cg_destroy(child_dst);
138 free(child_dst_procs);
139 free(child_dst);
140
141 cg_destroy(child_src);
142 free(child_src_procs);
143 free(child_src);
144
145 cg_destroy(parent);
146 free(parent_procs);
147 free(parent);
148
149 return ret;
150}
151
152static int test_cpuset_perms_object_allow(const char *root)
153{
154 return test_cpuset_perms_object(root, true);
155}
156
157static int test_cpuset_perms_object_deny(const char *root)
158{
159 return test_cpuset_perms_object(root, false);
160}
161
162/*
163 * Migrate a process between parent and child implicitely
164 * Implicit migration happens when a controller is enabled/disabled.
165 *
166 */
167static int test_cpuset_perms_subtree(const char *root)
168{
169 char *parent = NULL, *child = NULL;
170 char *parent_procs = NULL, *parent_subctl = NULL, *child_procs = NULL;
171 const uid_t test_euid = TEST_UID;
172 int object_pid = 0;
173 int ret = KSFT_FAIL;
174
175 parent = cg_name(root, "cpuset_test_0");
176 if (!parent)
177 goto cleanup;
178 parent_procs = cg_name(parent, "cgroup.procs");
179 if (!parent_procs)
180 goto cleanup;
181 parent_subctl = cg_name(parent, "cgroup.subtree_control");
182 if (!parent_subctl)
183 goto cleanup;
184 if (cg_create(parent))
185 goto cleanup;
186
187 child = cg_name(parent, "cpuset_test_1");
188 if (!child)
189 goto cleanup;
190 child_procs = cg_name(child, "cgroup.procs");
191 if (!child_procs)
192 goto cleanup;
193 if (cg_create(child))
194 goto cleanup;
195
196 /* Enable permissions as in a delegated subtree */
197 if (chown(parent_procs, test_euid, -1) ||
198 chown(parent_subctl, test_euid, -1) ||
199 chown(child_procs, test_euid, -1))
200 goto cleanup;
201
202 /* Put a privileged child in the subtree and modify controller state
203 * from an unprivileged process, the main process remains privileged
204 * for cleanup.
205 * The unprivileged child runs in subtree too to avoid parent and
206 * internal-node constraing violation.
207 */
208 object_pid = cg_run_nowait(child, idle_process_fn, NULL);
209 if (object_pid < 0)
210 goto cleanup;
211
212 if (cg_run(child, do_controller_fn, parent) != EXIT_SUCCESS)
213 goto cleanup;
214
215 ret = KSFT_PASS;
216
217cleanup:
218 if (object_pid > 0) {
219 (void)kill(object_pid, SIGTERM);
220 (void)clone_reap(object_pid, WEXITED);
221 }
222
223 cg_destroy(child);
224 free(child_procs);
225 free(child);
226
227 cg_destroy(parent);
228 free(parent_subctl);
229 free(parent_procs);
230 free(parent);
231
232 return ret;
233}
234
235
236#define T(x) { x, #x }
237struct cpuset_test {
238 int (*fn)(const char *root);
239 const char *name;
240} tests[] = {
241 T(test_cpuset_perms_object_allow),
242 T(test_cpuset_perms_object_deny),
243 T(test_cpuset_perms_subtree),
244};
245#undef T
246
247int main(int argc, char *argv[])
248{
249 char root[PATH_MAX];
250 int i, ret = EXIT_SUCCESS;
251
252 if (cg_find_unified_root(root, sizeof(root)))
253 ksft_exit_skip("cgroup v2 isn't mounted\n");
254
255 if (cg_read_strstr(root, "cgroup.subtree_control", "cpuset"))
256 if (cg_write(root, "cgroup.subtree_control", "+cpuset"))
257 ksft_exit_skip("Failed to set cpuset controller\n");
258
259 for (i = 0; i < ARRAY_SIZE(tests); i++) {
260 switch (tests[i].fn(root)) {
261 case KSFT_PASS:
262 ksft_test_result_pass("%s\n", tests[i].name);
263 break;
264 case KSFT_SKIP:
265 ksft_test_result_skip("%s\n", tests[i].name);
266 break;
267 default:
268 ret = EXIT_FAILURE;
269 ksft_test_result_fail("%s\n", tests[i].name);
270 break;
271 }
272 }
273
274 return ret;
275}
tools/testing/selftests/cgroup/test_cpuset_prs.sh+1−1
@@ -10,7 +10,7 @@ · 9 unmodified lines
10skip_test() {10skip_test() {
11 echo "$1"11 echo "$1"
12 echo "Test SKIPPED"12 echo "Test SKIPPED"
13 exit 013 exit 4 # ksft_skip
14}14}
15 15
16[[ $(id -u) -eq 0 ]] || skip_test "Test must be run as root!"16[[ $(id -u) -eq 0 ]] || skip_test "Test must be run as root!"