diff --git a/include/uapi/linux/taskstats.h b/include/uapi/linux/taskstats.h index 5e8ca16a9079..86f36b60d57e 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,19 @@ 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 */ +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + __u64 unreclaimable; /* KB */ +#endif + /* version 1 ends here */ +}; /* * Commands sent from userspace @@ -181,6 +195,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 +209,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 +221,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 1457a92b1252..cb5ebb0b2dd0 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -24,6 +24,7 @@ #include #include #include +#include /* * Maximum length of a cpumask that can be specified in @@ -41,7 +42,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. @@ -68,6 +70,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, @@ -625,6 +632,68 @@ 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) { + __acquire(p->alloc_lock); /* Fake it out ;) */ +#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)); +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + stats->unreclaimable = + K(get_mm_counter(p->mm, MM_UNRECLAIMABLE)); +#endif +#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; @@ -683,6 +752,115 @@ 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)) + __acquire(p->alloc_lock); /* Fake it out ;) */ + 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]) @@ -798,6 +976,13 @@ static const struct genl_ops taskstats_ops[] = { /* policy enforced later */ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_HASPOL, }, + { + .cmd = TASKSTATS2_CMD_GET, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = taskstats2_user_cmd, + .dumpit = taskstats2_foreach, + /* policy enforced later */ + }, { .cmd = CGROUPSTATS_CMD_GET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, @@ -820,6 +1005,7 @@ static int taskstats_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, switch (ops->cmd) { case TASKSTATS_CMD_GET: + case TASKSTATS2_CMD_GET: policy = taskstats_cmd_get_policy; break; case CGROUPSTATS_CMD_GET: