Loading include/uapi/linux/sysstats.h 0 → 100644 +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 */ include/uapi/linux/taskstats.h +15 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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 Loading @@ -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, }; Loading @@ -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, }; Loading @@ -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, }; Loading kernel/taskstats.c +330 −1 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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 Loading @@ -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. Loading @@ -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; Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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]) Loading Loading @@ -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 = { Loading Loading
include/uapi/linux/sysstats.h 0 → 100644 +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 */
include/uapi/linux/taskstats.h +15 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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 Loading @@ -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, }; Loading @@ -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, }; Loading @@ -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, }; Loading
kernel/taskstats.c +330 −1 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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 Loading @@ -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. Loading @@ -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; Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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]) Loading Loading @@ -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 = { Loading