hulk inclusion category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/9206 ---------------------------------------- A race condition exists when switching the scheduling class of an xcu group while tasks are being enqueued. The issue occurs due to the following interleaving: mkdir -p group -> create an xcu group echo cfs > group/xcu.sched_class echo pid > group/cgroup.procs -> xcu_sched_class_write -> mutex_lock(&xcg_mutex); -> xcu_cg_set_sched_class -> xcg_perxcu_cfs_rq_deinit -> mutex_lock(&xcu->xcu_lock); -> cfs_rq = NULL -> mutex_unlock(&xcu->xcu_lock); -> vstream_kick -> mutex_lock(&xcu->xcu_lock); -> enqueue_ctx -> NULL pointer dereference -> mutex_unlock(&xcu->xcu_lock); -> xcu_cfs_cg_init -> alloc cfs_rq -> mutex_unlock(&xcg_mutex); To prevent this, add a sanity check in xse_integrity_check() to ensure that the cfs_rq associated with the xse is valid when the scheduling class is CFS. This returns -EINVAL early if the cfs_rq is temporarily NULL during the transition, avoiding the kernel crash. Fixes: 43bbefc53356 ("xsched: Add XCU control group implementation and its backend in xsched CFS") Signed-off-by: Liu Kai <liukai284@huawei.com> --- include/linux/xsched.h | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/include/linux/xsched.h b/include/linux/xsched.h index c3617386cb8d..ea5cf910a99b 100644 --- a/include/linux/xsched.h +++ b/include/linux/xsched.h @@ -65,7 +65,6 @@ extern struct list_head xsched_class_list; #define for_each_vstream_in_ctx(vs, ctx) \ list_for_each_entry((vs), &((ctx)->vstream_list), ctx_node) - /* Manages xsched RT-like class linked list based runqueue. * * Now RT-like class runqueue structs is identical @@ -302,8 +301,6 @@ struct xsched_group { #define parent_xse_of(__xse) (&(xse_parent_grp_xcu((__xse))->xse)) -#define xsched_cfs_rq_of(xse) (xse_parent_grp_xcu((xse))->cfs_rq) - #define xsched_group_cfs_rq(__xg, __id) ((__xg)->perxcu_priv[(__id)].cfs_rq) #define for_each_xse(__xse) \ @@ -313,6 +310,14 @@ struct xsched_group { #define for_each_xsched_group(__xg) \ for (; (__xg) != root_xcg; (__xg) = (__xg)->parent) +static inline struct xsched_rq_cfs *xsched_cfs_rq_of(struct xsched_entity *xse) +{ + if (xse->cfs.cfs_rq) + return xse->cfs.cfs_rq; + + return xse_parent_grp_xcu((xse))->cfs_rq; +} + /** * Only group xsched entities are permitted to call. * @@ -334,12 +339,15 @@ static inline bool xsched_entity_throttled(struct xsched_entity *xse) return cfs_rq->throttled; } -#else - -#define xsched_cfs_rq_of(xse) (&((xse)->xcu->xrq.cfs)) +#else /* !CONFIG_CGROUP_XCU */ #define for_each_xse(__xse) for (; (__xse); (__xse) = NULL) +static inline struct xsched_rq_cfs *xsched_cfs_rq_of(struct xsched_entity *xse) +{ + return &(xse->xcu->xrq.cfs); +} + #endif /* CONFIG_CGROUP_XCU */ static inline int xse_integrity_check(struct xsched_entity *xse) @@ -354,6 +362,12 @@ static inline int xse_integrity_check(struct xsched_entity *xse) return -EINVAL; } +#ifdef CONFIG_XCU_SCHED_CFS + /* The cfs_rq of xse may be NULL in some scenarios */ + if (xse->class == &fair_xsched_class && !xsched_cfs_rq_of(xse)) + return -EINVAL; +#endif + #ifdef CONFIG_CGROUP_XCU if (xse->is_group && !xse_this_cfs_rq(xse)) { // Can only be in the free process -- 2.34.1