xref: /openbmc/linux/kernel/taskstats.c (revision 7d94dddd438bcba97db44f120da39bb001b5249f)
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>
216f44993fSShailabh Nagar #include <linux/delayacct.h>
22f9fd8914SShailabh Nagar #include <linux/cpumask.h>
23f9fd8914SShailabh Nagar #include <linux/percpu.h>
24c757249aSShailabh Nagar #include <net/genetlink.h>
25c757249aSShailabh Nagar #include <asm/atomic.h>
26c757249aSShailabh Nagar 
27f9fd8914SShailabh Nagar /*
28f9fd8914SShailabh Nagar  * Maximum length of a cpumask that can be specified in
29f9fd8914SShailabh Nagar  * the TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK attribute
30f9fd8914SShailabh Nagar  */
31f9fd8914SShailabh Nagar #define TASKSTATS_CPUMASK_MAXLEN	(100+6*NR_CPUS)
32f9fd8914SShailabh Nagar 
33c757249aSShailabh Nagar static DEFINE_PER_CPU(__u32, taskstats_seqnum) = { 0 };
34c757249aSShailabh Nagar static int family_registered;
35c757249aSShailabh Nagar kmem_cache_t *taskstats_cache;
36c757249aSShailabh Nagar 
37c757249aSShailabh Nagar static struct genl_family family = {
38c757249aSShailabh Nagar 	.id		= GENL_ID_GENERATE,
39c757249aSShailabh Nagar 	.name		= TASKSTATS_GENL_NAME,
40c757249aSShailabh Nagar 	.version	= TASKSTATS_GENL_VERSION,
41c757249aSShailabh Nagar 	.maxattr	= TASKSTATS_CMD_ATTR_MAX,
42c757249aSShailabh Nagar };
43c757249aSShailabh Nagar 
44c757249aSShailabh Nagar static struct nla_policy taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1]
45c757249aSShailabh Nagar __read_mostly = {
46c757249aSShailabh Nagar 	[TASKSTATS_CMD_ATTR_PID]  = { .type = NLA_U32 },
47c757249aSShailabh Nagar 	[TASKSTATS_CMD_ATTR_TGID] = { .type = NLA_U32 },
48f9fd8914SShailabh Nagar 	[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING },
49f9fd8914SShailabh Nagar 	[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },};
50f9fd8914SShailabh Nagar 
51f9fd8914SShailabh Nagar struct listener {
52f9fd8914SShailabh Nagar 	struct list_head list;
53f9fd8914SShailabh Nagar 	pid_t pid;
54bb129994SShailabh Nagar 	char valid;
55c757249aSShailabh Nagar };
56c757249aSShailabh Nagar 
57f9fd8914SShailabh Nagar struct listener_list {
58f9fd8914SShailabh Nagar 	struct rw_semaphore sem;
59f9fd8914SShailabh Nagar 	struct list_head list;
60f9fd8914SShailabh Nagar };
61f9fd8914SShailabh Nagar static DEFINE_PER_CPU(struct listener_list, listener_array);
62f9fd8914SShailabh Nagar 
63f9fd8914SShailabh Nagar enum actions {
64f9fd8914SShailabh Nagar 	REGISTER,
65f9fd8914SShailabh Nagar 	DEREGISTER,
66f9fd8914SShailabh Nagar 	CPU_DONT_CARE
67f9fd8914SShailabh Nagar };
68c757249aSShailabh Nagar 
69c757249aSShailabh Nagar static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp,
70c757249aSShailabh Nagar 			void **replyp, size_t size)
71c757249aSShailabh Nagar {
72c757249aSShailabh Nagar 	struct sk_buff *skb;
73c757249aSShailabh Nagar 	void *reply;
74c757249aSShailabh Nagar 
75c757249aSShailabh Nagar 	/*
76c757249aSShailabh Nagar 	 * If new attributes are added, please revisit this allocation
77c757249aSShailabh Nagar 	 */
78c757249aSShailabh Nagar 	skb = nlmsg_new(size);
79c757249aSShailabh Nagar 	if (!skb)
80c757249aSShailabh Nagar 		return -ENOMEM;
81c757249aSShailabh Nagar 
82c757249aSShailabh Nagar 	if (!info) {
83c757249aSShailabh Nagar 		int seq = get_cpu_var(taskstats_seqnum)++;
84c757249aSShailabh Nagar 		put_cpu_var(taskstats_seqnum);
85c757249aSShailabh Nagar 
86c757249aSShailabh Nagar 		reply = genlmsg_put(skb, 0, seq,
87c757249aSShailabh Nagar 				family.id, 0, 0,
88c757249aSShailabh Nagar 				cmd, family.version);
89c757249aSShailabh Nagar 	} else
90c757249aSShailabh Nagar 		reply = genlmsg_put(skb, info->snd_pid, info->snd_seq,
91c757249aSShailabh Nagar 				family.id, 0, 0,
92c757249aSShailabh Nagar 				cmd, family.version);
93c757249aSShailabh Nagar 	if (reply == NULL) {
94c757249aSShailabh Nagar 		nlmsg_free(skb);
95c757249aSShailabh Nagar 		return -EINVAL;
96c757249aSShailabh Nagar 	}
97c757249aSShailabh Nagar 
98c757249aSShailabh Nagar 	*skbp = skb;
99c757249aSShailabh Nagar 	*replyp = reply;
100c757249aSShailabh Nagar 	return 0;
101c757249aSShailabh Nagar }
102c757249aSShailabh Nagar 
103f9fd8914SShailabh Nagar /*
104f9fd8914SShailabh Nagar  * Send taskstats data in @skb to listener with nl_pid @pid
105f9fd8914SShailabh Nagar  */
106f9fd8914SShailabh Nagar static int send_reply(struct sk_buff *skb, pid_t pid)
107c757249aSShailabh Nagar {
108c757249aSShailabh Nagar 	struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data);
109f9fd8914SShailabh Nagar 	void *reply = genlmsg_data(genlhdr);
110c757249aSShailabh Nagar 	int rc;
111c757249aSShailabh Nagar 
112c757249aSShailabh Nagar 	rc = genlmsg_end(skb, reply);
113c757249aSShailabh Nagar 	if (rc < 0) {
114c757249aSShailabh Nagar 		nlmsg_free(skb);
115c757249aSShailabh Nagar 		return rc;
116c757249aSShailabh Nagar 	}
117c757249aSShailabh Nagar 
118c757249aSShailabh Nagar 	return genlmsg_unicast(skb, pid);
119c757249aSShailabh Nagar }
120c757249aSShailabh Nagar 
121f9fd8914SShailabh Nagar /*
122f9fd8914SShailabh Nagar  * Send taskstats data in @skb to listeners registered for @cpu's exit data
123f9fd8914SShailabh Nagar  */
124f9fd8914SShailabh Nagar static int send_cpu_listeners(struct sk_buff *skb, unsigned int cpu)
125f9fd8914SShailabh Nagar {
126f9fd8914SShailabh Nagar 	struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data);
127f9fd8914SShailabh Nagar 	struct listener_list *listeners;
128f9fd8914SShailabh Nagar 	struct listener *s, *tmp;
129f9fd8914SShailabh Nagar 	struct sk_buff *skb_next, *skb_cur = skb;
130f9fd8914SShailabh Nagar 	void *reply = genlmsg_data(genlhdr);
131bb129994SShailabh Nagar 	int rc, ret, delcount = 0;
132f9fd8914SShailabh Nagar 
133f9fd8914SShailabh Nagar 	rc = genlmsg_end(skb, reply);
134f9fd8914SShailabh Nagar 	if (rc < 0) {
135f9fd8914SShailabh Nagar 		nlmsg_free(skb);
136f9fd8914SShailabh Nagar 		return rc;
137f9fd8914SShailabh Nagar 	}
138f9fd8914SShailabh Nagar 
139f9fd8914SShailabh Nagar 	rc = 0;
140f9fd8914SShailabh Nagar 	listeners = &per_cpu(listener_array, cpu);
141bb129994SShailabh Nagar 	down_read(&listeners->sem);
142f9fd8914SShailabh Nagar 	list_for_each_entry_safe(s, tmp, &listeners->list, list) {
143f9fd8914SShailabh Nagar 		skb_next = NULL;
144f9fd8914SShailabh Nagar 		if (!list_is_last(&s->list, &listeners->list)) {
145f9fd8914SShailabh Nagar 			skb_next = skb_clone(skb_cur, GFP_KERNEL);
146f9fd8914SShailabh Nagar 			if (!skb_next) {
147f9fd8914SShailabh Nagar 				nlmsg_free(skb_cur);
148f9fd8914SShailabh Nagar 				rc = -ENOMEM;
149f9fd8914SShailabh Nagar 				break;
150f9fd8914SShailabh Nagar 			}
151f9fd8914SShailabh Nagar 		}
152f9fd8914SShailabh Nagar 		ret = genlmsg_unicast(skb_cur, s->pid);
153f9fd8914SShailabh Nagar 		if (ret == -ECONNREFUSED) {
154bb129994SShailabh Nagar 			s->valid = 0;
155bb129994SShailabh Nagar 			delcount++;
156f9fd8914SShailabh Nagar 			rc = ret;
157f9fd8914SShailabh Nagar 		}
158f9fd8914SShailabh Nagar 		skb_cur = skb_next;
159f9fd8914SShailabh Nagar 	}
160bb129994SShailabh Nagar 	up_read(&listeners->sem);
161f9fd8914SShailabh Nagar 
162bb129994SShailabh Nagar 	if (!delcount)
163bb129994SShailabh Nagar 		return rc;
164bb129994SShailabh Nagar 
165bb129994SShailabh Nagar 	/* Delete invalidated entries */
166bb129994SShailabh Nagar 	down_write(&listeners->sem);
167bb129994SShailabh Nagar 	list_for_each_entry_safe(s, tmp, &listeners->list, list) {
168bb129994SShailabh Nagar 		if (!s->valid) {
169bb129994SShailabh Nagar 			list_del(&s->list);
170bb129994SShailabh Nagar 			kfree(s);
171bb129994SShailabh Nagar 		}
172bb129994SShailabh Nagar 	}
173bb129994SShailabh Nagar 	up_write(&listeners->sem);
174f9fd8914SShailabh Nagar 	return rc;
175f9fd8914SShailabh Nagar }
176f9fd8914SShailabh Nagar 
177c757249aSShailabh Nagar static int fill_pid(pid_t pid, struct task_struct *pidtsk,
178c757249aSShailabh Nagar 		struct taskstats *stats)
179c757249aSShailabh Nagar {
180*7d94ddddSShailabh Nagar 	int rc = 0;
181c757249aSShailabh Nagar 	struct task_struct *tsk = pidtsk;
182c757249aSShailabh Nagar 
183c757249aSShailabh Nagar 	if (!pidtsk) {
184c757249aSShailabh Nagar 		read_lock(&tasklist_lock);
185c757249aSShailabh Nagar 		tsk = find_task_by_pid(pid);
186c757249aSShailabh Nagar 		if (!tsk) {
187c757249aSShailabh Nagar 			read_unlock(&tasklist_lock);
188c757249aSShailabh Nagar 			return -ESRCH;
189c757249aSShailabh Nagar 		}
190c757249aSShailabh Nagar 		get_task_struct(tsk);
191c757249aSShailabh Nagar 		read_unlock(&tasklist_lock);
192c757249aSShailabh Nagar 	} else
193c757249aSShailabh Nagar 		get_task_struct(tsk);
194c757249aSShailabh Nagar 
195c757249aSShailabh Nagar 	/*
196c757249aSShailabh Nagar 	 * Each accounting subsystem adds calls to its functions to
197c757249aSShailabh Nagar 	 * fill in relevant parts of struct taskstsats as follows
198c757249aSShailabh Nagar 	 *
199*7d94ddddSShailabh Nagar 	 *	per-task-foo(stats, tsk);
200c757249aSShailabh Nagar 	 */
201c757249aSShailabh Nagar 
202*7d94ddddSShailabh Nagar 	delayacct_add_tsk(stats, tsk);
2036f44993fSShailabh Nagar 	stats->version = TASKSTATS_VERSION;
2046f44993fSShailabh Nagar 
2056f44993fSShailabh Nagar 	/* Define err: label here if needed */
206c757249aSShailabh Nagar 	put_task_struct(tsk);
207c757249aSShailabh Nagar 	return rc;
208c757249aSShailabh Nagar 
209c757249aSShailabh Nagar }
210c757249aSShailabh Nagar 
211c757249aSShailabh Nagar static int fill_tgid(pid_t tgid, struct task_struct *tgidtsk,
212c757249aSShailabh Nagar 		struct taskstats *stats)
213c757249aSShailabh Nagar {
214c757249aSShailabh Nagar 	struct task_struct *tsk, *first;
215ad4ecbcbSShailabh Nagar 	unsigned long flags;
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 	 */
221c757249aSShailabh Nagar 	first = tgidtsk;
222c757249aSShailabh Nagar 	if (!first) {
223ad4ecbcbSShailabh Nagar 		read_lock(&tasklist_lock);
224c757249aSShailabh Nagar 		first = find_task_by_pid(tgid);
225c757249aSShailabh Nagar 		if (!first) {
226c757249aSShailabh Nagar 			read_unlock(&tasklist_lock);
227c757249aSShailabh Nagar 			return -ESRCH;
228c757249aSShailabh Nagar 		}
229ad4ecbcbSShailabh Nagar 		get_task_struct(first);
230ad4ecbcbSShailabh Nagar 		read_unlock(&tasklist_lock);
231ad4ecbcbSShailabh Nagar 	} else
232ad4ecbcbSShailabh Nagar 		get_task_struct(first);
233ad4ecbcbSShailabh Nagar 
234ad4ecbcbSShailabh Nagar 	/* Start with stats from dead tasks */
235ad4ecbcbSShailabh Nagar 	spin_lock_irqsave(&first->signal->stats_lock, flags);
236ad4ecbcbSShailabh Nagar 	if (first->signal->stats)
237ad4ecbcbSShailabh Nagar 		memcpy(stats, first->signal->stats, sizeof(*stats));
238ad4ecbcbSShailabh Nagar 	spin_unlock_irqrestore(&first->signal->stats_lock, flags);
239ad4ecbcbSShailabh Nagar 
240c757249aSShailabh Nagar 	tsk = first;
241ad4ecbcbSShailabh Nagar 	read_lock(&tasklist_lock);
242c757249aSShailabh Nagar 	do {
243ad4ecbcbSShailabh Nagar 		if (tsk->exit_state == EXIT_ZOMBIE && thread_group_leader(tsk))
244ad4ecbcbSShailabh Nagar 			continue;
245c757249aSShailabh Nagar 		/*
246ad4ecbcbSShailabh Nagar 		 * Accounting subsystem can call its functions here to
247c757249aSShailabh Nagar 		 * fill in relevant parts of struct taskstsats as follows
248c757249aSShailabh Nagar 		 *
249ad4ecbcbSShailabh Nagar 		 *	per-task-foo(stats, tsk);
250c757249aSShailabh Nagar 		 */
251ad4ecbcbSShailabh Nagar 		delayacct_add_tsk(stats, tsk);
2526f44993fSShailabh Nagar 
253c757249aSShailabh Nagar 	} while_each_thread(first, tsk);
254c757249aSShailabh Nagar 	read_unlock(&tasklist_lock);
2556f44993fSShailabh Nagar 	stats->version = TASKSTATS_VERSION;
2566f44993fSShailabh Nagar 
257c757249aSShailabh Nagar 	/*
258ad4ecbcbSShailabh Nagar 	 * Accounting subsytems can also add calls here to modify
259ad4ecbcbSShailabh Nagar 	 * fields of taskstats.
260c757249aSShailabh Nagar 	 */
261c757249aSShailabh Nagar 
262ad4ecbcbSShailabh Nagar 	return 0;
263c757249aSShailabh Nagar }
264c757249aSShailabh Nagar 
265ad4ecbcbSShailabh Nagar 
266ad4ecbcbSShailabh Nagar static void fill_tgid_exit(struct task_struct *tsk)
267ad4ecbcbSShailabh Nagar {
268ad4ecbcbSShailabh Nagar 	unsigned long flags;
269ad4ecbcbSShailabh Nagar 
270ad4ecbcbSShailabh Nagar 	spin_lock_irqsave(&tsk->signal->stats_lock, flags);
271ad4ecbcbSShailabh Nagar 	if (!tsk->signal->stats)
272ad4ecbcbSShailabh Nagar 		goto ret;
273ad4ecbcbSShailabh Nagar 
274ad4ecbcbSShailabh Nagar 	/*
275ad4ecbcbSShailabh Nagar 	 * Each accounting subsystem calls its functions here to
276ad4ecbcbSShailabh Nagar 	 * accumalate its per-task stats for tsk, into the per-tgid structure
277ad4ecbcbSShailabh Nagar 	 *
278ad4ecbcbSShailabh Nagar 	 *	per-task-foo(tsk->signal->stats, tsk);
279ad4ecbcbSShailabh Nagar 	 */
280ad4ecbcbSShailabh Nagar 	delayacct_add_tsk(tsk->signal->stats, tsk);
281ad4ecbcbSShailabh Nagar ret:
282ad4ecbcbSShailabh Nagar 	spin_unlock_irqrestore(&tsk->signal->stats_lock, flags);
283ad4ecbcbSShailabh Nagar 	return;
284ad4ecbcbSShailabh Nagar }
285ad4ecbcbSShailabh Nagar 
286f9fd8914SShailabh Nagar static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd)
287f9fd8914SShailabh Nagar {
288f9fd8914SShailabh Nagar 	struct listener_list *listeners;
289f9fd8914SShailabh Nagar 	struct listener *s, *tmp;
290f9fd8914SShailabh Nagar 	unsigned int cpu;
291f9fd8914SShailabh Nagar 	cpumask_t mask = *maskp;
292ad4ecbcbSShailabh Nagar 
293f9fd8914SShailabh Nagar 	if (!cpus_subset(mask, cpu_possible_map))
294f9fd8914SShailabh Nagar 		return -EINVAL;
295f9fd8914SShailabh Nagar 
296f9fd8914SShailabh Nagar 	if (isadd == REGISTER) {
297f9fd8914SShailabh Nagar 		for_each_cpu_mask(cpu, mask) {
298f9fd8914SShailabh Nagar 			s = kmalloc_node(sizeof(struct listener), GFP_KERNEL,
299f9fd8914SShailabh Nagar 					 cpu_to_node(cpu));
300f9fd8914SShailabh Nagar 			if (!s)
301f9fd8914SShailabh Nagar 				goto cleanup;
302f9fd8914SShailabh Nagar 			s->pid = pid;
303f9fd8914SShailabh Nagar 			INIT_LIST_HEAD(&s->list);
304bb129994SShailabh Nagar 			s->valid = 1;
305f9fd8914SShailabh Nagar 
306f9fd8914SShailabh Nagar 			listeners = &per_cpu(listener_array, cpu);
307f9fd8914SShailabh Nagar 			down_write(&listeners->sem);
308f9fd8914SShailabh Nagar 			list_add(&s->list, &listeners->list);
309f9fd8914SShailabh Nagar 			up_write(&listeners->sem);
310f9fd8914SShailabh Nagar 		}
311f9fd8914SShailabh Nagar 		return 0;
312f9fd8914SShailabh Nagar 	}
313f9fd8914SShailabh Nagar 
314f9fd8914SShailabh Nagar 	/* Deregister or cleanup */
315f9fd8914SShailabh Nagar cleanup:
316f9fd8914SShailabh Nagar 	for_each_cpu_mask(cpu, mask) {
317f9fd8914SShailabh Nagar 		listeners = &per_cpu(listener_array, cpu);
318f9fd8914SShailabh Nagar 		down_write(&listeners->sem);
319f9fd8914SShailabh Nagar 		list_for_each_entry_safe(s, tmp, &listeners->list, list) {
320f9fd8914SShailabh Nagar 			if (s->pid == pid) {
321f9fd8914SShailabh Nagar 				list_del(&s->list);
322f9fd8914SShailabh Nagar 				kfree(s);
323f9fd8914SShailabh Nagar 				break;
324f9fd8914SShailabh Nagar 			}
325f9fd8914SShailabh Nagar 		}
326f9fd8914SShailabh Nagar 		up_write(&listeners->sem);
327f9fd8914SShailabh Nagar 	}
328f9fd8914SShailabh Nagar 	return 0;
329f9fd8914SShailabh Nagar }
330f9fd8914SShailabh Nagar 
331f9fd8914SShailabh Nagar static int parse(struct nlattr *na, cpumask_t *mask)
332f9fd8914SShailabh Nagar {
333f9fd8914SShailabh Nagar 	char *data;
334f9fd8914SShailabh Nagar 	int len;
335f9fd8914SShailabh Nagar 	int ret;
336f9fd8914SShailabh Nagar 
337f9fd8914SShailabh Nagar 	if (na == NULL)
338f9fd8914SShailabh Nagar 		return 1;
339f9fd8914SShailabh Nagar 	len = nla_len(na);
340f9fd8914SShailabh Nagar 	if (len > TASKSTATS_CPUMASK_MAXLEN)
341f9fd8914SShailabh Nagar 		return -E2BIG;
342f9fd8914SShailabh Nagar 	if (len < 1)
343f9fd8914SShailabh Nagar 		return -EINVAL;
344f9fd8914SShailabh Nagar 	data = kmalloc(len, GFP_KERNEL);
345f9fd8914SShailabh Nagar 	if (!data)
346f9fd8914SShailabh Nagar 		return -ENOMEM;
347f9fd8914SShailabh Nagar 	nla_strlcpy(data, na, len);
348f9fd8914SShailabh Nagar 	ret = cpulist_parse(data, *mask);
349f9fd8914SShailabh Nagar 	kfree(data);
350f9fd8914SShailabh Nagar 	return ret;
351f9fd8914SShailabh Nagar }
352f9fd8914SShailabh Nagar 
353f9fd8914SShailabh Nagar static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
354c757249aSShailabh Nagar {
355c757249aSShailabh Nagar 	int rc = 0;
356c757249aSShailabh Nagar 	struct sk_buff *rep_skb;
357c757249aSShailabh Nagar 	struct taskstats stats;
358c757249aSShailabh Nagar 	void *reply;
359c757249aSShailabh Nagar 	size_t size;
360c757249aSShailabh Nagar 	struct nlattr *na;
361f9fd8914SShailabh Nagar 	cpumask_t mask;
362f9fd8914SShailabh Nagar 
363f9fd8914SShailabh Nagar 	rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], &mask);
364f9fd8914SShailabh Nagar 	if (rc < 0)
365f9fd8914SShailabh Nagar 		return rc;
366f9fd8914SShailabh Nagar 	if (rc == 0)
367f9fd8914SShailabh Nagar 		return add_del_listener(info->snd_pid, &mask, REGISTER);
368f9fd8914SShailabh Nagar 
369f9fd8914SShailabh Nagar 	rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], &mask);
370f9fd8914SShailabh Nagar 	if (rc < 0)
371f9fd8914SShailabh Nagar 		return rc;
372f9fd8914SShailabh Nagar 	if (rc == 0)
373f9fd8914SShailabh Nagar 		return add_del_listener(info->snd_pid, &mask, DEREGISTER);
374c757249aSShailabh Nagar 
375c757249aSShailabh Nagar 	/*
376c757249aSShailabh Nagar 	 * Size includes space for nested attributes
377c757249aSShailabh Nagar 	 */
378c757249aSShailabh Nagar 	size = nla_total_size(sizeof(u32)) +
379c757249aSShailabh Nagar 		nla_total_size(sizeof(struct taskstats)) + nla_total_size(0);
380c757249aSShailabh Nagar 
381c757249aSShailabh Nagar 	memset(&stats, 0, sizeof(stats));
382c757249aSShailabh Nagar 	rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, &reply, size);
383c757249aSShailabh Nagar 	if (rc < 0)
384c757249aSShailabh Nagar 		return rc;
385c757249aSShailabh Nagar 
386c757249aSShailabh Nagar 	if (info->attrs[TASKSTATS_CMD_ATTR_PID]) {
387c757249aSShailabh Nagar 		u32 pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]);
388c757249aSShailabh Nagar 		rc = fill_pid(pid, NULL, &stats);
389c757249aSShailabh Nagar 		if (rc < 0)
390c757249aSShailabh Nagar 			goto err;
391c757249aSShailabh Nagar 
392c757249aSShailabh Nagar 		na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_PID);
393c757249aSShailabh Nagar 		NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_PID, pid);
394c757249aSShailabh Nagar 		NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS,
395c757249aSShailabh Nagar 				stats);
396c757249aSShailabh Nagar 	} else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) {
397c757249aSShailabh Nagar 		u32 tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]);
398c757249aSShailabh Nagar 		rc = fill_tgid(tgid, NULL, &stats);
399c757249aSShailabh Nagar 		if (rc < 0)
400c757249aSShailabh Nagar 			goto err;
401c757249aSShailabh Nagar 
402c757249aSShailabh Nagar 		na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_TGID);
403c757249aSShailabh Nagar 		NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_TGID, tgid);
404c757249aSShailabh Nagar 		NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS,
405c757249aSShailabh Nagar 				stats);
406c757249aSShailabh Nagar 	} else {
407c757249aSShailabh Nagar 		rc = -EINVAL;
408c757249aSShailabh Nagar 		goto err;
409c757249aSShailabh Nagar 	}
410c757249aSShailabh Nagar 
411c757249aSShailabh Nagar 	nla_nest_end(rep_skb, na);
412c757249aSShailabh Nagar 
413f9fd8914SShailabh Nagar 	return send_reply(rep_skb, info->snd_pid);
414c757249aSShailabh Nagar 
415c757249aSShailabh Nagar nla_put_failure:
416c757249aSShailabh Nagar 	return genlmsg_cancel(rep_skb, reply);
417c757249aSShailabh Nagar err:
418c757249aSShailabh Nagar 	nlmsg_free(rep_skb);
419c757249aSShailabh Nagar 	return rc;
420c757249aSShailabh Nagar }
421c757249aSShailabh Nagar 
422f9fd8914SShailabh Nagar void taskstats_exit_alloc(struct taskstats **ptidstats, unsigned int *mycpu)
423f9fd8914SShailabh Nagar {
424f9fd8914SShailabh Nagar 	struct listener_list *listeners;
425f9fd8914SShailabh Nagar 	struct taskstats *tmp;
426f9fd8914SShailabh Nagar 	/*
427f9fd8914SShailabh Nagar 	 * This is the cpu on which the task is exiting currently and will
428f9fd8914SShailabh Nagar 	 * be the one for which the exit event is sent, even if the cpu
429f9fd8914SShailabh Nagar 	 * on which this function is running changes later.
430f9fd8914SShailabh Nagar 	 */
431f9fd8914SShailabh Nagar 	*mycpu = raw_smp_processor_id();
432f9fd8914SShailabh Nagar 
433f9fd8914SShailabh Nagar 	*ptidstats = NULL;
434f9fd8914SShailabh Nagar 	tmp = kmem_cache_zalloc(taskstats_cache, SLAB_KERNEL);
435f9fd8914SShailabh Nagar 	if (!tmp)
436f9fd8914SShailabh Nagar 		return;
437f9fd8914SShailabh Nagar 
438f9fd8914SShailabh Nagar 	listeners = &per_cpu(listener_array, *mycpu);
439f9fd8914SShailabh Nagar 	down_read(&listeners->sem);
440f9fd8914SShailabh Nagar 	if (!list_empty(&listeners->list)) {
441f9fd8914SShailabh Nagar 		*ptidstats = tmp;
442f9fd8914SShailabh Nagar 		tmp = NULL;
443f9fd8914SShailabh Nagar 	}
444f9fd8914SShailabh Nagar 	up_read(&listeners->sem);
445f9fd8914SShailabh Nagar 	kfree(tmp);
446f9fd8914SShailabh Nagar }
447f9fd8914SShailabh Nagar 
448c757249aSShailabh Nagar /* Send pid data out on exit */
449c757249aSShailabh Nagar void taskstats_exit_send(struct task_struct *tsk, struct taskstats *tidstats,
450f9fd8914SShailabh Nagar 			int group_dead, unsigned int mycpu)
451c757249aSShailabh Nagar {
452c757249aSShailabh Nagar 	int rc;
453c757249aSShailabh Nagar 	struct sk_buff *rep_skb;
454c757249aSShailabh Nagar 	void *reply;
455c757249aSShailabh Nagar 	size_t size;
456c757249aSShailabh Nagar 	int is_thread_group;
457c757249aSShailabh Nagar 	struct nlattr *na;
458ad4ecbcbSShailabh Nagar 	unsigned long flags;
459c757249aSShailabh Nagar 
460c757249aSShailabh Nagar 	if (!family_registered || !tidstats)
461c757249aSShailabh Nagar 		return;
462c757249aSShailabh Nagar 
463ad4ecbcbSShailabh Nagar 	spin_lock_irqsave(&tsk->signal->stats_lock, flags);
464ad4ecbcbSShailabh Nagar 	is_thread_group = tsk->signal->stats ? 1 : 0;
465ad4ecbcbSShailabh Nagar 	spin_unlock_irqrestore(&tsk->signal->stats_lock, flags);
466c757249aSShailabh Nagar 
467ad4ecbcbSShailabh Nagar 	rc = 0;
468c757249aSShailabh Nagar 	/*
469c757249aSShailabh Nagar 	 * Size includes space for nested attributes
470c757249aSShailabh Nagar 	 */
471c757249aSShailabh Nagar 	size = nla_total_size(sizeof(u32)) +
472c757249aSShailabh Nagar 		nla_total_size(sizeof(struct taskstats)) + nla_total_size(0);
473c757249aSShailabh Nagar 
474c757249aSShailabh Nagar 	if (is_thread_group)
475c757249aSShailabh Nagar 		size = 2 * size;	/* PID + STATS + TGID + STATS */
476c757249aSShailabh Nagar 
477c757249aSShailabh Nagar 	rc = prepare_reply(NULL, TASKSTATS_CMD_NEW, &rep_skb, &reply, size);
478c757249aSShailabh Nagar 	if (rc < 0)
479c757249aSShailabh Nagar 		goto ret;
480c757249aSShailabh Nagar 
481c757249aSShailabh Nagar 	rc = fill_pid(tsk->pid, tsk, tidstats);
482c757249aSShailabh Nagar 	if (rc < 0)
483c757249aSShailabh Nagar 		goto err_skb;
484c757249aSShailabh Nagar 
485c757249aSShailabh Nagar 	na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_PID);
486c757249aSShailabh Nagar 	NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_PID, (u32)tsk->pid);
487c757249aSShailabh Nagar 	NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS,
488c757249aSShailabh Nagar 			*tidstats);
489c757249aSShailabh Nagar 	nla_nest_end(rep_skb, na);
490c757249aSShailabh Nagar 
491ad4ecbcbSShailabh Nagar 	if (!is_thread_group)
492ad4ecbcbSShailabh Nagar 		goto send;
493c757249aSShailabh Nagar 
494c757249aSShailabh Nagar 	/*
495ad4ecbcbSShailabh Nagar 	 * tsk has/had a thread group so fill the tsk->signal->stats structure
496ad4ecbcbSShailabh Nagar 	 * Doesn't matter if tsk is the leader or the last group member leaving
497c757249aSShailabh Nagar 	 */
498ad4ecbcbSShailabh Nagar 
499ad4ecbcbSShailabh Nagar 	fill_tgid_exit(tsk);
500ad4ecbcbSShailabh Nagar 	if (!group_dead)
501ad4ecbcbSShailabh Nagar 		goto send;
502c757249aSShailabh Nagar 
503c757249aSShailabh Nagar 	na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_TGID);
504c757249aSShailabh Nagar 	NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_TGID, (u32)tsk->tgid);
505ad4ecbcbSShailabh Nagar 	/* No locking needed for tsk->signal->stats since group is dead */
506c757249aSShailabh Nagar 	NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS,
507ad4ecbcbSShailabh Nagar 			*tsk->signal->stats);
508c757249aSShailabh Nagar 	nla_nest_end(rep_skb, na);
509c757249aSShailabh Nagar 
510ad4ecbcbSShailabh Nagar send:
511f9fd8914SShailabh Nagar 	send_cpu_listeners(rep_skb, mycpu);
512ad4ecbcbSShailabh Nagar 	return;
513c757249aSShailabh Nagar 
514c757249aSShailabh Nagar nla_put_failure:
515c757249aSShailabh Nagar 	genlmsg_cancel(rep_skb, reply);
516c757249aSShailabh Nagar 	goto ret;
517c757249aSShailabh Nagar err_skb:
518c757249aSShailabh Nagar 	nlmsg_free(rep_skb);
519c757249aSShailabh Nagar ret:
520c757249aSShailabh Nagar 	return;
521c757249aSShailabh Nagar }
522c757249aSShailabh Nagar 
523c757249aSShailabh Nagar static struct genl_ops taskstats_ops = {
524c757249aSShailabh Nagar 	.cmd		= TASKSTATS_CMD_GET,
525f9fd8914SShailabh Nagar 	.doit		= taskstats_user_cmd,
526c757249aSShailabh Nagar 	.policy		= taskstats_cmd_get_policy,
527c757249aSShailabh Nagar };
528c757249aSShailabh Nagar 
529c757249aSShailabh Nagar /* Needed early in initialization */
530c757249aSShailabh Nagar void __init taskstats_init_early(void)
531c757249aSShailabh Nagar {
532f9fd8914SShailabh Nagar 	unsigned int i;
533f9fd8914SShailabh Nagar 
534c757249aSShailabh Nagar 	taskstats_cache = kmem_cache_create("taskstats_cache",
535c757249aSShailabh Nagar 						sizeof(struct taskstats),
536c757249aSShailabh Nagar 						0, SLAB_PANIC, NULL, NULL);
537f9fd8914SShailabh Nagar 	for_each_possible_cpu(i) {
538f9fd8914SShailabh Nagar 		INIT_LIST_HEAD(&(per_cpu(listener_array, i).list));
539f9fd8914SShailabh Nagar 		init_rwsem(&(per_cpu(listener_array, i).sem));
540f9fd8914SShailabh Nagar 	}
541c757249aSShailabh Nagar }
542c757249aSShailabh Nagar 
543c757249aSShailabh Nagar static int __init taskstats_init(void)
544c757249aSShailabh Nagar {
545c757249aSShailabh Nagar 	int rc;
546c757249aSShailabh Nagar 
547c757249aSShailabh Nagar 	rc = genl_register_family(&family);
548c757249aSShailabh Nagar 	if (rc)
549c757249aSShailabh Nagar 		return rc;
550c757249aSShailabh Nagar 
551c757249aSShailabh Nagar 	rc = genl_register_ops(&family, &taskstats_ops);
552c757249aSShailabh Nagar 	if (rc < 0)
553c757249aSShailabh Nagar 		goto err;
554c757249aSShailabh Nagar 
555c757249aSShailabh Nagar 	family_registered = 1;
556c757249aSShailabh Nagar 	return 0;
557c757249aSShailabh Nagar err:
558c757249aSShailabh Nagar 	genl_unregister_family(&family);
559c757249aSShailabh Nagar 	return rc;
560c757249aSShailabh Nagar }
561c757249aSShailabh Nagar 
562c757249aSShailabh Nagar /*
563c757249aSShailabh Nagar  * late initcall ensures initialization of statistics collection
564c757249aSShailabh Nagar  * mechanisms precedes initialization of the taskstats interface
565c757249aSShailabh Nagar  */
566c757249aSShailabh Nagar late_initcall(taskstats_init);
567