Skip to content

Commit

Permalink
ftrace: Replace FTRACE_FL_NOTRACE flag with a hash of ignored functions
Browse files Browse the repository at this point in the history
To prepare for the accounting system that will allow multiple users of
the function tracer, having the FTRACE_FL_NOTRACE as a flag in the
dyn_trace record does not make sense.

All ftrace_ops will soon have a hash of functions they should trace
and not trace. By making a global hash of functions not to trace makes
this easier for the transition.

Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
  • Loading branch information
Steven Rostedt authored and Steven Rostedt committed May 18, 2011
1 parent 9469234 commit b448c4e
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 27 deletions.
1 change: 0 additions & 1 deletion include/linux/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ enum {
FTRACE_FL_FREE = (1 << 0),
FTRACE_FL_FILTER = (1 << 1),
FTRACE_FL_ENABLED = (1 << 2),
FTRACE_FL_NOTRACE = (1 << 3),
};

struct dyn_ftrace {
Expand Down
176 changes: 150 additions & 26 deletions kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
/* hash bits for specific function selection */
#define FTRACE_HASH_BITS 7
#define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS)
#define FTRACE_HASH_MAX_BITS 10

/* ftrace_enabled is a method to turn ftrace on or off */
int ftrace_enabled __read_mostly;
Expand Down Expand Up @@ -865,6 +866,22 @@ enum {
FTRACE_START_FUNC_RET = (1 << 3),
FTRACE_STOP_FUNC_RET = (1 << 4),
};
struct ftrace_func_entry {
struct hlist_node hlist;
unsigned long ip;
};

struct ftrace_hash {
unsigned long size_bits;
struct hlist_head *buckets;
unsigned long count;
};

static struct hlist_head notrace_buckets[1 << FTRACE_HASH_MAX_BITS];
static struct ftrace_hash notrace_hash = {
.size_bits = FTRACE_HASH_MAX_BITS,
.buckets = notrace_buckets,
};

static int ftrace_filtered;

Expand All @@ -889,6 +906,79 @@ static struct ftrace_page *ftrace_pages;

static struct dyn_ftrace *ftrace_free_records;

static struct ftrace_func_entry *
ftrace_lookup_ip(struct ftrace_hash *hash, unsigned long ip)
{
unsigned long key;
struct ftrace_func_entry *entry;
struct hlist_head *hhd;
struct hlist_node *n;

if (!hash->count)
return NULL;

if (hash->size_bits > 0)
key = hash_long(ip, hash->size_bits);
else
key = 0;

hhd = &hash->buckets[key];

hlist_for_each_entry_rcu(entry, n, hhd, hlist) {
if (entry->ip == ip)
return entry;
}
return NULL;
}

static int add_hash_entry(struct ftrace_hash *hash, unsigned long ip)
{
struct ftrace_func_entry *entry;
struct hlist_head *hhd;
unsigned long key;

entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;

if (hash->size_bits)
key = hash_long(ip, hash->size_bits);
else
key = 0;

entry->ip = ip;
hhd = &hash->buckets[key];
hlist_add_head(&entry->hlist, hhd);
hash->count++;

return 0;
}

static void
remove_hash_entry(struct ftrace_hash *hash,
struct ftrace_func_entry *entry)
{
hlist_del(&entry->hlist);
kfree(entry);
hash->count--;
}

static void ftrace_hash_clear(struct ftrace_hash *hash)
{
struct hlist_head *hhd;
struct hlist_node *tp, *tn;
struct ftrace_func_entry *entry;
int size = 1 << hash->size_bits;
int i;

for (i = 0; i < size; i++) {
hhd = &hash->buckets[i];
hlist_for_each_entry_safe(entry, tp, tn, hhd, hlist)
remove_hash_entry(hash, entry);
}
FTRACE_WARN_ON(hash->count);
}

/*
* This is a double for. Do not use 'break' to break out of the loop,
* you must use a goto.
Expand Down Expand Up @@ -1032,7 +1122,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
* If we want to enable it and filtering is on, enable it only if
* it's filtered
*/
if (enable && !(rec->flags & FTRACE_FL_NOTRACE)) {
if (enable && !ftrace_lookup_ip(&notrace_hash, rec->ip)) {
if (!ftrace_filtered || (rec->flags & FTRACE_FL_FILTER))
flag = FTRACE_FL_ENABLED;
}
Expand Down Expand Up @@ -1465,7 +1555,7 @@ t_next(struct seq_file *m, void *v, loff_t *pos)
!(rec->flags & FTRACE_FL_FILTER)) ||

((iter->flags & FTRACE_ITER_NOTRACE) &&
!(rec->flags & FTRACE_FL_NOTRACE))) {
!ftrace_lookup_ip(&notrace_hash, rec->ip))) {
rec = NULL;
goto retry;
}
Expand Down Expand Up @@ -1609,14 +1699,15 @@ static void ftrace_filter_reset(int enable)
{
struct ftrace_page *pg;
struct dyn_ftrace *rec;
unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;

mutex_lock(&ftrace_lock);
if (enable)
if (enable) {
ftrace_filtered = 0;
do_for_each_ftrace_rec(pg, rec) {
rec->flags &= ~type;
} while_for_each_ftrace_rec();
do_for_each_ftrace_rec(pg, rec) {
rec->flags &= ~FTRACE_FL_FILTER;
} while_for_each_ftrace_rec();
} else
ftrace_hash_clear(&notrace_hash);
mutex_unlock(&ftrace_lock);
}

