summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2016-12-27 14:49:04 -0500
committerTejun Heo <tj@kernel.org>2016-12-27 14:49:04 -0500
commitb4b90a8e86f2539a028d68077b45e8511dd2adb0 (patch)
tree27bbbcc7d7e18b1f288e982fc01d2b2f631ac5c8
parente90cbebc3fa5caea4c8bfeb0d0157a0cee53efc7 (diff)
cgroup: reimplement reading "cgroup.procs" on cgroup v2
On v1, "tasks" and "cgroup.procs" are expected to be sorted which makes the implementation expensive and unnecessarily complicated involving result cache management. v2 doesn't have the sorting requirement, so it can just iterate and print processes one by one. seq_files are either read sequentially or reset to position zero, so the implementation doesn't even need to worry about seeking. This keeps the css_task_iter across multiple read(2) calls and migrations of new processes always append won't miss processes which are newly migrated in before each read(2). Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Acked-by: Zefan Li <lizefan@huawei.com>
-rw-r--r--kernel/cgroup.c63
1 files changed, 58 insertions, 5 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 87167e40c4fa..fd684bfd154d 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -4426,6 +4426,60 @@ out_err:
4426 return ret; 4426 return ret;
4427} 4427}
4428 4428
4429static void cgroup_procs_release(struct kernfs_open_file *of)
4430{
4431 if (of->priv) {
4432 css_task_iter_end(of->priv);
4433 kfree(of->priv);
4434 }
4435}
4436
4437static void *cgroup_procs_next(struct seq_file *s, void *v, loff_t *pos)
4438{
4439 struct kernfs_open_file *of = s->private;
4440 struct css_task_iter *it = of->priv;
4441 struct task_struct *task;
4442
4443 do {
4444 task = css_task_iter_next(it);
4445 } while (task && !thread_group_leader(task));
4446
4447 return task;
4448}
4449
4450static void *cgroup_procs_start(struct seq_file *s, loff_t *pos)
4451{
4452 struct kernfs_open_file *of = s->private;
4453 struct cgroup *cgrp = seq_css(s)->cgroup;
4454 struct css_task_iter *it = of->priv;
4455
4456 /*
4457 * When a seq_file is seeked, it's always traversed sequentially
4458 * from position 0, so we can simply keep iterating on !0 *pos.
4459 */
4460 if (!it) {
4461 if (WARN_ON_ONCE((*pos)++))
4462 return ERR_PTR(-EINVAL);
4463
4464 it = kzalloc(sizeof(*it), GFP_KERNEL);
4465 if (!it)
4466 return ERR_PTR(-ENOMEM);
4467 of->priv = it;
4468 css_task_iter_start(&cgrp->self, it);
4469 } else if (!(*pos)++) {
4470 css_task_iter_end(it);
4471 css_task_iter_start(&cgrp->self, it);
4472 }
4473
4474 return cgroup_procs_next(s, NULL, NULL);
4475}
4476
4477static int cgroup_procs_show(struct seq_file *s, void *v)
4478{
4479 seq_printf(s, "%d\n", task_tgid_vnr(v));
4480 return 0;
4481}
4482
4429/* 4483/*
4430 * Stuff for reading the 'tasks'/'procs' files. 4484 * Stuff for reading the 'tasks'/'procs' files.
4431 * 4485 *
@@ -4914,11 +4968,10 @@ static struct cftype cgroup_dfl_base_files[] = {
4914 { 4968 {
4915 .name = "cgroup.procs", 4969 .name = "cgroup.procs",
4916 .file_offset = offsetof(struct cgroup, procs_file), 4970 .file_offset = offsetof(struct cgroup, procs_file),
4917 .seq_start = cgroup_pidlist_start, 4971 .release = cgroup_procs_release,
4918 .seq_next = cgroup_pidlist_next, 4972 .seq_start = cgroup_procs_start,
4919 .seq_stop = cgroup_pidlist_stop, 4973 .seq_next = cgroup_procs_next,
4920 .seq_show = cgroup_pidlist_show, 4974 .seq_show = cgroup_procs_show,
4921 .private = CGROUP_FILE_PROCS,
4922 .write = cgroup_procs_write, 4975 .write = cgroup_procs_write,
4923 }, 4976 },
4924 { 4977 {