Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit acffd6f4 authored by Vinayak Menon's avatar Vinayak Menon Committed by Prakash Gupta
Browse files

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: default avatarVinayak Menon <vinmenon@codeaurora.org>
[guptap@codeaurora.org: enforce policy later using pre_doit]
Signed-off-by: default avatarPrakash Gupta <guptap@codeaurora.org>
parent 5a5b186d
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -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,
};

+187 −1
Original line number Diff line number Diff line
@@ -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,115 @@ static int cmd_attr_tgid(struct genl_info *info)
	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: