cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

taskstats.c (16155B)


      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
     32static DEFINE_PER_CPU(__u32, taskstats_seqnum);
     33static int family_registered;
     34struct kmem_cache *taskstats_cache;
     35
     36static struct genl_family family;
     37
     38static 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
     44static const struct nla_policy cgroupstats_cmd_get_policy[] = {
     45	[CGROUPSTATS_CMD_ATTR_FD] = { .type = NLA_U32 },
     46};
     47
     48struct listener {
     49	struct list_head list;
     50	pid_t pid;
     51	char valid;
     52};
     53
     54struct listener_list {
     55	struct rw_semaphore sem;
     56	struct list_head list;
     57};
     58static DEFINE_PER_CPU(struct listener_list, listener_array);
     59
     60enum actions {
     61	REGISTER,
     62	DEREGISTER,
     63	CPU_DONT_CARE
     64};
     65
     66static 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 */
     97static 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 */
    110static 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
    157static 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
    174static 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
    201static 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
    213static 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;
    265out:
    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
    276static 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);
    291ret:
    292	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
    293	return;
    294}
    295
    296static 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;
    331exists:
    332			up_write(&listeners->sem);
    333			kfree(s); /* nop if NULL */
    334		}
    335		return 0;
    336	}
    337
    338	/* Deregister or cleanup */
    339cleanup:
    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
    355static 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
    377static 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);
    403err:
    404	return NULL;
    405}
    406
    407static 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
    452err:
    453	fdput(f);
    454	return rc;
    455}
    456
    457static 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);
    468out:
    469	free_cpumask_var(mask);
    470	return rc;
    471}
    472
    473static 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);
    484out:
    485	free_cpumask_var(mask);
    486	return rc;
    487}
    488
    489static 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
    500static 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);
    524err:
    525	nlmsg_free(rep_skb);
    526	return rc;
    527}
    528
    529static 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);
    553err:
    554	nlmsg_free(rep_skb);
    555	return rc;
    556}
    557
    558static 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
    572static 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 */
    605void 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
    660send:
    661	send_cpu_listeners(rep_skb, listeners);
    662	return;
    663err:
    664	nlmsg_free(rep_skb);
    665}
    666
    667static 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
    685static 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 */
    695void __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
    706static 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 */
    723late_initcall(taskstats_init);