1c757249aSShailabh Nagar /* 2c757249aSShailabh Nagar * taskstats.c - Export per-task statistics to userland 3c757249aSShailabh Nagar * 4c757249aSShailabh Nagar * Copyright (C) Shailabh Nagar, IBM Corp. 2006 5c757249aSShailabh Nagar * (C) Balbir Singh, IBM Corp. 2006 6c757249aSShailabh Nagar * 7c757249aSShailabh Nagar * This program is free software; you can redistribute it and/or modify 8c757249aSShailabh Nagar * it under the terms of the GNU General Public License as published by 9c757249aSShailabh Nagar * the Free Software Foundation; either version 2 of the License, or 10c757249aSShailabh Nagar * (at your option) any later version. 11c757249aSShailabh Nagar * 12c757249aSShailabh Nagar * This program is distributed in the hope that it will be useful, 13c757249aSShailabh Nagar * but WITHOUT ANY WARRANTY; without even the implied warranty of 14c757249aSShailabh Nagar * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15c757249aSShailabh Nagar * GNU General Public License for more details. 16c757249aSShailabh Nagar * 17c757249aSShailabh Nagar */ 18c757249aSShailabh Nagar 19c757249aSShailabh Nagar #include <linux/kernel.h> 20c757249aSShailabh Nagar #include <linux/taskstats_kern.h> 21f3cef7a9SJay Lan #include <linux/tsacct_kern.h> 226f44993fSShailabh Nagar #include <linux/delayacct.h> 239acc1853SJay Lan #include <linux/tsacct_kern.h> 24f9fd8914SShailabh Nagar #include <linux/cpumask.h> 25f9fd8914SShailabh Nagar #include <linux/percpu.h> 26c757249aSShailabh Nagar #include <net/genetlink.h> 27c757249aSShailabh Nagar #include <asm/atomic.h> 28c757249aSShailabh Nagar 29f9fd8914SShailabh Nagar /* 30f9fd8914SShailabh Nagar * Maximum length of a cpumask that can be specified in 31f9fd8914SShailabh Nagar * the TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK attribute 32f9fd8914SShailabh Nagar */ 33f9fd8914SShailabh Nagar #define TASKSTATS_CPUMASK_MAXLEN (100+6*NR_CPUS) 34f9fd8914SShailabh Nagar 35c757249aSShailabh Nagar static DEFINE_PER_CPU(__u32, taskstats_seqnum) = { 0 }; 36c757249aSShailabh Nagar static int family_registered; 37e18b890bSChristoph Lameter struct kmem_cache *taskstats_cache; 38c757249aSShailabh Nagar 39c757249aSShailabh Nagar static struct genl_family family = { 40c757249aSShailabh Nagar .id = GENL_ID_GENERATE, 41c757249aSShailabh Nagar .name = TASKSTATS_GENL_NAME, 42c757249aSShailabh Nagar .version = TASKSTATS_GENL_VERSION, 43c757249aSShailabh Nagar .maxattr = TASKSTATS_CMD_ATTR_MAX, 44c757249aSShailabh Nagar }; 45c757249aSShailabh Nagar 46c757249aSShailabh Nagar static struct nla_policy taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] 47c757249aSShailabh Nagar __read_mostly = { 48c757249aSShailabh Nagar [TASKSTATS_CMD_ATTR_PID] = { .type = NLA_U32 }, 49c757249aSShailabh Nagar [TASKSTATS_CMD_ATTR_TGID] = { .type = NLA_U32 }, 50f9fd8914SShailabh Nagar [TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING }, 51f9fd8914SShailabh Nagar [TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },}; 52f9fd8914SShailabh Nagar 53f9fd8914SShailabh Nagar struct listener { 54f9fd8914SShailabh Nagar struct list_head list; 55f9fd8914SShailabh Nagar pid_t pid; 56bb129994SShailabh Nagar char valid; 57c757249aSShailabh Nagar }; 58c757249aSShailabh Nagar 59f9fd8914SShailabh Nagar struct listener_list { 60f9fd8914SShailabh Nagar struct rw_semaphore sem; 61f9fd8914SShailabh Nagar struct list_head list; 62f9fd8914SShailabh Nagar }; 63f9fd8914SShailabh Nagar static DEFINE_PER_CPU(struct listener_list, listener_array); 64f9fd8914SShailabh Nagar 65f9fd8914SShailabh Nagar enum actions { 66f9fd8914SShailabh Nagar REGISTER, 67f9fd8914SShailabh Nagar DEREGISTER, 68f9fd8914SShailabh Nagar CPU_DONT_CARE 69f9fd8914SShailabh Nagar }; 70c757249aSShailabh Nagar 71c757249aSShailabh Nagar static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp, 72c757249aSShailabh Nagar void **replyp, size_t size) 73c757249aSShailabh Nagar { 74c757249aSShailabh Nagar struct sk_buff *skb; 75c757249aSShailabh Nagar void *reply; 76c757249aSShailabh Nagar 77c757249aSShailabh Nagar /* 78c757249aSShailabh Nagar * If new attributes are added, please revisit this allocation 79c757249aSShailabh Nagar */ 803dabc715SThomas Graf skb = genlmsg_new(size, GFP_KERNEL); 81c757249aSShailabh Nagar if (!skb) 82c757249aSShailabh Nagar return -ENOMEM; 83c757249aSShailabh Nagar 84c757249aSShailabh Nagar if (!info) { 85c757249aSShailabh Nagar int seq = get_cpu_var(taskstats_seqnum)++; 86c757249aSShailabh Nagar put_cpu_var(taskstats_seqnum); 87c757249aSShailabh Nagar 8817c157c8SThomas Graf reply = genlmsg_put(skb, 0, seq, &family, 0, cmd); 89c757249aSShailabh Nagar } else 9017c157c8SThomas Graf reply = genlmsg_put_reply(skb, info, &family, 0, cmd); 91c757249aSShailabh Nagar if (reply == NULL) { 92c757249aSShailabh Nagar nlmsg_free(skb); 93c757249aSShailabh Nagar return -EINVAL; 94c757249aSShailabh Nagar } 95c757249aSShailabh Nagar 96c757249aSShailabh Nagar *skbp = skb; 97c757249aSShailabh Nagar *replyp = reply; 98c757249aSShailabh Nagar return 0; 99c757249aSShailabh Nagar } 100c757249aSShailabh Nagar 101f9fd8914SShailabh Nagar /* 102f9fd8914SShailabh Nagar * Send taskstats data in @skb to listener with nl_pid @pid 103f9fd8914SShailabh Nagar */ 104f9fd8914SShailabh Nagar static int send_reply(struct sk_buff *skb, pid_t pid) 105c757249aSShailabh Nagar { 106c757249aSShailabh Nagar struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data); 107f9fd8914SShailabh Nagar void *reply = genlmsg_data(genlhdr); 108c757249aSShailabh Nagar int rc; 109c757249aSShailabh Nagar 110c757249aSShailabh Nagar rc = genlmsg_end(skb, reply); 111c757249aSShailabh Nagar if (rc < 0) { 112c757249aSShailabh Nagar nlmsg_free(skb); 113c757249aSShailabh Nagar return rc; 114c757249aSShailabh Nagar } 115c757249aSShailabh Nagar 116c757249aSShailabh Nagar return genlmsg_unicast(skb, pid); 117c757249aSShailabh Nagar } 118c757249aSShailabh Nagar 119f9fd8914SShailabh Nagar /* 120f9fd8914SShailabh Nagar * Send taskstats data in @skb to listeners registered for @cpu's exit data 121f9fd8914SShailabh Nagar */ 122115085eaSOleg Nesterov static void send_cpu_listeners(struct sk_buff *skb, 123115085eaSOleg Nesterov struct listener_list *listeners) 124f9fd8914SShailabh Nagar { 125f9fd8914SShailabh Nagar struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data); 126f9fd8914SShailabh Nagar struct listener *s, *tmp; 127f9fd8914SShailabh Nagar struct sk_buff *skb_next, *skb_cur = skb; 128f9fd8914SShailabh Nagar void *reply = genlmsg_data(genlhdr); 129d94a0415SShailabh Nagar int rc, delcount = 0; 130f9fd8914SShailabh Nagar 131f9fd8914SShailabh Nagar rc = genlmsg_end(skb, reply); 132f9fd8914SShailabh Nagar if (rc < 0) { 133f9fd8914SShailabh Nagar nlmsg_free(skb); 134d94a0415SShailabh Nagar return; 135f9fd8914SShailabh Nagar } 136f9fd8914SShailabh Nagar 137f9fd8914SShailabh Nagar rc = 0; 138bb129994SShailabh Nagar down_read(&listeners->sem); 139d94a0415SShailabh Nagar list_for_each_entry(s, &listeners->list, list) { 140f9fd8914SShailabh Nagar skb_next = NULL; 141f9fd8914SShailabh Nagar if (!list_is_last(&s->list, &listeners->list)) { 142f9fd8914SShailabh Nagar skb_next = skb_clone(skb_cur, GFP_KERNEL); 143d94a0415SShailabh Nagar if (!skb_next) 144f9fd8914SShailabh Nagar break; 145f9fd8914SShailabh Nagar } 146d94a0415SShailabh Nagar rc = genlmsg_unicast(skb_cur, s->pid); 147d94a0415SShailabh Nagar if (rc == -ECONNREFUSED) { 148bb129994SShailabh Nagar s->valid = 0; 149bb129994SShailabh Nagar delcount++; 150f9fd8914SShailabh Nagar } 151f9fd8914SShailabh Nagar skb_cur = skb_next; 152f9fd8914SShailabh Nagar } 153bb129994SShailabh Nagar up_read(&listeners->sem); 154f9fd8914SShailabh Nagar 155d94a0415SShailabh Nagar if (skb_cur) 156d94a0415SShailabh Nagar nlmsg_free(skb_cur); 157d94a0415SShailabh Nagar 158bb129994SShailabh Nagar if (!delcount) 159d94a0415SShailabh Nagar return; 160bb129994SShailabh Nagar 161bb129994SShailabh Nagar /* Delete invalidated entries */ 162bb129994SShailabh Nagar down_write(&listeners->sem); 163bb129994SShailabh Nagar list_for_each_entry_safe(s, tmp, &listeners->list, list) { 164bb129994SShailabh Nagar if (!s->valid) { 165bb129994SShailabh Nagar list_del(&s->list); 166bb129994SShailabh Nagar kfree(s); 167bb129994SShailabh Nagar } 168bb129994SShailabh Nagar } 169bb129994SShailabh Nagar up_write(&listeners->sem); 170f9fd8914SShailabh Nagar } 171f9fd8914SShailabh Nagar 172a98b6094SOleg Nesterov static int fill_pid(pid_t pid, struct task_struct *tsk, 173c757249aSShailabh Nagar struct taskstats *stats) 174c757249aSShailabh Nagar { 1757d94ddddSShailabh Nagar int rc = 0; 176c757249aSShailabh Nagar 177c757249aSShailabh Nagar if (!tsk) { 178a98b6094SOleg Nesterov rcu_read_lock(); 179a98b6094SOleg Nesterov tsk = find_task_by_pid(pid); 180a98b6094SOleg Nesterov if (tsk) 181c757249aSShailabh Nagar get_task_struct(tsk); 182a98b6094SOleg Nesterov rcu_read_unlock(); 183a98b6094SOleg Nesterov if (!tsk) 184a98b6094SOleg Nesterov return -ESRCH; 185c757249aSShailabh Nagar } else 186c757249aSShailabh Nagar get_task_struct(tsk); 187c757249aSShailabh Nagar 188c757249aSShailabh Nagar /* 189c757249aSShailabh Nagar * Each accounting subsystem adds calls to its functions to 190c757249aSShailabh Nagar * fill in relevant parts of struct taskstsats as follows 191c757249aSShailabh Nagar * 1927d94ddddSShailabh Nagar * per-task-foo(stats, tsk); 193c757249aSShailabh Nagar */ 194c757249aSShailabh Nagar 1957d94ddddSShailabh Nagar delayacct_add_tsk(stats, tsk); 196f3cef7a9SJay Lan 197f3cef7a9SJay Lan /* fill in basic acct fields */ 1986f44993fSShailabh Nagar stats->version = TASKSTATS_VERSION; 199f3cef7a9SJay Lan bacct_add_tsk(stats, tsk); 2006f44993fSShailabh Nagar 2019acc1853SJay Lan /* fill in extended acct fields */ 2029acc1853SJay Lan xacct_add_tsk(stats, tsk); 2039acc1853SJay Lan 2046f44993fSShailabh Nagar /* Define err: label here if needed */ 205c757249aSShailabh Nagar put_task_struct(tsk); 206c757249aSShailabh Nagar return rc; 207c757249aSShailabh Nagar 208c757249aSShailabh Nagar } 209c757249aSShailabh Nagar 210a98b6094SOleg Nesterov static int fill_tgid(pid_t tgid, struct task_struct *first, 211c757249aSShailabh Nagar struct taskstats *stats) 212c757249aSShailabh Nagar { 213a98b6094SOleg Nesterov struct task_struct *tsk; 214ad4ecbcbSShailabh Nagar unsigned long flags; 215a98b6094SOleg Nesterov int rc = -ESRCH; 216c757249aSShailabh Nagar 217ad4ecbcbSShailabh Nagar /* 218ad4ecbcbSShailabh Nagar * Add additional stats from live tasks except zombie thread group 219ad4ecbcbSShailabh Nagar * leaders who are already counted with the dead tasks 220ad4ecbcbSShailabh Nagar */ 221a98b6094SOleg Nesterov rcu_read_lock(); 222a98b6094SOleg Nesterov if (!first) 223c757249aSShailabh Nagar first = find_task_by_pid(tgid); 224ad4ecbcbSShailabh Nagar 225a98b6094SOleg Nesterov if (!first || !lock_task_sighand(first, &flags)) 226a98b6094SOleg Nesterov goto out; 227fca178c0SOleg Nesterov 228ad4ecbcbSShailabh Nagar if (first->signal->stats) 229ad4ecbcbSShailabh Nagar memcpy(stats, first->signal->stats, sizeof(*stats)); 230ad4ecbcbSShailabh Nagar 231a98b6094SOleg Nesterov tsk = first; 232c757249aSShailabh Nagar do { 233d7c3f5f2SOleg Nesterov if (tsk->exit_state) 234ad4ecbcbSShailabh Nagar continue; 235c757249aSShailabh Nagar /* 236ad4ecbcbSShailabh Nagar * Accounting subsystem can call its functions here to 237c757249aSShailabh Nagar * fill in relevant parts of struct taskstsats as follows 238c757249aSShailabh Nagar * 239ad4ecbcbSShailabh Nagar * per-task-foo(stats, tsk); 240c757249aSShailabh Nagar */ 241ad4ecbcbSShailabh Nagar delayacct_add_tsk(stats, tsk); 2426f44993fSShailabh Nagar 243c757249aSShailabh Nagar } while_each_thread(first, tsk); 2446f44993fSShailabh Nagar 245a98b6094SOleg Nesterov unlock_task_sighand(first, &flags); 246a98b6094SOleg Nesterov rc = 0; 247a98b6094SOleg Nesterov out: 248a98b6094SOleg Nesterov rcu_read_unlock(); 249a98b6094SOleg Nesterov 250a98b6094SOleg Nesterov stats->version = TASKSTATS_VERSION; 251c757249aSShailabh Nagar /* 252ad4ecbcbSShailabh Nagar * Accounting subsytems can also add calls here to modify 253ad4ecbcbSShailabh Nagar * fields of taskstats. 254c757249aSShailabh Nagar */ 255a98b6094SOleg Nesterov return rc; 256c757249aSShailabh Nagar } 257c757249aSShailabh Nagar 258ad4ecbcbSShailabh Nagar 259ad4ecbcbSShailabh Nagar static void fill_tgid_exit(struct task_struct *tsk) 260ad4ecbcbSShailabh Nagar { 261ad4ecbcbSShailabh Nagar unsigned long flags; 262ad4ecbcbSShailabh Nagar 263b8534d7bSOleg Nesterov spin_lock_irqsave(&tsk->sighand->siglock, flags); 264ad4ecbcbSShailabh Nagar if (!tsk->signal->stats) 265ad4ecbcbSShailabh Nagar goto ret; 266ad4ecbcbSShailabh Nagar 267ad4ecbcbSShailabh Nagar /* 268ad4ecbcbSShailabh Nagar * Each accounting subsystem calls its functions here to 269ad4ecbcbSShailabh Nagar * accumalate its per-task stats for tsk, into the per-tgid structure 270ad4ecbcbSShailabh Nagar * 271ad4ecbcbSShailabh Nagar * per-task-foo(tsk->signal->stats, tsk); 272ad4ecbcbSShailabh Nagar */ 273ad4ecbcbSShailabh Nagar delayacct_add_tsk(tsk->signal->stats, tsk); 274ad4ecbcbSShailabh Nagar ret: 275b8534d7bSOleg Nesterov spin_unlock_irqrestore(&tsk->sighand->siglock, flags); 276ad4ecbcbSShailabh Nagar return; 277ad4ecbcbSShailabh Nagar } 278ad4ecbcbSShailabh Nagar 279f9fd8914SShailabh Nagar static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd) 280f9fd8914SShailabh Nagar { 281f9fd8914SShailabh Nagar struct listener_list *listeners; 282f9fd8914SShailabh Nagar struct listener *s, *tmp; 283f9fd8914SShailabh Nagar unsigned int cpu; 284f9fd8914SShailabh Nagar cpumask_t mask = *maskp; 285ad4ecbcbSShailabh Nagar 286f9fd8914SShailabh Nagar if (!cpus_subset(mask, cpu_possible_map)) 287f9fd8914SShailabh Nagar return -EINVAL; 288f9fd8914SShailabh Nagar 289f9fd8914SShailabh Nagar if (isadd == REGISTER) { 290f9fd8914SShailabh Nagar for_each_cpu_mask(cpu, mask) { 291f9fd8914SShailabh Nagar s = kmalloc_node(sizeof(struct listener), GFP_KERNEL, 292f9fd8914SShailabh Nagar cpu_to_node(cpu)); 293f9fd8914SShailabh Nagar if (!s) 294f9fd8914SShailabh Nagar goto cleanup; 295f9fd8914SShailabh Nagar s->pid = pid; 296f9fd8914SShailabh Nagar INIT_LIST_HEAD(&s->list); 297bb129994SShailabh Nagar s->valid = 1; 298f9fd8914SShailabh Nagar 299f9fd8914SShailabh Nagar listeners = &per_cpu(listener_array, cpu); 300f9fd8914SShailabh Nagar down_write(&listeners->sem); 301f9fd8914SShailabh Nagar list_add(&s->list, &listeners->list); 302f9fd8914SShailabh Nagar up_write(&listeners->sem); 303f9fd8914SShailabh Nagar } 304f9fd8914SShailabh Nagar return 0; 305f9fd8914SShailabh Nagar } 306f9fd8914SShailabh Nagar 307f9fd8914SShailabh Nagar /* Deregister or cleanup */ 308f9fd8914SShailabh Nagar cleanup: 309f9fd8914SShailabh Nagar for_each_cpu_mask(cpu, mask) { 310f9fd8914SShailabh Nagar listeners = &per_cpu(listener_array, cpu); 311f9fd8914SShailabh Nagar down_write(&listeners->sem); 312f9fd8914SShailabh Nagar list_for_each_entry_safe(s, tmp, &listeners->list, list) { 313f9fd8914SShailabh Nagar if (s->pid == pid) { 314f9fd8914SShailabh Nagar list_del(&s->list); 315f9fd8914SShailabh Nagar kfree(s); 316f9fd8914SShailabh Nagar break; 317f9fd8914SShailabh Nagar } 318f9fd8914SShailabh Nagar } 319f9fd8914SShailabh Nagar up_write(&listeners->sem); 320f9fd8914SShailabh Nagar } 321f9fd8914SShailabh Nagar return 0; 322f9fd8914SShailabh Nagar } 323f9fd8914SShailabh Nagar 324f9fd8914SShailabh Nagar static int parse(struct nlattr *na, cpumask_t *mask) 325f9fd8914SShailabh Nagar { 326f9fd8914SShailabh Nagar char *data; 327f9fd8914SShailabh Nagar int len; 328f9fd8914SShailabh Nagar int ret; 329f9fd8914SShailabh Nagar 330f9fd8914SShailabh Nagar if (na == NULL) 331f9fd8914SShailabh Nagar return 1; 332f9fd8914SShailabh Nagar len = nla_len(na); 333f9fd8914SShailabh Nagar if (len > TASKSTATS_CPUMASK_MAXLEN) 334f9fd8914SShailabh Nagar return -E2BIG; 335f9fd8914SShailabh Nagar if (len < 1) 336f9fd8914SShailabh Nagar return -EINVAL; 337f9fd8914SShailabh Nagar data = kmalloc(len, GFP_KERNEL); 338f9fd8914SShailabh Nagar if (!data) 339f9fd8914SShailabh Nagar return -ENOMEM; 340f9fd8914SShailabh Nagar nla_strlcpy(data, na, len); 341f9fd8914SShailabh Nagar ret = cpulist_parse(data, *mask); 342f9fd8914SShailabh Nagar kfree(data); 343f9fd8914SShailabh Nagar return ret; 344f9fd8914SShailabh Nagar } 345f9fd8914SShailabh Nagar 346f9fd8914SShailabh Nagar static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) 347c757249aSShailabh Nagar { 348c757249aSShailabh Nagar int rc = 0; 349c757249aSShailabh Nagar struct sk_buff *rep_skb; 350c757249aSShailabh Nagar struct taskstats stats; 351c757249aSShailabh Nagar void *reply; 352c757249aSShailabh Nagar size_t size; 353c757249aSShailabh Nagar struct nlattr *na; 354f9fd8914SShailabh Nagar cpumask_t mask; 355f9fd8914SShailabh Nagar 356f9fd8914SShailabh Nagar rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], &mask); 357f9fd8914SShailabh Nagar if (rc < 0) 358f9fd8914SShailabh Nagar return rc; 359f9fd8914SShailabh Nagar if (rc == 0) 360f9fd8914SShailabh Nagar return add_del_listener(info->snd_pid, &mask, REGISTER); 361f9fd8914SShailabh Nagar 362f9fd8914SShailabh Nagar rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], &mask); 363f9fd8914SShailabh Nagar if (rc < 0) 364f9fd8914SShailabh Nagar return rc; 365f9fd8914SShailabh Nagar if (rc == 0) 366f9fd8914SShailabh Nagar return add_del_listener(info->snd_pid, &mask, DEREGISTER); 367c757249aSShailabh Nagar 368c757249aSShailabh Nagar /* 369c757249aSShailabh Nagar * Size includes space for nested attributes 370c757249aSShailabh Nagar */ 371c757249aSShailabh Nagar size = nla_total_size(sizeof(u32)) + 372c757249aSShailabh Nagar nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); 373c757249aSShailabh Nagar 374c757249aSShailabh Nagar memset(&stats, 0, sizeof(stats)); 375c757249aSShailabh Nagar rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, &reply, size); 376c757249aSShailabh Nagar if (rc < 0) 377c757249aSShailabh Nagar return rc; 378c757249aSShailabh Nagar 379c757249aSShailabh Nagar if (info->attrs[TASKSTATS_CMD_ATTR_PID]) { 380c757249aSShailabh Nagar u32 pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); 381c757249aSShailabh Nagar rc = fill_pid(pid, NULL, &stats); 382c757249aSShailabh Nagar if (rc < 0) 383c757249aSShailabh Nagar goto err; 384c757249aSShailabh Nagar 385c757249aSShailabh Nagar na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_PID); 386c757249aSShailabh Nagar NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_PID, pid); 387c757249aSShailabh Nagar NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, 388c757249aSShailabh Nagar stats); 389c757249aSShailabh Nagar } else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) { 390c757249aSShailabh Nagar u32 tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); 391c757249aSShailabh Nagar rc = fill_tgid(tgid, NULL, &stats); 392c757249aSShailabh Nagar if (rc < 0) 393c757249aSShailabh Nagar goto err; 394c757249aSShailabh Nagar 395c757249aSShailabh Nagar na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_TGID); 396c757249aSShailabh Nagar NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_TGID, tgid); 397c757249aSShailabh Nagar NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, 398c757249aSShailabh Nagar stats); 399c757249aSShailabh Nagar } else { 400c757249aSShailabh Nagar rc = -EINVAL; 401c757249aSShailabh Nagar goto err; 402c757249aSShailabh Nagar } 403c757249aSShailabh Nagar 404c757249aSShailabh Nagar nla_nest_end(rep_skb, na); 405c757249aSShailabh Nagar 406f9fd8914SShailabh Nagar return send_reply(rep_skb, info->snd_pid); 407c757249aSShailabh Nagar 408c757249aSShailabh Nagar nla_put_failure: 409d46a3d0dSOleg Nesterov rc = genlmsg_cancel(rep_skb, reply); 410c757249aSShailabh Nagar err: 411c757249aSShailabh Nagar nlmsg_free(rep_skb); 412c757249aSShailabh Nagar return rc; 413c757249aSShailabh Nagar } 414c757249aSShailabh Nagar 415*34ec1234SOleg Nesterov static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk) 416*34ec1234SOleg Nesterov { 417*34ec1234SOleg Nesterov struct signal_struct *sig = tsk->signal; 418*34ec1234SOleg Nesterov struct taskstats *stats; 419*34ec1234SOleg Nesterov 420*34ec1234SOleg Nesterov if (sig->stats || thread_group_empty(tsk)) 421*34ec1234SOleg Nesterov goto ret; 422*34ec1234SOleg Nesterov 423*34ec1234SOleg Nesterov /* No problem if kmem_cache_zalloc() fails */ 424*34ec1234SOleg Nesterov stats = kmem_cache_zalloc(taskstats_cache, GFP_KERNEL); 425*34ec1234SOleg Nesterov 426*34ec1234SOleg Nesterov spin_lock_irq(&tsk->sighand->siglock); 427*34ec1234SOleg Nesterov if (!sig->stats) { 428*34ec1234SOleg Nesterov sig->stats = stats; 429*34ec1234SOleg Nesterov stats = NULL; 430*34ec1234SOleg Nesterov } 431*34ec1234SOleg Nesterov spin_unlock_irq(&tsk->sighand->siglock); 432*34ec1234SOleg Nesterov 433*34ec1234SOleg Nesterov if (stats) 434*34ec1234SOleg Nesterov kmem_cache_free(taskstats_cache, stats); 435*34ec1234SOleg Nesterov ret: 436*34ec1234SOleg Nesterov return sig->stats; 437*34ec1234SOleg Nesterov } 438*34ec1234SOleg Nesterov 439c757249aSShailabh Nagar /* Send pid data out on exit */ 440115085eaSOleg Nesterov void taskstats_exit(struct task_struct *tsk, int group_dead) 441c757249aSShailabh Nagar { 442c757249aSShailabh Nagar int rc; 443115085eaSOleg Nesterov struct listener_list *listeners; 444115085eaSOleg Nesterov struct taskstats *tidstats; 445c757249aSShailabh Nagar struct sk_buff *rep_skb; 446c757249aSShailabh Nagar void *reply; 447c757249aSShailabh Nagar size_t size; 448c757249aSShailabh Nagar int is_thread_group; 449c757249aSShailabh Nagar struct nlattr *na; 450c757249aSShailabh Nagar 4514a279ff1SOleg Nesterov if (!family_registered) 452c757249aSShailabh Nagar return; 453c757249aSShailabh Nagar 454c757249aSShailabh Nagar /* 455c757249aSShailabh Nagar * Size includes space for nested attributes 456c757249aSShailabh Nagar */ 457c757249aSShailabh Nagar size = nla_total_size(sizeof(u32)) + 458c757249aSShailabh Nagar nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); 459c757249aSShailabh Nagar 460*34ec1234SOleg Nesterov is_thread_group = !!taskstats_tgid_alloc(tsk); 4614a279ff1SOleg Nesterov if (is_thread_group) { 4624a279ff1SOleg Nesterov /* PID + STATS + TGID + STATS */ 4634a279ff1SOleg Nesterov size = 2 * size; 4644a279ff1SOleg Nesterov /* fill the tsk->signal->stats structure */ 4654a279ff1SOleg Nesterov fill_tgid_exit(tsk); 4664a279ff1SOleg Nesterov } 4674a279ff1SOleg Nesterov 468115085eaSOleg Nesterov listeners = &__raw_get_cpu_var(listener_array); 469115085eaSOleg Nesterov if (list_empty(&listeners->list)) 470115085eaSOleg Nesterov return; 471115085eaSOleg Nesterov 472115085eaSOleg Nesterov tidstats = kmem_cache_zalloc(taskstats_cache, GFP_KERNEL); 4734a279ff1SOleg Nesterov if (!tidstats) 4744a279ff1SOleg Nesterov return; 475c757249aSShailabh Nagar 476c757249aSShailabh Nagar rc = prepare_reply(NULL, TASKSTATS_CMD_NEW, &rep_skb, &reply, size); 477c757249aSShailabh Nagar if (rc < 0) 478115085eaSOleg Nesterov goto free_stats; 479c757249aSShailabh Nagar 480c757249aSShailabh Nagar rc = fill_pid(tsk->pid, tsk, tidstats); 481c757249aSShailabh Nagar if (rc < 0) 482c757249aSShailabh Nagar goto err_skb; 483c757249aSShailabh Nagar 484c757249aSShailabh Nagar na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_PID); 485c757249aSShailabh Nagar NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_PID, (u32)tsk->pid); 486c757249aSShailabh Nagar NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, 487c757249aSShailabh Nagar *tidstats); 488c757249aSShailabh Nagar nla_nest_end(rep_skb, na); 489c757249aSShailabh Nagar 490ad4ecbcbSShailabh Nagar if (!is_thread_group) 491ad4ecbcbSShailabh Nagar goto send; 492c757249aSShailabh Nagar 493c757249aSShailabh Nagar /* 494ad4ecbcbSShailabh Nagar * Doesn't matter if tsk is the leader or the last group member leaving 495c757249aSShailabh Nagar */ 496ad4ecbcbSShailabh Nagar if (!group_dead) 497ad4ecbcbSShailabh Nagar goto send; 498c757249aSShailabh Nagar 499c757249aSShailabh Nagar na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_TGID); 500c757249aSShailabh Nagar NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_TGID, (u32)tsk->tgid); 501ad4ecbcbSShailabh Nagar /* No locking needed for tsk->signal->stats since group is dead */ 502c757249aSShailabh Nagar NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, 503ad4ecbcbSShailabh Nagar *tsk->signal->stats); 504c757249aSShailabh Nagar nla_nest_end(rep_skb, na); 505c757249aSShailabh Nagar 506ad4ecbcbSShailabh Nagar send: 507115085eaSOleg Nesterov send_cpu_listeners(rep_skb, listeners); 508115085eaSOleg Nesterov free_stats: 509115085eaSOleg Nesterov kmem_cache_free(taskstats_cache, tidstats); 510ad4ecbcbSShailabh Nagar return; 511c757249aSShailabh Nagar 512c757249aSShailabh Nagar nla_put_failure: 513c757249aSShailabh Nagar genlmsg_cancel(rep_skb, reply); 514c757249aSShailabh Nagar err_skb: 515c757249aSShailabh Nagar nlmsg_free(rep_skb); 516115085eaSOleg Nesterov goto free_stats; 517c757249aSShailabh Nagar } 518c757249aSShailabh Nagar 519c757249aSShailabh Nagar static struct genl_ops taskstats_ops = { 520c757249aSShailabh Nagar .cmd = TASKSTATS_CMD_GET, 521f9fd8914SShailabh Nagar .doit = taskstats_user_cmd, 522c757249aSShailabh Nagar .policy = taskstats_cmd_get_policy, 523c757249aSShailabh Nagar }; 524c757249aSShailabh Nagar 525c757249aSShailabh Nagar /* Needed early in initialization */ 526c757249aSShailabh Nagar void __init taskstats_init_early(void) 527c757249aSShailabh Nagar { 528f9fd8914SShailabh Nagar unsigned int i; 529f9fd8914SShailabh Nagar 530c757249aSShailabh Nagar taskstats_cache = kmem_cache_create("taskstats_cache", 531c757249aSShailabh Nagar sizeof(struct taskstats), 532c757249aSShailabh Nagar 0, SLAB_PANIC, NULL, NULL); 533f9fd8914SShailabh Nagar for_each_possible_cpu(i) { 534f9fd8914SShailabh Nagar INIT_LIST_HEAD(&(per_cpu(listener_array, i).list)); 535f9fd8914SShailabh Nagar init_rwsem(&(per_cpu(listener_array, i).sem)); 536f9fd8914SShailabh Nagar } 537c757249aSShailabh Nagar } 538c757249aSShailabh Nagar 539c757249aSShailabh Nagar static int __init taskstats_init(void) 540c757249aSShailabh Nagar { 541c757249aSShailabh Nagar int rc; 542c757249aSShailabh Nagar 543c757249aSShailabh Nagar rc = genl_register_family(&family); 544c757249aSShailabh Nagar if (rc) 545c757249aSShailabh Nagar return rc; 546c757249aSShailabh Nagar 547c757249aSShailabh Nagar rc = genl_register_ops(&family, &taskstats_ops); 548c757249aSShailabh Nagar if (rc < 0) 549c757249aSShailabh Nagar goto err; 550c757249aSShailabh Nagar 551c757249aSShailabh Nagar family_registered = 1; 552c757249aSShailabh Nagar return 0; 553c757249aSShailabh Nagar err: 554c757249aSShailabh Nagar genl_unregister_family(&family); 555c757249aSShailabh Nagar return rc; 556c757249aSShailabh Nagar } 557c757249aSShailabh Nagar 558c757249aSShailabh Nagar /* 559c757249aSShailabh Nagar * late initcall ensures initialization of statistics collection 560c757249aSShailabh Nagar * mechanisms precedes initialization of the taskstats interface 561c757249aSShailabh Nagar */ 562c757249aSShailabh Nagar late_initcall(taskstats_init); 563