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