1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2c757249aSShailabh Nagar /* 3c757249aSShailabh Nagar * taskstats.c - Export per-task statistics to userland 4c757249aSShailabh Nagar * 5c757249aSShailabh Nagar * Copyright (C) Shailabh Nagar, IBM Corp. 2006 6c757249aSShailabh Nagar * (C) Balbir Singh, IBM Corp. 2006 7c757249aSShailabh Nagar */ 8c757249aSShailabh Nagar 9c757249aSShailabh Nagar #include <linux/kernel.h> 10c757249aSShailabh Nagar #include <linux/taskstats_kern.h> 11f3cef7a9SJay Lan #include <linux/tsacct_kern.h> 12*0e0af57eSDr. Thomas Orgis #include <linux/acct.h> 136f44993fSShailabh Nagar #include <linux/delayacct.h> 14f9fd8914SShailabh Nagar #include <linux/cpumask.h> 15f9fd8914SShailabh Nagar #include <linux/percpu.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 17846c7bb0SBalbir Singh #include <linux/cgroupstats.h> 18846c7bb0SBalbir Singh #include <linux/cgroup.h> 19846c7bb0SBalbir Singh #include <linux/fs.h> 20846c7bb0SBalbir Singh #include <linux/file.h> 214bd6e32aSEric W. Biederman #include <linux/pid_namespace.h> 22c757249aSShailabh Nagar #include <net/genetlink.h> 2360063497SArun Sharma #include <linux/atomic.h> 248c733420SZhang Xiao #include <linux/sched/cputime.h> 25c757249aSShailabh Nagar 26f9fd8914SShailabh Nagar /* 27f9fd8914SShailabh Nagar * Maximum length of a cpumask that can be specified in 28f9fd8914SShailabh Nagar * the TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK attribute 29f9fd8914SShailabh Nagar */ 30f9fd8914SShailabh Nagar #define TASKSTATS_CPUMASK_MAXLEN (100+6*NR_CPUS) 31f9fd8914SShailabh Nagar 32b81f3ea9SVegard Nossum static DEFINE_PER_CPU(__u32, taskstats_seqnum); 33c757249aSShailabh Nagar static int family_registered; 34e18b890bSChristoph Lameter struct kmem_cache *taskstats_cache; 35c757249aSShailabh Nagar 36489111e5SJohannes Berg static struct genl_family family; 37c757249aSShailabh Nagar 387c1e0926SJakub Kicinski static const struct nla_policy taskstats_cmd_get_policy[] = { 39c757249aSShailabh Nagar [TASKSTATS_CMD_ATTR_PID] = { .type = NLA_U32 }, 40c757249aSShailabh Nagar [TASKSTATS_CMD_ATTR_TGID] = { .type = NLA_U32 }, 41f9fd8914SShailabh Nagar [TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING }, 42f9fd8914SShailabh Nagar [TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },}; 43f9fd8914SShailabh Nagar 447c1e0926SJakub Kicinski static const struct nla_policy cgroupstats_cmd_get_policy[] = { 45846c7bb0SBalbir Singh [CGROUPSTATS_CMD_ATTR_FD] = { .type = NLA_U32 }, 46846c7bb0SBalbir Singh }; 47846c7bb0SBalbir Singh 48f9fd8914SShailabh Nagar struct listener { 49f9fd8914SShailabh Nagar struct list_head list; 50f9fd8914SShailabh Nagar pid_t pid; 51bb129994SShailabh Nagar char valid; 52c757249aSShailabh Nagar }; 53c757249aSShailabh Nagar 54f9fd8914SShailabh Nagar struct listener_list { 55f9fd8914SShailabh Nagar struct rw_semaphore sem; 56f9fd8914SShailabh Nagar struct list_head list; 57f9fd8914SShailabh Nagar }; 58f9fd8914SShailabh Nagar static DEFINE_PER_CPU(struct listener_list, listener_array); 59f9fd8914SShailabh Nagar 60f9fd8914SShailabh Nagar enum actions { 61f9fd8914SShailabh Nagar REGISTER, 62f9fd8914SShailabh Nagar DEREGISTER, 63f9fd8914SShailabh Nagar CPU_DONT_CARE 64f9fd8914SShailabh Nagar }; 65c757249aSShailabh Nagar 66c757249aSShailabh Nagar static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp, 6737167485SOleg Nesterov size_t size) 68c757249aSShailabh Nagar { 69c757249aSShailabh Nagar struct sk_buff *skb; 70c757249aSShailabh Nagar void *reply; 71c757249aSShailabh Nagar 72c757249aSShailabh Nagar /* 73c757249aSShailabh Nagar * If new attributes are added, please revisit this allocation 74c757249aSShailabh Nagar */ 753dabc715SThomas Graf skb = genlmsg_new(size, GFP_KERNEL); 76c757249aSShailabh Nagar if (!skb) 77c757249aSShailabh Nagar return -ENOMEM; 78c757249aSShailabh Nagar 79c757249aSShailabh Nagar if (!info) { 80cd85fc58SChristoph Lameter int seq = this_cpu_inc_return(taskstats_seqnum) - 1; 81c757249aSShailabh Nagar 8217c157c8SThomas Graf reply = genlmsg_put(skb, 0, seq, &family, 0, cmd); 83c757249aSShailabh Nagar } else 8417c157c8SThomas Graf reply = genlmsg_put_reply(skb, info, &family, 0, cmd); 85c757249aSShailabh Nagar if (reply == NULL) { 86c757249aSShailabh Nagar nlmsg_free(skb); 87c757249aSShailabh Nagar return -EINVAL; 88c757249aSShailabh Nagar } 89c757249aSShailabh Nagar 90c757249aSShailabh Nagar *skbp = skb; 91c757249aSShailabh Nagar return 0; 92c757249aSShailabh Nagar } 93c757249aSShailabh Nagar 94f9fd8914SShailabh Nagar /* 95f9fd8914SShailabh Nagar * Send taskstats data in @skb to listener with nl_pid @pid 96f9fd8914SShailabh Nagar */ 97134e6375SJohannes Berg static int send_reply(struct sk_buff *skb, struct genl_info *info) 98c757249aSShailabh Nagar { 99b529ccf2SArnaldo Carvalho de Melo struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb)); 100f9fd8914SShailabh Nagar void *reply = genlmsg_data(genlhdr); 101c757249aSShailabh Nagar 102053c095aSJohannes Berg genlmsg_end(skb, reply); 103c757249aSShailabh Nagar 104134e6375SJohannes Berg return genlmsg_reply(skb, info); 105c757249aSShailabh Nagar } 106c757249aSShailabh Nagar 107f9fd8914SShailabh Nagar /* 108f9fd8914SShailabh Nagar * Send taskstats data in @skb to listeners registered for @cpu's exit data 109f9fd8914SShailabh Nagar */ 110115085eaSOleg Nesterov static void send_cpu_listeners(struct sk_buff *skb, 111115085eaSOleg Nesterov struct listener_list *listeners) 112f9fd8914SShailabh Nagar { 113b529ccf2SArnaldo Carvalho de Melo struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb)); 114f9fd8914SShailabh Nagar struct listener *s, *tmp; 115f9fd8914SShailabh Nagar struct sk_buff *skb_next, *skb_cur = skb; 116f9fd8914SShailabh Nagar void *reply = genlmsg_data(genlhdr); 11792333baaSLukas Bulwahn int delcount = 0; 118f9fd8914SShailabh Nagar 119053c095aSJohannes Berg genlmsg_end(skb, reply); 120f9fd8914SShailabh Nagar 121bb129994SShailabh Nagar down_read(&listeners->sem); 122d94a0415SShailabh Nagar list_for_each_entry(s, &listeners->list, list) { 12392333baaSLukas Bulwahn int rc; 12492333baaSLukas Bulwahn 125f9fd8914SShailabh Nagar skb_next = NULL; 126f9fd8914SShailabh Nagar if (!list_is_last(&s->list, &listeners->list)) { 127f9fd8914SShailabh Nagar skb_next = skb_clone(skb_cur, GFP_KERNEL); 128d94a0415SShailabh Nagar if (!skb_next) 129f9fd8914SShailabh Nagar break; 130f9fd8914SShailabh Nagar } 131134e6375SJohannes Berg rc = genlmsg_unicast(&init_net, skb_cur, s->pid); 132d94a0415SShailabh Nagar if (rc == -ECONNREFUSED) { 133bb129994SShailabh Nagar s->valid = 0; 134bb129994SShailabh Nagar delcount++; 135f9fd8914SShailabh Nagar } 136f9fd8914SShailabh Nagar skb_cur = skb_next; 137f9fd8914SShailabh Nagar } 138bb129994SShailabh Nagar up_read(&listeners->sem); 139f9fd8914SShailabh Nagar 140d94a0415SShailabh Nagar if (skb_cur) 141d94a0415SShailabh Nagar nlmsg_free(skb_cur); 142d94a0415SShailabh Nagar 143bb129994SShailabh Nagar if (!delcount) 144d94a0415SShailabh Nagar return; 145bb129994SShailabh Nagar 146bb129994SShailabh Nagar /* Delete invalidated entries */ 147bb129994SShailabh Nagar down_write(&listeners->sem); 148bb129994SShailabh Nagar list_for_each_entry_safe(s, tmp, &listeners->list, list) { 149bb129994SShailabh Nagar if (!s->valid) { 150bb129994SShailabh Nagar list_del(&s->list); 151bb129994SShailabh Nagar kfree(s); 152bb129994SShailabh Nagar } 153bb129994SShailabh Nagar } 154bb129994SShailabh Nagar up_write(&listeners->sem); 155f9fd8914SShailabh Nagar } 156f9fd8914SShailabh Nagar 157*0e0af57eSDr. Thomas Orgis static void exe_add_tsk(struct taskstats *stats, struct task_struct *tsk) 158*0e0af57eSDr. Thomas Orgis { 159*0e0af57eSDr. Thomas Orgis /* No idea if I'm allowed to access that here, now. */ 160*0e0af57eSDr. Thomas Orgis struct file *exe_file = get_task_exe_file(tsk); 161*0e0af57eSDr. Thomas Orgis 162*0e0af57eSDr. Thomas Orgis if (exe_file) { 163*0e0af57eSDr. Thomas Orgis /* Following cp_new_stat64() in stat.c . */ 164*0e0af57eSDr. Thomas Orgis stats->ac_exe_dev = 165*0e0af57eSDr. Thomas Orgis huge_encode_dev(exe_file->f_inode->i_sb->s_dev); 166*0e0af57eSDr. Thomas Orgis stats->ac_exe_inode = exe_file->f_inode->i_ino; 167*0e0af57eSDr. Thomas Orgis fput(exe_file); 168*0e0af57eSDr. Thomas Orgis } else { 169*0e0af57eSDr. Thomas Orgis stats->ac_exe_dev = 0; 170*0e0af57eSDr. Thomas Orgis stats->ac_exe_inode = 0; 171*0e0af57eSDr. Thomas Orgis } 172*0e0af57eSDr. Thomas Orgis } 173*0e0af57eSDr. Thomas Orgis 1744bd6e32aSEric W. Biederman static void fill_stats(struct user_namespace *user_ns, 1754bd6e32aSEric W. Biederman struct pid_namespace *pid_ns, 1764bd6e32aSEric W. Biederman struct task_struct *tsk, struct taskstats *stats) 177c757249aSShailabh Nagar { 17851de4d90SOleg Nesterov memset(stats, 0, sizeof(*stats)); 179c757249aSShailabh Nagar /* 180c757249aSShailabh Nagar * Each accounting subsystem adds calls to its functions to 181c757249aSShailabh Nagar * fill in relevant parts of struct taskstsats as follows 182c757249aSShailabh Nagar * 1837d94ddddSShailabh Nagar * per-task-foo(stats, tsk); 184c757249aSShailabh Nagar */ 185c757249aSShailabh Nagar 1867d94ddddSShailabh Nagar delayacct_add_tsk(stats, tsk); 187f3cef7a9SJay Lan 188f3cef7a9SJay Lan /* fill in basic acct fields */ 1896f44993fSShailabh Nagar stats->version = TASKSTATS_VERSION; 190b663a79cSMaxim Uvarov stats->nvcsw = tsk->nvcsw; 191b663a79cSMaxim Uvarov stats->nivcsw = tsk->nivcsw; 1924bd6e32aSEric W. Biederman bacct_add_tsk(user_ns, pid_ns, stats, tsk); 1936f44993fSShailabh Nagar 1949acc1853SJay Lan /* fill in extended acct fields */ 1959acc1853SJay Lan xacct_add_tsk(stats, tsk); 196*0e0af57eSDr. Thomas Orgis 197*0e0af57eSDr. Thomas Orgis /* add executable info */ 198*0e0af57eSDr. Thomas Orgis exe_add_tsk(stats, tsk); 199c757249aSShailabh Nagar } 200c757249aSShailabh Nagar 2013d9e0cf1SMichael Holzheu static int fill_stats_for_pid(pid_t pid, struct taskstats *stats) 202c757249aSShailabh Nagar { 203a98b6094SOleg Nesterov struct task_struct *tsk; 2043d9e0cf1SMichael Holzheu 2052ee08260SMike Rapoport tsk = find_get_task_by_vpid(pid); 2063d9e0cf1SMichael Holzheu if (!tsk) 2073d9e0cf1SMichael Holzheu return -ESRCH; 2084bd6e32aSEric W. Biederman fill_stats(current_user_ns(), task_active_pid_ns(current), tsk, stats); 2093d9e0cf1SMichael Holzheu put_task_struct(tsk); 2103d9e0cf1SMichael Holzheu return 0; 2113d9e0cf1SMichael Holzheu } 2123d9e0cf1SMichael Holzheu 2133d9e0cf1SMichael Holzheu static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) 2143d9e0cf1SMichael Holzheu { 2153d9e0cf1SMichael Holzheu struct task_struct *tsk, *first; 216ad4ecbcbSShailabh Nagar unsigned long flags; 217a98b6094SOleg Nesterov int rc = -ESRCH; 2188c733420SZhang Xiao u64 delta, utime, stime; 2198c733420SZhang Xiao u64 start_time; 220c757249aSShailabh Nagar 221ad4ecbcbSShailabh Nagar /* 222ad4ecbcbSShailabh Nagar * Add additional stats from live tasks except zombie thread group 223ad4ecbcbSShailabh Nagar * leaders who are already counted with the dead tasks 224ad4ecbcbSShailabh Nagar */ 225a98b6094SOleg Nesterov rcu_read_lock(); 226cb41d6d0SPavel Emelyanov first = find_task_by_vpid(tgid); 227ad4ecbcbSShailabh Nagar 228a98b6094SOleg Nesterov if (!first || !lock_task_sighand(first, &flags)) 229a98b6094SOleg Nesterov goto out; 230fca178c0SOleg Nesterov 231ad4ecbcbSShailabh Nagar if (first->signal->stats) 232ad4ecbcbSShailabh Nagar memcpy(stats, first->signal->stats, sizeof(*stats)); 23351de4d90SOleg Nesterov else 23451de4d90SOleg Nesterov memset(stats, 0, sizeof(*stats)); 235ad4ecbcbSShailabh Nagar 236a98b6094SOleg Nesterov tsk = first; 2378c733420SZhang Xiao start_time = ktime_get_ns(); 238c757249aSShailabh Nagar do { 239d7c3f5f2SOleg Nesterov if (tsk->exit_state) 240ad4ecbcbSShailabh Nagar continue; 241c757249aSShailabh Nagar /* 242ad4ecbcbSShailabh Nagar * Accounting subsystem can call its functions here to 243c757249aSShailabh Nagar * fill in relevant parts of struct taskstsats as follows 244c757249aSShailabh Nagar * 245ad4ecbcbSShailabh Nagar * per-task-foo(stats, tsk); 246c757249aSShailabh Nagar */ 247ad4ecbcbSShailabh Nagar delayacct_add_tsk(stats, tsk); 2486f44993fSShailabh Nagar 2498c733420SZhang Xiao /* calculate task elapsed time in nsec */ 2508c733420SZhang Xiao delta = start_time - tsk->start_time; 2518c733420SZhang Xiao /* Convert to micro seconds */ 2528c733420SZhang Xiao do_div(delta, NSEC_PER_USEC); 2538c733420SZhang Xiao stats->ac_etime += delta; 2548c733420SZhang Xiao 2558c733420SZhang Xiao task_cputime(tsk, &utime, &stime); 2568c733420SZhang Xiao stats->ac_utime += div_u64(utime, NSEC_PER_USEC); 2578c733420SZhang Xiao stats->ac_stime += div_u64(stime, NSEC_PER_USEC); 2588c733420SZhang Xiao 259b663a79cSMaxim Uvarov stats->nvcsw += tsk->nvcsw; 260b663a79cSMaxim Uvarov stats->nivcsw += tsk->nivcsw; 261c757249aSShailabh Nagar } while_each_thread(first, tsk); 2626f44993fSShailabh Nagar 263a98b6094SOleg Nesterov unlock_task_sighand(first, &flags); 264a98b6094SOleg Nesterov rc = 0; 265a98b6094SOleg Nesterov out: 266a98b6094SOleg Nesterov rcu_read_unlock(); 267a98b6094SOleg Nesterov 268a98b6094SOleg Nesterov stats->version = TASKSTATS_VERSION; 269c757249aSShailabh Nagar /* 2703a4fa0a2SRobert P. J. Day * Accounting subsystems can also add calls here to modify 271ad4ecbcbSShailabh Nagar * fields of taskstats. 272c757249aSShailabh Nagar */ 273a98b6094SOleg Nesterov return rc; 274c757249aSShailabh Nagar } 275c757249aSShailabh Nagar 276ad4ecbcbSShailabh Nagar static void fill_tgid_exit(struct task_struct *tsk) 277ad4ecbcbSShailabh Nagar { 278ad4ecbcbSShailabh Nagar unsigned long flags; 279ad4ecbcbSShailabh Nagar 280b8534d7bSOleg Nesterov spin_lock_irqsave(&tsk->sighand->siglock, flags); 281ad4ecbcbSShailabh Nagar if (!tsk->signal->stats) 282ad4ecbcbSShailabh Nagar goto ret; 283ad4ecbcbSShailabh Nagar 284ad4ecbcbSShailabh Nagar /* 285ad4ecbcbSShailabh Nagar * Each accounting subsystem calls its functions here to 286ad4ecbcbSShailabh Nagar * accumalate its per-task stats for tsk, into the per-tgid structure 287ad4ecbcbSShailabh Nagar * 288ad4ecbcbSShailabh Nagar * per-task-foo(tsk->signal->stats, tsk); 289ad4ecbcbSShailabh Nagar */ 290ad4ecbcbSShailabh Nagar delayacct_add_tsk(tsk->signal->stats, tsk); 291ad4ecbcbSShailabh Nagar ret: 292b8534d7bSOleg Nesterov spin_unlock_irqrestore(&tsk->sighand->siglock, flags); 293ad4ecbcbSShailabh Nagar return; 294ad4ecbcbSShailabh Nagar } 295ad4ecbcbSShailabh Nagar 29641c7bb95SRusty Russell static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd) 297f9fd8914SShailabh Nagar { 298f9fd8914SShailabh Nagar struct listener_list *listeners; 29926c4caeaSVasiliy Kulikov struct listener *s, *tmp, *s2; 300f9fd8914SShailabh Nagar unsigned int cpu; 3010d20633bSChen Gang int ret = 0; 302ad4ecbcbSShailabh Nagar 30341c7bb95SRusty Russell if (!cpumask_subset(mask, cpu_possible_mask)) 304f9fd8914SShailabh Nagar return -EINVAL; 305f9fd8914SShailabh Nagar 3064bd6e32aSEric W. Biederman if (current_user_ns() != &init_user_ns) 3074bd6e32aSEric W. Biederman return -EINVAL; 3084bd6e32aSEric W. Biederman 3094bd6e32aSEric W. Biederman if (task_active_pid_ns(current) != &init_pid_ns) 3104bd6e32aSEric W. Biederman return -EINVAL; 3114bd6e32aSEric W. Biederman 312f9fd8914SShailabh Nagar if (isadd == REGISTER) { 31341c7bb95SRusty Russell for_each_cpu(cpu, mask) { 31426c4caeaSVasiliy Kulikov s = kmalloc_node(sizeof(struct listener), 31526c4caeaSVasiliy Kulikov GFP_KERNEL, cpu_to_node(cpu)); 3160d20633bSChen Gang if (!s) { 3170d20633bSChen Gang ret = -ENOMEM; 318f9fd8914SShailabh Nagar goto cleanup; 3190d20633bSChen Gang } 320f9fd8914SShailabh Nagar s->pid = pid; 321bb129994SShailabh Nagar s->valid = 1; 322f9fd8914SShailabh Nagar 323f9fd8914SShailabh Nagar listeners = &per_cpu(listener_array, cpu); 324f9fd8914SShailabh Nagar down_write(&listeners->sem); 325dfc428b6SOleg Nesterov list_for_each_entry(s2, &listeners->list, list) { 326a7295898SOleg Nesterov if (s2->pid == pid && s2->valid) 327dfc428b6SOleg Nesterov goto exists; 32826c4caeaSVasiliy Kulikov } 329f9fd8914SShailabh Nagar list_add(&s->list, &listeners->list); 33026c4caeaSVasiliy Kulikov s = NULL; 331dfc428b6SOleg Nesterov exists: 332f9fd8914SShailabh Nagar up_write(&listeners->sem); 333dfc428b6SOleg Nesterov kfree(s); /* nop if NULL */ 334f9fd8914SShailabh Nagar } 335f9fd8914SShailabh Nagar return 0; 336f9fd8914SShailabh Nagar } 337f9fd8914SShailabh Nagar 338f9fd8914SShailabh Nagar /* Deregister or cleanup */ 339f9fd8914SShailabh Nagar cleanup: 34041c7bb95SRusty Russell for_each_cpu(cpu, mask) { 341f9fd8914SShailabh Nagar listeners = &per_cpu(listener_array, cpu); 342f9fd8914SShailabh Nagar down_write(&listeners->sem); 343f9fd8914SShailabh Nagar list_for_each_entry_safe(s, tmp, &listeners->list, list) { 344f9fd8914SShailabh Nagar if (s->pid == pid) { 345f9fd8914SShailabh Nagar list_del(&s->list); 346f9fd8914SShailabh Nagar kfree(s); 347f9fd8914SShailabh Nagar break; 348f9fd8914SShailabh Nagar } 349f9fd8914SShailabh Nagar } 350f9fd8914SShailabh Nagar up_write(&listeners->sem); 351f9fd8914SShailabh Nagar } 3520d20633bSChen Gang return ret; 353f9fd8914SShailabh Nagar } 354f9fd8914SShailabh Nagar 35541c7bb95SRusty Russell static int parse(struct nlattr *na, struct cpumask *mask) 356f9fd8914SShailabh Nagar { 357f9fd8914SShailabh Nagar char *data; 358f9fd8914SShailabh Nagar int len; 359f9fd8914SShailabh Nagar int ret; 360f9fd8914SShailabh Nagar 361f9fd8914SShailabh Nagar if (na == NULL) 362f9fd8914SShailabh Nagar return 1; 363f9fd8914SShailabh Nagar len = nla_len(na); 364f9fd8914SShailabh Nagar if (len > TASKSTATS_CPUMASK_MAXLEN) 365f9fd8914SShailabh Nagar return -E2BIG; 366f9fd8914SShailabh Nagar if (len < 1) 367f9fd8914SShailabh Nagar return -EINVAL; 368f9fd8914SShailabh Nagar data = kmalloc(len, GFP_KERNEL); 369f9fd8914SShailabh Nagar if (!data) 370f9fd8914SShailabh Nagar return -ENOMEM; 371872f6903SFrancis Laniel nla_strscpy(data, na, len); 37229c0177eSRusty Russell ret = cpulist_parse(data, mask); 373f9fd8914SShailabh Nagar kfree(data); 374f9fd8914SShailabh Nagar return ret; 375f9fd8914SShailabh Nagar } 376f9fd8914SShailabh Nagar 37751de4d90SOleg Nesterov static struct taskstats *mk_reply(struct sk_buff *skb, int type, u32 pid) 37868062b86SOleg Nesterov { 37951de4d90SOleg Nesterov struct nlattr *na, *ret; 38068062b86SOleg Nesterov int aggr; 38168062b86SOleg Nesterov 38237167485SOleg Nesterov aggr = (type == TASKSTATS_TYPE_PID) 38337167485SOleg Nesterov ? TASKSTATS_TYPE_AGGR_PID 38437167485SOleg Nesterov : TASKSTATS_TYPE_AGGR_TGID; 38568062b86SOleg Nesterov 386ae0be8deSMichal Kubecek na = nla_nest_start_noflag(skb, aggr); 38737167485SOleg Nesterov if (!na) 38837167485SOleg Nesterov goto err; 3894be2c95dSJeff Mahoney 3903fa58266SChen Gang if (nla_put(skb, type, sizeof(pid), &pid) < 0) { 3913fa58266SChen Gang nla_nest_cancel(skb, na); 39251de4d90SOleg Nesterov goto err; 3933fa58266SChen Gang } 39480df5542SNicolas Dichtel ret = nla_reserve_64bit(skb, TASKSTATS_TYPE_STATS, 39580df5542SNicolas Dichtel sizeof(struct taskstats), TASKSTATS_TYPE_NULL); 3963fa58266SChen Gang if (!ret) { 3973fa58266SChen Gang nla_nest_cancel(skb, na); 39851de4d90SOleg Nesterov goto err; 3993fa58266SChen Gang } 40068062b86SOleg Nesterov nla_nest_end(skb, na); 40168062b86SOleg Nesterov 40251de4d90SOleg Nesterov return nla_data(ret); 40351de4d90SOleg Nesterov err: 40451de4d90SOleg Nesterov return NULL; 40568062b86SOleg Nesterov } 40668062b86SOleg Nesterov 407846c7bb0SBalbir Singh static int cgroupstats_user_cmd(struct sk_buff *skb, struct genl_info *info) 408846c7bb0SBalbir Singh { 409846c7bb0SBalbir Singh int rc = 0; 410846c7bb0SBalbir Singh struct sk_buff *rep_skb; 411846c7bb0SBalbir Singh struct cgroupstats *stats; 412846c7bb0SBalbir Singh struct nlattr *na; 413846c7bb0SBalbir Singh size_t size; 414846c7bb0SBalbir Singh u32 fd; 4152903ff01SAl Viro struct fd f; 416846c7bb0SBalbir Singh 417846c7bb0SBalbir Singh na = info->attrs[CGROUPSTATS_CMD_ATTR_FD]; 418846c7bb0SBalbir Singh if (!na) 419846c7bb0SBalbir Singh return -EINVAL; 420846c7bb0SBalbir Singh 421846c7bb0SBalbir Singh fd = nla_get_u32(info->attrs[CGROUPSTATS_CMD_ATTR_FD]); 4222903ff01SAl Viro f = fdget(fd); 4232903ff01SAl Viro if (!f.file) 424f9615984SAdrian Bunk return 0; 425f9615984SAdrian Bunk 426846c7bb0SBalbir Singh size = nla_total_size(sizeof(struct cgroupstats)); 427846c7bb0SBalbir Singh 428846c7bb0SBalbir Singh rc = prepare_reply(info, CGROUPSTATS_CMD_NEW, &rep_skb, 429846c7bb0SBalbir Singh size); 430846c7bb0SBalbir Singh if (rc < 0) 431846c7bb0SBalbir Singh goto err; 432846c7bb0SBalbir Singh 433846c7bb0SBalbir Singh na = nla_reserve(rep_skb, CGROUPSTATS_TYPE_CGROUP_STATS, 434846c7bb0SBalbir Singh sizeof(struct cgroupstats)); 43525353b33SAlan Cox if (na == NULL) { 4360324b5a4SJesper Juhl nlmsg_free(rep_skb); 43725353b33SAlan Cox rc = -EMSGSIZE; 43825353b33SAlan Cox goto err; 43925353b33SAlan Cox } 44025353b33SAlan Cox 441846c7bb0SBalbir Singh stats = nla_data(na); 442846c7bb0SBalbir Singh memset(stats, 0, sizeof(*stats)); 443846c7bb0SBalbir Singh 444b583043eSAl Viro rc = cgroupstats_build(stats, f.file->f_path.dentry); 445f9615984SAdrian Bunk if (rc < 0) { 446f9615984SAdrian Bunk nlmsg_free(rep_skb); 447846c7bb0SBalbir Singh goto err; 448846c7bb0SBalbir Singh } 449846c7bb0SBalbir Singh 450134e6375SJohannes Berg rc = send_reply(rep_skb, info); 451f9615984SAdrian Bunk 452846c7bb0SBalbir Singh err: 4532903ff01SAl Viro fdput(f); 454846c7bb0SBalbir Singh return rc; 455846c7bb0SBalbir Singh } 456846c7bb0SBalbir Singh 45793233125SMichael Holzheu static int cmd_attr_register_cpumask(struct genl_info *info) 458c757249aSShailabh Nagar { 45941c7bb95SRusty Russell cpumask_var_t mask; 46093233125SMichael Holzheu int rc; 461f9fd8914SShailabh Nagar 46241c7bb95SRusty Russell if (!alloc_cpumask_var(&mask, GFP_KERNEL)) 46341c7bb95SRusty Russell return -ENOMEM; 46441c7bb95SRusty Russell rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask); 465f9fd8914SShailabh Nagar if (rc < 0) 46693233125SMichael Holzheu goto out; 46715e47304SEric W. Biederman rc = add_del_listener(info->snd_portid, mask, REGISTER); 46893233125SMichael Holzheu out: 46941c7bb95SRusty Russell free_cpumask_var(mask); 470f9fd8914SShailabh Nagar return rc; 47141c7bb95SRusty Russell } 472c757249aSShailabh Nagar 47393233125SMichael Holzheu static int cmd_attr_deregister_cpumask(struct genl_info *info) 47493233125SMichael Holzheu { 47593233125SMichael Holzheu cpumask_var_t mask; 47693233125SMichael Holzheu int rc; 47793233125SMichael Holzheu 47893233125SMichael Holzheu if (!alloc_cpumask_var(&mask, GFP_KERNEL)) 47993233125SMichael Holzheu return -ENOMEM; 48093233125SMichael Holzheu rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask); 48193233125SMichael Holzheu if (rc < 0) 48293233125SMichael Holzheu goto out; 48315e47304SEric W. Biederman rc = add_del_listener(info->snd_portid, mask, DEREGISTER); 48493233125SMichael Holzheu out: 48593233125SMichael Holzheu free_cpumask_var(mask); 48693233125SMichael Holzheu return rc; 48793233125SMichael Holzheu } 48893233125SMichael Holzheu 4894be2c95dSJeff Mahoney static size_t taskstats_packet_size(void) 4904be2c95dSJeff Mahoney { 4914be2c95dSJeff Mahoney size_t size; 4924be2c95dSJeff Mahoney 4934be2c95dSJeff Mahoney size = nla_total_size(sizeof(u32)) + 49480df5542SNicolas Dichtel nla_total_size_64bit(sizeof(struct taskstats)) + 49580df5542SNicolas Dichtel nla_total_size(0); 49680df5542SNicolas Dichtel 4974be2c95dSJeff Mahoney return size; 4984be2c95dSJeff Mahoney } 4994be2c95dSJeff Mahoney 50093233125SMichael Holzheu static int cmd_attr_pid(struct genl_info *info) 50193233125SMichael Holzheu { 50293233125SMichael Holzheu struct taskstats *stats; 50393233125SMichael Holzheu struct sk_buff *rep_skb; 50493233125SMichael Holzheu size_t size; 50593233125SMichael Holzheu u32 pid; 50693233125SMichael Holzheu int rc; 50793233125SMichael Holzheu 5084be2c95dSJeff Mahoney size = taskstats_packet_size(); 509c757249aSShailabh Nagar 51037167485SOleg Nesterov rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); 511c757249aSShailabh Nagar if (rc < 0) 512c757249aSShailabh Nagar return rc; 513c757249aSShailabh Nagar 51451de4d90SOleg Nesterov rc = -EINVAL; 51593233125SMichael Holzheu pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); 51651de4d90SOleg Nesterov stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid); 51751de4d90SOleg Nesterov if (!stats) 51837167485SOleg Nesterov goto err; 519c757249aSShailabh Nagar 5203d9e0cf1SMichael Holzheu rc = fill_stats_for_pid(pid, stats); 52151de4d90SOleg Nesterov if (rc < 0) 52237167485SOleg Nesterov goto err; 52393233125SMichael Holzheu return send_reply(rep_skb, info); 52493233125SMichael Holzheu err: 52593233125SMichael Holzheu nlmsg_free(rep_skb); 52693233125SMichael Holzheu return rc; 52793233125SMichael Holzheu } 52893233125SMichael Holzheu 52993233125SMichael Holzheu static int cmd_attr_tgid(struct genl_info *info) 53093233125SMichael Holzheu { 53193233125SMichael Holzheu struct taskstats *stats; 53293233125SMichael Holzheu struct sk_buff *rep_skb; 53393233125SMichael Holzheu size_t size; 53493233125SMichael Holzheu u32 tgid; 53593233125SMichael Holzheu int rc; 53693233125SMichael Holzheu 5374be2c95dSJeff Mahoney size = taskstats_packet_size(); 53893233125SMichael Holzheu 53993233125SMichael Holzheu rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); 54093233125SMichael Holzheu if (rc < 0) 54193233125SMichael Holzheu return rc; 54293233125SMichael Holzheu 54393233125SMichael Holzheu rc = -EINVAL; 54493233125SMichael Holzheu tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); 54551de4d90SOleg Nesterov stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid); 54651de4d90SOleg Nesterov if (!stats) 54737167485SOleg Nesterov goto err; 548c757249aSShailabh Nagar 5493d9e0cf1SMichael Holzheu rc = fill_stats_for_tgid(tgid, stats); 55051de4d90SOleg Nesterov if (rc < 0) 55137167485SOleg Nesterov goto err; 552134e6375SJohannes Berg return send_reply(rep_skb, info); 553c757249aSShailabh Nagar err: 554c757249aSShailabh Nagar nlmsg_free(rep_skb); 555c757249aSShailabh Nagar return rc; 556c757249aSShailabh Nagar } 557c757249aSShailabh Nagar 55893233125SMichael Holzheu static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) 55993233125SMichael Holzheu { 56093233125SMichael Holzheu if (info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK]) 56193233125SMichael Holzheu return cmd_attr_register_cpumask(info); 56293233125SMichael Holzheu else if (info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK]) 56393233125SMichael Holzheu return cmd_attr_deregister_cpumask(info); 56493233125SMichael Holzheu else if (info->attrs[TASKSTATS_CMD_ATTR_PID]) 56593233125SMichael Holzheu return cmd_attr_pid(info); 56693233125SMichael Holzheu else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) 56793233125SMichael Holzheu return cmd_attr_tgid(info); 56893233125SMichael Holzheu else 56993233125SMichael Holzheu return -EINVAL; 57093233125SMichael Holzheu } 57193233125SMichael Holzheu 57234ec1234SOleg Nesterov static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk) 57334ec1234SOleg Nesterov { 57434ec1234SOleg Nesterov struct signal_struct *sig = tsk->signal; 5750b8d616fSChristian Brauner struct taskstats *stats_new, *stats; 57634ec1234SOleg Nesterov 5770b8d616fSChristian Brauner /* Pairs with smp_store_release() below. */ 5780b8d616fSChristian Brauner stats = smp_load_acquire(&sig->stats); 5790b8d616fSChristian Brauner if (stats || thread_group_empty(tsk)) 5800b8d616fSChristian Brauner return stats; 58134ec1234SOleg Nesterov 58234ec1234SOleg Nesterov /* No problem if kmem_cache_zalloc() fails */ 5830b8d616fSChristian Brauner stats_new = kmem_cache_zalloc(taskstats_cache, GFP_KERNEL); 58434ec1234SOleg Nesterov 58534ec1234SOleg Nesterov spin_lock_irq(&tsk->sighand->siglock); 5860b8d616fSChristian Brauner stats = sig->stats; 5870b8d616fSChristian Brauner if (!stats) { 5880b8d616fSChristian Brauner /* 5890b8d616fSChristian Brauner * Pairs with smp_store_release() above and order the 5900b8d616fSChristian Brauner * kmem_cache_zalloc(). 5910b8d616fSChristian Brauner */ 5920b8d616fSChristian Brauner smp_store_release(&sig->stats, stats_new); 5930b8d616fSChristian Brauner stats = stats_new; 5940b8d616fSChristian Brauner stats_new = NULL; 59534ec1234SOleg Nesterov } 59634ec1234SOleg Nesterov spin_unlock_irq(&tsk->sighand->siglock); 59734ec1234SOleg Nesterov 5980b8d616fSChristian Brauner if (stats_new) 5990b8d616fSChristian Brauner kmem_cache_free(taskstats_cache, stats_new); 6000b8d616fSChristian Brauner 6010b8d616fSChristian Brauner return stats; 60234ec1234SOleg Nesterov } 60334ec1234SOleg Nesterov 604c757249aSShailabh Nagar /* Send pid data out on exit */ 605115085eaSOleg Nesterov void taskstats_exit(struct task_struct *tsk, int group_dead) 606c757249aSShailabh Nagar { 607c757249aSShailabh Nagar int rc; 608115085eaSOleg Nesterov struct listener_list *listeners; 60951de4d90SOleg Nesterov struct taskstats *stats; 610c757249aSShailabh Nagar struct sk_buff *rep_skb; 611c757249aSShailabh Nagar size_t size; 612c757249aSShailabh Nagar int is_thread_group; 613c757249aSShailabh Nagar 6144a279ff1SOleg Nesterov if (!family_registered) 615c757249aSShailabh Nagar return; 616c757249aSShailabh Nagar 617c757249aSShailabh Nagar /* 618c757249aSShailabh Nagar * Size includes space for nested attributes 619c757249aSShailabh Nagar */ 6204be2c95dSJeff Mahoney size = taskstats_packet_size(); 621c757249aSShailabh Nagar 62234ec1234SOleg Nesterov is_thread_group = !!taskstats_tgid_alloc(tsk); 6234a279ff1SOleg Nesterov if (is_thread_group) { 6244a279ff1SOleg Nesterov /* PID + STATS + TGID + STATS */ 6254a279ff1SOleg Nesterov size = 2 * size; 6264a279ff1SOleg Nesterov /* fill the tsk->signal->stats structure */ 6274a279ff1SOleg Nesterov fill_tgid_exit(tsk); 6284a279ff1SOleg Nesterov } 6294a279ff1SOleg Nesterov 6304a32fea9SChristoph Lameter listeners = raw_cpu_ptr(&listener_array); 631115085eaSOleg Nesterov if (list_empty(&listeners->list)) 632115085eaSOleg Nesterov return; 633115085eaSOleg Nesterov 63437167485SOleg Nesterov rc = prepare_reply(NULL, TASKSTATS_CMD_NEW, &rep_skb, size); 635c757249aSShailabh Nagar if (rc < 0) 63651de4d90SOleg Nesterov return; 637c757249aSShailabh Nagar 6384bd6e32aSEric W. Biederman stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, 6394bd6e32aSEric W. Biederman task_pid_nr_ns(tsk, &init_pid_ns)); 64051de4d90SOleg Nesterov if (!stats) 64137167485SOleg Nesterov goto err; 64251de4d90SOleg Nesterov 6434bd6e32aSEric W. Biederman fill_stats(&init_user_ns, &init_pid_ns, tsk, stats); 644*0e0af57eSDr. Thomas Orgis if (group_dead) 645*0e0af57eSDr. Thomas Orgis stats->ac_flag |= AGROUP; 646c757249aSShailabh Nagar 647c757249aSShailabh Nagar /* 648ad4ecbcbSShailabh Nagar * Doesn't matter if tsk is the leader or the last group member leaving 649c757249aSShailabh Nagar */ 65068062b86SOleg Nesterov if (!is_thread_group || !group_dead) 651ad4ecbcbSShailabh Nagar goto send; 652c757249aSShailabh Nagar 6534bd6e32aSEric W. Biederman stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, 6544bd6e32aSEric W. Biederman task_tgid_nr_ns(tsk, &init_pid_ns)); 65551de4d90SOleg Nesterov if (!stats) 65637167485SOleg Nesterov goto err; 65751de4d90SOleg Nesterov 65851de4d90SOleg Nesterov memcpy(stats, tsk->signal->stats, sizeof(*stats)); 659c757249aSShailabh Nagar 660ad4ecbcbSShailabh Nagar send: 661115085eaSOleg Nesterov send_cpu_listeners(rep_skb, listeners); 662ad4ecbcbSShailabh Nagar return; 66337167485SOleg Nesterov err: 664c757249aSShailabh Nagar nlmsg_free(rep_skb); 665c757249aSShailabh Nagar } 666c757249aSShailabh Nagar 6677c1e0926SJakub Kicinski static const struct genl_ops taskstats_ops[] = { 66888d36a99SJohannes Berg { 669c757249aSShailabh Nagar .cmd = TASKSTATS_CMD_GET, 670ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 671f9fd8914SShailabh Nagar .doit = taskstats_user_cmd, 6727c1e0926SJakub Kicinski .policy = taskstats_cmd_get_policy, 6737c1e0926SJakub Kicinski .maxattr = ARRAY_SIZE(taskstats_cmd_get_policy) - 1, 6747c1e0926SJakub Kicinski .flags = GENL_ADMIN_PERM, 67588d36a99SJohannes Berg }, 67688d36a99SJohannes Berg { 677846c7bb0SBalbir Singh .cmd = CGROUPSTATS_CMD_GET, 678ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 679846c7bb0SBalbir Singh .doit = cgroupstats_user_cmd, 6807c1e0926SJakub Kicinski .policy = cgroupstats_cmd_get_policy, 6817c1e0926SJakub Kicinski .maxattr = ARRAY_SIZE(cgroupstats_cmd_get_policy) - 1, 68288d36a99SJohannes Berg }, 683846c7bb0SBalbir Singh }; 684846c7bb0SBalbir Singh 68556989f6dSJohannes Berg static struct genl_family family __ro_after_init = { 686489111e5SJohannes Berg .name = TASKSTATS_GENL_NAME, 687489111e5SJohannes Berg .version = TASKSTATS_GENL_VERSION, 688489111e5SJohannes Berg .module = THIS_MODULE, 6897c1e0926SJakub Kicinski .ops = taskstats_ops, 6907c1e0926SJakub Kicinski .n_ops = ARRAY_SIZE(taskstats_ops), 691489111e5SJohannes Berg }; 692489111e5SJohannes Berg 693c757249aSShailabh Nagar /* Needed early in initialization */ 694c757249aSShailabh Nagar void __init taskstats_init_early(void) 695c757249aSShailabh Nagar { 696f9fd8914SShailabh Nagar unsigned int i; 697f9fd8914SShailabh Nagar 6980a31bd5fSChristoph Lameter taskstats_cache = KMEM_CACHE(taskstats, SLAB_PANIC); 699f9fd8914SShailabh Nagar for_each_possible_cpu(i) { 700f9fd8914SShailabh Nagar INIT_LIST_HEAD(&(per_cpu(listener_array, i).list)); 701f9fd8914SShailabh Nagar init_rwsem(&(per_cpu(listener_array, i).sem)); 702f9fd8914SShailabh Nagar } 703c757249aSShailabh Nagar } 704c757249aSShailabh Nagar 705c757249aSShailabh Nagar static int __init taskstats_init(void) 706c757249aSShailabh Nagar { 707c757249aSShailabh Nagar int rc; 708c757249aSShailabh Nagar 709489111e5SJohannes Berg rc = genl_register_family(&family); 710c757249aSShailabh Nagar if (rc) 711c757249aSShailabh Nagar return rc; 712c757249aSShailabh Nagar 713c757249aSShailabh Nagar family_registered = 1; 714f9b182e2SMandeep Singh Baines pr_info("registered taskstats version %d\n", TASKSTATS_GENL_VERSION); 715c757249aSShailabh Nagar return 0; 716c757249aSShailabh Nagar } 717c757249aSShailabh Nagar 718c757249aSShailabh Nagar /* 719c757249aSShailabh Nagar * late initcall ensures initialization of statistics collection 720c757249aSShailabh Nagar * mechanisms precedes initialization of the taskstats interface 721c757249aSShailabh Nagar */ 722c757249aSShailabh Nagar late_initcall(taskstats_init); 723