diff options
author | Paul Menage <menage@google.com> | 2008-07-25 04:46:59 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-25 13:53:35 -0400 |
commit | e788e066c651b1bbf4a927dc95395c1aa13be436 (patch) | |
tree | 8ffb80dc5917ba5b77e17ab2b71afcea3aadfef8 /kernel/cgroup.c | |
parent | db3b14978abc02041046ed8353f0899cb58ffffc (diff) |
cgroup files: move the release_agent file to use typed handlers
Adds cgroup_release_agent_write() and cgroup_release_agent_show()
methods to handle writing/reading the path to a cgroup hierarchy's
release agent. As a result, cgroup_common_file_read() is now unnecessary.
As part of the change, a previously-tolerated race in
cgroup_release_agent() is avoided by copying the current
release_agent_path prior to calling call_usermode_helper().
Signed-off-by: Paul Menage <menage@google.com>
Cc: Paul Jackson <pj@sgi.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Balbir Singh <balbir@in.ibm.com>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/cgroup.c')
-rw-r--r-- | kernel/cgroup.c | 125 |
1 files changed, 57 insertions, 68 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 3a99cc2df860..0120b5d67a73 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
@@ -89,11 +89,7 @@ struct cgroupfs_root { | |||
89 | /* Hierarchy-specific flags */ | 89 | /* Hierarchy-specific flags */ |
90 | unsigned long flags; | 90 | unsigned long flags; |
91 | 91 | ||
92 | /* The path to use for release notifications. No locking | 92 | /* The path to use for release notifications. */ |
93 | * between setting and use - so if userspace updates this | ||
94 | * while child cgroups exist, you could miss a | ||
95 | * notification. We ensure that it's always a valid | ||
96 | * NUL-terminated string */ | ||
97 | char release_agent_path[PATH_MAX]; | 93 | char release_agent_path[PATH_MAX]; |
98 | }; | 94 | }; |
99 | 95 | ||
@@ -1329,6 +1325,45 @@ enum cgroup_filetype { | |||
1329 | FILE_RELEASE_AGENT, | 1325 | FILE_RELEASE_AGENT, |
1330 | }; | 1326 | }; |
1331 | 1327 | ||
1328 | /** | ||
1329 | * cgroup_lock_live_group - take cgroup_mutex and check that cgrp is alive. | ||
1330 | * @cgrp: the cgroup to be checked for liveness | ||
1331 | * | ||
1332 | * Returns true (with lock held) on success, or false (with no lock | ||
1333 | * held) on failure. | ||
1334 | */ | ||
1335 | int cgroup_lock_live_group(struct cgroup *cgrp) | ||
1336 | { | ||
1337 | mutex_lock(&cgroup_mutex); | ||
1338 | if (cgroup_is_removed(cgrp)) { | ||
1339 | mutex_unlock(&cgroup_mutex); | ||
1340 | return false; | ||
1341 | } | ||
1342 | return true; | ||
1343 | } | ||
1344 | |||
1345 | static int cgroup_release_agent_write(struct cgroup *cgrp, struct cftype *cft, | ||
1346 | const char *buffer) | ||
1347 | { | ||
1348 | BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); | ||
1349 | if (!cgroup_lock_live_group(cgrp)) | ||
1350 | return -ENODEV; | ||
1351 | strcpy(cgrp->root->release_agent_path, buffer); | ||
1352 | mutex_unlock(&cgroup_mutex); | ||
1353 | return 0; | ||
1354 | } | ||
1355 | |||
1356 | static int cgroup_release_agent_show(struct cgroup *cgrp, struct cftype *cft, | ||
1357 | struct seq_file *seq) | ||
1358 | { | ||
1359 | if (!cgroup_lock_live_group(cgrp)) | ||
1360 | return -ENODEV; | ||
1361 | seq_puts(seq, cgrp->root->release_agent_path); | ||
1362 | seq_putc(seq, '\n'); | ||
1363 | mutex_unlock(&cgroup_mutex); | ||
1364 | return 0; | ||
1365 | } | ||
1366 | |||
1332 | static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, | 1367 | static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, |
1333 | struct file *file, | 1368 | struct file *file, |
1334 | const char __user *userbuf, | 1369 | const char __user *userbuf, |
@@ -1443,10 +1478,6 @@ static ssize_t cgroup_common_file_write(struct cgroup *cgrp, | |||
1443 | else | 1478 | else |
1444 | clear_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); | 1479 | clear_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); |
1445 | break; | 1480 | break; |
1446 | case FILE_RELEASE_AGENT: | ||
1447 | BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); | ||
1448 | strcpy(cgrp->root->release_agent_path, buffer); | ||
1449 | break; | ||
1450 | default: | 1481 | default: |
1451 | retval = -EINVAL; | 1482 | retval = -EINVAL; |
1452 | goto out2; | 1483 | goto out2; |
@@ -1506,49 +1537,6 @@ static ssize_t cgroup_read_s64(struct cgroup *cgrp, struct cftype *cft, | |||
1506 | return simple_read_from_buffer(buf, nbytes, ppos, tmp, len); | 1537 | return simple_read_from_buffer(buf, nbytes, ppos, tmp, len); |
1507 | } | 1538 | } |
1508 | 1539 | ||
1509 | static ssize_t cgroup_common_file_read(struct cgroup *cgrp, | ||
1510 | struct cftype *cft, | ||
1511 | struct file *file, | ||
1512 | char __user *buf, | ||
1513 | size_t nbytes, loff_t *ppos) | ||
1514 | { | ||
1515 | enum cgroup_filetype type = cft->private; | ||
1516 | char *page; | ||
1517 | ssize_t retval = 0; | ||
1518 | char *s; | ||
1519 | |||
1520 | if (!(page = (char *)__get_free_page(GFP_KERNEL))) | ||
1521 | return -ENOMEM; | ||
1522 | |||
1523 | s = page; | ||
1524 | |||
1525 | switch (type) { | ||
1526 | case FILE_RELEASE_AGENT: | ||
1527 | { | ||
1528 | struct cgroupfs_root *root; | ||
1529 | size_t n; | ||
1530 | mutex_lock(&cgroup_mutex); | ||
1531 | root = cgrp->root; | ||
1532 | n = strnlen(root->release_agent_path, | ||
1533 | sizeof(root->release_agent_path)); | ||
1534 | n = min(n, (size_t) PAGE_SIZE); | ||
1535 | strncpy(s, root->release_agent_path, n); | ||
1536 | mutex_unlock(&cgroup_mutex); | ||
1537 | s += n; | ||
1538 | break; | ||
1539 | } | ||
1540 | default: | ||
1541 | retval = -EINVAL; | ||
1542 | goto out; | ||
1543 | } | ||
1544 | *s++ = '\n'; | ||
1545 | |||
1546 | retval = simple_read_from_buffer(buf, nbytes, ppos, page, s - page); | ||
1547 | out: | ||
1548 | free_page((unsigned long)page); | ||
1549 | return retval; | ||
1550 | } | ||
1551 | |||
1552 | static ssize_t cgroup_file_read(struct file *file, char __user *buf, | 1540 | static ssize_t cgroup_file_read(struct file *file, char __user *buf, |
1553 | size_t nbytes, loff_t *ppos) | 1541 | size_t nbytes, loff_t *ppos) |
1554 | { | 1542 | { |
@@ -1606,6 +1594,7 @@ int cgroup_seqfile_release(struct inode *inode, struct file *file) | |||
1606 | 1594 | ||
1607 | static struct file_operations cgroup_seqfile_operations = { | 1595 | static struct file_operations cgroup_seqfile_operations = { |
1608 | .read = seq_read, | 1596 | .read = seq_read, |
1597 | .write = cgroup_file_write, | ||
1609 | .llseek = seq_lseek, | 1598 | .llseek = seq_lseek, |
1610 | .release = cgroup_seqfile_release, | 1599 | .release = cgroup_seqfile_release, |
1611 | }; | 1600 | }; |
@@ -2283,8 +2272,9 @@ static struct cftype files[] = { | |||
2283 | 2272 | ||
2284 | static struct cftype cft_release_agent = { | 2273 | static struct cftype cft_release_agent = { |
2285 | .name = "release_agent", | 2274 | .name = "release_agent", |
2286 | .read = cgroup_common_file_read, | 2275 | .read_seq_string = cgroup_release_agent_show, |
2287 | .write = cgroup_common_file_write, | 2276 | .write_string = cgroup_release_agent_write, |
2277 | .max_write_len = PATH_MAX, | ||
2288 | .private = FILE_RELEASE_AGENT, | 2278 | .private = FILE_RELEASE_AGENT, |
2289 | }; | 2279 | }; |
2290 | 2280 | ||
@@ -3111,27 +3101,24 @@ static void cgroup_release_agent(struct work_struct *work) | |||
3111 | while (!list_empty(&release_list)) { | 3101 | while (!list_empty(&release_list)) { |
3112 | char *argv[3], *envp[3]; | 3102 | char *argv[3], *envp[3]; |
3113 | int i; | 3103 | int i; |
3114 | char *pathbuf; | 3104 | char *pathbuf = NULL, *agentbuf = NULL; |
3115 | struct cgroup *cgrp = list_entry(release_list.next, | 3105 | struct cgroup *cgrp = list_entry(release_list.next, |
3116 | struct cgroup, | 3106 | struct cgroup, |
3117 | release_list); | 3107 | release_list); |
3118 | list_del_init(&cgrp->release_list); | 3108 | list_del_init(&cgrp->release_list); |
3119 | spin_unlock(&release_list_lock); | 3109 | spin_unlock(&release_list_lock); |
3120 | pathbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); | 3110 | pathbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); |
3121 | if (!pathbuf) { | 3111 | if (!pathbuf) |
3122 | spin_lock(&release_list_lock); | 3112 | goto continue_free; |
3123 | continue; | 3113 | if (cgroup_path(cgrp, pathbuf, PAGE_SIZE) < 0) |
3124 | } | 3114 | goto continue_free; |
3125 | 3115 | agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL); | |
3126 | if (cgroup_path(cgrp, pathbuf, PAGE_SIZE) < 0) { | 3116 | if (!agentbuf) |
3127 | kfree(pathbuf); | 3117 | goto continue_free; |
3128 | spin_lock(&release_list_lock); | ||
3129 | continue; | ||
3130 | } | ||
3131 | 3118 | ||
3132 | i = 0; | 3119 | i = 0; |
3133 | argv[i++] = cgrp->root->release_agent_path; | 3120 | argv[i++] = agentbuf; |
3134 | argv[i++] = (char *)pathbuf; | 3121 | argv[i++] = pathbuf; |
3135 | argv[i] = NULL; | 3122 | argv[i] = NULL; |
3136 | 3123 | ||
3137 | i = 0; | 3124 | i = 0; |
@@ -3145,8 +3132,10 @@ static void cgroup_release_agent(struct work_struct *work) | |||
3145 | * be a slow process */ | 3132 | * be a slow process */ |
3146 | mutex_unlock(&cgroup_mutex); | 3133 | mutex_unlock(&cgroup_mutex); |
3147 | call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); | 3134 | call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); |
3148 | kfree(pathbuf); | ||
3149 | mutex_lock(&cgroup_mutex); | 3135 | mutex_lock(&cgroup_mutex); |
3136 | continue_free: | ||
3137 | kfree(pathbuf); | ||
3138 | kfree(agentbuf); | ||
3150 | spin_lock(&release_list_lock); | 3139 | spin_lock(&release_list_lock); |
3151 | } | 3140 | } |
3152 | spin_unlock(&release_list_lock); | 3141 | spin_unlock(&release_list_lock); |