runtime/linux/stp_tracepoint.c - systemtap
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/notifier.h>
#include <linux/tracepoint.h>
#include <linux/slab.h>
#include <linux/jhash.h>
#include <linux/module.h>
#include "stp_tracepoint.h"
static
DEFINE_MUTEX(stp_tracepoint_mutex);
#define TRACEPOINT_HASH_BITS 6
#define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS)
static
struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
struct tracepoint_entry {
struct hlist_node hlist;
struct tracepoint *tp;
int refcount;
struct list_head probes;
char name[0];
};
struct stp_tp_probe {
struct tracepoint_func tp_func;
struct list_head list;
};
static
int add_probe(struct tracepoint_entry *e, void *probe, void *data)
{
struct stp_tp_probe *p;
int found = 0;
list_for_each_entry(p, &e->probes, list) {
if (p->tp_func.func == probe && p->tp_func.data == data) {
found = 1;
break;
}
}
if (found)
return -EEXIST;
p = _stp_kmalloc_gfp(sizeof(struct stp_tp_probe),
STP_ALLOC_SLEEP_FLAGS);
if (!p)
return -ENOMEM;
p->tp_func.func = probe;
p->tp_func.data = data;
list_add(&p->list, &e->probes);
return 0;
}
static
int remove_probe(struct tracepoint_entry *e, void *probe, void *data)
{
struct stp_tp_probe *p;
int found = 0;
list_for_each_entry(p, &e->probes, list) {
if (p->tp_func.func == probe && p->tp_func.data == data) {
found = 1;
break;
}
}
if (found) {
list_del(&p->list);
_stp_kfree(p);
return 0;
} else {
return -ENOENT;
}
}
static
struct tracepoint_entry *get_tracepoint(const char *name)
{
struct hlist_head *head;
struct tracepoint_entry *e;
u32 hash = jhash(name, strlen(name), 0);
head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
hlist_for_each_entry(e, head, hlist) {
if (!strcmp(name, e->name))
return e;
}
return NULL;
}
static
struct tracepoint_entry *add_tracepoint(const char *name)
{
struct hlist_head *head;
struct tracepoint_entry *e;
size_t name_len = strlen(name) + 1;
u32 hash = jhash(name, name_len - 1, 0);
head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
hlist_for_each_entry(e, head, hlist) {
if (!strcmp(name, e->name)) {
return ERR_PTR(-EEXIST); }
}
e = _stp_kmalloc_gfp(sizeof(struct tracepoint_entry) + name_len,
STP_ALLOC_SLEEP_FLAGS);
if (!e)
return ERR_PTR(-ENOMEM);
memcpy(&e->name[0], name, name_len);
e->tp = NULL;
e->refcount = 0;
INIT_LIST_HEAD(&e->probes);
hlist_add_head(&e->hlist, head);
return e;
}
static
void remove_tracepoint(struct tracepoint_entry *e)
{
hlist_del(&e->hlist);
_stp_kfree(e);
}
static
void do_tracepoint_probe_register(struct tracepoint *tp,
void *probe, void *data)
{
int ret = tracepoint_probe_register(tp, probe, data);
if (ret) {
dbug_tp(1, "error (%d) registering probe '%s'\n",
ret, tp->name);
} else {
dbug_tp(2, "registered probe '%s'\n", tp->name);
}
}
static
void do_tracepoint_probe_unregister(struct tracepoint *tp,
void *probe, void *data)
{
int ret = tracepoint_probe_unregister(tp, probe, data);
if (ret) {
dbug_tp(1, "error (%d) unregistering probe '%s'\n",
ret, tp->name);
} else {
dbug_tp(2, "unregistered probe '%s'\n", tp->name);
}
}
int stp_tracepoint_probe_register(const char *name, void *probe, void *data)
{
struct tracepoint_entry *e;
int ret = 0;
mutex_lock(&stp_tracepoint_mutex);
e = get_tracepoint(name);
if (!e) {
e = add_tracepoint(name);
if (IS_ERR(e)) {
ret = PTR_ERR(e);
goto end;
}
}
ret = add_probe(e, probe, data);
if (ret)
goto end;
e->refcount++;
dbug_tp(2, "added probe on '%s'\n", name);
if (e->tp) {
do_tracepoint_probe_register(e->tp, probe, data);
}
end:
mutex_unlock(&stp_tracepoint_mutex);
if (ret) {
dbug_tp(1, "error (%d) adding probe on '%s'\n", ret, name);
}
return ret;
}
int stp_tracepoint_probe_unregister(const char *name, void *probe, void *data)
{
struct tracepoint_entry *e;
int ret = 0;
mutex_lock(&stp_tracepoint_mutex);
e = get_tracepoint(name);
if (!e) {
ret = -ENOENT;
goto end;
}
ret = remove_probe(e, probe, data);
if (ret)
goto end;
if (e->tp) {
do_tracepoint_probe_unregister(e->tp, probe, data);
}
if (!--e->refcount)
remove_tracepoint(e);
dbug_tp(2, "removed probe on '%s'\n", name);
end:
mutex_unlock(&stp_tracepoint_mutex);
if (ret) {
dbug_tp(1, "error (%d) removing probe on '%s'\n", ret, name);
}
return ret;
}
#ifdef CONFIG_MODULES
static
int stp_tracepoint_coming(struct tp_module *tp_mod)
{
int i;
mutex_lock(&stp_tracepoint_mutex);
for (i = 0; i < tp_mod->mod->num_tracepoints; i++) {
struct tracepoint *tp;
struct tracepoint_entry *e;
struct stp_tp_probe *p;
tp = tp_mod->mod->tracepoints_ptrs[i];
e = get_tracepoint(tp->name);
if (!e) {
e = add_tracepoint(tp->name);
if (IS_ERR(e)) {
dbug_tp(1, "error (%ld) adding %s:%s\n",
PTR_ERR(e), tp_mod->mod->name, tp->name);
continue;
}
}
if (e->tp) {
if (e->tp != tp) {
dbug_tp(1, "found duplicate '%s'\n", tp->name);
}
continue;
}
e->tp = tp;
e->refcount++;
dbug_tp(2, "added %s:%s\n", tp_mod->mod->name, tp->name);
list_for_each_entry(p, &e->probes, list) {
do_tracepoint_probe_register(e->tp,
p->tp_func.func, p->tp_func.data);
}
}
mutex_unlock(&stp_tracepoint_mutex);
return 0;
}
static
int stp_tracepoint_going(struct tp_module *tp_mod)
{
int i;
mutex_lock(&stp_tracepoint_mutex);
for (i = 0; i < tp_mod->mod->num_tracepoints; i++) {
struct tracepoint *tp;
struct tracepoint_entry *e;
struct stp_tp_probe *p;
tp = tp_mod->mod->tracepoints_ptrs[i];
e = get_tracepoint(tp->name);
if (!e || !e->tp)
continue;
list_for_each_entry(p, &e->probes, list) {
do_tracepoint_probe_unregister(e->tp,
p->tp_func.func, p->tp_func.data);
}
e->tp = NULL;
if (!--e->refcount)
remove_tracepoint(e);
dbug_tp(2, "removed %s:%s\n", tp_mod->mod->name, tp->name);
}
mutex_unlock(&stp_tracepoint_mutex);
return 0;
}
static
int stp_tracepoint_notify(struct notifier_block *self,
unsigned long val, void *data)
{
struct tp_module *tp_mod = data;
int ret = 0;
switch (val) {
case MODULE_STATE_COMING:
ret = stp_tracepoint_coming(tp_mod);
break;
case MODULE_STATE_GOING:
ret = stp_tracepoint_going(tp_mod);
break;
default:
break;
}
return ret;
}
static
struct notifier_block stp_tracepoint_notifier = {
.notifier_call = stp_tracepoint_notify,
.priority = 0,
};
static
int stp_tracepoint_module_init(void)
{
return register_tracepoint_module_notifier(&stp_tracepoint_notifier);
}
static
void stp_tracepoint_module_exit(void)
{
(void)unregister_tracepoint_module_notifier(&stp_tracepoint_notifier);
}
#else
static
int stp_tracepoint_module_init(void)
{
return 0;
}
static
void stp_tracepoint_module_exit(void)
{
}
#endif
static
void stp_kernel_tracepoint_add(struct tracepoint *tp, void *priv)
{
struct tracepoint_entry *e;
struct stp_tp_probe *p;
int *ret = priv;
mutex_lock(&stp_tracepoint_mutex);
e = get_tracepoint(tp->name);
if (!e) {
e = add_tracepoint(tp->name);
if (IS_ERR(e)) {
dbug_tp(1, "error (%ld) adding kernel:%s\n",
PTR_ERR(e), tp->name);
*ret = (int) PTR_ERR(e);
goto end;
}
}
if (e->tp) {
if (e->tp != tp) {
dbug_tp(1, "found duplicate '%s'\n", tp->name);
}
goto end;
}
e->tp = tp;
e->refcount++;
dbug_tp(2, "added kernel:%s\n", tp->name);
list_for_each_entry(p, &e->probes, list) {
do_tracepoint_probe_register(e->tp,
p->tp_func.func, p->tp_func.data);
}
end:
mutex_unlock(&stp_tracepoint_mutex);
}
static
void stp_kernel_tracepoint_remove(struct tracepoint *tp, void *priv)
{
struct tracepoint_entry *e;
int *ret = priv;
mutex_lock(&stp_tracepoint_mutex);
e = get_tracepoint(tp->name);
if (!e || e->refcount != 1 || !list_empty(&e->probes)) {
*ret = -EINVAL;
goto end;
}
remove_tracepoint(e);
dbug_tp(2, "removed kernel:%s\n", tp->name);
end:
mutex_unlock(&stp_tracepoint_mutex);
}
static
int stp_tracepoint_init(void)
{
int error_ret = 0;
int ret = 0;
for_each_kernel_tracepoint(stp_kernel_tracepoint_add, &ret);
if (!ret)
ret = stp_tracepoint_module_init();
if (!ret)
return 0;
for_each_kernel_tracepoint(stp_kernel_tracepoint_remove, &error_ret);
return ret;
}
static
void stp_tracepoint_exit(void)
{
int i, ret = 0;
stp_tracepoint_module_exit();
for_each_kernel_tracepoint(stp_kernel_tracepoint_remove, &ret);
mutex_lock(&stp_tracepoint_mutex);
for (i = 0; i < TRACEPOINT_TABLE_SIZE; i++) {
struct hlist_head *head = &tracepoint_table[i];
if (!hlist_empty(head)) {
dbug_tp(1, "tracepoint_table[%d] is not empty!\n", i);
}
}
mutex_unlock(&stp_tracepoint_mutex);
}