aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2015-11-05 00:12:24 -0500
committerTejun Heo <tj@kernel.org>2015-11-16 10:58:26 -0500
commit34c06254ff82a815fdccdfae7517a06c9b768cee (patch)
tree95b146ae8a28f04a91a84983a407b463460662cb
parent8005c49d9aea74d382f474ce11afbbc7d7130bec (diff)
cgroup: fix cftype->file_offset handling
6f60eade2433 ("cgroup: generalize obtaining the handles of and notifying cgroup files") introduced cftype->file_offset so that the handles for per-css file instances can be recorded. These handles then can be used, for example, to generate file modified notifications. Unfortunately, it made the wrong assumption that files are created once for a given css and removed on its destruction. Due to the dependencies among subsystems, a css may be hidden from userland and then later shown again. This is implemented by removing and re-creating the affected files, so the associated kernfs_node for a given cgroup file may change over time. This incorrect assumption led to the corruption of css->files lists. Reimplement cftype->file_offset handling so that cgroup_file->kn is protected by a lock and updated as files are created and destroyed. This also makes keeping them on per-cgroup list unnecessary. Signed-off-by: Tejun Heo <tj@kernel.org> Reported-by: James Sedgwick <jsedgwick@fb.com> Fixes: 6f60eade2433 ("cgroup: generalize obtaining the handles of and notifying cgroup files") Acked-by: Johannes Weiner <hannes@cmpxchg.org> Acked-by: Zefan Li <lizefan@huawei.com>
-rw-r--r--include/linux/cgroup-defs.h4
-rw-r--r--include/linux/cgroup.h14
-rw-r--r--kernel/cgroup.c42
3 files changed, 35 insertions, 25 deletions
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index 60d44b26276d..869fd4a3d28e 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -90,7 +90,6 @@ enum {
90 */ 90 */
91struct cgroup_file { 91struct cgroup_file {
92 /* do not access any fields from outside cgroup core */ 92 /* do not access any fields from outside cgroup core */
93 struct list_head node; /* anchored at css->files */
94 struct kernfs_node *kn; 93 struct kernfs_node *kn;
95}; 94};
96 95
@@ -134,9 +133,6 @@ struct cgroup_subsys_state {
134 */ 133 */
135 u64 serial_nr; 134 u64 serial_nr;
136 135
137 /* all cgroup_files associated with this css */
138 struct list_head files;
139
140 /* percpu_ref killing and RCU release */ 136 /* percpu_ref killing and RCU release */
141 struct rcu_head rcu_head; 137 struct rcu_head rcu_head;
142 struct work_struct destroy_work; 138 struct work_struct destroy_work;
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 22e3754f89c5..f64083030ad5 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -88,6 +88,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from);
88int cgroup_add_dfl_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); 88int cgroup_add_dfl_cftypes(struct cgroup_subsys *ss, struct cftype *cfts);
89int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); 89int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts);
90int cgroup_rm_cftypes(struct cftype *cfts); 90int cgroup_rm_cftypes(struct cftype *cfts);
91void cgroup_file_notify(struct cgroup_file *cfile);
91 92
92char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen); 93char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen);
93int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry); 94int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry);
@@ -516,19 +517,6 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp)
516 pr_cont_kernfs_path(cgrp->kn); 517 pr_cont_kernfs_path(cgrp->kn);
517} 518}
518 519
519/**
520 * cgroup_file_notify - generate a file modified event for a cgroup_file
521 * @cfile: target cgroup_file
522 *
523 * @cfile must have been obtained by setting cftype->file_offset.
524 */
525static inline void cgroup_file_notify(struct cgroup_file *cfile)
526{
527 /* might not have been created due to one of the CFTYPE selector flags */
528 if (cfile->kn)
529 kernfs_notify(cfile->kn);
530}
531
532#else /* !CONFIG_CGROUPS */ 520#else /* !CONFIG_CGROUPS */
533 521
534struct cgroup_subsys_state; 522struct cgroup_subsys_state;
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index f1603c153890..b316debadeb3 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -98,6 +98,12 @@ static DEFINE_SPINLOCK(css_set_lock);
98static DEFINE_SPINLOCK(cgroup_idr_lock); 98static DEFINE_SPINLOCK(cgroup_idr_lock);
99 99
100/* 100/*
101 * Protects cgroup_file->kn for !self csses. It synchronizes notifications
102 * against file removal/re-creation across css hiding.
103 */
104static DEFINE_SPINLOCK(cgroup_file_kn_lock);
105
106/*
101 * Protects cgroup_subsys->release_agent_path. Modifying it also requires 107 * Protects cgroup_subsys->release_agent_path. Modifying it also requires
102 * cgroup_mutex. Reading requires either cgroup_mutex or this spinlock. 108 * cgroup_mutex. Reading requires either cgroup_mutex or this spinlock.
103 */ 109 */
@@ -1393,6 +1399,16 @@ static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
1393 char name[CGROUP_FILE_NAME_MAX]; 1399 char name[CGROUP_FILE_NAME_MAX];
1394 1400
1395 lockdep_assert_held(&cgroup_mutex); 1401 lockdep_assert_held(&cgroup_mutex);
1402
1403 if (cft->file_offset) {
1404 struct cgroup_subsys_state *css = cgroup_css(cgrp, cft->ss);
1405 struct cgroup_file *cfile = (void *)css + cft->file_offset;
1406
1407 spin_lock_irq(&cgroup_file_kn_lock);
1408 cfile->kn = NULL;
1409 spin_unlock_irq(&cgroup_file_kn_lock);
1410 }
1411
1396 kernfs_remove_by_name(cgrp->kn, cgroup_file_name(cgrp, cft, name)); 1412 kernfs_remove_by_name(cgrp->kn, cgroup_file_name(cgrp, cft, name));
1397} 1413}
1398 1414
@@ -1856,7 +1872,6 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
1856 1872
1857 INIT_LIST_HEAD(&cgrp->self.sibling); 1873 INIT_LIST_HEAD(&cgrp->self.sibling);
1858 INIT_LIST_HEAD(&cgrp->self.children); 1874 INIT_LIST_HEAD(&cgrp->self.children);
1859 INIT_LIST_HEAD(&cgrp->self.files);
1860 INIT_LIST_HEAD(&cgrp->cset_links); 1875 INIT_LIST_HEAD(&cgrp->cset_links);
1861 INIT_LIST_HEAD(&cgrp->pidlists); 1876 INIT_LIST_HEAD(&cgrp->pidlists);
1862 mutex_init(&cgrp->pidlist_mutex); 1877 mutex_init(&cgrp->pidlist_mutex);
@@ -3313,9 +3328,9 @@ static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
3313 if (cft->file_offset) { 3328 if (cft->file_offset) {
3314 struct cgroup_file *cfile = (void *)css + cft->file_offset; 3329 struct cgroup_file *cfile = (void *)css + cft->file_offset;
3315 3330
3316 kernfs_get(kn); 3331 spin_lock_irq(&cgroup_file_kn_lock);
3317 cfile->kn = kn; 3332 cfile->kn = kn;
3318 list_add(&cfile->node, &css->files); 3333 spin_unlock_irq(&cgroup_file_kn_lock);
3319 } 3334 }
3320 3335
3321 return 0; 3336 return 0;
@@ -3553,6 +3568,22 @@ int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
3553} 3568}
3554 3569
3555/** 3570/**
3571 * cgroup_file_notify - generate a file modified event for a cgroup_file
3572 * @cfile: target cgroup_file
3573 *
3574 * @cfile must have been obtained by setting cftype->file_offset.
3575 */
3576void cgroup_file_notify(struct cgroup_file *cfile)
3577{
3578 unsigned long flags;
3579
3580 spin_lock_irqsave(&cgroup_file_kn_lock, flags);
3581 if (cfile->kn)
3582 kernfs_notify(cfile->kn);
3583 spin_unlock_irqrestore(&cgroup_file_kn_lock, flags);
3584}
3585
3586/**
3556 * cgroup_task_count - count the number of tasks in a cgroup. 3587 * cgroup_task_count - count the number of tasks in a cgroup.
3557 * @cgrp: the cgroup in question 3588 * @cgrp: the cgroup in question
3558 * 3589 *
@@ -4613,13 +4644,9 @@ static void css_free_work_fn(struct work_struct *work)
4613 container_of(work, struct cgroup_subsys_state, destroy_work); 4644 container_of(work, struct cgroup_subsys_state, destroy_work);
4614 struct cgroup_subsys *ss = css->ss; 4645 struct cgroup_subsys *ss = css->ss;
4615 struct cgroup *cgrp = css->cgroup; 4646 struct cgroup *cgrp = css->cgroup;
4616 struct cgroup_file *cfile;
4617 4647
4618 percpu_ref_exit(&css->refcnt); 4648 percpu_ref_exit(&css->refcnt);
4619 4649
4620 list_for_each_entry(cfile, &css->files, node)
4621 kernfs_put(cfile->kn);
4622
4623 if (ss) { 4650 if (ss) {
4624 /* css free path */ 4651 /* css free path */
4625 int id = css->id; 4652 int id = css->id;
@@ -4724,7 +4751,6 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
4724 css->ss = ss; 4751 css->ss = ss;
4725 INIT_LIST_HEAD(&css->sibling); 4752 INIT_LIST_HEAD(&css->sibling);
4726 INIT_LIST_HEAD(&css->children); 4753 INIT_LIST_HEAD(&css->children);
4727 INIT_LIST_HEAD(&css->files);
4728 css->serial_nr = css_serial_nr_next++; 4754 css->serial_nr = css_serial_nr_next++;
4729 4755
4730 if (cgroup_parent(cgrp)) { 4756 if (cgroup_parent(cgrp)) {