diff options
author | Paul Menage <menage@google.com> | 2008-07-25 04:46:58 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-25 13:53:35 -0400 |
commit | db3b14978abc02041046ed8353f0899cb58ffffc (patch) | |
tree | 355ba027c6354bd13bcbb5318e8478b3de4599e9 | |
parent | ce16b49d37e748574f7fabc2726268d542d0aa1a (diff) |
cgroup files: add write_string cgroup control file method
This patch adds a write_string() method for cgroups control files. The
semantics are that a buffer is copied from userspace to kernelspace
and the handler function invoked on that buffer. The buffer is
guaranteed to be nul-terminated, and no longer than max_write_len
(defaulting to 64 bytes if unspecified). Later patches will convert
existing raw file write handlers in control group subsystems to use
this method.
Signed-off-by: Paul Menage <menage@google.com>
Cc: Paul Jackson <pj@sgi.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Acked-by: 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>
-rw-r--r-- | include/linux/cgroup.h | 14 | ||||
-rw-r--r-- | kernel/cgroup.c | 35 |
2 files changed, 49 insertions, 0 deletions
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 88a734edccbc..f5379455bb59 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h | |||
@@ -205,6 +205,13 @@ struct cftype { | |||
205 | * subsystem, followed by a period */ | 205 | * subsystem, followed by a period */ |
206 | char name[MAX_CFTYPE_NAME]; | 206 | char name[MAX_CFTYPE_NAME]; |
207 | int private; | 207 | int private; |
208 | |||
209 | /* | ||
210 | * If non-zero, defines the maximum length of string that can | ||
211 | * be passed to write_string; defaults to 64 | ||
212 | */ | ||
213 | size_t max_write_len; | ||
214 | |||
208 | int (*open)(struct inode *inode, struct file *file); | 215 | int (*open)(struct inode *inode, struct file *file); |
209 | ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, | 216 | ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, |
210 | struct file *file, | 217 | struct file *file, |
@@ -249,6 +256,13 @@ struct cftype { | |||
249 | int (*write_s64)(struct cgroup *cgrp, struct cftype *cft, s64 val); | 256 | int (*write_s64)(struct cgroup *cgrp, struct cftype *cft, s64 val); |
250 | 257 | ||
251 | /* | 258 | /* |
259 | * write_string() is passed a nul-terminated kernelspace | ||
260 | * buffer of maximum length determined by max_write_len. | ||
261 | * Returns 0 or -ve error code. | ||
262 | */ | ||
263 | int (*write_string)(struct cgroup *cgrp, struct cftype *cft, | ||
264 | const char *buffer); | ||
265 | /* | ||
252 | * trigger() callback can be used to get some kick from the | 266 | * trigger() callback can be used to get some kick from the |
253 | * userspace, when the actual string written is not important | 267 | * userspace, when the actual string written is not important |
254 | * at all. The private field can be used to determine the | 268 | * at all. The private field can be used to determine the |
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 70d083c6fb6b..3a99cc2df860 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
@@ -1363,6 +1363,39 @@ static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, | |||
1363 | return retval; | 1363 | return retval; |
1364 | } | 1364 | } |
1365 | 1365 | ||
1366 | static ssize_t cgroup_write_string(struct cgroup *cgrp, struct cftype *cft, | ||
1367 | struct file *file, | ||
1368 | const char __user *userbuf, | ||
1369 | size_t nbytes, loff_t *unused_ppos) | ||
1370 | { | ||
1371 | char local_buffer[64]; | ||
1372 | int retval = 0; | ||
1373 | size_t max_bytes = cft->max_write_len; | ||
1374 | char *buffer = local_buffer; | ||
1375 | |||
1376 | if (!max_bytes) | ||
1377 | max_bytes = sizeof(local_buffer) - 1; | ||
1378 | if (nbytes >= max_bytes) | ||
1379 | return -E2BIG; | ||
1380 | /* Allocate a dynamic buffer if we need one */ | ||
1381 | if (nbytes >= sizeof(local_buffer)) { | ||
1382 | buffer = kmalloc(nbytes + 1, GFP_KERNEL); | ||
1383 | if (buffer == NULL) | ||
1384 | return -ENOMEM; | ||
1385 | } | ||
1386 | if (nbytes && copy_from_user(buffer, userbuf, nbytes)) | ||
1387 | return -EFAULT; | ||
1388 | |||
1389 | buffer[nbytes] = 0; /* nul-terminate */ | ||
1390 | strstrip(buffer); | ||
1391 | retval = cft->write_string(cgrp, cft, buffer); | ||
1392 | if (!retval) | ||
1393 | retval = nbytes; | ||
1394 | if (buffer != local_buffer) | ||
1395 | kfree(buffer); | ||
1396 | return retval; | ||
1397 | } | ||
1398 | |||
1366 | static ssize_t cgroup_common_file_write(struct cgroup *cgrp, | 1399 | static ssize_t cgroup_common_file_write(struct cgroup *cgrp, |
1367 | struct cftype *cft, | 1400 | struct cftype *cft, |
1368 | struct file *file, | 1401 | struct file *file, |
@@ -1440,6 +1473,8 @@ static ssize_t cgroup_file_write(struct file *file, const char __user *buf, | |||
1440 | return cft->write(cgrp, cft, file, buf, nbytes, ppos); | 1473 | return cft->write(cgrp, cft, file, buf, nbytes, ppos); |
1441 | if (cft->write_u64 || cft->write_s64) | 1474 | if (cft->write_u64 || cft->write_s64) |
1442 | return cgroup_write_X64(cgrp, cft, file, buf, nbytes, ppos); | 1475 | return cgroup_write_X64(cgrp, cft, file, buf, nbytes, ppos); |
1476 | if (cft->write_string) | ||
1477 | return cgroup_write_string(cgrp, cft, file, buf, nbytes, ppos); | ||
1443 | if (cft->trigger) { | 1478 | if (cft->trigger) { |
1444 | int ret = cft->trigger(cgrp, (unsigned int)cft->private); | 1479 | int ret = cft->trigger(cgrp, (unsigned int)cft->private); |
1445 | return ret ? ret : nbytes; | 1480 | return ret ? ret : nbytes; |