struct ftrace_ops ops;
struct trace_array *tr;
struct list_head list;
+ void *data;
int ref;
};
probe_ops = probe->probe_ops;
if (probe_ops->print)
- return probe_ops->print(m, probe_entry->ip, probe_ops, NULL);
+ return probe_ops->print(m, probe_entry->ip, probe_ops, probe->data);
seq_printf(m, "%ps:%ps\n", (void *)probe_entry->ip,
(void *)probe_ops->func);
* on the hash. rcu_read_lock is too dangerous here.
*/
preempt_disable_notrace();
- probe_ops->func(ip, parent_ip, probe->tr, probe_ops, NULL);
+ probe_ops->func(ip, parent_ip, probe->tr, probe_ops, probe->data);
preempt_enable_notrace();
}
if (!probe->ref) {
probe_ops = probe->probe_ops;
+ /*
+ * Sending zero as ip tells probe_ops to free
+ * the probe->data itself
+ */
+ if (probe_ops->free)
+ probe_ops->free(probe_ops, probe->tr, 0, probe->data);
list_del(&probe->list);
kfree(probe);
}
*/
if (probe_ops->init) {
ret = probe_ops->init(probe_ops, tr,
- entry->ip, data);
- if (ret < 0)
+ entry->ip, data,
+ &probe->data);
+ if (ret < 0) {
+ if (probe_ops->free && count)
+ probe_ops->free(probe_ops, tr,
+ 0, probe->data);
+ probe->data = NULL;
goto out;
+ }
}
count++;
}
hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
if (ftrace_lookup_ip(old_hash, entry->ip))
continue;
- probe_ops->free(probe_ops, tr, entry->ip, NULL);
+ probe_ops->free(probe_ops, tr, entry->ip, probe->data);
}
}
goto out_unlock;
hlist_for_each_entry_safe(entry, tmp, &hhd, hlist) {
hlist_del(&entry->hlist);
if (probe_ops->free)
- probe_ops->free(probe_ops, tr, entry->ip, NULL);
+ probe_ops->free(probe_ops, tr, entry->ip, probe->data);
kfree(entry);
}
mutex_unlock(&ftrace_lock);
static void
ftrace_snapshot(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
tracing_snapshot();
}
static void
ftrace_count_snapshot(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = data;
long *count = NULL;
if (mapper)
ftrace_snapshot_print(struct seq_file *m, unsigned long ip,
struct ftrace_probe_ops *ops, void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = data;
long *count = NULL;
seq_printf(m, "%ps:", (void *)ip);
static int
ftrace_snapshot_init(struct ftrace_probe_ops *ops, struct trace_array *tr,
- unsigned long ip, void *data)
+ unsigned long ip, void *init_data, void **data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = *data;
+
+ if (!mapper) {
+ mapper = allocate_ftrace_func_mapper();
+ if (!mapper)
+ return -ENOMEM;
+ *data = mapper;
+ }
- return ftrace_func_mapper_add_ip(mapper, ip, data);
+ return ftrace_func_mapper_add_ip(mapper, ip, init_data);
}
static void
ftrace_snapshot_free(struct ftrace_probe_ops *ops, struct trace_array *tr,
- unsigned long ip, void **data)
+ unsigned long ip, void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = data;
+
+ if (!ip) {
+ if (!mapper)
+ return;
+ free_ftrace_func_mapper(mapper, NULL);
+ return;
+ }
ftrace_func_mapper_remove_ip(mapper, ip);
}
if (!strlen(number))
goto out_reg;
- if (!ops->private_data) {
- ops->private_data = allocate_ftrace_func_mapper();
- if (!ops->private_data)
- return -ENOMEM;
- }
-
/*
* We use the callback data field (which is a pointer)
* as our counter.
unsigned long parent_ip,
struct trace_array *tr,
struct ftrace_probe_ops *ops,
- void **data);
+ void *data);
int (*init)(struct ftrace_probe_ops *ops,
struct trace_array *tr,
- unsigned long ip, void *data);
+ unsigned long ip, void *init_data,
+ void **data);
void (*free)(struct ftrace_probe_ops *ops,
struct trace_array *tr,
- unsigned long ip, void **data);
+ unsigned long ip, void *data);
int (*print)(struct seq_file *m,
unsigned long ip,
struct ftrace_probe_ops *ops,
void *data);
- void *private_data;
};
struct ftrace_func_mapper;
static void
event_enable_probe(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **_data)
+ void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
- struct event_probe_data *data;
+ struct ftrace_func_mapper *mapper = data;
+ struct event_probe_data *edata;
void **pdata;
pdata = ftrace_func_mapper_find_ip(mapper, ip);
if (!pdata || !*pdata)
return;
- data = *pdata;
- update_event_probe(data);
+ edata = *pdata;
+ update_event_probe(edata);
}
static void
event_enable_count_probe(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **_data)
+ void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
- struct event_probe_data *data;
+ struct ftrace_func_mapper *mapper = data;
+ struct event_probe_data *edata;
void **pdata;
pdata = ftrace_func_mapper_find_ip(mapper, ip);
if (!pdata || !*pdata)
return;
- data = *pdata;
+ edata = *pdata;
- if (!data->count)
+ if (!edata->count)
return;
/* Skip if the event is in a state we want to switch to */
- if (data->enable == !(data->file->flags & EVENT_FILE_FL_SOFT_DISABLED))
+ if (edata->enable == !(edata->file->flags & EVENT_FILE_FL_SOFT_DISABLED))
return;
- if (data->count != -1)
- (data->count)--;
+ if (edata->count != -1)
+ (edata->count)--;
- update_event_probe(data);
+ update_event_probe(edata);
}
static int
event_enable_print(struct seq_file *m, unsigned long ip,
- struct ftrace_probe_ops *ops, void *_data)
+ struct ftrace_probe_ops *ops, void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
- struct event_probe_data *data;
+ struct ftrace_func_mapper *mapper = data;
+ struct event_probe_data *edata;
void **pdata;
pdata = ftrace_func_mapper_find_ip(mapper, ip);
if (WARN_ON_ONCE(!pdata || !*pdata))
return 0;
- data = *pdata;
+ edata = *pdata;
seq_printf(m, "%ps:", (void *)ip);
seq_printf(m, "%s:%s:%s",
- data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR,
- data->file->event_call->class->system,
- trace_event_name(data->file->event_call));
+ edata->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR,
+ edata->file->event_call->class->system,
+ trace_event_name(edata->file->event_call));
- if (data->count == -1)
+ if (edata->count == -1)
seq_puts(m, ":unlimited\n");
else
- seq_printf(m, ":count=%ld\n", data->count);
+ seq_printf(m, ":count=%ld\n", edata->count);
return 0;
}
static int
event_enable_init(struct ftrace_probe_ops *ops, struct trace_array *tr,
- unsigned long ip, void *_data)
+ unsigned long ip, void *init_data, void **data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
- struct event_probe_data *data = _data;
+ struct ftrace_func_mapper *mapper = *data;
+ struct event_probe_data *edata = init_data;
int ret;
- ret = ftrace_func_mapper_add_ip(mapper, ip, data);
+ if (!mapper) {
+ mapper = allocate_ftrace_func_mapper();
+ if (!mapper)
+ return -ENODEV;
+ *data = mapper;
+ }
+
+ ret = ftrace_func_mapper_add_ip(mapper, ip, edata);
if (ret < 0)
return ret;
- data->ref++;
+ edata->ref++;
+
+ return 0;
+}
+
+static int free_probe_data(void *data)
+{
+ struct event_probe_data *edata = data;
+ edata->ref--;
+ if (!edata->ref) {
+ /* Remove the SOFT_MODE flag */
+ __ftrace_event_enable_disable(edata->file, 0, 1);
+ module_put(edata->file->event_call->mod);
+ kfree(edata);
+ }
return 0;
}
static void
event_enable_free(struct ftrace_probe_ops *ops, struct trace_array *tr,
- unsigned long ip, void **_data)
+ unsigned long ip, void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
- struct event_probe_data *data;
+ struct ftrace_func_mapper *mapper = data;
+ struct event_probe_data *edata;
+
+ if (!ip) {
+ if (!mapper)
+ return;
+ free_ftrace_func_mapper(mapper, free_probe_data);
+ return;
+ }
- data = ftrace_func_mapper_remove_ip(mapper, ip);
+ edata = ftrace_func_mapper_remove_ip(mapper, ip);
- if (WARN_ON_ONCE(!data))
+ if (WARN_ON_ONCE(!edata))
return;
- if (WARN_ON_ONCE(data->ref <= 0))
+ if (WARN_ON_ONCE(edata->ref <= 0))
return;
- data->ref--;
- if (!data->ref) {
- /* Remove the SOFT_MODE flag */
- __ftrace_event_enable_disable(data->file, 0, 1);
- module_put(data->file->event_call->mod);
- kfree(data);
- }
+ free_probe_data(edata);
}
static struct ftrace_probe_ops event_enable_probe_ops = {
ret = -ENOMEM;
- if (!ops->private_data) {
- ops->private_data = allocate_ftrace_func_mapper();
- if (!ops->private_data)
- goto out;
- }
-
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
goto out;
#ifdef CONFIG_DYNAMIC_FTRACE
static void update_traceon_count(struct ftrace_probe_ops *ops,
- unsigned long ip, bool on)
+ unsigned long ip, bool on,
+ void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = data;
long *count;
long old_count;
static void
ftrace_traceon_count(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
- update_traceon_count(ops, ip, 1);
+ update_traceon_count(ops, ip, 1, data);
}
static void
ftrace_traceoff_count(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
- update_traceon_count(ops, ip, 0);
+ update_traceon_count(ops, ip, 0, data);
}
static void
ftrace_traceon(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
if (tracing_is_on())
return;
static void
ftrace_traceoff(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
if (!tracing_is_on())
return;
static void
ftrace_stacktrace(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
trace_dump_stack(STACK_SKIP);
}
static void
ftrace_stacktrace_count(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = data;
long *count;
long old_count;
long new_count;
} while (new_count != old_count);
}
-static int update_count(struct ftrace_probe_ops *ops, unsigned long ip)
+static int update_count(struct ftrace_probe_ops *ops, unsigned long ip,
+ void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = data;
long *count = NULL;
if (mapper)
static void
ftrace_dump_probe(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
- if (update_count(ops, ip))
+ if (update_count(ops, ip, data))
ftrace_dump(DUMP_ALL);
}
static void
ftrace_cpudump_probe(unsigned long ip, unsigned long parent_ip,
struct trace_array *tr, struct ftrace_probe_ops *ops,
- void **data)
+ void *data)
{
- if (update_count(ops, ip))
+ if (update_count(ops, ip, data))
ftrace_dump(DUMP_ORIG);
}
static int
ftrace_probe_print(const char *name, struct seq_file *m,
- unsigned long ip, struct ftrace_probe_ops *ops)
+ unsigned long ip, struct ftrace_probe_ops *ops,
+ void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = data;
long *count = NULL;
seq_printf(m, "%ps:%s", (void *)ip, name);
struct ftrace_probe_ops *ops,
void *data)
{
- return ftrace_probe_print("traceon", m, ip, ops);
+ return ftrace_probe_print("traceon", m, ip, ops, data);
}
static int
ftrace_traceoff_print(struct seq_file *m, unsigned long ip,
struct ftrace_probe_ops *ops, void *data)
{
- return ftrace_probe_print("traceoff", m, ip, ops);
+ return ftrace_probe_print("traceoff", m, ip, ops, data);
}
static int
ftrace_stacktrace_print(struct seq_file *m, unsigned long ip,
struct ftrace_probe_ops *ops, void *data)
{
- return ftrace_probe_print("stacktrace", m, ip, ops);
+ return ftrace_probe_print("stacktrace", m, ip, ops, data);
}
static int
ftrace_dump_print(struct seq_file *m, unsigned long ip,
struct ftrace_probe_ops *ops, void *data)
{
- return ftrace_probe_print("dump", m, ip, ops);
+ return ftrace_probe_print("dump", m, ip, ops, data);
}
static int
ftrace_cpudump_print(struct seq_file *m, unsigned long ip,
struct ftrace_probe_ops *ops, void *data)
{
- return ftrace_probe_print("cpudump", m, ip, ops);
+ return ftrace_probe_print("cpudump", m, ip, ops, data);
}
static int
ftrace_count_init(struct ftrace_probe_ops *ops, struct trace_array *tr,
- unsigned long ip, void *data)
+ unsigned long ip, void *init_data, void **data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = *data;
+
+ if (!mapper) {
+ mapper = allocate_ftrace_func_mapper();
+ if (!mapper)
+ return -ENOMEM;
+ *data = mapper;
+ }
- return ftrace_func_mapper_add_ip(mapper, ip, data);
+ return ftrace_func_mapper_add_ip(mapper, ip, init_data);
}
static void
ftrace_count_free(struct ftrace_probe_ops *ops, struct trace_array *tr,
- unsigned long ip, void **_data)
+ unsigned long ip, void *data)
{
- struct ftrace_func_mapper *mapper = ops->private_data;
+ struct ftrace_func_mapper *mapper = data;
+
+ if (!ip) {
+ free_ftrace_func_mapper(mapper, NULL);
+ return;
+ }
ftrace_func_mapper_remove_ip(mapper, ip);
}
if (!strlen(number))
goto out_reg;
- if (!ops->private_data) {
- ops->private_data = allocate_ftrace_func_mapper();
- if (!ops->private_data)
- return -ENOMEM;
- }
-
/*
* We use the callback data field (which is a pointer)
* as our counter.