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> 23f9fd8914SShailabh Nagar #include <linux/cpumask.h> 24f9fd8914SShailabh Nagar #include <linux/percpu.h> 25846c7bb0SBalbir Singh #include <linux/cgroupstats.h> 26846c7bb0SBalbir Singh #include <linux/cgroup.h> 27846c7bb0SBalbir Singh #include <linux/fs.h> 28846c7bb0SBalbir Singh #include <linux/file.h> 29c757249aSShailabh Nagar #include <net/genetlink.h> 30c757249aSShailabh Nagar #include <asm/atomic.h> 31c757249aSShailabh Nagar 32f9fd8914SShailabh Nagar /* 33f9fd8914SShailabh Nagar * Maximum length of a cpumask that can be specified in 34f9fd8914SShailabh Nagar * the TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK attribute 35f9fd8914SShailabh Nagar */ 36f9fd8914SShailabh Nagar #define TASKSTATS_CPUMASK_MAXLEN (100+6*NR_CPUS) 37f9fd8914SShailabh Nagar 38c757249aSShailabh Nagar static DEFINE_PER_CPU(__u32, taskstats_seqnum) = { 0 }; 39c757249aSShailabh Nagar static int family_registered; 40e18b890bSChristoph Lameter struct kmem_cache *taskstats_cache; 41c757249aSShailabh Nagar 42c757249aSShailabh Nagar static struct genl_family family = { 43c757249aSShailabh Nagar .id = GENL_ID_GENERATE, 44c757249aSShailabh Nagar .name = TASKSTATS_GENL_NAME, 45c757249aSShailabh Nagar .version = TASKSTATS_GENL_VERSION, 46c757249aSShailabh Nagar .maxattr = TASKSTATS_CMD_ATTR_MAX, 47c757249aSShailabh Nagar }; 48c757249aSShailabh Nagar 49c757249aSShailabh Nagar static struct nla_policy taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] 50c757249aSShailabh Nagar __read_mostly = { 51c757249aSShailabh Nagar [TASKSTATS_CMD_ATTR_PID] = { .type = NLA_U32 }, 52c757249aSShailabh Nagar [TASKSTATS_CMD_ATTR_TGID] = { .type = NLA_U32 }, 53f9fd8914SShailabh Nagar [TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING }, 54f9fd8914SShailabh Nagar [TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },}; 55f9fd8914SShailabh Nagar 56846c7bb0SBalbir Singh static struct nla_policy 57846c7bb0SBalbir Singh cgroupstats_cmd_get_policy[CGROUPSTATS_CMD_ATTR_MAX+1] __read_mostly = { 58846c7bb0SBalbir Singh [CGROUPSTATS_CMD_ATTR_FD] = { .type = NLA_U32 }, 59846c7bb0SBalbir Singh }; 60846c7bb0SBalbir Singh 61f9fd8914SShailabh Nagar struct listener { 62f9fd8914SShailabh Nagar struct list_head list; 63f9fd8914SShailabh Nagar pid_t pid; 64bb129994SShailabh Nagar char valid; 65c757249aSShailabh Nagar }; 66c757249aSShailabh Nagar 67f9fd8914SShailabh Nagar struct listener_list { 68f9fd8914SShailabh Nagar struct rw_semaphore sem; 69f9fd8914SShailabh Nagar struct list_head list; 70f9fd8914SShailabh Nagar }; 71f9fd8914SShailabh Nagar static DEFINE_PER_CPU(struct listener_list, listener_array); 72f9fd8914SShailabh Nagar 73f9fd8914SShailabh Nagar enum actions { 74f9fd8914SShailabh Nagar REGISTER, 75f9fd8914SShailabh Nagar DEREGISTER, 76f9fd8914SShailabh Nagar CPU_DONT_CARE 77f9fd8914SShailabh Nagar }; 78c757249aSShailabh Nagar 79c757249aSShailabh Nagar static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp, 8037167485SOleg Nesterov size_t size) 81c757249aSShailabh Nagar { 82c757249aSShailabh Nagar struct sk_buff *skb; 83c757249aSShailabh Nagar void *reply; 84c757249aSShailabh Nagar 85c757249aSShailabh Nagar /* 86c757249aSShailabh Nagar * If new attributes are added, please revisit this allocation 87c757249aSShailabh Nagar */ 883dabc715SThomas Graf skb = genlmsg_new(size, GFP_KERNEL); 89c757249aSShailabh Nagar if (!skb) 90c757249aSShailabh Nagar return -ENOMEM; 91c757249aSShailabh Nagar 92c757249aSShailabh Nagar if (!info) { 93c757249aSShailabh Nagar int seq = get_cpu_var(taskstats_seqnum)++; 94c757249aSShailabh Nagar put_cpu_var(taskstats_seqnum); 95c757249aSShailabh Nagar 9617c157c8SThomas Graf reply = genlmsg_put(skb, 0, seq, &family, 0, cmd); 97c757249aSShailabh Nagar } else 9817c157c8SThomas Graf reply = genlmsg_put_reply(skb, info, &family, 0, cmd); 99c757249aSShailabh Nagar if (reply == NULL) { 100c757249aSShailabh Nagar nlmsg_free(skb); 101c757249aSShailabh Nagar return -EINVAL; 102c757249aSShailabh Nagar } 103c757249aSShailabh Nagar 104c757249aSShailabh Nagar *skbp = skb; 105c757249aSShailabh Nagar return 0; 106c757249aSShailabh Nagar } 107c757249aSShailabh Nagar 108f9fd8914SShailabh Nagar /* 109f9fd8914SShailabh Nagar * Send taskstats data in @skb to listener with nl_pid @pid 110f9fd8914SShailabh Nagar */ 111f9fd8914SShailabh Nagar static int send_reply(struct sk_buff *skb, pid_t pid) 112c757249aSShailabh Nagar { 113b529ccf2SArnaldo Carvalho de Melo struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb)); 114f9fd8914SShailabh Nagar void *reply = genlmsg_data(genlhdr); 115c757249aSShailabh Nagar int rc; 116c757249aSShailabh Nagar 117c757249aSShailabh Nagar rc = genlmsg_end(skb, reply); 118c757249aSShailabh Nagar if (rc < 0) { 119c757249aSShailabh Nagar nlmsg_free(skb); 120c757249aSShailabh Nagar return rc; 121c757249aSShailabh Nagar } 122c757249aSShailabh Nagar 123c757249aSShailabh Nagar return genlmsg_unicast(skb, pid); 124c757249aSShailabh Nagar } 125c757249aSShailabh Nagar 126f9fd8914SShailabh Nagar /* 127f9fd8914SShailabh Nagar * Send taskstats data in @skb to listeners registered for @cpu's exit data 128f9fd8914SShailabh Nagar */ 129115085eaSOleg Nesterov static void send_cpu_listeners(struct sk_buff *skb, 130115085eaSOleg Nesterov struct listener_list *listeners) 131f9fd8914SShailabh Nagar { 132b529ccf2SArnaldo Carvalho de Melo struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb)); 133f9fd8914SShailabh Nagar struct listener *s, *tmp; 134f9fd8914SShailabh Nagar struct sk_buff *skb_next, *skb_cur = skb; 135f9fd8914SShailabh Nagar void *reply = genlmsg_data(genlhdr); 136d94a0415SShailabh Nagar int rc, delcount = 0; 137f9fd8914SShailabh Nagar 138f9fd8914SShailabh Nagar rc = genlmsg_end(skb, reply); 139f9fd8914SShailabh Nagar if (rc < 0) { 140f9fd8914SShailabh Nagar nlmsg_free(skb); 141d94a0415SShailabh Nagar return; 142f9fd8914SShailabh Nagar } 143f9fd8914SShailabh Nagar 144f9fd8914SShailabh Nagar rc = 0; 145bb129994SShailabh Nagar down_read(&listeners->sem); 146d94a0415SShailabh Nagar list_for_each_entry(s, &listeners->list, list) { 147f9fd8914SShailabh Nagar skb_next = NULL; 148f9fd8914SShailabh Nagar if (!list_is_last(&s->list, &listeners->list)) { 149f9fd8914SShailabh Nagar skb_next = skb_clone(skb_cur, GFP_KERNEL); 150d94a0415SShailabh Nagar if (!skb_next) 151f9fd8914SShailabh Nagar break; 152f9fd8914SShailabh Nagar } 153d94a0415SShailabh Nagar rc = genlmsg_unicast(skb_cur, s->pid); 154d94a0415SShailabh Nagar if (rc == -ECONNREFUSED) { 155bb129994SShailabh Nagar s->valid = 0; 156bb129994SShailabh Nagar delcount++; 157f9fd8914SShailabh Nagar } 158f9fd8914SShailabh Nagar skb_cur = skb_next; 159f9fd8914SShailabh Nagar } 160bb129994SShailabh Nagar up_read(&listeners->sem); 161f9fd8914SShailabh Nagar 162d94a0415SShailabh Nagar if (skb_cur) 163d94a0415SShailabh Nagar nlmsg_free(skb_cur); 164d94a0415SShailabh Nagar 165bb129994SShailabh Nagar if (!delcount) 166d94a0415SShailabh Nagar return; 167bb129994SShailabh Nagar 168bb129994SShailabh Nagar /* Delete invalidated entries */ 169bb129994SShailabh Nagar down_write(&listeners->sem); 170bb129994SShailabh Nagar list_for_each_entry_safe(s, tmp, &listeners->list, list) { 171bb129994SShailabh Nagar if (!s->valid) { 172bb129994SShailabh Nagar list_del(&s->list); 173bb129994SShailabh Nagar kfree(s); 174bb129994SShailabh Nagar } 175bb129994SShailabh Nagar } 176bb129994SShailabh Nagar up_write(&listeners->sem); 177f9fd8914SShailabh Nagar } 178f9fd8914SShailabh Nagar 179a98b6094SOleg Nesterov static int fill_pid(pid_t pid, struct task_struct *tsk, 180c757249aSShailabh Nagar struct taskstats *stats) 181c757249aSShailabh Nagar { 1827d94ddddSShailabh Nagar int rc = 0; 183c757249aSShailabh Nagar 184c757249aSShailabh Nagar if (!tsk) { 185a98b6094SOleg Nesterov rcu_read_lock(); 186a98b6094SOleg Nesterov tsk = find_task_by_pid(pid); 187a98b6094SOleg Nesterov if (tsk) 188c757249aSShailabh Nagar get_task_struct(tsk); 189a98b6094SOleg Nesterov rcu_read_unlock(); 190a98b6094SOleg Nesterov if (!tsk) 191a98b6094SOleg Nesterov return -ESRCH; 192c757249aSShailabh Nagar } else 193c757249aSShailabh Nagar get_task_struct(tsk); 194c757249aSShailabh Nagar 19551de4d90SOleg Nesterov memset(stats, 0, sizeof(*stats)); 196c757249aSShailabh Nagar /* 197c757249aSShailabh Nagar * Each accounting subsystem adds calls to its functions to 198c757249aSShailabh Nagar * fill in relevant parts of struct taskstsats as follows 199c757249aSShailabh Nagar * 2007d94ddddSShailabh Nagar * per-task-foo(stats, tsk); 201c757249aSShailabh Nagar */ 202c757249aSShailabh Nagar 2037d94ddddSShailabh Nagar delayacct_add_tsk(stats, tsk); 204f3cef7a9SJay Lan 205f3cef7a9SJay Lan /* fill in basic acct fields */ 2066f44993fSShailabh Nagar stats->version = TASKSTATS_VERSION; 207b663a79cSMaxim Uvarov stats->nvcsw = tsk->nvcsw; 208b663a79cSMaxim Uvarov stats->nivcsw = tsk->nivcsw; 209f3cef7a9SJay Lan bacct_add_tsk(stats, tsk); 2106f44993fSShailabh Nagar 2119acc1853SJay Lan /* fill in extended acct fields */ 2129acc1853SJay Lan xacct_add_tsk(stats, tsk); 2139acc1853SJay Lan 2146f44993fSShailabh Nagar /* Define err: label here if needed */ 215c757249aSShailabh Nagar put_task_struct(tsk); 216c757249aSShailabh Nagar return rc; 217c757249aSShailabh Nagar 218c757249aSShailabh Nagar } 219c757249aSShailabh Nagar 220a98b6094SOleg Nesterov static int fill_tgid(pid_t tgid, struct task_struct *first, 221c757249aSShailabh Nagar struct taskstats *stats) 222c757249aSShailabh Nagar { 223a98b6094SOleg Nesterov struct task_struct *tsk; 224ad4ecbcbSShailabh Nagar unsigned long flags; 225a98b6094SOleg Nesterov int rc = -ESRCH; 226c757249aSShailabh Nagar 227ad4ecbcbSShailabh Nagar /* 228ad4ecbcbSShailabh Nagar * Add additional stats from live tasks except zombie thread group 229ad4ecbcbSShailabh Nagar * leaders who are already counted with the dead tasks 230ad4ecbcbSShailabh Nagar */ 231a98b6094SOleg Nesterov rcu_read_lock(); 232a98b6094SOleg Nesterov if (!first) 233c757249aSShailabh Nagar first = find_task_by_pid(tgid); 234ad4ecbcbSShailabh Nagar 235a98b6094SOleg Nesterov if (!first || !lock_task_sighand(first, &flags)) 236a98b6094SOleg Nesterov goto out; 237fca178c0SOleg Nesterov 238ad4ecbcbSShailabh Nagar if (first->signal->stats) 239ad4ecbcbSShailabh Nagar memcpy(stats, first->signal->stats, sizeof(*stats)); 24051de4d90SOleg Nesterov else 24151de4d90SOleg Nesterov memset(stats, 0, sizeof(*stats)); 242ad4ecbcbSShailabh Nagar 243a98b6094SOleg Nesterov tsk = first; 244c757249aSShailabh Nagar do { 245d7c3f5f2SOleg Nesterov if (tsk->exit_state) 246ad4ecbcbSShailabh Nagar continue; 247c757249aSShailabh Nagar /* 248ad4ecbcbSShailabh Nagar * Accounting subsystem can call its functions here to 249c757249aSShailabh Nagar * fill in relevant parts of struct taskstsats as follows 250c757249aSShailabh Nagar * 251ad4ecbcbSShailabh Nagar * per-task-foo(stats, tsk); 252c757249aSShailabh Nagar */ 253ad4ecbcbSShailabh Nagar delayacct_add_tsk(stats, tsk); 2546f44993fSShailabh Nagar 255b663a79cSMaxim Uvarov stats->nvcsw += tsk->nvcsw; 256b663a79cSMaxim Uvarov stats->nivcsw += tsk->nivcsw; 257c757249aSShailabh Nagar } while_each_thread(first, tsk); 2586f44993fSShailabh Nagar 259a98b6094SOleg Nesterov unlock_task_sighand(first, &flags); 260a98b6094SOleg Nesterov rc = 0; 261a98b6094SOleg Nesterov out: 262a98b6094SOleg Nesterov rcu_read_unlock(); 263a98b6094SOleg Nesterov 264a98b6094SOleg Nesterov stats->version = TASKSTATS_VERSION; 265c757249aSShailabh Nagar /* 2663a4fa0a2SRobert P. J. Day * Accounting subsystems can also add calls here to modify 267ad4ecbcbSShailabh Nagar * fields of taskstats. 268c757249aSShailabh Nagar */ 269a98b6094SOleg Nesterov return rc; 270c757249aSShailabh Nagar } 271c757249aSShailabh Nagar 272ad4ecbcbSShailabh Nagar 273ad4ecbcbSShailabh Nagar static void fill_tgid_exit(struct task_struct *tsk) 274ad4ecbcbSShailabh Nagar { 275ad4ecbcbSShailabh Nagar unsigned long flags; 276ad4ecbcbSShailabh Nagar 277b8534d7bSOleg Nesterov spin_lock_irqsave(&tsk->sighand->siglock, flags); 278ad4ecbcbSShailabh Nagar if (!tsk->signal->stats) 279ad4ecbcbSShailabh Nagar goto ret; 280ad4ecbcbSShailabh Nagar 281ad4ecbcbSShailabh Nagar /* 282ad4ecbcbSShailabh Nagar * Each accounting subsystem calls its functions here to 283ad4ecbcbSShailabh Nagar * accumalate its per-task stats for tsk, into the per-tgid structure 284ad4ecbcbSShailabh Nagar * 285ad4ecbcbSShailabh Nagar * per-task-foo(tsk->signal->stats, tsk); 286ad4ecbcbSShailabh Nagar */ 287ad4ecbcbSShailabh Nagar delayacct_add_tsk(tsk->signal->stats, tsk); 288ad4ecbcbSShailabh Nagar ret: 289b8534d7bSOleg Nesterov spin_unlock_irqrestore(&tsk->sighand->siglock, flags); 290ad4ecbcbSShailabh Nagar return; 291ad4ecbcbSShailabh Nagar } 292ad4ecbcbSShailabh Nagar 293f9fd8914SShailabh Nagar static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd) 294f9fd8914SShailabh Nagar { 295f9fd8914SShailabh Nagar struct listener_list *listeners; 296f9fd8914SShailabh Nagar struct listener *s, *tmp; 297f9fd8914SShailabh Nagar unsigned int cpu; 298f9fd8914SShailabh Nagar cpumask_t mask = *maskp; 299ad4ecbcbSShailabh Nagar 300f9fd8914SShailabh Nagar if (!cpus_subset(mask, cpu_possible_map)) 301f9fd8914SShailabh Nagar return -EINVAL; 302f9fd8914SShailabh Nagar 303f9fd8914SShailabh Nagar if (isadd == REGISTER) { 304f9fd8914SShailabh Nagar for_each_cpu_mask(cpu, mask) { 305f9fd8914SShailabh Nagar s = kmalloc_node(sizeof(struct listener), GFP_KERNEL, 306f9fd8914SShailabh Nagar cpu_to_node(cpu)); 307f9fd8914SShailabh Nagar if (!s) 308f9fd8914SShailabh Nagar goto cleanup; 309f9fd8914SShailabh Nagar s->pid = pid; 310f9fd8914SShailabh Nagar INIT_LIST_HEAD(&s->list); 311bb129994SShailabh Nagar s->valid = 1; 312f9fd8914SShailabh Nagar 313f9fd8914SShailabh Nagar listeners = &per_cpu(listener_array, cpu); 314f9fd8914SShailabh Nagar down_write(&listeners->sem); 315f9fd8914SShailabh Nagar list_add(&s->list, &listeners->list); 316f9fd8914SShailabh Nagar up_write(&listeners->sem); 317f9fd8914SShailabh Nagar } 318f9fd8914SShailabh Nagar return 0; 319f9fd8914SShailabh Nagar } 320f9fd8914SShailabh Nagar 321f9fd8914SShailabh Nagar /* Deregister or cleanup */ 322f9fd8914SShailabh Nagar cleanup: 323f9fd8914SShailabh Nagar for_each_cpu_mask(cpu, mask) { 324f9fd8914SShailabh Nagar listeners = &per_cpu(listener_array, cpu); 325f9fd8914SShailabh Nagar down_write(&listeners->sem); 326f9fd8914SShailabh Nagar list_for_each_entry_safe(s, tmp, &listeners->list, list) { 327f9fd8914SShailabh Nagar if (s->pid == pid) { 328f9fd8914SShailabh Nagar list_del(&s->list); 329f9fd8914SShailabh Nagar kfree(s); 330f9fd8914SShailabh Nagar break; 331f9fd8914SShailabh Nagar } 332f9fd8914SShailabh Nagar } 333f9fd8914SShailabh Nagar up_write(&listeners->sem); 334f9fd8914SShailabh Nagar } 335f9fd8914SShailabh Nagar return 0; 336f9fd8914SShailabh Nagar } 337f9fd8914SShailabh Nagar 338f9fd8914SShailabh Nagar static int parse(struct nlattr *na, cpumask_t *mask) 339f9fd8914SShailabh Nagar { 340f9fd8914SShailabh Nagar char *data; 341f9fd8914SShailabh Nagar int len; 342f9fd8914SShailabh Nagar int ret; 343f9fd8914SShailabh Nagar 344f9fd8914SShailabh Nagar if (na == NULL) 345f9fd8914SShailabh Nagar return 1; 346f9fd8914SShailabh Nagar len = nla_len(na); 347f9fd8914SShailabh Nagar if (len > TASKSTATS_CPUMASK_MAXLEN) 348f9fd8914SShailabh Nagar return -E2BIG; 349f9fd8914SShailabh Nagar if (len < 1) 350f9fd8914SShailabh Nagar return -EINVAL; 351f9fd8914SShailabh Nagar data = kmalloc(len, GFP_KERNEL); 352f9fd8914SShailabh Nagar if (!data) 353f9fd8914SShailabh Nagar return -ENOMEM; 354f9fd8914SShailabh Nagar nla_strlcpy(data, na, len); 355f9fd8914SShailabh Nagar ret = cpulist_parse(data, *mask); 356f9fd8914SShailabh Nagar kfree(data); 357f9fd8914SShailabh Nagar return ret; 358f9fd8914SShailabh Nagar } 359f9fd8914SShailabh Nagar 36051de4d90SOleg Nesterov static struct taskstats *mk_reply(struct sk_buff *skb, int type, u32 pid) 36168062b86SOleg Nesterov { 36251de4d90SOleg Nesterov struct nlattr *na, *ret; 36368062b86SOleg Nesterov int aggr; 36468062b86SOleg Nesterov 36537167485SOleg Nesterov aggr = (type == TASKSTATS_TYPE_PID) 36637167485SOleg Nesterov ? TASKSTATS_TYPE_AGGR_PID 36737167485SOleg Nesterov : TASKSTATS_TYPE_AGGR_TGID; 36868062b86SOleg Nesterov 36968062b86SOleg Nesterov na = nla_nest_start(skb, aggr); 37037167485SOleg Nesterov if (!na) 37137167485SOleg Nesterov goto err; 37251de4d90SOleg Nesterov if (nla_put(skb, type, sizeof(pid), &pid) < 0) 37351de4d90SOleg Nesterov goto err; 37451de4d90SOleg Nesterov ret = nla_reserve(skb, TASKSTATS_TYPE_STATS, sizeof(struct taskstats)); 37551de4d90SOleg Nesterov if (!ret) 37651de4d90SOleg Nesterov goto err; 37768062b86SOleg Nesterov nla_nest_end(skb, na); 37868062b86SOleg Nesterov 37951de4d90SOleg Nesterov return nla_data(ret); 38051de4d90SOleg Nesterov err: 38151de4d90SOleg Nesterov return NULL; 38268062b86SOleg Nesterov } 38368062b86SOleg Nesterov 384846c7bb0SBalbir Singh static int cgroupstats_user_cmd(struct sk_buff *skb, struct genl_info *info) 385846c7bb0SBalbir Singh { 386846c7bb0SBalbir Singh int rc = 0; 387846c7bb0SBalbir Singh struct sk_buff *rep_skb; 388846c7bb0SBalbir Singh struct cgroupstats *stats; 389846c7bb0SBalbir Singh struct nlattr *na; 390846c7bb0SBalbir Singh size_t size; 391846c7bb0SBalbir Singh u32 fd; 392846c7bb0SBalbir Singh struct file *file; 393846c7bb0SBalbir Singh int fput_needed; 394846c7bb0SBalbir Singh 395846c7bb0SBalbir Singh na = info->attrs[CGROUPSTATS_CMD_ATTR_FD]; 396846c7bb0SBalbir Singh if (!na) 397846c7bb0SBalbir Singh return -EINVAL; 398846c7bb0SBalbir Singh 399846c7bb0SBalbir Singh fd = nla_get_u32(info->attrs[CGROUPSTATS_CMD_ATTR_FD]); 400846c7bb0SBalbir Singh file = fget_light(fd, &fput_needed); 401*f9615984SAdrian Bunk if (!file) 402*f9615984SAdrian Bunk return 0; 403*f9615984SAdrian Bunk 404846c7bb0SBalbir Singh size = nla_total_size(sizeof(struct cgroupstats)); 405846c7bb0SBalbir Singh 406846c7bb0SBalbir Singh rc = prepare_reply(info, CGROUPSTATS_CMD_NEW, &rep_skb, 407846c7bb0SBalbir Singh size); 408846c7bb0SBalbir Singh if (rc < 0) 409846c7bb0SBalbir Singh goto err; 410846c7bb0SBalbir Singh 411846c7bb0SBalbir Singh na = nla_reserve(rep_skb, CGROUPSTATS_TYPE_CGROUP_STATS, 412846c7bb0SBalbir Singh sizeof(struct cgroupstats)); 413846c7bb0SBalbir Singh stats = nla_data(na); 414846c7bb0SBalbir Singh memset(stats, 0, sizeof(*stats)); 415846c7bb0SBalbir Singh 416846c7bb0SBalbir Singh rc = cgroupstats_build(stats, file->f_dentry); 417*f9615984SAdrian Bunk if (rc < 0) { 418*f9615984SAdrian Bunk nlmsg_free(rep_skb); 419846c7bb0SBalbir Singh goto err; 420846c7bb0SBalbir Singh } 421846c7bb0SBalbir Singh 422*f9615984SAdrian Bunk rc = send_reply(rep_skb, info->snd_pid); 423*f9615984SAdrian Bunk 424846c7bb0SBalbir Singh err: 425846c7bb0SBalbir Singh fput_light(file, fput_needed); 426846c7bb0SBalbir Singh return rc; 427846c7bb0SBalbir Singh } 428846c7bb0SBalbir Singh 429f9fd8914SShailabh Nagar static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) 430c757249aSShailabh Nagar { 431c757249aSShailabh Nagar int rc = 0; 432c757249aSShailabh Nagar struct sk_buff *rep_skb; 43351de4d90SOleg Nesterov struct taskstats *stats; 434c757249aSShailabh Nagar size_t size; 435f9fd8914SShailabh Nagar cpumask_t mask; 436f9fd8914SShailabh Nagar 437f9fd8914SShailabh Nagar rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], &mask); 438f9fd8914SShailabh Nagar if (rc < 0) 439f9fd8914SShailabh Nagar return rc; 440f9fd8914SShailabh Nagar if (rc == 0) 441f9fd8914SShailabh Nagar return add_del_listener(info->snd_pid, &mask, REGISTER); 442f9fd8914SShailabh Nagar 443f9fd8914SShailabh Nagar rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], &mask); 444f9fd8914SShailabh Nagar if (rc < 0) 445f9fd8914SShailabh Nagar return rc; 446f9fd8914SShailabh Nagar if (rc == 0) 447f9fd8914SShailabh Nagar return add_del_listener(info->snd_pid, &mask, DEREGISTER); 448c757249aSShailabh Nagar 449c757249aSShailabh Nagar /* 450c757249aSShailabh Nagar * Size includes space for nested attributes 451c757249aSShailabh Nagar */ 452c757249aSShailabh Nagar size = nla_total_size(sizeof(u32)) + 453c757249aSShailabh Nagar nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); 454c757249aSShailabh Nagar 45537167485SOleg Nesterov rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); 456c757249aSShailabh Nagar if (rc < 0) 457c757249aSShailabh Nagar return rc; 458c757249aSShailabh Nagar 45951de4d90SOleg Nesterov rc = -EINVAL; 460c757249aSShailabh Nagar if (info->attrs[TASKSTATS_CMD_ATTR_PID]) { 461c757249aSShailabh Nagar u32 pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); 46251de4d90SOleg Nesterov stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid); 46351de4d90SOleg Nesterov if (!stats) 46437167485SOleg Nesterov goto err; 465c757249aSShailabh Nagar 46651de4d90SOleg Nesterov rc = fill_pid(pid, NULL, stats); 46751de4d90SOleg Nesterov if (rc < 0) 46837167485SOleg Nesterov goto err; 469c757249aSShailabh Nagar } else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) { 470c757249aSShailabh Nagar u32 tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); 47151de4d90SOleg Nesterov stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid); 47251de4d90SOleg Nesterov if (!stats) 47337167485SOleg Nesterov goto err; 474c757249aSShailabh Nagar 47551de4d90SOleg Nesterov rc = fill_tgid(tgid, NULL, stats); 47651de4d90SOleg Nesterov if (rc < 0) 47737167485SOleg Nesterov goto err; 47851de4d90SOleg Nesterov } else 479c757249aSShailabh Nagar goto err; 480c757249aSShailabh Nagar 481f9fd8914SShailabh Nagar return send_reply(rep_skb, info->snd_pid); 482c757249aSShailabh Nagar err: 483c757249aSShailabh Nagar nlmsg_free(rep_skb); 484c757249aSShailabh Nagar return rc; 485c757249aSShailabh Nagar } 486c757249aSShailabh Nagar 48734ec1234SOleg Nesterov static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk) 48834ec1234SOleg Nesterov { 48934ec1234SOleg Nesterov struct signal_struct *sig = tsk->signal; 49034ec1234SOleg Nesterov struct taskstats *stats; 49134ec1234SOleg Nesterov 49234ec1234SOleg Nesterov if (sig->stats || thread_group_empty(tsk)) 49334ec1234SOleg Nesterov goto ret; 49434ec1234SOleg Nesterov 49534ec1234SOleg Nesterov /* No problem if kmem_cache_zalloc() fails */ 49634ec1234SOleg Nesterov stats = kmem_cache_zalloc(taskstats_cache, GFP_KERNEL); 49734ec1234SOleg Nesterov 49834ec1234SOleg Nesterov spin_lock_irq(&tsk->sighand->siglock); 49934ec1234SOleg Nesterov if (!sig->stats) { 50034ec1234SOleg Nesterov sig->stats = stats; 50134ec1234SOleg Nesterov stats = NULL; 50234ec1234SOleg Nesterov } 50334ec1234SOleg Nesterov spin_unlock_irq(&tsk->sighand->siglock); 50434ec1234SOleg Nesterov 50534ec1234SOleg Nesterov if (stats) 50634ec1234SOleg Nesterov kmem_cache_free(taskstats_cache, stats); 50734ec1234SOleg Nesterov ret: 50834ec1234SOleg Nesterov return sig->stats; 50934ec1234SOleg Nesterov } 51034ec1234SOleg Nesterov 511c757249aSShailabh Nagar /* Send pid data out on exit */ 512115085eaSOleg Nesterov void taskstats_exit(struct task_struct *tsk, int group_dead) 513c757249aSShailabh Nagar { 514c757249aSShailabh Nagar int rc; 515115085eaSOleg Nesterov struct listener_list *listeners; 51651de4d90SOleg Nesterov struct taskstats *stats; 517c757249aSShailabh Nagar struct sk_buff *rep_skb; 518c757249aSShailabh Nagar size_t size; 519c757249aSShailabh Nagar int is_thread_group; 520c757249aSShailabh Nagar 5214a279ff1SOleg Nesterov if (!family_registered) 522c757249aSShailabh Nagar return; 523c757249aSShailabh Nagar 524c757249aSShailabh Nagar /* 525c757249aSShailabh Nagar * Size includes space for nested attributes 526c757249aSShailabh Nagar */ 527c757249aSShailabh Nagar size = nla_total_size(sizeof(u32)) + 528c757249aSShailabh Nagar nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); 529c757249aSShailabh Nagar 53034ec1234SOleg Nesterov is_thread_group = !!taskstats_tgid_alloc(tsk); 5314a279ff1SOleg Nesterov if (is_thread_group) { 5324a279ff1SOleg Nesterov /* PID + STATS + TGID + STATS */ 5334a279ff1SOleg Nesterov size = 2 * size; 5344a279ff1SOleg Nesterov /* fill the tsk->signal->stats structure */ 5354a279ff1SOleg Nesterov fill_tgid_exit(tsk); 5364a279ff1SOleg Nesterov } 5374a279ff1SOleg Nesterov 538115085eaSOleg Nesterov listeners = &__raw_get_cpu_var(listener_array); 539115085eaSOleg Nesterov if (list_empty(&listeners->list)) 540115085eaSOleg Nesterov return; 541115085eaSOleg Nesterov 54237167485SOleg Nesterov rc = prepare_reply(NULL, TASKSTATS_CMD_NEW, &rep_skb, size); 543c757249aSShailabh Nagar if (rc < 0) 54451de4d90SOleg Nesterov return; 545c757249aSShailabh Nagar 54651de4d90SOleg Nesterov stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, tsk->pid); 54751de4d90SOleg Nesterov if (!stats) 54837167485SOleg Nesterov goto err; 54951de4d90SOleg Nesterov 55051de4d90SOleg Nesterov rc = fill_pid(tsk->pid, tsk, stats); 551c757249aSShailabh Nagar if (rc < 0) 55237167485SOleg Nesterov goto err; 553c757249aSShailabh Nagar 554c757249aSShailabh Nagar /* 555ad4ecbcbSShailabh Nagar * Doesn't matter if tsk is the leader or the last group member leaving 556c757249aSShailabh Nagar */ 55768062b86SOleg Nesterov if (!is_thread_group || !group_dead) 558ad4ecbcbSShailabh Nagar goto send; 559c757249aSShailabh Nagar 56051de4d90SOleg Nesterov stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tsk->tgid); 56151de4d90SOleg Nesterov if (!stats) 56237167485SOleg Nesterov goto err; 56351de4d90SOleg Nesterov 56451de4d90SOleg Nesterov memcpy(stats, tsk->signal->stats, sizeof(*stats)); 565c757249aSShailabh Nagar 566ad4ecbcbSShailabh Nagar send: 567115085eaSOleg Nesterov send_cpu_listeners(rep_skb, listeners); 568ad4ecbcbSShailabh Nagar return; 56937167485SOleg Nesterov err: 570c757249aSShailabh Nagar nlmsg_free(rep_skb); 571c757249aSShailabh Nagar } 572c757249aSShailabh Nagar 573c757249aSShailabh Nagar static struct genl_ops taskstats_ops = { 574c757249aSShailabh Nagar .cmd = TASKSTATS_CMD_GET, 575f9fd8914SShailabh Nagar .doit = taskstats_user_cmd, 576c757249aSShailabh Nagar .policy = taskstats_cmd_get_policy, 577c757249aSShailabh Nagar }; 578c757249aSShailabh Nagar 579846c7bb0SBalbir Singh static struct genl_ops cgroupstats_ops = { 580846c7bb0SBalbir Singh .cmd = CGROUPSTATS_CMD_GET, 581846c7bb0SBalbir Singh .doit = cgroupstats_user_cmd, 582846c7bb0SBalbir Singh .policy = cgroupstats_cmd_get_policy, 583846c7bb0SBalbir Singh }; 584846c7bb0SBalbir Singh 585c757249aSShailabh Nagar /* Needed early in initialization */ 586c757249aSShailabh Nagar void __init taskstats_init_early(void) 587c757249aSShailabh Nagar { 588f9fd8914SShailabh Nagar unsigned int i; 589f9fd8914SShailabh Nagar 5900a31bd5fSChristoph Lameter taskstats_cache = KMEM_CACHE(taskstats, SLAB_PANIC); 591f9fd8914SShailabh Nagar for_each_possible_cpu(i) { 592f9fd8914SShailabh Nagar INIT_LIST_HEAD(&(per_cpu(listener_array, i).list)); 593f9fd8914SShailabh Nagar init_rwsem(&(per_cpu(listener_array, i).sem)); 594f9fd8914SShailabh Nagar } 595c757249aSShailabh Nagar } 596c757249aSShailabh Nagar 597c757249aSShailabh Nagar static int __init taskstats_init(void) 598c757249aSShailabh Nagar { 599c757249aSShailabh Nagar int rc; 600c757249aSShailabh Nagar 601c757249aSShailabh Nagar rc = genl_register_family(&family); 602c757249aSShailabh Nagar if (rc) 603c757249aSShailabh Nagar return rc; 604c757249aSShailabh Nagar 605c757249aSShailabh Nagar rc = genl_register_ops(&family, &taskstats_ops); 606c757249aSShailabh Nagar if (rc < 0) 607c757249aSShailabh Nagar goto err; 608c757249aSShailabh Nagar 609846c7bb0SBalbir Singh rc = genl_register_ops(&family, &cgroupstats_ops); 610846c7bb0SBalbir Singh if (rc < 0) 611846c7bb0SBalbir Singh goto err_cgroup_ops; 612846c7bb0SBalbir Singh 613c757249aSShailabh Nagar family_registered = 1; 614846c7bb0SBalbir Singh printk("registered taskstats version %d\n", TASKSTATS_GENL_VERSION); 615c757249aSShailabh Nagar return 0; 616846c7bb0SBalbir Singh err_cgroup_ops: 617846c7bb0SBalbir Singh genl_unregister_ops(&family, &taskstats_ops); 618c757249aSShailabh Nagar err: 619c757249aSShailabh Nagar genl_unregister_family(&family); 620c757249aSShailabh Nagar return rc; 621c757249aSShailabh Nagar } 622c757249aSShailabh Nagar 623c757249aSShailabh Nagar /* 624c757249aSShailabh Nagar * late initcall ensures initialization of statistics collection 625c757249aSShailabh Nagar * mechanisms precedes initialization of the taskstats interface 626c757249aSShailabh Nagar */ 627c757249aSShailabh Nagar late_initcall(taskstats_init); 628