[PATCH 10/24] task_diag: add ability to dump children (v2)
From: Andrey Vagin <hidden>
Date: 2015-07-06 08:55:31
Also in:
lkml
Subsystem:
the rest · Maintainer:
Linus Torvalds
Now we can dump all task or children of a specified task.
It's an example how this interface can be expanded for different
use-cases.
v2: Fixes from David Ahern
Add missing break in iter_stop
Fix 8-byte alignment issues
Cc: David Ahern <redacted>
Signed-off-by: Andrey Vagin <redacted>
---
include/uapi/linux/task_diag.h | 1 +
kernel/taskdiag.c | 117 +++++++++++++++++++++++++++++++++++------
2 files changed, 102 insertions(+), 16 deletions(-)
diff --git a/include/uapi/linux/task_diag.h b/include/uapi/linux/task_diag.h
index 0e659d6..af192db 100644
--- a/include/uapi/linux/task_diag.h
+++ b/include/uapi/linux/task_diag.h@@ -63,6 +63,7 @@ struct task_diag_creds { }; #define TASK_DIAG_DUMP_ALL 0 +#define TASK_DIAG_DUMP_CHILDREN 1 struct task_diag_pid { __u64 show_flags;
diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c
index 5123e12..6dd3361 100644
--- a/kernel/taskdiag.c
+++ b/kernel/taskdiag.c@@ -184,42 +184,127 @@ err: return err; } +struct task_iter { + struct task_diag_pid req; + struct pid_namespace *ns; + struct netlink_callback *cb; + struct task_struct *parent; + + union { + struct tgid_iter tgid; + struct { + unsigned int pos; + struct task_struct *task; + }; + }; +}; + +static void iter_stop(struct task_iter *iter) +{ + struct task_struct *task; + + if (iter->parent) + put_task_struct(iter->parent); + + switch (iter->req.dump_strategy) { + case TASK_DIAG_DUMP_ALL: + task = iter->tgid.task; + break; + default: + task = iter->task; + } + if (task) + put_task_struct(task); +} + +static struct task_struct *iter_start(struct task_iter *iter) +{ + if (iter->req.pid > 0) { + rcu_read_lock(); + iter->parent = find_task_by_pid_ns(iter->req.pid, iter->ns); + if (iter->parent) + get_task_struct(iter->parent); + rcu_read_unlock(); + } + + switch (iter->req.dump_strategy) { + case TASK_DIAG_DUMP_CHILDREN: + + if (iter->parent == NULL) + return ERR_PTR(-ESRCH); + + iter->pos = iter->cb->args[0]; + iter->task = task_next_child(iter->parent, NULL, iter->pos); + return iter->task; + + case TASK_DIAG_DUMP_ALL: + iter->tgid.tgid = iter->cb->args[0]; + iter->tgid.task = NULL; + iter->tgid = next_tgid(iter->ns, iter->tgid); + return iter->tgid.task; + } + + return ERR_PTR(-EINVAL); +} + +static struct task_struct *iter_next(struct task_iter *iter) +{ + switch (iter->req.dump_strategy) { + case TASK_DIAG_DUMP_CHILDREN: + iter->pos++; + iter->task = task_next_child(iter->parent, iter->task, iter->pos); + iter->cb->args[0] = iter->pos; + return iter->task; + + case TASK_DIAG_DUMP_ALL: + iter->tgid.tgid += 1; + iter->tgid = next_tgid(iter->ns, iter->tgid); + iter->cb->args[0] = iter->tgid.tgid; + return iter->tgid.task; + } + + return NULL; +} + int taskdiag_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct pid_namespace *ns = task_active_pid_ns(current); - struct tgid_iter iter; + struct task_iter iter; struct nlattr *na; - struct task_diag_pid req; + struct task_struct *task; int rc; - if (nlmsg_len(cb->nlh) < GENL_HDRLEN + sizeof(req)) + if (nlmsg_len(cb->nlh) < GENL_HDRLEN + sizeof(iter.req)) return -EINVAL; na = nlmsg_data(cb->nlh) + GENL_HDRLEN; if (na->nla_type < 0) return -EINVAL; - memcpy(&req, nla_data(na), sizeof(req)); + memcpy(&iter.req, nla_data(na), sizeof(iter.req)); - iter.tgid = cb->args[0]; - iter.task = NULL; - for (iter = next_tgid(ns, iter); - iter.task; - iter.tgid += 1, iter = next_tgid(ns, iter)) { - if (!ptrace_may_access(iter.task, PTRACE_MODE_READ)) - continue; + iter.ns = ns; + iter.cb = cb; + iter.parent = NULL; - rc = task_diag_fill(iter.task, skb, req.show_flags, + task = iter_start(&iter); + if (IS_ERR(task)) + return PTR_ERR(task); + + for (; task; task = iter_next(&iter)) { + if (!ptrace_may_access(task, PTRACE_MODE_READ)) + continue; + rc = task_diag_fill(task, skb, iter.req.show_flags, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cb); if (rc < 0) { - put_task_struct(iter.task); - if (rc != -EMSGSIZE) + if (rc != -EMSGSIZE) { + iter_stop(&iter); return rc; + } break; } } - - cb->args[0] = iter.tgid; + iter_stop(&iter); return skb->len; }
--
2.1.0