From 1c49e2c4d7eda36707284a84b86cd84f7cc29c99 Mon Sep 17 00:00:00 2001 From: Vinayak Menon Date: Tue, 12 Feb 2019 17:03:57 +0530 Subject: [PATCH] taskstats: add a option to send all tasks data to user Adds a new taskstats command to share task memory statistics to userspace. The command works in two modes. It can share information per pid, and also send the statistics for all tasks within a given oom_score_adj range. Change-Id: Iae742ea4f96754022bc634d285c5ce140b32749d Signed-off-by: Vinayak Menon --- include/uapi/linux/taskstats.h | 15 +++ kernel/taskstats.c | 182 ++++++++++++++++++++++++++++++++- 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/taskstats.h b/include/uapi/linux/taskstats.h index 5e8ca16a9079..125b5a9488a7 100644 --- a/include/uapi/linux/taskstats.h +++ b/include/uapi/linux/taskstats.h @@ -35,6 +35,7 @@ #define TASKSTATS_VERSION 9 +#define TASKSTATS2_VERSION 1 #define TS_COMM_LEN 32 /* should be >= TASK_COMM_LEN * in linux/sched.h */ @@ -170,6 +171,17 @@ struct taskstats { __u64 thrashing_delay_total; }; +struct taskstats2 { + __u16 version; + __s16 oom_score; + __u32 pid; + __u64 anon_rss; /* KB */ + __u64 file_rss; /* KB */ + __u64 swap_rss; /* KB */ + __u64 shmem_rss; /* KB */ + __u64 unreclaimable; /* KB */ + /* version 1 ends here */ +}; /* * Commands sent from userspace @@ -181,6 +193,7 @@ enum { TASKSTATS_CMD_UNSPEC = 0, /* Reserved */ TASKSTATS_CMD_GET, /* user->kernel request/get-response */ TASKSTATS_CMD_NEW, /* kernel->user event */ + TASKSTATS2_CMD_GET, /* user->kernel request/get-response */ __TASKSTATS_CMD_MAX, }; @@ -194,6 +207,7 @@ enum { TASKSTATS_TYPE_AGGR_PID, /* contains pid + stats */ TASKSTATS_TYPE_AGGR_TGID, /* contains tgid + stats */ TASKSTATS_TYPE_NULL, /* contains nothing */ + TASKSTATS_TYPE_FOREACH, /* contains stats */ __TASKSTATS_TYPE_MAX, }; @@ -205,6 +219,7 @@ enum { TASKSTATS_CMD_ATTR_TGID, TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, + TASKSTATS_CMD_ATTR_FOREACH, __TASKSTATS_CMD_ATTR_MAX, }; diff --git a/kernel/taskstats.c b/kernel/taskstats.c index c57c480aa6a8..a578d8f5131e 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -34,6 +34,7 @@ #include #include #include +#include /* * Maximum length of a cpumask that can be specified in @@ -51,7 +52,8 @@ static const struct nla_policy taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1 [TASKSTATS_CMD_ATTR_PID] = { .type = NLA_U32 }, [TASKSTATS_CMD_ATTR_TGID] = { .type = NLA_U32 }, [TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING }, - [TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },}; + [TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING }, + [TASKSTATS_CMD_ATTR_FOREACH] = { .type = NLA_U32 },}; /* * We have to use TASKSTATS_CMD_ATTR_MAX here, it is the maxattr in the family. @@ -78,6 +80,11 @@ struct listener_list { }; static DEFINE_PER_CPU(struct listener_list, listener_array); +struct tgid_iter { + unsigned int tgid; + struct task_struct *task; +}; + enum actions { REGISTER, DEREGISTER, @@ -633,6 +640,65 @@ static size_t taskstats_packet_size(void) return size; } +static int taskstats2_cmd_attr_pid(struct genl_info *info) +{ + struct taskstats2 *stats; + struct sk_buff *rep_skb; + struct nlattr *ret; + struct task_struct *tsk; + struct task_struct *p; + size_t size; + u32 pid; + int rc; + + size = nla_total_size_64bit(sizeof(struct taskstats2)); + + rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); + if (rc < 0) + return rc; + + rc = -EINVAL; + pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); + + ret = nla_reserve_64bit(rep_skb, TASKSTATS_TYPE_STATS, + sizeof(struct taskstats2), TASKSTATS_TYPE_NULL); + if (!ret) + goto err; + + stats = nla_data(ret); + + rcu_read_lock(); + tsk = find_task_by_vpid(pid); + if (tsk) + get_task_struct(tsk); + rcu_read_unlock(); + if (!tsk) { + rc = -ESRCH; + goto err; + } + memset(stats, 0, sizeof(*stats)); + stats->version = TASKSTATS2_VERSION; + stats->pid = task_pid_nr_ns(tsk, task_active_pid_ns(current)); + p = find_lock_task_mm(tsk); + if (p) { +#define K(x) ((x) << (PAGE_SHIFT - 10)) + stats->anon_rss = K(get_mm_counter(p->mm, MM_ANONPAGES)); + stats->file_rss = K(get_mm_counter(p->mm, MM_FILEPAGES)); + stats->shmem_rss = K(get_mm_counter(p->mm, MM_SHMEMPAGES)); + stats->swap_rss = K(get_mm_counter(p->mm, MM_SWAPENTS)); + stats->unreclaimable = + K(get_mm_counter(p->mm, MM_UNRECLAIMABLE)); +#undef K + task_unlock(p); + } + put_task_struct(tsk); + + return send_reply(rep_skb, info); +err: + nlmsg_free(rep_skb); + return rc; +} + static int cmd_attr_pid(struct genl_info *info) { struct taskstats *stats; @@ -691,6 +757,114 @@ err: return rc; } +static struct tgid_iter next_tgid(struct pid_namespace *ns, + struct tgid_iter iter) +{ + struct pid *pid; + + if (iter.task) + put_task_struct(iter.task); + rcu_read_lock(); +retry: + iter.task = NULL; + pid = find_ge_pid(iter.tgid, ns); + if (pid) { + iter.tgid = pid_nr_ns(pid, ns); + iter.task = pid_task(pid, PIDTYPE_PID); + if (!iter.task || !has_group_leader_pid(iter.task)) { + iter.tgid += 1; + goto retry; + } + get_task_struct(iter.task); + } + rcu_read_unlock(); + return iter; +} + +static int taskstats2_foreach(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct pid_namespace *ns = task_active_pid_ns(current); + struct tgid_iter iter; + void *reply; + struct nlattr *attr; + struct nlattr *nla; + struct taskstats2 *stats; + struct task_struct *p; + short oom_score; + short oom_score_min; + short oom_score_max; + u32 buf; + + nla = nla_find(nlmsg_attrdata(cb->nlh, GENL_HDRLEN), + nlmsg_attrlen(cb->nlh, GENL_HDRLEN), + TASKSTATS_TYPE_FOREACH); + buf = nla_get_u32(nla); + oom_score_min = (short) (buf & 0xFFFF); + oom_score_max = (short) ((buf >> 16) & 0xFFFF); + + 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 (iter.task->flags & PF_KTHREAD) + continue; + + oom_score = iter.task->signal->oom_score_adj; + if ((oom_score < oom_score_min) + || (oom_score > oom_score_max)) + continue; + + reply = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, &family, 0, TASKSTATS2_CMD_GET); + if (reply == NULL) { + put_task_struct(iter.task); + break; + } + attr = nla_reserve(skb, TASKSTATS_TYPE_FOREACH, + sizeof(struct taskstats2)); + if (!attr) { + put_task_struct(iter.task); + genlmsg_cancel(skb, reply); + break; + } + stats = nla_data(attr); + memset(stats, 0, sizeof(struct taskstats2)); + stats->version = TASKSTATS2_VERSION; + rcu_read_lock(); + stats->pid = task_pid_nr_ns(iter.task, + task_active_pid_ns(current)); + stats->oom_score = iter.task->signal->oom_score_adj; + rcu_read_unlock(); + p = find_lock_task_mm(iter.task); + if (p) { +#define K(x) ((x) << (PAGE_SHIFT - 10)) + stats->anon_rss = + K(get_mm_counter(p->mm, MM_ANONPAGES)); + stats->file_rss = + K(get_mm_counter(p->mm, MM_FILEPAGES)); + stats->shmem_rss = + K(get_mm_counter(p->mm, MM_SHMEMPAGES)); + stats->swap_rss = + K(get_mm_counter(p->mm, MM_SWAPENTS)); + task_unlock(p); +#undef K + } + genlmsg_end(skb, reply); + } + + cb->args[0] = iter.tgid; + return skb->len; +} + +static int taskstats2_user_cmd(struct sk_buff *skb, struct genl_info *info) +{ + if (info->attrs[TASKSTATS_CMD_ATTR_PID]) + return taskstats2_cmd_attr_pid(info); + else + return -EINVAL; +} + static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) { if (info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK]) @@ -797,6 +971,12 @@ static const struct genl_ops taskstats_ops[] = { .policy = taskstats_cmd_get_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = TASKSTATS2_CMD_GET, + .doit = taskstats2_user_cmd, + .dumpit = taskstats2_foreach, + .policy = taskstats_cmd_get_policy, + }, { .cmd = CGROUPSTATS_CMD_GET, .doit = cgroupstats_user_cmd,