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 <vinmenon@codeaurora.org>
This commit is contained in:
Vinayak Menon 2019-02-12 17:03:57 +05:30 committed by Gerrit - the friendly Code Review server
parent d9da1be52f
commit 1c49e2c4d7
2 changed files with 196 additions and 1 deletions

View File

@ -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,
};

View File

@ -34,6 +34,7 @@
#include <net/genetlink.h>
#include <linux/atomic.h>
#include <linux/sched/cputime.h>
#include <linux/oom.h>
/*
* 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,