From: Waiman Long <longman@redhat.com> Date: 2021-07-20 14:25:52
v3:
- Add two new patches (patches 2 & 3) to fix bugs found during the
testing process.
- Add a new patch to enable inotify event notification when partition
become invalid.
- Add a test to test event notification when partition become invalid.
v2:
- Drop v1 patch 1.
- Break out some cosmetic changes into a separate patch (patch #1).
- Add a new patch to clarify the transition to invalid partition root
is mainly caused by hotplug events.
- Enhance the partition root state test including CPU online/offline
behavior and fix issues found by the test.
This patchset fixes two bugs and makes four enhancements to the cpuset
v2 code.
Bug fixes:
Patch 2: Fix a hotplug handling bug when just all cpus in subparts_cpus
are offlined.
Patch 3: Fix violation of cpuset locking rule.
Enhancements:
Patch 4: Enable event notification on "cpuset.cpus.partition" when
a partition become invalid.
Patch 5: Clarify the use of invalid partition root and add new checks
to make sure that normal cpuset control file operations will not be
allowed to create invalid partition root. It also fixes some of the
issues in existing code.
Patch 6: Add a new partition state "isolated" to create a partition
root without load balancing. This is for handling intermitten workloads
that have a strict low latency requirement.
Patch 7: Allow partition roots that are not the top cpuset to distribute
all its cpus to child partitions as long as there is no task associated
with that partition root. This allows more flexibility for middleware
to manage multiple partitions.
Patch 8 updates the cgroup-v2.rst file accordingly. Patch 9 adds a new
cpuset test to test the new cpuset partition code.
Waiman Long (9):
cgroup/cpuset: Miscellaneous code cleanup
cgroup/cpuset: Fix a partition bug with hotplug
cgroup/cpuset: Fix violation of cpuset locking rule
cgroup/cpuset: Enable event notification when partition become invalid
cgroup/cpuset: Clarify the use of invalid partition root
cgroup/cpuset: Add a new isolated cpus.partition type
cgroup/cpuset: Allow non-top parent partition root to distribute out
all CPUs
cgroup/cpuset: Update description of cpuset.cpus.partition in
cgroup-v2.rst
kselftest/cgroup: Add cpuset v2 partition root state test
Documentation/admin-guide/cgroup-v2.rst | 94 ++-
kernel/cgroup/cpuset.c | 360 +++++++---
tools/testing/selftests/cgroup/Makefile | 5 +-
.../selftests/cgroup/test_cpuset_prs.sh | 626 ++++++++++++++++++
tools/testing/selftests/cgroup/wait_inotify.c | 67 ++
5 files changed, 1007 insertions(+), 145 deletions(-)
create mode 100755 tools/testing/selftests/cgroup/test_cpuset_prs.sh
create mode 100644 tools/testing/selftests/cgroup/wait_inotify.c
--
2.18.1
From: Waiman Long <longman@redhat.com> Date: 2021-07-20 14:25:52
The cpuset fields that manage partition root state do not strictly
follow the cpuset locking rule that update to cpuset has to be done
with both the callback_lock and cpuset_mutex held. This is now fixed
by making sure that the locking rule is upheld.
Fixes: 3881b86128d0 ("cpuset: Add an error state to cpuset.sched.partition")
Fixes: 4b842da276a8 ("cpuset: Make CPU hotplug work with partition)
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 58 +++++++++++++++++++++++++-----------------
1 file changed, 35 insertions(+), 23 deletions(-)
@@ -1148,6 +1148,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,structcpuset*parent=parent_cs(cpuset);intadding;/* Moving cpus from effective_cpus to subparts_cpus */intdeleting;/* Moving cpus from subparts_cpus to effective_cpus */+intnew_prs;boolpart_error=false;/* Partition error? */percpu_rwsem_assert_held(&cpuset_rwsem);
@@ -1183,6 +1184,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,*Acpumaskupdatecannotmakeparent'seffective_cpusbecomeempty.*/adding=deleting=false;+new_prs=cpuset->partition_root_state;if(cmd==partcmd_enable){cpumask_copy(tmp->addmask,cpuset->cpus_allowed);adding=true;
@@ -1247,11 +1249,11 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,switch(cpuset->partition_root_state){casePRS_ENABLED:if(part_error)-cpuset->partition_root_state=PRS_ERROR;+new_prs=PRS_ERROR;break;casePRS_ERROR:if(!part_error)-cpuset->partition_root_state=PRS_ENABLED;+new_prs=PRS_ENABLED;break;}/*
@@ -1260,10 +1262,10 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,part_error=(prev_prs==PRS_ERROR);}-if(!part_error&&(cpuset->partition_root_state==PRS_ERROR))+if(!part_error&&(new_prs==PRS_ERROR))return0;/* Nothing need to be done */-if(cpuset->partition_root_state==PRS_ERROR){+if(new_prs==PRS_ERROR){/**Removeallitscpusfromparent'ssubparts_cpus.*/
@@ -1272,7 +1274,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,parent->subparts_cpus);}-if(!adding&&!deleting)+if(!adding&&!deleting&&(new_prs==cpuset->partition_root_state))return0;/*
@@ -1299,6 +1301,9 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,}parent->nr_subparts_cpus=cpumask_weight(parent->subparts_cpus);++if(cpuset->partition_root_state!=new_prs)+cpuset->partition_root_state=new_prs;spin_unlock_irq(&callback_lock);returncmd==partcmd_update;
@@ -1981,14 +1987,12 @@ static int update_prstate(struct cpuset *cs, int new_prs)update_flag(CS_CPU_EXCLUSIVE,cs,0);gotoout;}-cs->partition_root_state=PRS_ENABLED;}else{/**Turningoffpartitionrootwillclearthe*CS_CPU_EXCLUSIVEbit.*/-if(cs->partition_root_state==PRS_ERROR){-cs->partition_root_state=PRS_DISABLED;+if(old_prs==PRS_ERROR){update_flag(CS_CPU_EXCLUSIVE,cs,0);err=0;gotoout;
@@ -1999,8 +2003,6 @@ static int update_prstate(struct cpuset *cs, int new_prs)if(err)gotoout;-cs->partition_root_state=PRS_DISABLED;-/* Turning off CS_CPU_EXCLUSIVE will not return error */update_flag(CS_CPU_EXCLUSIVE,cs,0);}
@@ -2017,6 +2019,12 @@ static int update_prstate(struct cpuset *cs, int new_prs)rebuild_sched_domains_locked();out:+if(!err){+spin_lock_irq(&callback_lock);+cs->partition_root_state=new_prs;+spin_unlock_irq(&callback_lock);+}+free_cpumasks(NULL,&tmpmask);returnerr;}
From: Waiman Long <longman@redhat.com> Date: 2021-07-20 14:29:05
Currently, a parent partition root cannot distribute all its CPUs to
child partition roots with no CPUs left. However in some use cases,
a management application may want to create a parent partition root as
a management unit with no task associated with it and has all its CPUs
distributed to various child partition roots dynamically according to
their needs. Leaving a cpu in the parent partition root in such a case is
now a waste.
To accommodate such use cases, a parent partition root can now have
all its CPUs distributed to its child partition roots as long as:
1) it is not the top cpuset; and
2) there is no task directly associated with the parent.
Once an empty parent partition root is formed, no new task can be moved
into it.
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 90 +++++++++++++++++++++++++++++-------------
1 file changed, 63 insertions(+), 27 deletions(-)
@@ -2231,6 +2257,13 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)(cpumask_empty(cs->cpus_allowed)||nodes_empty(cs->mems_allowed)))gotoout_unlock;+/*+*Ondefaulthierarchy,taskcannotbemovedtoacpusetwithempty+*effectivecpus.+*/+if(is_in_v2_mode()&&cpumask_empty(cs->effective_cpus))+gotoout_unlock;+cgroup_taskset_for_each(task,css,tset){ret=task_can_attach(task,cs->cpus_allowed);if(ret)
@@ -3098,7 +3131,8 @@ hotplug_update_tasks(struct cpuset *cs,structcpumask*new_cpus,nodemask_t*new_mems,boolcpus_updated,boolmems_updated){-if(cpumask_empty(new_cpus))+/* A partition root is allowed to have empty effective cpus */+if(cpumask_empty(new_cpus)&&!is_partition_root(cs))cpumask_copy(new_cpus,parent_cs(cs)->effective_cpus);if(nodes_empty(*new_mems))*new_mems=parent_cs(cs)->effective_mems;
From: Waiman Long <longman@redhat.com> Date: 2021-07-20 14:32:58
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=TBD
commit 994fb794cb252edd124a46ca0994e37a4726a100
Author: Waiman Long [off-list ref]
Date: Sat, 19 Jun 2021 13:28:19 -0400
cgroup/cpuset: Add a new isolated cpus.partition type
Cpuset v1 uses the sched_load_balance control file to determine if load
balancing should be enabled. Cpuset v2 gets rid of sched_load_balance
as its use may require disabling load balancing at cgroup root.
For workloads that require very low latency like DPDK, the latency
jitters caused by periodic load balancing may exceed the desired
latency limit.
When cpuset v2 is in use, the only way to avoid this latency cost is to
use the "isolcpus=" kernel boot option to isolate a set of CPUs. After
the kernel boot, however, there is no way to add or remove CPUs from
this isolated set. For workloads that are more dynamic in nature, that
means users have to provision enough CPUs for the worst case situation
resulting in excess idle CPUs.
To address this issue for cpuset v2, a new cpuset.cpus.partition type
"isolated" is added which allows the creation of a cpuset partition
without load balancing. This will allow system administrators to
dynamically adjust the size of isolated partition to the current need
of the workload without rebooting the system.
Signed-off-by: Waiman Long [off-list ref]
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 48 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 43 insertions(+), 5 deletions(-)
@@ -1992,6 +2001,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)interr,old_prs=cs->partition_root_state;structcpuset*parent=parent_cs(cs);structtmpmaskstmpmask;+boolsched_domain_rebuilt=false;if(old_prs==new_prs)return0;
@@ -2026,6 +2036,22 @@ static int update_prstate(struct cpuset *cs, int new_prs)update_flag(CS_CPU_EXCLUSIVE,cs,0);gotoout;}++if(new_prs==PRS_ISOLATED){+/*+*Disabletheloadbalanceflagshouldnotreturnan+*errorunlessthesystemisrunningoutofmemory.+*/+update_flag(CS_SCHED_LOAD_BALANCE,cs,0);+sched_domain_rebuilt=true;+}+}elseif(old_prs&&new_prs){+/*+*Achangeinloadbalancestateonly,nochangeincpumasks.+*/+update_flag(CS_SCHED_LOAD_BALANCE,cs,(new_prs!=PRS_ISOLATED));+err=0;+gotoout;/* Sched domain is rebuilt in update_flag() */}else{/**SwitchbacktomemberisalwaysallowedifPRS_ERROR.
@@ -2050,6 +2076,12 @@ static int update_prstate(struct cpuset *cs, int new_prs)reset_flag:/* Turning off CS_CPU_EXCLUSIVE will not return error */update_flag(CS_CPU_EXCLUSIVE,cs,0);++if(!is_sched_load_balance(cs)){+/* Make sure load balance is on */+update_flag(CS_SCHED_LOAD_BALANCE,cs,1);+sched_domain_rebuilt=true;+}}/*
@@ -2062,7 +2094,8 @@ static int update_prstate(struct cpuset *cs, int new_prs)if(parent->child_ecpus_count)update_sibling_cpumasks(parent,cs,&tmpmask);-rebuild_sched_domains_locked();+if(!sched_domain_rebuilt)+rebuild_sched_domains_locked();out:if(!err){spin_lock_irq(&callback_lock);
From: Waiman Long <longman@redhat.com> Date: 2021-07-20 14:33:08
In cpuset_hotplug_workfn(), the detection of whether the cpu list
has been changed is done by comparing the effective cpus of the top
cpuset with the cpu_active_mask. However, in the rare case that just
all the CPUs in the subparts_cpus are offlined, the detection fails
and the partition states are not updated correctly. Fix it by forcing
the cpus_updated flag to true in this particular case.
Fixes: 4b842da276a8 ("cpuset: Make CPU hotplug work with partition")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 7 +++++++
1 file changed, 7 insertions(+)
From: Waiman Long <longman@redhat.com> Date: 2021-07-20 14:34:02
Use more descriptive variable names for update_prstate(), remove
unnecessary code and fix some typos. There is no functional change.
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 40 +++++++++++++++++++---------------------
1 file changed, 19 insertions(+), 21 deletions(-)
@@ -1978,7 +1976,7 @@ static int update_prstate(struct cpuset *cs, int val)gotoout;err=update_parent_subparts_cpumask(cs,partcmd_enable,-NULL,&tmp);+NULL,&tmpmask);if(err){update_flag(CS_CPU_EXCLUSIVE,cs,0);gotoout;
@@ -1990,18 +1988,18 @@ static int update_prstate(struct cpuset *cs, int val)*CS_CPU_EXCLUSIVEbit.*/if(cs->partition_root_state==PRS_ERROR){-cs->partition_root_state=0;+cs->partition_root_state=PRS_DISABLED;update_flag(CS_CPU_EXCLUSIVE,cs,0);err=0;gotoout;}err=update_parent_subparts_cpumask(cs,partcmd_disable,-NULL,&tmp);+NULL,&tmpmask);if(err)gotoout;-cs->partition_root_state=0;+cs->partition_root_state=PRS_DISABLED;/* Turning off CS_CPU_EXCLUSIVE will not return error */update_flag(CS_CPU_EXCLUSIVE,cs,0);
@@ -2015,11 +2013,11 @@ static int update_prstate(struct cpuset *cs, int val)update_tasks_cpumask(parent);if(parent->child_ecpus_count)-update_sibling_cpumasks(parent,cs,&tmp);+update_sibling_cpumasks(parent,cs,&tmpmask);rebuild_sched_domains_locked();out:-free_cpumasks(NULL,&tmp);+free_cpumasks(NULL,&tmpmask);returnerr;}
From: Waiman Long <longman@redhat.com> Date: 2021-07-20 14:35:27
For cpuset partition, the special state of PRS_ERROR (invalid partition
root) was originally designed to handle hotplug events. In this state,
CPUs allocated to the partition root is released back to the parent
but the cpuset exclusive flags remain unchanged.
Since partition root sets the CPU_EXCLUSIVE flag, cpuset.cpus changes
that break the cpu exclusivity rule will not be allowed. However,
other changes to cpuset.cpus on a partition root may still cause it to
become invalid. This is undesriable as we don't want accidental change
to cpuset.cpus to invalidate a partition root.
Additional checks are now added to make sure that regular cpuset control
file manipulations are not allowed to make a partition root invalid. These
additional checks are:
1) A partition root can't be changed to member if it has child partition
roots.
2) Removing CPUs from cpuset.cpus that causes it to become invalid is
not allowed.
Comments are also added to clarify that a partition root becomes
invalid only when an external event like hotplug that causes all the
CPUs allocated to a partition root to become unavailable.
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 144 ++++++++++++++++++++++++-----------------
1 file changed, 86 insertions(+), 58 deletions(-)
@@ -2008,20 +2028,26 @@ static int update_prstate(struct cpuset *cs, int new_prs)}}else{/*-*Turningoffpartitionrootwillclearthe-*CS_CPU_EXCLUSIVEbit.+*SwitchbacktomemberisalwaysallowedifPRS_ERROR.*/if(old_prs==PRS_ERROR){-update_flag(CS_CPU_EXCLUSIVE,cs,0);err=0;-gotoout;+gotoreset_flag;}+/*+*Apartitionrootcannotberevertedtomemberifsome+*CPUshavebeendistributedtochildpartitionroots.+*/+if(!cpumask_empty(cs->subparts_cpus))+return-EBUSY;+err=update_parent_subparts_cpumask(cs,partcmd_disable,NULL,&tmpmask);if(err)gotoout;+reset_flag:/* Turning off CS_CPU_EXCLUSIVE will not return error */update_flag(CS_CPU_EXCLUSIVE,cs,0);}
From: Waiman Long <longman@redhat.com> Date: 2021-07-20 14:35:45
A valid cpuset partition can become invalid if all its CPUs are offlined
or somehow removed. This can happen through external events without
"cpuset.cpus.partition" being touched at all.
Users that rely on the property of a partition being present do not
currently have a simple way to get such an event notified other than
constant periodic polling which is both inefficient and cumbersome.
To make life easier for those users, event notification is now enabled
for "cpuset.cpus.partition" when it goes into or out of an invalid
partition state.
Suggested-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 49 ++++++++++++++++++++++++++++++++----------
1 file changed, 38 insertions(+), 11 deletions(-)
@@ -1148,7 +1164,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,structcpuset*parent=parent_cs(cpuset);intadding;/* Moving cpus from effective_cpus to subparts_cpus */intdeleting;/* Moving cpus from subparts_cpus to effective_cpus */-intnew_prs;+intold_prs,new_prs;boolpart_error=false;/* Partition error? */percpu_rwsem_assert_held(&cpuset_rwsem);
@@ -1184,7 +1200,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,*Acpumaskupdatecannotmakeparent'seffective_cpusbecomeempty.*/adding=deleting=false;-new_prs=cpuset->partition_root_state;+old_prs=new_prs=cpuset->partition_root_state;if(cmd==partcmd_enable){cpumask_copy(tmp->addmask,cpuset->cpus_allowed);adding=true;
@@ -1274,7 +1290,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,parent->subparts_cpus);}-if(!adding&&!deleting&&(new_prs==cpuset->partition_root_state))+if(!adding&&!deleting&&(new_prs==old_prs))return0;/*
@@ -1302,9 +1318,11 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,parent->nr_subparts_cpus=cpumask_weight(parent->subparts_cpus);-if(cpuset->partition_root_state!=new_prs)+if(old_prs!=new_prs)cpuset->partition_root_state=new_prs;+spin_unlock_irq(&callback_lock);+notify_partition_change(cpuset,old_prs,new_prs);returncmd==partcmd_update;}
From: Waiman Long <longman@redhat.com> Date: 2021-07-20 14:35:47
Update Documentation/admin-guide/cgroup-v2.rst on the newly introduced
"isolated" cpuset partition type as well as the ability to create
non-top cpuset partition with no cpu allocated to it.
Signed-off-by: Waiman Long <longman@redhat.com>
---
Documentation/admin-guide/cgroup-v2.rst | 94 +++++++++++++++----------
1 file changed, 58 insertions(+), 36 deletions(-)
@@ -2080,8 +2080,9 @@ Cpuset Interface Files It accepts only the following input values when written to. ======== ================================- "root" a partition root- "member" a non-root member of a partition+ "member" Non-root member of a partition+ "root" Partition root+ "isolated" Partition root without load balancing ======== ================================ When set to be a partition root, the current cgroup is the
@@ -2090,9 +2091,14 @@ Cpuset Interface Files partition roots themselves and their descendants. The root cgroup is always a partition root.- There are constraints on where a partition root can be set.- It can only be set in a cgroup if all the following conditions- are true.+ When set to "isolated", the CPUs in that partition root will+ be in an isolated state without any load balancing from the+ scheduler. Tasks in such a partition must be explicitly bound+ to each individual CPU.++ There are constraints on where a partition root can be set+ ("root" or "isolated"). It can only be set in a cgroup if all+ the following conditions are true.1) The "cpuset.cpus" is not empty and the list of CPUs are exclusive, i.e. they are not shared by any of its siblings.
@@ -2103,51 +2109,67 @@ Cpuset Interface Files eliminating corner cases that have to be handled if such a condition is allowed.- Setting it to partition root will take the CPUs away from the- effective CPUs of the parent cgroup. Once it is set, this+ Setting it to a partition root will take the CPUs away from+ the effective CPUs of the parent cgroup. Once it is set, this file cannot be reverted back to "member" if there are any child cgroups with cpuset enabled.- A parent partition cannot distribute all its CPUs to its- child partitions. There must be at least one cpu left in the- parent partition.+ A parent partition may distribute all its CPUs to its child+ partitions as long as it is not the root cgroup and there is no+ task directly associated with that parent partition. Otherwise,+ there must be at least one cpu left in the parent partition.+ A new task cannot be moved to a partition root with no effective+ cpu.++ Once becoming a partition root, changes to "cpuset.cpus"+ is generally allowed as long as the first condition above+ (cpu exclusivity rule) is true. Other constraints for this+ operation are as follows.- Once becoming a partition root, changes to "cpuset.cpus" is- generally allowed as long as the first condition above is true,- the change will not take away all the CPUs from the parent- partition and the new "cpuset.cpus" value is a superset of its- children's "cpuset.cpus" values.+1) Any newly added CPUs must be a subset of the parent's+ "cpuset.cpus.effective".+2) Taking away all the CPUs from the parent's "cpuset.cpus.effective"+ is only allowed if there is no task associated with the+ parent partition.+3) Deletion of CPUs that have been distributed to child partition+ roots are not allowed. Sometimes, external factors like changes to ancestors' "cpuset.cpus" or cpu hotplug can cause the state of the partition- root to change. On read, the "cpuset.sched.partition" file- can show the following values.+ root to change. On read, the "cpuset.cpus.partition" file can+ show the following values. ============== ============================== "member" Non-root member of a partition "root" Partition root+ "isolated" Partition root without load balancing "root invalid" Invalid partition root ============== ==============================- It is a partition root if the first 2 partition root conditions- above are true and at least one CPU from "cpuset.cpus" is- granted by the parent cgroup.-- A partition root can become invalid if none of CPUs requested- in "cpuset.cpus" can be granted by the parent cgroup or the- parent cgroup is no longer a partition root itself. In this- case, it is not a real partition even though the restriction- of the first partition root condition above will still apply.- The cpu affinity of all the tasks in the cgroup will then be- associated with CPUs in the nearest ancestor partition.-- An invalid partition root can be transitioned back to a- real partition root if at least one of the requested CPUs- can now be granted by its parent. In this case, the cpu- affinity of all the tasks in the formerly invalid partition- will be associated to the CPUs of the newly formed partition.- Changing the partition state of an invalid partition root to- "member" is always allowed even if child cpusets are present.+ A partition root becomes invalid if all the CPUs requested in+ "cpuset.cpus" become unavailable. This can happen if all the+ CPUs have been offlined, or the state of an ancestor partition+ root become invalid. In this case, it is not a real partition+ even though the restriction of the cpu exclusivity rule will+ still apply. The cpu affinity of all the tasks in the cgroup+ will then be associated with CPUs in the nearest ancestor+ partition.++ In the special case of a parent partition competing with a child+ partition for the only CPU left, the parent partition wins and+ the child partition becomes invalid.++ An invalid partition root can be transitioned back to a real+ partition root if at least one of the requested CPUs become+ available again. In this case, the cpu affinity of all the tasks+ in the formerly invalid partition will be associated to the CPUs+ of the newly formed partition. Changing the partition state of+ an invalid partition root to "member" is always allowed even if+ child cpusets are present. However changing a partition root back+ to member will not be allowed if child partitions are present.++ Poll and inotify events are triggered when transition to or+ from invalid partition root happens. Device controller
On Tue, Jul 20, 2021 at 10:18:26AM -0400, Waiman Long wrote:
Use more descriptive variable names for update_prstate(), remove
unnecessary code and fix some typos. There is no functional change.
Signed-off-by: Waiman Long <longman@redhat.com>
On Tue, Jul 20, 2021 at 10:18:27AM -0400, Waiman Long wrote:
In cpuset_hotplug_workfn(), the detection of whether the cpu list
has been changed is done by comparing the effective cpus of the top
cpuset with the cpu_active_mask. However, in the rare case that just
all the CPUs in the subparts_cpus are offlined, the detection fails
and the partition states are not updated correctly. Fix it by forcing
the cpus_updated flag to true in this particular case.
Fixes: 4b842da276a8 ("cpuset: Make CPU hotplug work with partition")
Signed-off-by: Waiman Long <longman@redhat.com>
Applied to cgroup/for-5.15 w/ a minor update to the comment (I dropped
"just" before "all". It read weird to me.)
Thanks.
--
tejun
On Tue, Jul 20, 2021 at 10:18:28AM -0400, Waiman Long wrote:
The cpuset fields that manage partition root state do not strictly
follow the cpuset locking rule that update to cpuset has to be done
with both the callback_lock and cpuset_mutex held. This is now fixed
by making sure that the locking rule is upheld.
Fixes: 3881b86128d0 ("cpuset: Add an error state to cpuset.sched.partition")
Fixes: 4b842da276a8 ("cpuset: Make CPU hotplug work with partition)
Signed-off-by: Waiman Long <longman@redhat.com>
Hello,
On Tue, Jul 20, 2021 at 10:18:25AM -0400, Waiman Long wrote:
v3:
- Add two new patches (patches 2 & 3) to fix bugs found during the
testing process.
- Add a new patch to enable inotify event notification when partition
become invalid.
- Add a test to test event notification when partition become invalid.
I applied parts of the series. I think there was a bit of miscommunication.
I meant that we should use the invalid state as the only way to indicate
errors as long as the error state is something which can be reached through
hot unplug or other uncontrollable changes, and require users to monitor the
state transitions for confirmation and error handling.
Thanks.
--
tejun
On Tue, Jul 20, 2021 at 10:18:31AM -0400, Waiman Long wrote:
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=TBD
commit 994fb794cb252edd124a46ca0994e37a4726a100
Author: Waiman Long [off-list ref]
Date: Sat, 19 Jun 2021 13:28:19 -0400
cgroup/cpuset: Add a new isolated cpus.partition type
Cpuset v1 uses the sched_load_balance control file to determine if load
balancing should be enabled. Cpuset v2 gets rid of sched_load_balance
as its use may require disabling load balancing at cgroup root.
For workloads that require very low latency like DPDK, the latency
jitters caused by periodic load balancing may exceed the desired
latency limit.
When cpuset v2 is in use, the only way to avoid this latency cost is to
use the "isolcpus=" kernel boot option to isolate a set of CPUs. After
the kernel boot, however, there is no way to add or remove CPUs from
this isolated set. For workloads that are more dynamic in nature, that
means users have to provision enough CPUs for the worst case situation
resulting in excess idle CPUs.
To address this issue for cpuset v2, a new cpuset.cpus.partition type
"isolated" is added which allows the creation of a cpuset partition
without load balancing. This will allow system administrators to
dynamically adjust the size of isolated partition to the current need
of the workload without rebooting the system.
Signed-off-by: Waiman Long [off-list ref]
Signed-off-by: Waiman Long <longman@redhat.com>
Nice! And while we are adding a new ABI, can we take advantage of that and
add a specific semantic that if a new isolated partition matches a subset of
"isolcpus=", it automatically maps to it. This means that any further
modification to that isolated partition will also modify the associated
isolcpus= subset.
Or to summarize, when we create a new isolated partition, remove the associated
CPUs from isolcpus= ?
Thanks.
From: Waiman Long <hidden> Date: 2021-07-27 15:56:32
On 7/27/21 7:42 AM, Frederic Weisbecker wrote:
On Tue, Jul 20, 2021 at 10:18:31AM -0400, Waiman Long wrote:
quoted
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=TBD
commit 994fb794cb252edd124a46ca0994e37a4726a100
Author: Waiman Long [off-list ref]
Date: Sat, 19 Jun 2021 13:28:19 -0400
cgroup/cpuset: Add a new isolated cpus.partition type
Cpuset v1 uses the sched_load_balance control file to determine if load
balancing should be enabled. Cpuset v2 gets rid of sched_load_balance
as its use may require disabling load balancing at cgroup root.
For workloads that require very low latency like DPDK, the latency
jitters caused by periodic load balancing may exceed the desired
latency limit.
When cpuset v2 is in use, the only way to avoid this latency cost is to
use the "isolcpus=" kernel boot option to isolate a set of CPUs. After
the kernel boot, however, there is no way to add or remove CPUs from
this isolated set. For workloads that are more dynamic in nature, that
means users have to provision enough CPUs for the worst case situation
resulting in excess idle CPUs.
To address this issue for cpuset v2, a new cpuset.cpus.partition type
"isolated" is added which allows the creation of a cpuset partition
without load balancing. This will allow system administrators to
dynamically adjust the size of isolated partition to the current need
of the workload without rebooting the system.
Signed-off-by: Waiman Long [off-list ref]
Signed-off-by: Waiman Long <longman@redhat.com>
Nice! And while we are adding a new ABI, can we take advantage of that and
add a specific semantic that if a new isolated partition matches a subset of
"isolcpus=", it automatically maps to it. This means that any further
modification to that isolated partition will also modify the associated
isolcpus= subset.
Or to summarize, when we create a new isolated partition, remove the associated
CPUs from isolcpus= ?
We can certainly do that as a follow-on. Another idea that I have been
thinking about is to automatically generating a isolated partition under
root to match the given isolcpus parameter when the v2 filesystem is
mounted. That needs more experimentation and testing to verify that it
can work.
Cheers,
Longman
From: Waiman Long <hidden> Date: 2021-07-27 20:17:04
On 7/26/21 6:59 PM, Tejun Heo wrote:
On Tue, Jul 20, 2021 at 10:18:27AM -0400, Waiman Long wrote:
quoted
In cpuset_hotplug_workfn(), the detection of whether the cpu list
has been changed is done by comparing the effective cpus of the top
cpuset with the cpu_active_mask. However, in the rare case that just
all the CPUs in the subparts_cpus are offlined, the detection fails
and the partition states are not updated correctly. Fix it by forcing
the cpus_updated flag to true in this particular case.
Fixes: 4b842da276a8 ("cpuset: Make CPU hotplug work with partition")
Signed-off-by: Waiman Long <longman@redhat.com>
Applied to cgroup/for-5.15 w/ a minor update to the comment (I dropped
"just" before "all". It read weird to me.)
Thanks.
From: Waiman Long <hidden> Date: 2021-07-27 20:26:40
On 7/26/21 7:14 PM, Tejun Heo wrote:
On Tue, Jul 20, 2021 at 10:18:29AM -0400, Waiman Long wrote:
quoted
+static inline void notify_partition_change(struct cpuset *cs,
+ int old_prs, int new_prs)
+{
+ if ((old_prs == new_prs) ||
+ ((old_prs != PRS_ERROR) && (new_prs != PRS_ERROR)))
+ return;
+ cgroup_file_notify(&cs->partition_file);
I'd generate an event on any state changes. The user have to read the file
to find out what happened anyway.
Thanks.
From my own testing with "inotify_add_watch(fd, file, IN_MODIFY)",
poll() will return with a event whenever a user write to
cpuset.cpus.partition control file. I haven't really look into the sysfs
code yet, but I believe event generation will be automatic in this case.
So I don't think I need to explicitly add a cgroup_file_notify() when
users modify the control file directly. Other indirect modification may
cause the partition value to change to/from PRS_ERROR and I should have
captured all those changes in this patchset. I will update the patch to
note this point to make it more clear.
Cheers,
Longman
From: Waiman Long <hidden> Date: 2021-07-27 20:46:26
On 7/27/21 4:26 PM, Waiman Long wrote:
On 7/26/21 7:14 PM, Tejun Heo wrote:
quoted
On Tue, Jul 20, 2021 at 10:18:29AM -0400, Waiman Long wrote:
quoted
+static inline void notify_partition_change(struct cpuset *cs,
+ int old_prs, int new_prs)
+{
+ if ((old_prs == new_prs) ||
+ ((old_prs != PRS_ERROR) && (new_prs != PRS_ERROR)))
+ return;
+ cgroup_file_notify(&cs->partition_file);
I'd generate an event on any state changes. The user have to read the
file
to find out what happened anyway.
Thanks.
From my own testing with "inotify_add_watch(fd, file, IN_MODIFY)",
poll() will return with a event whenever a user write to
cpuset.cpus.partition control file. I haven't really look into the
sysfs code yet, but I believe event generation will be automatic in
this case. So I don't think I need to explicitly add a
cgroup_file_notify() when users modify the control file directly.
Other indirect modification may cause the partition value to change
to/from PRS_ERROR and I should have captured all those changes in this
patchset. I will update the patch to note this point to make it more
clear.
After thinking about it a bit more it, it is probably not a problem to
call cgroup_file_notify() for every change as this is not in a
performance critical path anyway. I will do some more testing to find
out if doing cgroup_file_notify() for regular file write will cause an
extra duplicated event to be sent out, I will probably stay with the
current patch. Otherwise, I can change it to always call
cgroup_file_notify().
Cheers,
Longman
From: Waiman Long <hidden> Date: 2021-07-27 21:14:36
On 7/26/21 7:17 PM, Tejun Heo wrote:
Hello,
On Tue, Jul 20, 2021 at 10:18:25AM -0400, Waiman Long wrote:
quoted
v3:
- Add two new patches (patches 2 & 3) to fix bugs found during the
testing process.
- Add a new patch to enable inotify event notification when partition
become invalid.
- Add a test to test event notification when partition become invalid.
I applied parts of the series. I think there was a bit of miscommunication.
I meant that we should use the invalid state as the only way to indicate
errors as long as the error state is something which can be reached through
hot unplug or other uncontrollable changes, and require users to monitor the
state transitions for confirmation and error handling.
Yes, that is the point of adding the event notification patch.
In the current code, direct write to cpuset.cpus.partition are strictly
controlled and invalid transitions are rejected. However, changes to
cpuset.cpus that do not break the cpu exclusivity rule or cpu hot plug
may cause a partition to changed to invalid. What is currently done in
this patchset is to add extra guards to reject those cpuset.cpus change
that cause the partition to become invalid since changes that break cpu
exclusivity rule will be rejected anyway. I can leave out those extra
guards and allow those invalid cpuset.cpus change to go forward and
change the partition to invalid instead if this is what you want.
However, if we have a complicated partition setup with multiple child
partitions. Invalid cpuset.cpus change in a parent partition will cause
all the child partitions to become invalid too. That is the scenario
that I don't want to happen inadvertently. Alternatively, we can
restrict those invalid changes if a child partition exist and let it
pass through and make it invalid if it is a standalone partition.
Please let me know which approach do you want me to take.
Cheers,
Longman
From: Michal Koutný <mkoutny@suse.com> Date: 2021-07-28 16:09:06
Hello Waiman.
On Tue, Jul 20, 2021 at 10:18:31AM -0400, Waiman Long [off-list ref] wrote:
quoted hunk
@@ -2026,6 +2036,22 @@ static int update_prstate(struct cpuset *cs, int new_prs)
[...]
+ } else if (old_prs && new_prs) {
If an isolated root partition becomes invalid (new_prs == PRS_ERROR)...
+ /*
+ * A change in load balance state only, no change in cpumasks.
+ */
+ update_flag(CS_SCHED_LOAD_BALANCE, cs, (new_prs != PRS_ISOLATED));
...this seems to erase information about CS_SCHED_LOAD_BALANCE zeroness.
IOW, if there's an isolated partition that becomes invalid and later
valid again (a cpu is (re)added), it will be a normal root partition
without the requested isolation, which is IMO undesired.
I may have overlooked something in broader context but it seems to me
the invalidity should be saved independently of the root/isolated type.
Regards,
Michal
From: Waiman Long <hidden> Date: 2021-07-28 16:28:04
On 7/28/21 12:09 PM, Michal Koutný wrote:
Hello Waiman.
On Tue, Jul 20, 2021 at 10:18:31AM -0400, Waiman Long [off-list ref] wrote:
quoted
@@ -2026,6 +2036,22 @@ static int update_prstate(struct cpuset *cs, int new_prs)
[...]
+ } else if (old_prs && new_prs) {
If an isolated root partition becomes invalid (new_prs == PRS_ERROR)...
quoted
+ /*
+ * A change in load balance state only, no change in cpumasks.
+ */
+ update_flag(CS_SCHED_LOAD_BALANCE, cs, (new_prs != PRS_ISOLATED));
...this seems to erase information about CS_SCHED_LOAD_BALANCE zeroness.
IOW, if there's an isolated partition that becomes invalid and later
valid again (a cpu is (re)added), it will be a normal root partition
without the requested isolation, which is IMO undesired.
I may have overlooked something in broader context but it seems to me
the invalidity should be saved independently of the root/isolated type.
PRS_ERROR cannot be passed to update_prstate(). For this patchset,
PRS_ERROR can only be set by changes in hotplug. The current design will
maintain the set flag (CS_SCHED_LOAD_BALANCE) and use it to decide to
switch back to PRS_ENABLED or PRS_ISOLATED when the cpus are available
again.
Cheers,
Longman
From: Michal Koutný <mkoutny@suse.com> Date: 2021-07-28 17:25:26
On Wed, Jul 28, 2021 at 12:27:58PM -0400, Waiman Long [off-list ref] wrote:
PRS_ERROR cannot be passed to update_prstate(). For this patchset, PRS_ERROR
can only be set by changes in hotplug. The current design will maintain the
set flag (CS_SCHED_LOAD_BALANCE) and use it to decide to switch back to
PRS_ENABLED or PRS_ISOLATED when the cpus are available again.
I see it now, thanks. (I still find a bit weird that the "isolated"
partition will be shown as "root invalid" when it's lacking cpus
(instead of "isolated invalid" and returning to "isolated") but I can
understand the approach of having just one "root invalid" for all.)
This patch can have
Reviewed-by: Michal Koutný <mkoutny@suse.com>
On Tue, Jul 27, 2021 at 11:56:25AM -0400, Waiman Long wrote:
On 7/27/21 7:42 AM, Frederic Weisbecker wrote:
quoted
On Tue, Jul 20, 2021 at 10:18:31AM -0400, Waiman Long wrote:
quoted
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=TBD
commit 994fb794cb252edd124a46ca0994e37a4726a100
Author: Waiman Long [off-list ref]
Date: Sat, 19 Jun 2021 13:28:19 -0400
cgroup/cpuset: Add a new isolated cpus.partition type
Cpuset v1 uses the sched_load_balance control file to determine if load
balancing should be enabled. Cpuset v2 gets rid of sched_load_balance
as its use may require disabling load balancing at cgroup root.
For workloads that require very low latency like DPDK, the latency
jitters caused by periodic load balancing may exceed the desired
latency limit.
When cpuset v2 is in use, the only way to avoid this latency cost is to
use the "isolcpus=" kernel boot option to isolate a set of CPUs. After
the kernel boot, however, there is no way to add or remove CPUs from
this isolated set. For workloads that are more dynamic in nature, that
means users have to provision enough CPUs for the worst case situation
resulting in excess idle CPUs.
To address this issue for cpuset v2, a new cpuset.cpus.partition type
"isolated" is added which allows the creation of a cpuset partition
without load balancing. This will allow system administrators to
dynamically adjust the size of isolated partition to the current need
of the workload without rebooting the system.
Signed-off-by: Waiman Long [off-list ref]
Signed-off-by: Waiman Long <longman@redhat.com>
Nice! And while we are adding a new ABI, can we take advantage of that and
add a specific semantic that if a new isolated partition matches a subset of
"isolcpus=", it automatically maps to it. This means that any further
modification to that isolated partition will also modify the associated
isolcpus= subset.
Or to summarize, when we create a new isolated partition, remove the associated
CPUs from isolcpus= ?
We can certainly do that as a follow-on.
I'm just concerned that this feature gets merged before we add that new
isolcpus= implicit mapping, which technically is a new ABI. Well I guess I
should hurry up and try to propose a patchset quickly once I'm back from
vacation :-)
Another idea that I have been
thinking about is to automatically generating a isolated partition under
root to match the given isolcpus parameter when the v2 filesystem is
mounted. That needs more experimentation and testing to verify that it can
work.
I thought about that too, mounting an "isolcpus" subdirectory withing the top
cpuset but I was worried it could break userspace that wouldn't expect that new
thing to show up.
Thanks.
Hello, Waiman. Sorry about the delay. Was off for a while.
On Tue, Jul 27, 2021 at 05:14:27PM -0400, Waiman Long wrote:
However, if we have a complicated partition setup with multiple child
partitions. Invalid cpuset.cpus change in a parent partition will cause all
the child partitions to become invalid too. That is the scenario that I
don't want to happen inadvertently. Alternatively, we can restrict those
I don't think there's anything fundamentally wrong with it given the
requirement that userland has to monitor invalid state transitions.
The same mass transition can happen through cpu hotplug operations,
right?
invalid changes if a child partition exist and let it pass through and make
it invalid if it is a standalone partition.
Please let me know which approach do you want me to take.
I think it'd be best if we can stick to some principles rather than
trying to adjust it for specific scenarios. e.g.:
* If a given state can be reached through cpu hot [un]plug, any
configuration attempt which reaches the same state should be allowed
with the same end result as cpu hot [un]plug.
* If a given state can't ever be reached in whichever way, the
configuration attempting to reach such state should be rejected.
Thanks.
--
tejun
From: Waiman Long <hidden> Date: 2021-08-10 01:12:27
On 8/9/21 6:46 PM, Tejun Heo wrote:
Hello, Waiman. Sorry about the delay. Was off for a while.
On Tue, Jul 27, 2021 at 05:14:27PM -0400, Waiman Long wrote:
quoted
However, if we have a complicated partition setup with multiple child
partitions. Invalid cpuset.cpus change in a parent partition will cause all
the child partitions to become invalid too. That is the scenario that I
don't want to happen inadvertently. Alternatively, we can restrict those
I don't think there's anything fundamentally wrong with it given the
requirement that userland has to monitor invalid state transitions.
The same mass transition can happen through cpu hotplug operations,
right?
quoted
invalid changes if a child partition exist and let it pass through and make
it invalid if it is a standalone partition.
Please let me know which approach do you want me to take.
I think it'd be best if we can stick to some principles rather than
trying to adjust it for specific scenarios. e.g.:
* If a given state can be reached through cpu hot [un]plug, any
configuration attempt which reaches the same state should be allowed
with the same end result as cpu hot [un]plug.
* If a given state can't ever be reached in whichever way, the
configuration attempting to reach such state should be rejected.
OK, I got it. I will make the necessary changes and submit a new patch
series.
Thanks,
Longman