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

Commit 5a881b4b authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "taskstats: add a option to send all tasks data to user"

parents 20465617 1c49e2c4
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
 * Copyright (c) 2019, The Linux Foundation. All rights reserved.
 */

#ifndef _LINUX_SYSSTATS_H
#define _LINUX_SYSSTATS_H

#include <linux/types.h>
#include <linux/taskstats.h>
#include <linux/cgroupstats.h>

#define SYSSTATS_VERSION	1

/*
 * Data shared between user space and kernel space
 * Each member is aligned to a 8 byte boundary.
 * All values in KB.
 */
struct sys_memstats {
	__u64	version;
	__u64	memtotal;
	__u64	vmalloc_total;
	__u64	reclaimable;
	__u64	zram_compressed;
	__u64	swap_used;
	__u64	swap_total;
	__u64	unreclaimable;
	__u64	buffer;
	__u64	slab_reclaimable;
	__u64	slab_unreclaimable;
	__u64	free_cma;
	__u64	file_mapped;
	__u64	swapcache;
	__u64	pagetable;
	__u64	kernelstack;
	__u64	shmem;
	__u64	dma_nr_free_pages;
	__u64	dma_nr_active_anon;
	__u64	dma_nr_inactive_anon;
	__u64	dma_nr_active_file;
	__u64	dma_nr_inactive_file;
	__u64	normal_nr_free_pages;
	__u64	normal_nr_active_anon;
	__u64	normal_nr_inactive_anon;
	__u64	normal_nr_active_file;
	__u64	normal_nr_inactive_file;
	__u64	movable_nr_free_pages;
	__u64	movable_nr_active_anon;
	__u64	movable_nr_inactive_anon;
	__u64	movable_nr_active_file;
	__u64	movable_nr_inactive_file;
	__u64	highmem_nr_free_pages;
	__u64	highmem_nr_active_anon;
	__u64	highmem_nr_inactive_anon;
	__u64	highmem_nr_active_file;
	__u64	highmem_nr_inactive_file;
	/* version 1 ends here */
};

/*
 * Commands sent from userspace
 * Not versioned. New commands should only be inserted at the enum's end.
 */

enum {
	SYSSTATS_CMD_UNSPEC = __CGROUPSTATS_CMD_MAX,	/* Reserved */
	SYSSTATS_CMD_GET,		/* user->kernel request/get-response */
	SYSSTATS_CMD_NEW,		/* kernel->user event */
};

#define SYSSTATS_CMD_UNSPEC SYSSTATS_CMD_UNSPEC
#define SYSSTATS_CMD_GET SYSSTATS_CMD_GET
#define SYSSTATS_CMD_NEW SYSSTATS_CMD_NEW

enum {
	SYSSTATS_TYPE_UNSPEC = 0,	/* Reserved */
	SYSSTATS_TYPE_SYSMEM_STATS,	/* contains name + memory stats */
};

#define SYSSTATS_TYPE_UNSPEC SYSSTATS_TYPE_UNSPEC
#define SYSSTATS_TYPE_SYSMEM_STATS SYSSTATS_TYPE_SYSMEM_STATS

enum {
	SYSSTATS_CMD_ATTR_UNSPEC = 0,
	SYSSTATS_CMD_ATTR_SYSMEM_STATS,
};

#define SYSSTATS_CMD_ATTR_UNSPEC SYSSTATS_CMD_ATTR_UNSPEC
#define SYSSTATS_CMD_ATTR_SYSMEM_STATS SYSSTATS_CMD_ATTR_SYSMEM_STATS

#endif /* _LINUX_SYSSTATS_H */
+15 −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,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,
};

+330 −1
Original line number Diff line number Diff line
@@ -23,7 +23,10 @@
#include <linux/cpumask.h>
#include <linux/percpu.h>
#include <linux/slab.h>
#include <linux/swap.h>
#include <linux/vmalloc.h>
#include <linux/cgroupstats.h>
#include <linux/sysstats.h>
#include <linux/cgroup.h>
#include <linux/fs.h>
#include <linux/file.h>
@@ -31,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
@@ -48,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.
@@ -58,6 +63,11 @@ static const struct nla_policy cgroupstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX
	[CGROUPSTATS_CMD_ATTR_FD] = { .type = NLA_U32 },
};

static const struct nla_policy
		sysstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] = {
	[SYSSTATS_CMD_ATTR_SYSMEM_STATS] = { .type = NLA_U32 },
};

struct listener {
	struct list_head list;
	pid_t pid;
@@ -70,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,
@@ -396,6 +411,142 @@ static struct taskstats *mk_reply(struct sk_buff *skb, int type, u32 pid)
	return NULL;
}