Expand Down Expand Up @@ -1716,13 +1807,36 @@ static int ftrace_match(char *str, char *regex, int len, int type)
return matched;
}

static void
update_record(struct dyn_ftrace *rec, unsigned long flag, int not)
static int
update_record(struct dyn_ftrace *rec, int enable, int not)
{
if (not)
rec->flags &= ~flag;
else
rec->flags |= flag;
struct ftrace_func_entry *entry;
struct ftrace_hash *hash = &notrace_hash;
int ret = 0;

if (enable) {
if (not)
rec->flags &= ~FTRACE_FL_FILTER;
else
rec->flags |= FTRACE_FL_FILTER;
} else {
if (not) {
/* Do nothing if it doesn't exist */
entry = ftrace_lookup_ip(hash, rec->ip);
if (!entry)
return 0;

remove_hash_entry(hash, entry);
} else {
/* Do nothing if it exists */
entry = ftrace_lookup_ip(hash, rec->ip);
if (entry)
return 0;

ret = add_hash_entry(hash, rec->ip);
}
}
return ret;
}

static int
Expand Down Expand Up @@ -1754,16 +1868,14 @@ static int match_records(char *buff, int len, char *mod, int enable, int not)
struct dyn_ftrace *rec;
int type = MATCH_FULL;
char *search = buff;
unsigned long flag;
int found = 0;
int ret;

if (len) {
type = filter_parse_regex(buff, len, &search, &not);
search_len = strlen(search);
}

flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;

mutex_lock(&ftrace_lock);

if (unlikely(ftrace_disabled))
Expand All @@ -1772,7 +1884,11 @@ static int match_records(char *buff, int len, char *mod, int enable, int not)
do_for_each_ftrace_rec(pg, rec) {

if (ftrace_match_record(rec, mod, search, search_len, type)) {
update_record(rec, flag, not);
ret = update_record(rec, enable, not);
if (ret < 0) {
found = ret;
goto out_unlock;
}
found = 1;
}
/*
Expand Down Expand Up @@ -1821,6 +1937,7 @@ static int
ftrace_mod_callback(char *func, char *cmd, char *param, int enable)
{
char *mod;
int ret = -EINVAL;

/*
* cmd == 'mod' because we only registered this func
Expand All @@ -1832,15 +1949,19 @@ ftrace_mod_callback(char *func, char *cmd, char *param, int enable)

/* we must have a module name */
if (!param)
return -EINVAL;
return ret;

mod = strsep(&param, ":");
if (!strlen(mod))
return -EINVAL;
return ret;

if (ftrace_match_module_records(func, mod, enable))
return 0;
return -EINVAL;
ret = ftrace_match_module_records(func, mod, enable);
if (!ret)
ret = -EINVAL;
if (ret < 0)
return ret;

return 0;
}

static struct ftrace_func_command ftrace_mod_cmd = {
Expand Down Expand Up @@ -2132,14 +2253,17 @@ static int ftrace_process_regex(char *buff, int len, int enable)
{
char *func, *command, *next = buff;
struct ftrace_func_command *p;
int ret = -EINVAL;
int ret;

func = strsep(&next, ":");

if (!next) {
if (ftrace_match_records(func, len, enable))
return 0;
return ret;
ret = ftrace_match_records(func, len, enable);
if (!ret)
ret = -EINVAL;
if (ret < 0)
return ret;
return 0;
}

/* command found */
Expand Down

0 comments on commit b448c4e

Please sign in to comment.