If a program needs monitor lots of process's status, it needs two syscalls for every process. The first one is telling kernel which pid/tgid should be monitored by send a command(write socket) to kernel. The second one is read the statistics by read socket. This patch add a new interface /dev/taskstats to reduce two syscalls to one ioctl. The user just set the target pid/tgid to the struct taskstats.ac_pid, then kernel will collect statistics for that pid/tgid.
Signed-off-by: Weiping Zhang <zhangweip...@didiglobal.com> --- kernel/taskstats.c | 71 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/kernel/taskstats.c b/kernel/taskstats.c index a2802b6ff4bb..68188cc9663d 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -21,6 +21,8 @@ #include <net/genetlink.h> #include <linux/atomic.h> #include <linux/sched/cputime.h> +#include <linux/miscdevice.h> +#include <linux/uio.h> /* * Maximum length of a cpumask that can be specified in @@ -666,6 +668,67 @@ static struct genl_family family __ro_after_init = { .n_ops = ARRAY_SIZE(taskstats_ops), }; +static long taskstats_ioctl_pid_tgid(unsigned long arg, bool tgid) +{ + struct taskstats kstat; + struct taskstats *ustat = (struct taskstats *)arg; + u32 id; + unsigned long offset = offsetof(struct taskstats, ac_pid); + long ret; + + /* userspace set monitored pid/tgid to the field "ac_pid" */ + if (unlikely(copy_from_user(&id, (void *)(arg + offset), sizeof(u32)))) + return -EFAULT; + + if (tgid) + ret = fill_stats_for_tgid(id, &kstat); + else + ret = fill_stats_for_pid(id, &kstat); + if (ret < 0) + return ret; + + if (unlikely(copy_to_user(ustat, &kstat, sizeof(struct taskstats)))) + return -EFAULT; + + return 0; +} + +static long taskstats_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret; + + switch (cmd) { + case TASKSTATS_CMD_ATTR_PID: + ret = taskstats_ioctl_pid_tgid(arg, false); + break; + case TASKSTATS_CMD_ATTR_TGID: + ret = taskstats_ioctl_pid_tgid(arg, true); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct file_operations taskstats_ctl_fops = { + .open = nonseekable_open, + .unlocked_ioctl = taskstats_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = taskstats_ioctl, +#endif + .owner = THIS_MODULE, + .llseek = noop_llseek, +}; + +static struct miscdevice taskstats_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "taskstats", + .fops = &taskstats_ctl_fops, +}; + + /* Needed early in initialization */ void __init taskstats_init_early(void) { @@ -682,9 +745,15 @@ static int __init taskstats_init(void) { int rc; + rc = misc_register(&taskstats_misc); + if (rc < 0) + return rc; + rc = genl_register_family(&family); - if (rc) + if (rc) { + misc_deregister(&taskstats_misc); return rc; + } family_registered = 1; pr_info("registered taskstats version %d\n", TASKSTATS_GENL_VERSION); -- 2.17.2