diff options
author | Tejun Heo <tj@kernel.org> | 2013-06-25 14:48:32 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-07-21 21:21:25 -0400 |
commit | b6891ed4e66b65e5d6bb36964af0d65a08590018 (patch) | |
tree | e076e3ab8dd110873fe714af8e27c2e6d395b617 /include | |
parent | 83d0eb79752482bb888fb6d86ceed8971272f8b4 (diff) |
cgroup: fix RCU accesses to task->cgroups
commit 14611e51a57df10240817d8ada510842faf0ec51 upstream.
task->cgroups is a RCU pointer pointing to struct css_set. A task
switches to a different css_set on cgroup migration but a css_set
doesn't change once created and its pointers to cgroup_subsys_states
aren't RCU protected.
task_subsys_state[_check]() is the macro to acquire css given a task
and subsys_id pair. It RCU-dereferences task->cgroups->subsys[] not
task->cgroups, so the RCU pointer task->cgroups ends up being
dereferenced without read_barrier_depends() after it. It's broken.
Fix it by introducing task_css_set[_check]() which does
RCU-dereference on task->cgroups. task_subsys_state[_check]() is
reimplemented to directly dereference ->subsys[] of the css_set
returned from task_css_set[_check]().
This removes some of sparse RCU warnings in cgroup.
v2: Fixed unbalanced parenthsis and there's no need to use
rcu_dereference_raw() when !CONFIG_PROVE_RCU. Both spotted by Li.
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Fengguang Wu <fengguang.wu@intel.com>
Acked-by: Li Zefan <lizefan@huawei.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'include')
-rw-r--r-- | include/linux/cgroup.h | 58 |
1 files changed, 48 insertions, 10 deletions
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 8bda1294c035..8852d370c720 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h | |||
@@ -646,22 +646,60 @@ static inline struct cgroup_subsys_state *cgroup_subsys_state( | |||
646 | return cgrp->subsys[subsys_id]; | 646 | return cgrp->subsys[subsys_id]; |
647 | } | 647 | } |
648 | 648 | ||
649 | /* | 649 | /** |
650 | * function to get the cgroup_subsys_state which allows for extra | 650 | * task_css_set_check - obtain a task's css_set with extra access conditions |
651 | * rcu_dereference_check() conditions, such as locks used during the | 651 | * @task: the task to obtain css_set for |
652 | * cgroup_subsys::attach() methods. | 652 | * @__c: extra condition expression to be passed to rcu_dereference_check() |
653 | * | ||
654 | * A task's css_set is RCU protected, initialized and exited while holding | ||
655 | * task_lock(), and can only be modified while holding both cgroup_mutex | ||
656 | * and task_lock() while the task is alive. This macro verifies that the | ||
657 | * caller is inside proper critical section and returns @task's css_set. | ||
658 | * | ||
659 | * The caller can also specify additional allowed conditions via @__c, such | ||
660 | * as locks used during the cgroup_subsys::attach() methods. | ||
653 | */ | 661 | */ |
654 | #ifdef CONFIG_PROVE_RCU | 662 | #ifdef CONFIG_PROVE_RCU |
655 | extern struct mutex cgroup_mutex; | 663 | extern struct mutex cgroup_mutex; |
656 | #define task_subsys_state_check(task, subsys_id, __c) \ | 664 | #define task_css_set_check(task, __c) \ |
657 | rcu_dereference_check((task)->cgroups->subsys[(subsys_id)], \ | 665 | rcu_dereference_check((task)->cgroups, \ |
658 | lockdep_is_held(&(task)->alloc_lock) || \ | 666 | lockdep_is_held(&(task)->alloc_lock) || \ |
659 | lockdep_is_held(&cgroup_mutex) || (__c)) | 667 | lockdep_is_held(&cgroup_mutex) || (__c)) |
660 | #else | 668 | #else |
661 | #define task_subsys_state_check(task, subsys_id, __c) \ | 669 | #define task_css_set_check(task, __c) \ |
662 | rcu_dereference((task)->cgroups->subsys[(subsys_id)]) | 670 | rcu_dereference((task)->cgroups) |
663 | #endif | 671 | #endif |
664 | 672 | ||
673 | /** | ||
674 | * task_subsys_state_check - obtain css for (task, subsys) w/ extra access conds | ||
675 | * @task: the target task | ||
676 | * @subsys_id: the target subsystem ID | ||
677 | * @__c: extra condition expression to be passed to rcu_dereference_check() | ||
678 | * | ||
679 | * Return the cgroup_subsys_state for the (@task, @subsys_id) pair. The | ||
680 | * synchronization rules are the same as task_css_set_check(). | ||
681 | */ | ||
682 | #define task_subsys_state_check(task, subsys_id, __c) \ | ||
683 | task_css_set_check((task), (__c))->subsys[(subsys_id)] | ||
684 | |||
685 | /** | ||
686 | * task_css_set - obtain a task's css_set | ||
687 | * @task: the task to obtain css_set for | ||
688 | * | ||
689 | * See task_css_set_check(). | ||
690 | */ | ||
691 | static inline struct css_set *task_css_set(struct task_struct *task) | ||
692 | { | ||
693 | return task_css_set_check(task, false); | ||
694 | } | ||
695 | |||
696 | /** | ||
697 | * task_subsys_state - obtain css for (task, subsys) | ||
698 | * @task: the target task | ||
699 | * @subsys_id: the target subsystem ID | ||
700 | * | ||
701 | * See task_subsys_state_check(). | ||
702 | */ | ||
665 | static inline struct cgroup_subsys_state * | 703 | static inline struct cgroup_subsys_state * |
666 | task_subsys_state(struct task_struct *task, int subsys_id) | 704 | task_subsys_state(struct task_struct *task, int subsys_id) |
667 | { | 705 | { |