xref: /openbmc/linux/kernel/taskstats.c (revision 34ec12349c8a9505adc59d72f92b4595bc2483ff)
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