xref: /openbmc/linux/kernel/taskstats.c (revision e40573a4)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * taskstats.c - Export per-task statistics to userland
4  *
5  * Copyright (C) Shailabh Nagar, IBM Corp. 2006
6  *           (C) Balbir Singh,   IBM Corp. 2006
7  */
8 
9 #include <linux/kernel.h>
10 #include <linux/taskstats_kern.h>
11 #include <linux/tsacct_kern.h>
12 #include <linux/acct.h>
13 #include <linux/delayacct.h>
14 #include <linux/cpumask.h>
15 #include <linux/percpu.h>
16 #include <linux/slab.h>
17 #include <linux/cgroupstats.h>
18 #include <linux/cgroup.h>
19 #include <linux/fs.h>
20 #include <linux/file.h>
21 #include <linux/pid_namespace.h>
22 #include <net/genetlink.h>
23 #include <linux/atomic.h>
24 #include <linux/sched/cputime.h>
25 
26 /*
27  * Maximum length of a cpumask that can be specified in
28  * the TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK attribute
29  */
30 #define TASKSTATS_CPUMASK_MAXLEN	(100+6*NR_CPUS)
31 
32 static DEFINE_PER_CPU(__u32, taskstats_seqnum);
33 static int family_registered;
34 struct kmem_cache *taskstats_cache;
35 
36 static struct genl_family family;
37 
38 static const struct nla_policy taskstats_cmd_get_policy[] = {
39 	[TASKSTATS_CMD_ATTR_PID]  = { .type = NLA_U32 },
40 	[TASKSTATS_CMD_ATTR_TGID] = { .type = NLA_U32 },
41 	[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING },
42 	[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },};
43 
44 static const struct nla_policy cgroupstats_cmd_get_policy[] = {
45 	[CGROUPSTATS_CMD_ATTR_FD] = { .type = NLA_U32 },
46 };
47 
48 struct listener {
49 	struct list_head list;
50 	pid_t pid;
51 	char valid;
52 };
53 
54 struct listener_list {
55 	struct rw_semaphore sem;
56 	struct list_head list;
57 };
58 static DEFINE_PER_CPU(struct listener_list, listener_array);
59 
60 enum actions {
61 	REGISTER,
62 	DEREGISTER,
63 	CPU_DONT_CARE
64 };
65 
66 static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp,
67 				size_t size)
68 {
69 	struct sk_buff *skb;
70 	void *reply;
71 
72 	/*
73 	 * If new attributes are added, please revisit this allocation
74 	 */
75 	skb = genlmsg_new(size, GFP_KERNEL);
76 	if (!skb)
77 		return -ENOMEM;
78 
79 	if (!info) {
80 		int seq = this_cpu_inc_return(taskstats_seqnum) - 1;
81 
82 		reply = genlmsg_put(skb, 0, seq, &family, 0, cmd);
83 	} else
84 		reply = genlmsg_put_reply(skb, info, &family, 0, cmd);
85 	if (reply == NULL) {
86 		nlmsg_free(skb);
87 		return -EINVAL;
88 	}
89 
90 	*skbp = skb;
91 	return 0;
92 }
93 
94 /*
95  * Send taskstats data in @skb to listener with nl_pid @pid
96  */
97 static int send_reply(struct sk_buff *skb, struct genl_info *info)
98 {
99 	struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
100 	void *reply = genlmsg_data(genlhdr);
101 
102 	genlmsg_end(skb, reply);
103 
104 	return genlmsg_reply(skb, info);
105 }
106 
107 /*
108  * Send taskstats data in @skb to listeners registered for @cpu's exit data
109  */
110 static void send_cpu_listeners(struct sk_buff *skb,
111 					struct listener_list *listeners)
112 {
113 	struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
114 	struct listener *s, *tmp;
115 	struct sk_buff *skb_next, *skb_cur = skb;
116 	void *reply = genlmsg_data(genlhdr);
117 	int delcount = 0;
118 
119 	genlmsg_end(skb, reply);
120 
121 	down_read(&listeners->sem);
122 	list_for_each_entry(s, &listeners->list, list) {
123 		int rc;
124 
125 		skb_next = NULL;
126 		if (!list_is_last(&s->list, &listeners->list)) {
127 			skb_next = skb_clone(skb_cur, GFP_KERNEL);
128 			if (!skb_next)
129 				break;
130 		}
131 		rc = genlmsg_unicast(&init_net, skb_cur, s->pid);
132 		if (rc == -ECONNREFUSED) {
133 			s->valid = 0;
134 			delcount++;
135 		}
136 		skb_cur = skb_next;
137 	}
138 	up_read(&listeners->sem);
139 
140 	if (skb_cur)
141 		nlmsg_free(skb_cur);
142 
143 	if (!delcount)
144 		return;
145 
146 	/* Delete invalidated entries */
147 	down_write(&listeners->sem);
148 	list_for_each_entry_safe(s, tmp, &listeners->list, list) {
149 		if (!s->valid) {
150 			list_del(&s->list);
151 			kfree(s);
152 		}
153 	}
154 	up_write(&listeners->sem);
155 }
156 
157 static void exe_add_tsk(struct taskstats *stats, struct task_struct *tsk)
158 {
159 	/* No idea if I'm allowed to access that here, now. */
160 	struct file *exe_file = get_task_exe_file(tsk);
161 
162 	if (exe_file) {
163 		/* Following cp_new_stat64() in stat.c . */
164 		stats->ac_exe_dev =
165 			huge_encode_dev(exe_file->f_inode->i_sb->s_dev);
166 		stats->ac_exe_inode = exe_file->f_inode->i_ino;
167 		fput(exe_file);
168 	} else {
169 		stats->ac_exe_dev = 0;
170 		stats->ac_exe_inode = 0;
171 	}
172 }
173 
174 static void fill_stats(struct user_namespace *user_ns,
175 		       struct pid_namespace *pid_ns,
176 		       struct task_struct *tsk, struct taskstats *stats)
177 {
178 	memset(stats, 0, sizeof(*stats));
179 	/*
180 	 * Each accounting subsystem adds calls to its functions to
181 	 * fill in relevant parts of struct taskstsats as follows
182 	 *
183 	 *	per-task-foo(stats, tsk);
184 	 */
185 
186 	delayacct_add_tsk(stats, tsk);
187 
188 	/* fill in basic acct fields */
189 	stats->version = TASKSTATS_VERSION;
190 	stats->nvcsw = tsk->nvcsw;
191 	stats->nivcsw = tsk->nivcsw;
192 	bacct_add_tsk(user_ns, pid_ns, stats, tsk);
193 
194 	/* fill in extended acct fields */
195 	xacct_add_tsk(stats, tsk);
196 
197 	/* add executable info */
198 	exe_add_tsk(stats, tsk);
199 }
200 
201 static int fill_stats_for_pid(pid_t pid, struct taskstats *stats)
202 {
203 	struct task_struct *tsk;
204 
205 	tsk = find_get_task_by_vpid(pid);
206 	if (!tsk)
207 		return -ESRCH;
208 	fill_stats(current_user_ns(), task_active_pid_ns(current), tsk, stats);
209 	put_task_struct(tsk);
210 	return 0;
211 }
212 
213 static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats)
214 {
215 	struct task_struct *tsk, *first;
216 	unsigned long flags;
217 	int rc = -ESRCH;
218 	u64 delta, utime, stime;
219 	u64 start_time;
220 
221 	/*
222 	 * Add additional stats from live tasks except zombie thread group
223 	 * leaders who are already counted with the dead tasks
224 	 */
225 	rcu_read_lock();
226 	first = find_task_by_vpid(tgid);
227 
228 	if (!first || !lock_task_sighand(first, &flags))
229 		goto out;
230 
231 	if (first->signal->stats)
232 		memcpy(stats, first->signal->stats, sizeof(*stats));
233 	else
234 		memset(stats, 0, sizeof(*stats));
235 
236 	tsk = first;
237 	start_time = ktime_get_ns();
238 	do {
239 		if (tsk->exit_state)
240 			continue;
241 		/*
242 		 * Accounting subsystem can call its functions here to
243 		 * fill in relevant parts of struct taskstsats as follows
244 		 *
245 		 *	per-task-foo(stats, tsk);
246 		 */
247 		delayacct_add_tsk(stats, tsk);
248 
249 		/* calculate task elapsed time in nsec */
250 		delta = start_time - tsk->start_time;
251 		/* Convert to micro seconds */
252 		do_div(delta, NSEC_PER_USEC);
253 		stats->ac_etime += delta;
254 
255 		task_cputime(tsk, &utime, &stime);
256 		stats->ac_utime += div_u64(utime, NSEC_PER_USEC);
257 		stats->ac_stime += div_u64(stime, NSEC_PER_USEC);
258 
259 		stats->nvcsw += tsk->nvcsw;
260 		stats->nivcsw += tsk->nivcsw;
261 	} while_each_thread(first, tsk);
262 
263 	unlock_task_sighand(first, &flags);
264 	rc = 0;
265 out:
266 	rcu_read_unlock();
267 
268 	stats->version = TASKSTATS_VERSION;
269 	/*
270 	 * Accounting subsystems can also add calls here to modify
271 	 * fields of taskstats.
272 	 */
273 	return rc;
274 }
275 
276 static void fill_tgid_exit(struct task_struct *tsk)
277 {
278 	unsigned long flags;
279 
280 	spin_lock_irqsave(&tsk->sighand->siglock, flags);
281 	if (!tsk->signal->stats)
282 		goto ret;
283 
284 	/*
285 	 * Each accounting subsystem calls its functions here to
286 	 * accumalate its per-task stats for tsk, into the per-tgid structure
287 	 *
288 	 *	per-task-foo(tsk->signal->stats, tsk);
289 	 */
290 	delayacct_add_tsk(tsk->signal->stats, tsk);
291 ret:
292 	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
293 	return;
294 }
295 
296 static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd)
297 {
298 	struct listener_list *listeners;
299 	struct listener *s, *tmp, *s2;
300 	unsigned int cpu;
301 	int ret = 0;
302 
303 	if (!cpumask_subset(mask, cpu_possible_mask))
304 		return -EINVAL;
305 
306 	if (current_user_ns() != &init_user_ns)
307 		return -EINVAL;
308 
309 	if (task_active_pid_ns(current) != &init_pid_ns)
310 		return -EINVAL;
311 
312 	if (isadd == REGISTER) {
313 		for_each_cpu(cpu, mask) {
314 			s = kmalloc_node(sizeof(struct listener),
315 					GFP_KERNEL, cpu_to_node(cpu));
316 			if (!s) {
317 				ret = -ENOMEM;
318 				goto cleanup;
319 			}
320 			s->pid = pid;
321 			s->valid = 1;
322 
323 			listeners = &per_cpu(listener_array, cpu);
324 			down_write(&listeners->sem);
325 			list_for_each_entry(s2, &listeners->list, list) {
326 				if (s2->pid == pid && s2->valid)
327 					goto exists;
328 			}
329 			list_add(&s->list, &listeners->list);
330 			s = NULL;
331 exists:
332 			up_write(&listeners->sem);
333 			kfree(s); /* nop if NULL */
334 		}
335 		return 0;
336 	}
337 
338 	/* Deregister or cleanup */
339 cleanup:
340 	for_each_cpu(cpu, mask) {
341 		listeners = &per_cpu(listener_array, cpu);
342 		down_write(&listeners->sem);
343 		list_for_each_entry_safe(s, tmp, &listeners->list, list) {
344 			if (s->pid == pid) {
345 				list_del(&s->list);
346 				kfree(s);
347 				break;
348 			}
349 		}
350 		up_write(&listeners->sem);
351 	}
352 	return ret;
353 }
354 
355 static int parse(struct nlattr *na, struct cpumask *mask)
356 {
357 	char *data;
358 	int len;
359 	int ret;
360 
361 	if (na == NULL)
362 		return 1;
363 	len = nla_len(na);
364 	if (len > TASKSTATS_CPUMASK_MAXLEN)
365 		return -E2BIG;
366 	if (len < 1)
367 		return -EINVAL;
368 	data = kmalloc(len, GFP_KERNEL);
369 	if (!data)
370 		return -ENOMEM;
371 	nla_strscpy(data, na, len);
372 	ret = cpulist_parse(data, mask);
373 	kfree(data);
374 	return ret;
375 }
376 
377 static struct taskstats *mk_reply(struct sk_buff *skb, int type, u32 pid)
378 {
379 	struct nlattr *na, *ret;
380 	int aggr;
381 
382 	aggr = (type == TASKSTATS_TYPE_PID)
383 			? TASKSTATS_TYPE_AGGR_PID
384 			: TASKSTATS_TYPE_AGGR_TGID;
385 
386 	na = nla_nest_start_noflag(skb, aggr);
387 	if (!na)
388 		goto err;
389 
390 	if (nla_put(skb, type, sizeof(pid), &pid) < 0) {
391 		nla_nest_cancel(skb, na);
392 		goto err;
393 	}
394 	ret = nla_reserve_64bit(skb, TASKSTATS_TYPE_STATS,
395 				sizeof(struct taskstats), TASKSTATS_TYPE_NULL);
396 	if (!ret) {
397 		nla_nest_cancel(skb, na);
398 		goto err;
399 	}
400 	nla_nest_end(skb, na);
401 
402 	return nla_data(ret);
403 err:
404 	return NULL;
405 }
406 
407 static int cgroupstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
408 {
409 	int rc = 0;
410 	struct sk_buff *rep_skb;
411 	struct cgroupstats *stats;
412 	struct nlattr *na;
413 	size_t size;
414 	u32 fd;
415 	struct fd f;
416 
417 	na = info->attrs[CGROUPSTATS_CMD_ATTR_FD];
418 	if (!na)
419 		return -EINVAL;
420 
421 	fd = nla_get_u32(info->attrs[CGROUPSTATS_CMD_ATTR_FD]);
422 	f = fdget(fd);
423 	if (!f.file)
424 		return 0;
425 
426 	size = nla_total_size(sizeof(struct cgroupstats));
427 
428 	rc = prepare_reply(info, CGROUPSTATS_CMD_NEW, &rep_skb,
429 				size);
430 	if (rc < 0)
431 		goto err;
432 
433 	na = nla_reserve(rep_skb, CGROUPSTATS_TYPE_CGROUP_STATS,
434 				sizeof(struct cgroupstats));
435 	if (na == NULL) {
436 		nlmsg_free(rep_skb);
437 		rc = -EMSGSIZE;
438 		goto err;
439 	}
440 
441 	stats = nla_data(na);
442 	memset(stats, 0, sizeof(*stats));
443 
444 	rc = cgroupstats_build(stats, f.file->f_path.dentry);
445 	if (rc < 0) {
446 		nlmsg_free(rep_skb);
447 		goto err;
448 	}
449 
450 	rc = send_reply(rep_skb, info);
451 
452 err:
453 	fdput(f);
454 	return rc;
455 }
456 
457 static int cmd_attr_register_cpumask(struct genl_info *info)
458 {
459 	cpumask_var_t mask;
460 	int rc;
461 
462 	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
463 		return -ENOMEM;
464 	rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask);
465 	if (rc < 0)
466 		goto out;
467 	rc = add_del_listener(info->snd_portid, mask, REGISTER);
468 out:
469 	free_cpumask_var(mask);
470 	return rc;
471 }
472 
473 static int cmd_attr_deregister_cpumask(struct genl_info *info)
474 {
475 	cpumask_var_t mask;
476 	int rc;
477 
478 	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
479 		return -ENOMEM;
480 	rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask);
481 	if (rc < 0)
482 		goto out;
483 	rc = add_del_listener(info->snd_portid, mask, DEREGISTER);
484 out:
485 	free_cpumask_var(mask);
486 	return rc;
487 }
488 
489 static size_t taskstats_packet_size(void)
490 {
491 	size_t size;
492 
493 	size = nla_total_size(sizeof(u32)) +
494 		nla_total_size_64bit(sizeof(struct taskstats)) +
495 		nla_total_size(0);
496 
497 	return size;
498 }
499 
500 static int cmd_attr_pid(struct genl_info *info)
501 {
502 	struct taskstats *stats;
503 	struct sk_buff *rep_skb;
504 	size_t size;
505 	u32 pid;
506 	int rc;
507 
508 	size = taskstats_packet_size();
509 
510 	rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size);
511 	if (rc < 0)
512 		return rc;
513 
514 	rc = -EINVAL;
515 	pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]);
516 	stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid);
517 	if (!stats)
518 		goto err;
519 
520 	rc = fill_stats_for_pid(pid, stats);
521 	if (rc < 0)
522 		goto err;
523 	return send_reply(rep_skb, info);
524 err:
525 	nlmsg_free(rep_skb);
526 	return rc;
527 }
528 
529 static int cmd_attr_tgid(struct genl_info *info)
530 {
531 	struct taskstats *stats;
532 	struct sk_buff *rep_skb;
533 	size_t size;
534 	u32 tgid;
535 	int rc;
536 
537 	size = taskstats_packet_size();
538 
539 	rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size);
540 	if (rc < 0)
541 		return rc;
542 
543 	rc = -EINVAL;
544 	tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]);
545 	stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid);
546 	if (!stats)
547 		goto err;
548 
549 	rc = fill_stats_for_tgid(tgid, stats);
550 	if (rc < 0)
551 		goto err;
552 	return send_reply(rep_skb, info);
553 err:
554 	nlmsg_free(rep_skb);
555 	return rc;
556 }
557 
558 static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
559 {
560 	if (info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK])
561 		return cmd_attr_register_cpumask(info);
562 	else if (info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK])
563 		return cmd_attr_deregister_cpumask(info);
564 	else if (info->attrs[TASKSTATS_CMD_ATTR_PID])
565 		return cmd_attr_pid(info);
566 	else if (info->attrs[TASKSTATS_CMD_ATTR_TGID])
567 		return cmd_attr_tgid(info);
568 	else
569 		return -EINVAL;
570 }
571 
572 static struct taskstats *taskstats_tgid_alloc(struct task_struct *tsk)
573 {
574 	struct signal_struct *sig = tsk->signal;
575 	struct taskstats *stats_new, *stats;
576 
577 	/* Pairs with smp_store_release() below. */
578 	stats = smp_load_acquire(&sig->stats);
579 	if (stats || thread_group_empty(tsk))
580 		return stats;
581 
582 	/* No problem if kmem_cache_zalloc() fails */
583 	stats_new = kmem_cache_zalloc(taskstats_cache, GFP_KERNEL);
584 
585 	spin_lock_irq(&tsk->sighand->siglock);
586 	stats = sig->stats;
587 	if (!stats) {
588 		/*
589 		 * Pairs with smp_store_release() above and order the
590 		 * kmem_cache_zalloc().
591 		 */
592 		smp_store_release(&sig->stats, stats_new);
593 		stats = stats_new;
594 		stats_new = NULL;
595 	}
596 	spin_unlock_irq(&tsk->sighand->siglock);
597 
598 	if (stats_new)
599 		kmem_cache_free(taskstats_cache, stats_new);
600 
601 	return stats;
602 }
603 
604 /* Send pid data out on exit */
605 void taskstats_exit(struct task_struct *tsk, int group_dead)
606 {
607 	int rc;
608 	struct listener_list *listeners;
609 	struct taskstats *stats;
610 	struct sk_buff *rep_skb;
611 	size_t size;
612 	int is_thread_group;
613 
614 	if (!family_registered)
615 		return;
616 
617 	/*
618 	 * Size includes space for nested attributes
619 	 */
620 	size = taskstats_packet_size();
621 
622 	is_thread_group = !!taskstats_tgid_alloc(tsk);
623 	if (is_thread_group) {
624 		/* PID + STATS + TGID + STATS */
625 		size = 2 * size;
626 		/* fill the tsk->signal->stats structure */
627 		fill_tgid_exit(tsk);
628 	}
629 
630 	listeners = raw_cpu_ptr(&listener_array);
631 	if (list_empty(&listeners->list))
632 		return;
633 
634 	rc = prepare_reply(NULL, TASKSTATS_CMD_NEW, &rep_skb, size);
635 	if (rc < 0)
636 		return;
637 
638 	stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID,
639 			 task_pid_nr_ns(tsk, &init_pid_ns));
640 	if (!stats)
641 		goto err;
642 
643 	fill_stats(&init_user_ns, &init_pid_ns, tsk, stats);
644 	if (group_dead)
645 		stats->ac_flag |= AGROUP;
646 
647 	/*
648 	 * Doesn't matter if tsk is the leader or the last group member leaving
649 	 */
650 	if (!is_thread_group || !group_dead)
651 		goto send;
652 
653 	stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID,
654 			 task_tgid_nr_ns(tsk, &init_pid_ns));
655 	if (!stats)
656 		goto err;
657 
658 	memcpy(stats, tsk->signal->stats, sizeof(*stats));
659 
660 send:
661 	send_cpu_listeners(rep_skb, listeners);
662 	return;
663 err:
664 	nlmsg_free(rep_skb);
665 }
666 
667 static const struct genl_ops taskstats_ops[] = {
668 	{
669 		.cmd		= TASKSTATS_CMD_GET,
670 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
671 		.doit		= taskstats_user_cmd,
672 		.policy		= taskstats_cmd_get_policy,
673 		.maxattr	= ARRAY_SIZE(taskstats_cmd_get_policy) - 1,
674 		.flags		= GENL_ADMIN_PERM,
675 	},
676 	{
677 		.cmd		= CGROUPSTATS_CMD_GET,
678 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
679 		.doit		= cgroupstats_user_cmd,
680 		.policy		= cgroupstats_cmd_get_policy,
681 		.maxattr	= ARRAY_SIZE(cgroupstats_cmd_get_policy) - 1,
682 	},
683 };
684 
685 static struct genl_family family __ro_after_init = {
686 	.name		= TASKSTATS_GENL_NAME,
687 	.version	= TASKSTATS_GENL_VERSION,
688 	.module		= THIS_MODULE,
689 	.ops		= taskstats_ops,
690 	.n_ops		= ARRAY_SIZE(taskstats_ops),
691 	.netnsok	= true,
692 };
693 
694 /* Needed early in initialization */
695 void __init taskstats_init_early(void)
696 {
697 	unsigned int i;
698 
699 	taskstats_cache = KMEM_CACHE(taskstats, SLAB_PANIC);
700 	for_each_possible_cpu(i) {
701 		INIT_LIST_HEAD(&(per_cpu(listener_array, i).list));
702 		init_rwsem(&(per_cpu(listener_array, i).sem));
703 	}
704 }
705 
706 static int __init taskstats_init(void)
707 {
708 	int rc;
709 
710 	rc = genl_register_family(&family);
711 	if (rc)
712 		return rc;
713 
714 	family_registered = 1;
715 	pr_info("registered taskstats version %d\n", TASKSTATS_GENL_VERSION);
716 	return 0;
717 }
718 
719 /*
720  * late initcall ensures initialization of statistics collection
721  * mechanisms precedes initialization of the taskstats interface
722  */
723 late_initcall(taskstats_init);
724