#define K(x) ((x) << (PAGE_SHIFT - 10))
#ifndef CONFIG_NUMA
static void sysstats_fill_zoneinfo(struct sys_memstats *stats)
{
	pg_data_t *pgdat;
	struct zone *zone;
	struct zone *node_zones;
	unsigned long zspages = 0;

	pgdat = NODE_DATA(0);
	node_zones = pgdat->node_zones;

	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
		if (!populated_zone(zone))
			continue;

		zspages += zone_page_state(zone, NR_ZSPAGES);
		if (!strcmp(zone->name, "DMA")) {
			stats->dma_nr_free_pages =
				K(zone_page_state(zone, NR_FREE_PAGES));
			stats->dma_nr_active_anon =
				K(zone_page_state(zone, NR_ZONE_ACTIVE_ANON));
			stats->dma_nr_inactive_anon =
				K(zone_page_state(zone, NR_ZONE_INACTIVE_ANON));
			stats->dma_nr_active_file =
				K(zone_page_state(zone, NR_ZONE_ACTIVE_FILE));
			stats->dma_nr_inactive_file =
				K(zone_page_state(zone, NR_ZONE_INACTIVE_FILE));
		} else if (!strcmp(zone->name, "Normal")) {
			stats->normal_nr_free_pages =
				K(zone_page_state(zone, NR_FREE_PAGES));
			stats->normal_nr_active_anon =
				K(zone_page_state(zone, NR_ZONE_ACTIVE_ANON));
			stats->normal_nr_inactive_anon =
				K(zone_page_state(zone, NR_ZONE_INACTIVE_ANON));
			stats->normal_nr_active_file =
				K(zone_page_state(zone, NR_ZONE_ACTIVE_FILE));
			stats->normal_nr_inactive_file =
				K(zone_page_state(zone, NR_ZONE_INACTIVE_FILE));
		} else if (!strcmp(zone->name, "HighMem")) {
			stats->highmem_nr_free_pages =
				K(zone_page_state(zone, NR_FREE_PAGES));
			stats->highmem_nr_active_anon =
				K(zone_page_state(zone, NR_ZONE_ACTIVE_ANON));
			stats->highmem_nr_inactive_anon =
				K(zone_page_state(zone, NR_ZONE_INACTIVE_ANON));
			stats->highmem_nr_active_file =
				K(zone_page_state(zone, NR_ZONE_ACTIVE_FILE));
			stats->highmem_nr_inactive_file =
				K(zone_page_state(zone, NR_ZONE_INACTIVE_FILE));
		} else if (!strcmp(zone->name, "Movable")) {
			stats->movable_nr_free_pages =
				K(zone_page_state(zone, NR_FREE_PAGES));
			stats->movable_nr_active_anon =
				K(zone_page_state(zone, NR_ZONE_ACTIVE_ANON));
			stats->movable_nr_inactive_anon =
				K(zone_page_state(zone, NR_ZONE_INACTIVE_ANON));
			stats->movable_nr_active_file =
				K(zone_page_state(zone, NR_ZONE_ACTIVE_FILE));
			stats->movable_nr_inactive_file =
				K(zone_page_state(zone, NR_ZONE_INACTIVE_FILE));
		}
	}
	stats->zram_compressed = K(zspages);
}
#elif
static void sysstats_fill_zoneinfo(struct sys_memstats *stats)
{
}
#endif

static void sysstats_build(struct sys_memstats *stats)
{
	struct sysinfo i;

	si_meminfo(&i);
	si_swapinfo(&i);

	stats->version = SYSSTATS_VERSION;
	stats->memtotal = K(i.totalram);
	stats->reclaimable =
		global_node_page_state(NR_INDIRECTLY_RECLAIMABLE_BYTES) >> 10;
	stats->swap_used = K(i.totalswap - i.freeswap);
	stats->swap_total = K(i.totalswap);
	stats->vmalloc_total = K(vmalloc_nr_pages());
	stats->unreclaimable =
		K(global_node_page_state(NR_UNRECLAIMABLE_PAGES));
	stats->buffer = K(i.bufferram);
	stats->swapcache = K(total_swapcache_pages());
	stats->slab_reclaimable =
		K(global_node_page_state(NR_SLAB_RECLAIMABLE));
	stats->slab_unreclaimable =
		K(global_node_page_state(NR_SLAB_UNRECLAIMABLE));
	stats->free_cma = K(global_zone_page_state(NR_FREE_CMA_PAGES));
	stats->file_mapped = K(global_node_page_state(NR_FILE_MAPPED));
	stats->kernelstack = global_zone_page_state(NR_KERNEL_STACK_KB);
	stats->pagetable = K(global_zone_page_state(NR_PAGETABLE));
	stats->shmem = K(i.sharedram);
	sysstats_fill_zoneinfo(stats);
}
#undef K

static int sysstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
{
	int rc = 0;
	struct sk_buff *rep_skb;
	struct sys_memstats *stats;
	struct nlattr *na;
	size_t size;

	size = nla_total_size(sizeof(struct sys_memstats));

	rc = prepare_reply(info, SYSSTATS_CMD_NEW, &rep_skb,
				size);
	if (rc < 0)
		goto err;

	na = nla_reserve(rep_skb, SYSSTATS_TYPE_SYSMEM_STATS,
				sizeof(struct sys_memstats));
	if (na == NULL) {
		nlmsg_free(rep_skb);
		rc = -EMSGSIZE;
		goto err;
	}

	stats = nla_data(na);
	memset(stats, 0, sizeof(*stats));

	sysstats_build(stats);

	rc = send_reply(rep_skb, info);

err:
	return rc;
}

static int cgroupstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
{
	int rc = 0;
@@ -489,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;
@@ -547,6 +757,114 @@ 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))
			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])
@@ -653,11 +971,22 @@ 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,
		.policy		= cgroupstats_cmd_get_policy,
	},
	{
		.cmd		= SYSSTATS_CMD_GET,
		.doit		= sysstats_user_cmd,
		.policy		= sysstats_cmd_get_policy,
	},
};

static struct genl_family family __ro_after_init = {