linux/kernel/gcov/fs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  This code exports profiling data as debugfs files to userspace.
   4 *
   5 *    Copyright IBM Corp. 2009
   6 *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
   7 *
   8 *    Uses gcc-internal data definitions.
   9 *    Based on the gcov-kernel patch by:
  10 *               Hubertus Franke <frankeh@us.ibm.com>
  11 *               Nigel Hinds <nhinds@us.ibm.com>
  12 *               Rajan Ravindran <rajancr@us.ibm.com>
  13 *               Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
  14 *               Paul Larson
  15 *               Yi CDL Yang
  16 */
  17
  18#define pr_fmt(fmt)     "gcov: " fmt
  19
  20#include <linux/init.h>
  21#include <linux/module.h>
  22#include <linux/debugfs.h>
  23#include <linux/fs.h>
  24#include <linux/list.h>
  25#include <linux/string.h>
  26#include <linux/slab.h>
  27#include <linux/mutex.h>
  28#include <linux/seq_file.h>
  29#include <linux/mm.h>
  30#include "gcov.h"
  31
  32/**
  33 * struct gcov_node - represents a debugfs entry
  34 * @list: list head for child node list
  35 * @children: child nodes
  36 * @all: list head for list of all nodes
  37 * @parent: parent node
  38 * @loaded_info: array of pointers to profiling data sets for loaded object
  39 *   files.
  40 * @num_loaded: number of profiling data sets for loaded object files.
  41 * @unloaded_info: accumulated copy of profiling data sets for unloaded
  42 *   object files. Used only when gcov_persist=1.
  43 * @dentry: main debugfs entry, either a directory or data file
  44 * @links: associated symbolic links
  45 * @name: data file basename
  46 *
  47 * struct gcov_node represents an entity within the gcov/ subdirectory
  48 * of debugfs. There are directory and data file nodes. The latter represent
  49 * the actual synthesized data file plus any associated symbolic links which
  50 * are needed by the gcov tool to work correctly.
  51 */
  52struct gcov_node {
  53        struct list_head list;
  54        struct list_head children;
  55        struct list_head all;
  56        struct gcov_node *parent;
  57        struct gcov_info **loaded_info;
  58        struct gcov_info *unloaded_info;
  59        struct dentry *dentry;
  60        struct dentry **links;
  61        int num_loaded;
  62        char name[];
  63};
  64
  65static const char objtree[] = OBJTREE;
  66static const char srctree[] = SRCTREE;
  67static struct gcov_node root_node;
  68static LIST_HEAD(all_head);
  69static DEFINE_MUTEX(node_lock);
  70
  71/* If non-zero, keep copies of profiling data for unloaded modules. */
  72static int gcov_persist = 1;
  73
  74static int __init gcov_persist_setup(char *str)
  75{
  76        unsigned long val;
  77
  78        if (kstrtoul(str, 0, &val)) {
  79                pr_warn("invalid gcov_persist parameter '%s'\n", str);
  80                return 0;
  81        }
  82        gcov_persist = val;
  83        pr_info("setting gcov_persist to %d\n", gcov_persist);
  84
  85        return 1;
  86}
  87__setup("gcov_persist=", gcov_persist_setup);
  88
  89#define ITER_STRIDE     PAGE_SIZE
  90
  91/**
  92 * struct gcov_iterator - specifies current file position in logical records
  93 * @info: associated profiling data
  94 * @buffer: buffer containing file data
  95 * @size: size of buffer
  96 * @pos: current position in file
  97 */
  98struct gcov_iterator {
  99        struct gcov_info *info;
 100        size_t size;
 101        loff_t pos;
 102        char buffer[];
 103};
 104
 105/**
 106 * gcov_iter_new - allocate and initialize profiling data iterator
 107 * @info: profiling data set to be iterated
 108 *
 109 * Return file iterator on success, %NULL otherwise.
 110 */
 111static struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
 112{
 113        struct gcov_iterator *iter;
 114        size_t size;
 115
 116        /* Dry-run to get the actual buffer size. */
 117        size = convert_to_gcda(NULL, info);
 118
 119        iter = kvmalloc(struct_size(iter, buffer, size), GFP_KERNEL);
 120        if (!iter)
 121                return NULL;
 122
 123        iter->info = info;
 124        iter->size = size;
 125        convert_to_gcda(iter->buffer, info);
 126
 127        return iter;
 128}
 129
 130
 131/**
 132 * gcov_iter_free - free iterator data
 133 * @iter: file iterator
 134 */
 135static void gcov_iter_free(struct gcov_iterator *iter)
 136{
 137        kvfree(iter);
 138}
 139
 140/**
 141 * gcov_iter_get_info - return profiling data set for given file iterator
 142 * @iter: file iterator
 143 */
 144static struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
 145{
 146        return iter->info;
 147}
 148
 149/**
 150 * gcov_iter_start - reset file iterator to starting position
 151 * @iter: file iterator
 152 */
 153static void gcov_iter_start(struct gcov_iterator *iter)
 154{
 155        iter->pos = 0;
 156}
 157
 158/**
 159 * gcov_iter_next - advance file iterator to next logical record
 160 * @iter: file iterator
 161 *
 162 * Return zero if new position is valid, non-zero if iterator has reached end.
 163 */
 164static int gcov_iter_next(struct gcov_iterator *iter)
 165{
 166        if (iter->pos < iter->size)
 167                iter->pos += ITER_STRIDE;
 168
 169        if (iter->pos >= iter->size)
 170                return -EINVAL;
 171
 172        return 0;
 173}
 174
 175/**
 176 * gcov_iter_write - write data for current pos to seq_file
 177 * @iter: file iterator
 178 * @seq: seq_file handle
 179 *
 180 * Return zero on success, non-zero otherwise.
 181 */
 182static int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
 183{
 184        size_t len;
 185
 186        if (iter->pos >= iter->size)
 187                return -EINVAL;
 188
 189        len = ITER_STRIDE;
 190        if (iter->pos + len > iter->size)
 191                len = iter->size - iter->pos;
 192
 193        seq_write(seq, iter->buffer + iter->pos, len);
 194
 195        return 0;
 196}
 197
 198/*
 199 * seq_file.start() implementation for gcov data files. Note that the
 200 * gcov_iterator interface is designed to be more restrictive than seq_file
 201 * (no start from arbitrary position, etc.), to simplify the iterator
 202 * implementation.
 203 */
 204static void *gcov_seq_start(struct seq_file *seq, loff_t *pos)
 205{
 206        loff_t i;
 207
 208        gcov_iter_start(seq->private);
 209        for (i = 0; i < *pos; i++) {
 210                if (gcov_iter_next(seq->private))
 211                        return NULL;
 212        }
 213        return seq->private;
 214}
 215
 216/* seq_file.next() implementation for gcov data files. */
 217static void *gcov_seq_next(struct seq_file *seq, void *data, loff_t *pos)
 218{
 219        struct gcov_iterator *iter = data;
 220
 221        (*pos)++;
 222        if (gcov_iter_next(iter))
 223                return NULL;
 224
 225        return iter;
 226}
 227
 228/* seq_file.show() implementation for gcov data files. */
 229static int gcov_seq_show(struct seq_file *seq, void *data)
 230{
 231        struct gcov_iterator *iter = data;
 232
 233        if (gcov_iter_write(iter, seq))
 234                return -EINVAL;
 235        return 0;
 236}
 237
 238static void gcov_seq_stop(struct seq_file *seq, void *data)
 239{
 240        /* Unused. */
 241}
 242
 243static const struct seq_operations gcov_seq_ops = {
 244        .start  = gcov_seq_start,
 245        .next   = gcov_seq_next,
 246        .show   = gcov_seq_show,
 247        .stop   = gcov_seq_stop,
 248};
 249
 250/*
 251 * Return a profiling data set associated with the given node. This is
 252 * either a data set for a loaded object file or a data set copy in case
 253 * all associated object files have been unloaded.
 254 */
 255static struct gcov_info *get_node_info(struct gcov_node *node)
 256{
 257        if (node->num_loaded > 0)
 258                return node->loaded_info[0];
 259
 260        return node->unloaded_info;
 261}
 262
 263/*
 264 * Return a newly allocated profiling data set which contains the sum of
 265 * all profiling data associated with the given node.
 266 */
 267static struct gcov_info *get_accumulated_info(struct gcov_node *node)
 268{
 269        struct gcov_info *info;
 270        int i = 0;
 271
 272        if (node->unloaded_info)
 273                info = gcov_info_dup(node->unloaded_info);
 274        else
 275                info = gcov_info_dup(node->loaded_info[i++]);
 276        if (!info)
 277                return NULL;
 278        for (; i < node->num_loaded; i++)
 279                gcov_info_add(info, node->loaded_info[i]);
 280
 281        return info;
 282}
 283
 284/*
 285 * open() implementation for gcov data files. Create a copy of the profiling
 286 * data set and initialize the iterator and seq_file interface.
 287 */
 288static int gcov_seq_open(struct inode *inode, struct file *file)
 289{
 290        struct gcov_node *node = inode->i_private;
 291        struct gcov_iterator *iter;
 292        struct seq_file *seq;
 293        struct gcov_info *info;
 294        int rc = -ENOMEM;
 295
 296        mutex_lock(&node_lock);
 297        /*
 298         * Read from a profiling data copy to minimize reference tracking
 299         * complexity and concurrent access and to keep accumulating multiple
 300         * profiling data sets associated with one node simple.
 301         */
 302        info = get_accumulated_info(node);
 303        if (!info)
 304                goto out_unlock;
 305        iter = gcov_iter_new(info);
 306        if (!iter)
 307                goto err_free_info;
 308        rc = seq_open(file, &gcov_seq_ops);
 309        if (rc)
 310                goto err_free_iter_info;
 311        seq = file->private_data;
 312        seq->private = iter;
 313out_unlock:
 314        mutex_unlock(&node_lock);
 315        return rc;
 316
 317err_free_iter_info:
 318        gcov_iter_free(iter);
 319err_free_info:
 320        gcov_info_free(info);
 321        goto out_unlock;
 322}
 323
 324/*
 325 * release() implementation for gcov data files. Release resources allocated
 326 * by open().
 327 */
 328static int gcov_seq_release(struct inode *inode, struct file *file)
 329{
 330        struct gcov_iterator *iter;
 331        struct gcov_info *info;
 332        struct seq_file *seq;
 333
 334        seq = file->private_data;
 335        iter = seq->private;
 336        info = gcov_iter_get_info(iter);
 337        gcov_iter_free(iter);
 338        gcov_info_free(info);
 339        seq_release(inode, file);
 340
 341        return 0;
 342}
 343
 344/*
 345 * Find a node by the associated data file name. Needs to be called with
 346 * node_lock held.
 347 */
 348static struct gcov_node *get_node_by_name(const char *name)
 349{
 350        struct gcov_node *node;
 351        struct gcov_info *info;
 352
 353        list_for_each_entry(node, &all_head, all) {
 354                info = get_node_info(node);
 355                if (info && (strcmp(gcov_info_filename(info), name) == 0))
 356                        return node;
 357        }
 358
 359        return NULL;
 360}
 361
 362/*
 363 * Reset all profiling data associated with the specified node.
 364 */
 365static void reset_node(struct gcov_node *node)
 366{
 367        int i;
 368
 369        if (node->unloaded_info)
 370                gcov_info_reset(node->unloaded_info);
 371        for (i = 0; i < node->num_loaded; i++)
 372                gcov_info_reset(node->loaded_info[i]);
 373}
 374
 375static void remove_node(struct gcov_node *node);
 376
 377/*
 378 * write() implementation for gcov data files. Reset profiling data for the
 379 * corresponding file. If all associated object files have been unloaded,
 380 * remove the debug fs node as well.
 381 */
 382static ssize_t gcov_seq_write(struct file *file, const char __user *addr,
 383                              size_t len, loff_t *pos)
 384{
 385        struct seq_file *seq;
 386        struct gcov_info *info;
 387        struct gcov_node *node;
 388
 389        seq = file->private_data;
 390        info = gcov_iter_get_info(seq->private);
 391        mutex_lock(&node_lock);
 392        node = get_node_by_name(gcov_info_filename(info));
 393        if (node) {
 394                /* Reset counts or remove node for unloaded modules. */
 395                if (node->num_loaded == 0)
 396                        remove_node(node);
 397                else
 398                        reset_node(node);
 399        }
 400        /* Reset counts for open file. */
 401        gcov_info_reset(info);
 402        mutex_unlock(&node_lock);
 403
 404        return len;
 405}
 406
 407/*
 408 * Given a string <path> representing a file path of format:
 409 *   path/to/file.gcda
 410 * construct and return a new string:
 411 *   <dir/>path/to/file.<ext>
 412 */
 413static char *link_target(const char *dir, const char *path, const char *ext)
 414{
 415        char *target;
 416        char *old_ext;
 417        char *copy;
 418
 419        copy = kstrdup(path, GFP_KERNEL);
 420        if (!copy)
 421                return NULL;
 422        old_ext = strrchr(copy, '.');
 423        if (old_ext)
 424                *old_ext = '\0';
 425        if (dir)
 426                target = kasprintf(GFP_KERNEL, "%s/%s.%s", dir, copy, ext);
 427        else
 428                target = kasprintf(GFP_KERNEL, "%s.%s", copy, ext);
 429        kfree(copy);
 430
 431        return target;
 432}
 433
 434/*
 435 * Construct a string representing the symbolic link target for the given
 436 * gcov data file name and link type. Depending on the link type and the
 437 * location of the data file, the link target can either point to a
 438 * subdirectory of srctree, objtree or in an external location.
 439 */
 440static char *get_link_target(const char *filename, const struct gcov_link *ext)
 441{
 442        const char *rel;
 443        char *result;
 444
 445        if (strncmp(filename, objtree, strlen(objtree)) == 0) {
 446                rel = filename + strlen(objtree) + 1;
 447                if (ext->dir == SRC_TREE)
 448                        result = link_target(srctree, rel, ext->ext);
 449                else
 450                        result = link_target(objtree, rel, ext->ext);
 451        } else {
 452                /* External compilation. */
 453                result = link_target(NULL, filename, ext->ext);
 454        }
 455
 456        return result;
 457}
 458
 459#define SKEW_PREFIX     ".tmp_"
 460
 461/*
 462 * For a filename .tmp_filename.ext return filename.ext. Needed to compensate
 463 * for filename skewing caused by the mod-versioning mechanism.
 464 */
 465static const char *deskew(const char *basename)
 466{
 467        if (strncmp(basename, SKEW_PREFIX, sizeof(SKEW_PREFIX) - 1) == 0)
 468                return basename + sizeof(SKEW_PREFIX) - 1;
 469        return basename;
 470}
 471
 472/*
 473 * Create links to additional files (usually .c and .gcno files) which the
 474 * gcov tool expects to find in the same directory as the gcov data file.
 475 */
 476static void add_links(struct gcov_node *node, struct dentry *parent)
 477{
 478        const char *basename;
 479        char *target;
 480        int num;
 481        int i;
 482
 483        for (num = 0; gcov_link[num].ext; num++)
 484                /* Nothing. */;
 485        node->links = kcalloc(num, sizeof(struct dentry *), GFP_KERNEL);
 486        if (!node->links)
 487                return;
 488        for (i = 0; i < num; i++) {
 489                target = get_link_target(
 490                                gcov_info_filename(get_node_info(node)),
 491                                &gcov_link[i]);
 492                if (!target)
 493                        goto out_err;
 494                basename = kbasename(target);
 495                if (basename == target)
 496                        goto out_err;
 497                node->links[i] = debugfs_create_symlink(deskew(basename),
 498                                                        parent, target);
 499                kfree(target);
 500        }
 501
 502        return;
 503out_err:
 504        kfree(target);
 505        while (i-- > 0)
 506                debugfs_remove(node->links[i]);
 507        kfree(node->links);
 508        node->links = NULL;
 509}
 510
 511static const struct file_operations gcov_data_fops = {
 512        .open           = gcov_seq_open,
 513        .release        = gcov_seq_release,
 514        .read           = seq_read,
 515        .llseek         = seq_lseek,
 516        .write          = gcov_seq_write,
 517};
 518
 519/* Basic initialization of a new node. */
 520static void init_node(struct gcov_node *node, struct gcov_info *info,
 521                      const char *name, struct gcov_node *parent)
 522{
 523        INIT_LIST_HEAD(&node->list);
 524        INIT_LIST_HEAD(&node->children);
 525        INIT_LIST_HEAD(&node->all);
 526        if (node->loaded_info) {
 527                node->loaded_info[0] = info;
 528                node->num_loaded = 1;
 529        }
 530        node->parent = parent;
 531        if (name)
 532                strcpy(node->name, name);
 533}
 534
 535/*
 536 * Create a new node and associated debugfs entry. Needs to be called with
 537 * node_lock held.
 538 */
 539static struct gcov_node *new_node(struct gcov_node *parent,
 540                                  struct gcov_info *info, const char *name)
 541{
 542        struct gcov_node *node;
 543
 544        node = kzalloc(sizeof(struct gcov_node) + strlen(name) + 1, GFP_KERNEL);
 545        if (!node)
 546                goto err_nomem;
 547        if (info) {
 548                node->loaded_info = kcalloc(1, sizeof(struct gcov_info *),
 549                                           GFP_KERNEL);
 550                if (!node->loaded_info)
 551                        goto err_nomem;
 552        }
 553        init_node(node, info, name, parent);
 554        /* Differentiate between gcov data file nodes and directory nodes. */
 555        if (info) {
 556                node->dentry = debugfs_create_file(deskew(node->name), 0600,
 557                                        parent->dentry, node, &gcov_data_fops);
 558        } else
 559                node->dentry = debugfs_create_dir(node->name, parent->dentry);
 560        if (info)
 561                add_links(node, parent->dentry);
 562        list_add(&node->list, &parent->children);
 563        list_add(&node->all, &all_head);
 564
 565        return node;
 566
 567err_nomem:
 568        kfree(node);
 569        pr_warn("out of memory\n");
 570        return NULL;
 571}
 572
 573/* Remove symbolic links associated with node. */
 574static void remove_links(struct gcov_node *node)
 575{
 576        int i;
 577
 578        if (!node->links)
 579                return;
 580        for (i = 0; gcov_link[i].ext; i++)
 581                debugfs_remove(node->links[i]);
 582        kfree(node->links);
 583        node->links = NULL;
 584}
 585
 586/*
 587 * Remove node from all lists and debugfs and release associated resources.
 588 * Needs to be called with node_lock held.
 589 */
 590static void release_node(struct gcov_node *node)
 591{
 592        list_del(&node->list);
 593        list_del(&node->all);
 594        debugfs_remove(node->dentry);
 595        remove_links(node);
 596        kfree(node->loaded_info);
 597        if (node->unloaded_info)
 598                gcov_info_free(node->unloaded_info);
 599        kfree(node);
 600}
 601
 602/* Release node and empty parents. Needs to be called with node_lock held. */
 603static void remove_node(struct gcov_node *node)
 604{
 605        struct gcov_node *parent;
 606
 607        while ((node != &root_node) && list_empty(&node->children)) {
 608                parent = node->parent;
 609                release_node(node);
 610                node = parent;
 611        }
 612}
 613
 614/*
 615 * Find child node with given basename. Needs to be called with node_lock
 616 * held.
 617 */
 618static struct gcov_node *get_child_by_name(struct gcov_node *parent,
 619                                           const char *name)
 620{
 621        struct gcov_node *node;
 622
 623        list_for_each_entry(node, &parent->children, list) {
 624                if (strcmp(node->name, name) == 0)
 625                        return node;
 626        }
 627
 628        return NULL;
 629}
 630
 631/*
 632 * write() implementation for reset file. Reset all profiling data to zero
 633 * and remove nodes for which all associated object files are unloaded.
 634 */
 635static ssize_t reset_write(struct file *file, const char __user *addr,
 636                           size_t len, loff_t *pos)
 637{
 638        struct gcov_node *node;
 639
 640        mutex_lock(&node_lock);
 641restart:
 642        list_for_each_entry(node, &all_head, all) {
 643                if (node->num_loaded > 0)
 644                        reset_node(node);
 645                else if (list_empty(&node->children)) {
 646                        remove_node(node);
 647                        /* Several nodes may have gone - restart loop. */
 648                        goto restart;
 649                }
 650        }
 651        mutex_unlock(&node_lock);
 652
 653        return len;
 654}
 655
 656/* read() implementation for reset file. Unused. */
 657static ssize_t reset_read(struct file *file, char __user *addr, size_t len,
 658                          loff_t *pos)
 659{
 660        /* Allow read operation so that a recursive copy won't fail. */
 661        return 0;
 662}
 663
 664static const struct file_operations gcov_reset_fops = {
 665        .write  = reset_write,
 666        .read   = reset_read,
 667        .llseek = noop_llseek,
 668};
 669
 670/*
 671 * Create a node for a given profiling data set and add it to all lists and
 672 * debugfs. Needs to be called with node_lock held.
 673 */
 674static void add_node(struct gcov_info *info)
 675{
 676        char *filename;
 677        char *curr;
 678        char *next;
 679        struct gcov_node *parent;
 680        struct gcov_node *node;
 681
 682        filename = kstrdup(gcov_info_filename(info), GFP_KERNEL);
 683        if (!filename)
 684                return;
 685        parent = &root_node;
 686        /* Create directory nodes along the path. */
 687        for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) {
 688                if (curr == next)
 689                        continue;
 690                *next = 0;
 691                if (strcmp(curr, ".") == 0)
 692                        continue;
 693                if (strcmp(curr, "..") == 0) {
 694                        if (!parent->parent)
 695                                goto err_remove;
 696                        parent = parent->parent;
 697                        continue;
 698                }
 699                node = get_child_by_name(parent, curr);
 700                if (!node) {
 701                        node = new_node(parent, NULL, curr);
 702                        if (!node)
 703                                goto err_remove;
 704                }
 705                parent = node;
 706        }
 707        /* Create file node. */
 708        node = new_node(parent, info, curr);
 709        if (!node)
 710                goto err_remove;
 711out:
 712        kfree(filename);
 713        return;
 714
 715err_remove:
 716        remove_node(parent);
 717        goto out;
 718}
 719
 720/*
 721 * Associate a profiling data set with an existing node. Needs to be called
 722 * with node_lock held.
 723 */
 724static void add_info(struct gcov_node *node, struct gcov_info *info)
 725{
 726        struct gcov_info **loaded_info;
 727        int num = node->num_loaded;
 728
 729        /*
 730         * Prepare new array. This is done first to simplify cleanup in
 731         * case the new data set is incompatible, the node only contains
 732         * unloaded data sets and there's not enough memory for the array.
 733         */
 734        loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL);
 735        if (!loaded_info) {
 736                pr_warn("could not add '%s' (out of memory)\n",
 737                        gcov_info_filename(info));
 738                return;
 739        }
 740        memcpy(loaded_info, node->loaded_info,
 741               num * sizeof(struct gcov_info *));
 742        loaded_info[num] = info;
 743        /* Check if the new data set is compatible. */
 744        if (num == 0) {
 745                /*
 746                 * A module was unloaded, modified and reloaded. The new
 747                 * data set replaces the copy of the last one.
 748                 */
 749                if (!gcov_info_is_compatible(node->unloaded_info, info)) {
 750                        pr_warn("discarding saved data for %s "
 751                                "(incompatible version)\n",
 752                                gcov_info_filename(info));
 753                        gcov_info_free(node->unloaded_info);
 754                        node->unloaded_info = NULL;
 755                }
 756        } else {
 757                /*
 758                 * Two different versions of the same object file are loaded.
 759                 * The initial one takes precedence.
 760                 */
 761                if (!gcov_info_is_compatible(node->loaded_info[0], info)) {
 762                        pr_warn("could not add '%s' (incompatible "
 763                                "version)\n", gcov_info_filename(info));
 764                        kfree(loaded_info);
 765                        return;
 766                }
 767        }
 768        /* Overwrite previous array. */
 769        kfree(node->loaded_info);
 770        node->loaded_info = loaded_info;
 771        node->num_loaded = num + 1;
 772}
 773
 774/*
 775 * Return the index of a profiling data set associated with a node.
 776 */
 777static int get_info_index(struct gcov_node *node, struct gcov_info *info)
 778{
 779        int i;
 780
 781        for (i = 0; i < node->num_loaded; i++) {
 782                if (node->loaded_info[i] == info)
 783                        return i;
 784        }
 785        return -ENOENT;
 786}
 787
 788/*
 789 * Save the data of a profiling data set which is being unloaded.
 790 */
 791static void save_info(struct gcov_node *node, struct gcov_info *info)
 792{
 793        if (node->unloaded_info)
 794                gcov_info_add(node->unloaded_info, info);
 795        else {
 796                node->unloaded_info = gcov_info_dup(info);
 797                if (!node->unloaded_info) {
 798                        pr_warn("could not save data for '%s' "
 799                                "(out of memory)\n",
 800                                gcov_info_filename(info));
 801                }
 802        }
 803}
 804
 805/*
 806 * Disassociate a profiling data set from a node. Needs to be called with
 807 * node_lock held.
 808 */
 809static void remove_info(struct gcov_node *node, struct gcov_info *info)
 810{
 811        int i;
 812
 813        i = get_info_index(node, info);
 814        if (i < 0) {
 815                pr_warn("could not remove '%s' (not found)\n",
 816                        gcov_info_filename(info));
 817                return;
 818        }
 819        if (gcov_persist)
 820                save_info(node, info);
 821        /* Shrink array. */
 822        node->loaded_info[i] = node->loaded_info[node->num_loaded - 1];
 823        node->num_loaded--;
 824        if (node->num_loaded > 0)
 825                return;
 826        /* Last loaded data set was removed. */
 827        kfree(node->loaded_info);
 828        node->loaded_info = NULL;
 829        node->num_loaded = 0;
 830        if (!node->unloaded_info)
 831                remove_node(node);
 832}
 833
 834/*
 835 * Callback to create/remove profiling files when code compiled with
 836 * -fprofile-arcs is loaded/unloaded.
 837 */
 838void gcov_event(enum gcov_action action, struct gcov_info *info)
 839{
 840        struct gcov_node *node;
 841
 842        mutex_lock(&node_lock);
 843        node = get_node_by_name(gcov_info_filename(info));
 844        switch (action) {
 845        case GCOV_ADD:
 846                if (node)
 847                        add_info(node, info);
 848                else
 849                        add_node(info);
 850                break;
 851        case GCOV_REMOVE:
 852                if (node)
 853                        remove_info(node, info);
 854                else {
 855                        pr_warn("could not remove '%s' (not found)\n",
 856                                gcov_info_filename(info));
 857                }
 858                break;
 859        }
 860        mutex_unlock(&node_lock);
 861}
 862
 863/* Create debugfs entries. */
 864static __init int gcov_fs_init(void)
 865{
 866        init_node(&root_node, NULL, NULL, NULL);
 867        /*
 868         * /sys/kernel/debug/gcov will be parent for the reset control file
 869         * and all profiling files.
 870         */
 871        root_node.dentry = debugfs_create_dir("gcov", NULL);
 872        /*
 873         * Create reset file which resets all profiling counts when written
 874         * to.
 875         */
 876        debugfs_create_file("reset", 0600, root_node.dentry, NULL,
 877                            &gcov_reset_fops);
 878        /* Replay previous events to get our fs hierarchy up-to-date. */
 879        gcov_enable_events();
 880        return 0;
 881}
 882device_initcall(gcov_fs_init);
 883