// SPDX-License-Identifier: GPL-2.0 
 | 
/* 
 | 
 *  This code maintains a list of active profiling data structures. 
 | 
 * 
 | 
 *    Copyright IBM Corp. 2009 
 | 
 *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com> 
 | 
 * 
 | 
 *    Uses gcc-internal data definitions. 
 | 
 *    Based on the gcov-kernel patch by: 
 | 
 *         Hubertus Franke <frankeh@us.ibm.com> 
 | 
 *         Nigel Hinds <nhinds@us.ibm.com> 
 | 
 *         Rajan Ravindran <rajancr@us.ibm.com> 
 | 
 *         Peter Oberparleiter <oberpar@linux.vnet.ibm.com> 
 | 
 *         Paul Larson 
 | 
 */ 
 | 
  
 | 
#define pr_fmt(fmt)    "gcov: " fmt 
 | 
  
 | 
#include <linux/init.h> 
 | 
#include <linux/module.h> 
 | 
#include <linux/mutex.h> 
 | 
#include <linux/sched.h> 
 | 
#include "gcov.h" 
 | 
  
 | 
int gcov_events_enabled; 
 | 
DEFINE_MUTEX(gcov_lock); 
 | 
  
 | 
/** 
 | 
 * gcov_enable_events - enable event reporting through gcov_event() 
 | 
 * 
 | 
 * Turn on reporting of profiling data load/unload-events through the 
 | 
 * gcov_event() callback. Also replay all previous events once. This function 
 | 
 * is needed because some events are potentially generated too early for the 
 | 
 * callback implementation to handle them initially. 
 | 
 */ 
 | 
void gcov_enable_events(void) 
 | 
{ 
 | 
    struct gcov_info *info = NULL; 
 | 
  
 | 
    mutex_lock(&gcov_lock); 
 | 
    gcov_events_enabled = 1; 
 | 
  
 | 
    /* Perform event callback for previously registered entries. */ 
 | 
    while ((info = gcov_info_next(info))) { 
 | 
        gcov_event(GCOV_ADD, info); 
 | 
        cond_resched(); 
 | 
    } 
 | 
  
 | 
    mutex_unlock(&gcov_lock); 
 | 
} 
 | 
  
 | 
#ifdef CONFIG_MODULES 
 | 
/* Update list and generate events when modules are unloaded. */ 
 | 
static int gcov_module_notifier(struct notifier_block *nb, unsigned long event, 
 | 
                void *data) 
 | 
{ 
 | 
    struct module *mod = data; 
 | 
    struct gcov_info *info = NULL; 
 | 
    struct gcov_info *prev = NULL; 
 | 
  
 | 
    if (event != MODULE_STATE_GOING) 
 | 
        return NOTIFY_OK; 
 | 
    mutex_lock(&gcov_lock); 
 | 
  
 | 
    /* Remove entries located in module from linked list. */ 
 | 
    while ((info = gcov_info_next(info))) { 
 | 
        if (gcov_info_within_module(info, mod)) { 
 | 
            gcov_info_unlink(prev, info); 
 | 
            if (gcov_events_enabled) 
 | 
                gcov_event(GCOV_REMOVE, info); 
 | 
        } else 
 | 
            prev = info; 
 | 
    } 
 | 
  
 | 
    mutex_unlock(&gcov_lock); 
 | 
  
 | 
    return NOTIFY_OK; 
 | 
} 
 | 
  
 | 
static struct notifier_block gcov_nb = { 
 | 
    .notifier_call    = gcov_module_notifier, 
 | 
}; 
 | 
  
 | 
static int __init gcov_init(void) 
 | 
{ 
 | 
    return register_module_notifier(&gcov_nb); 
 | 
} 
 | 
device_initcall(gcov_init); 
 | 
#endif /* CONFIG_MODULES */ 
 |