Merge "taskstats: handle NULL nla case in taskstats2"

This commit is contained in:
qctecmdr 2020-06-15 02:08:40 -07:00 • committed by Gerrit - the friendly Code Review server
commit 514fa165a3
2 changed files with 209 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,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,
};

View file

@ -24,6 +24,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
@ -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,120 @@ 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);
if (!nla)
goto out;
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;
out:
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 +981,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 +1010,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: