Commit 19dba33c authored by Lai Jiangshan's avatar Lai Jiangshan Committed by Ingo Molnar

tracepoint: simplification for tracepoints using RCU

Impact: simplify implementation

Now, unused memory is handled by struct tp_probes.

old code use these three field to handle unused memory.
struct tracepoint_entry {
	...
	struct rcu_head rcu;
	void *oldptr;
	unsigned char rcu_pending:1;
	...
};

in this way, unused memory is handled by struct tracepoint_entry.
it bring reenter bug(it was fixed) and tracepoint.c is filled
full of ".*rcu.*" code statements. this patch removes all these.

and:
  rcu_barrier_sched() is removed.
  Do not need regain tracepoints_mutex after tracepoint_update_probes()
  several little cleanup.
Signed-off-by: default avatarLai Jiangshan <laijs@cn.fujitsu.com>
Acked-by: default avatarMathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 45beca08
...@@ -43,6 +43,7 @@ static DEFINE_MUTEX(tracepoints_mutex); ...@@ -43,6 +43,7 @@ static DEFINE_MUTEX(tracepoints_mutex);
*/ */
#define TRACEPOINT_HASH_BITS 6 #define TRACEPOINT_HASH_BITS 6
#define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS) #define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS)
static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
/* /*
* Note about RCU : * Note about RCU :
...@@ -54,40 +55,40 @@ struct tracepoint_entry { ...@@ -54,40 +55,40 @@ struct tracepoint_entry {
struct hlist_node hlist; struct hlist_node hlist;
void **funcs; void **funcs;
int refcount; /* Number of times armed. 0 if disarmed. */ int refcount; /* Number of times armed. 0 if disarmed. */
struct rcu_head rcu;
void *oldptr;
unsigned char rcu_pending:1;
char name[0]; char name[0];
}; };
static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; struct tp_probes {
struct rcu_head rcu;
void *probes[0];
};
static void free_old_closure(struct rcu_head *head) static inline void *allocate_probes(int count)
{ {
struct tracepoint_entry *entry = container_of(head, struct tp_probes *p = kmalloc(count * sizeof(void *)
struct tracepoint_entry, rcu); + sizeof(struct tp_probes), GFP_KERNEL);
kfree(entry->oldptr); return p == NULL ? NULL : p->probes;
/* Make sure we free the data before setting the pending flag to 0 */
smp_wmb();
entry->rcu_pending = 0;
} }
static void tracepoint_entry_free_old(struct tracepoint_entry *entry, void *old) static void rcu_free_old_probes(struct rcu_head *head)
{ {
if (!old) kfree(container_of(head, struct tp_probes, rcu));
return; }
entry->oldptr = old;
entry->rcu_pending = 1; static inline void release_probes(void *old)
/* write rcu_pending before calling the RCU callback */ {
smp_wmb(); if (old) {
call_rcu_sched(&entry->rcu, free_old_closure); struct tp_probes *tp_probes = container_of(old,
struct tp_probes, probes[0]);
call_rcu(&tp_probes->rcu, rcu_free_old_probes);
}
} }
static void debug_print_probes(struct tracepoint_entry *entry) static void debug_print_probes(struct tracepoint_entry *entry)
{ {
int i; int i;
if (!tracepoint_debug) if (!tracepoint_debug || !entry->funcs)
return; return;
for (i = 0; entry->funcs[i]; i++) for (i = 0; entry->funcs[i]; i++)
...@@ -111,12 +112,13 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe) ...@@ -111,12 +112,13 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe)
return ERR_PTR(-EEXIST); return ERR_PTR(-EEXIST);
} }
/* + 2 : one for new probe, one for NULL func */ /* + 2 : one for new probe, one for NULL func */
new = kzalloc((nr_probes + 2) * sizeof(void *), GFP_KERNEL); new = allocate_probes(nr_probes + 2);
if (new == NULL) if (new == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
if (old) if (old)
memcpy(new, old, nr_probes * sizeof(void *)); memcpy(new, old, nr_probes * sizeof(void *));
new[nr_probes] = probe; new[nr_probes] = probe;
new[nr_probes + 1] = NULL;
entry->refcount = nr_probes + 1; entry->refcount = nr_probes + 1;
entry->funcs = new; entry->funcs = new;
debug_print_probes(entry); debug_print_probes(entry);
...@@ -132,7 +134,7 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe) ...@@ -132,7 +134,7 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe)
old = entry->funcs; old = entry->funcs;
if (!old) if (!old)
return NULL; return ERR_PTR(-ENOENT);
debug_print_probes(entry); debug_print_probes(entry);
/* (N -> M), (N > 1, M >= 0) probes */ /* (N -> M), (N > 1, M >= 0) probes */
...@@ -151,13 +153,13 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe) ...@@ -151,13 +153,13 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe)
int j = 0; int j = 0;
/* N -> M, (N > 1, M > 0) */ /* N -> M, (N > 1, M > 0) */
/* + 1 for NULL */ /* + 1 for NULL */
new = kzalloc((nr_probes - nr_del + 1) new = allocate_probes(nr_probes - nr_del + 1);
* sizeof(void *), GFP_KERNEL);
if (new == NULL) if (new == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
for (i = 0; old[i]; i++) for (i = 0; old[i]; i++)
if ((probe && old[i] != probe)) if ((probe && old[i] != probe))
new[j++] = old[i]; new[j++] = old[i];
new[nr_probes - nr_del] = NULL;
entry->refcount = nr_probes - nr_del; entry->refcount = nr_probes - nr_del;
entry->funcs = new; entry->funcs = new;
} }
...@@ -215,7 +217,6 @@ static struct tracepoint_entry *add_tracepoint(const char *name) ...@@ -215,7 +217,6 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
memcpy(&e->name[0], name, name_len); memcpy(&e->name[0], name, name_len);
e->funcs = NULL; e->funcs = NULL;
e->refcount = 0; e->refcount = 0;
e->rcu_pending = 0;
hlist_add_head(&e->hlist, head); hlist_add_head(&e->hlist, head);
return e; return e;
} }
...@@ -224,32 +225,10 @@ static struct tracepoint_entry *add_tracepoint(const char *name) ...@@ -224,32 +225,10 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
* Remove the tracepoint from the tracepoint hash table. Must be called with * Remove the tracepoint from the tracepoint hash table. Must be called with
* mutex_lock held. * mutex_lock held.
*/ */
static int remove_tracepoint(const char *name) static inline void remove_tracepoint(struct tracepoint_entry *e)
{ {
struct hlist_head *head;
struct hlist_node *node;
struct tracepoint_entry *e;
int found = 0;
size_t len = strlen(name) + 1;
u32 hash = jhash(name, len-1, 0);
head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
hlist_for_each_entry(e, node, head, hlist) {
if (!strcmp(name, e->name)) {
found = 1;
break;
}
}
if (!found)
return -ENOENT;
if (e->refcount)
return -EBUSY;
hlist_del(&e->hlist); hlist_del(&e->hlist);
/* Make sure the call_rcu_sched has been executed */
if (e->rcu_pending)
rcu_barrier_sched();
kfree(e); kfree(e);
return 0;
} }
/* /*
...@@ -343,25 +322,17 @@ int tracepoint_probe_register(const char *name, void *probe) ...@@ -343,25 +322,17 @@ int tracepoint_probe_register(const char *name, void *probe)
goto end; goto end;
} }
} }
/*
* If we detect that a call_rcu_sched is pending for this tracepoint,
* make sure it's executed now.
*/
if (entry->rcu_pending)
rcu_barrier_sched();
old = tracepoint_entry_add_probe(entry, probe); old = tracepoint_entry_add_probe(entry, probe);
if (IS_ERR(old)) { if (IS_ERR(old)) {
if (!entry->refcount)
remove_tracepoint(entry);
ret = PTR_ERR(old); ret = PTR_ERR(old);
goto end; goto end;
} }
mutex_unlock(&tracepoints_mutex); mutex_unlock(&tracepoints_mutex);
tracepoint_update_probes(); /* may update entry */ tracepoint_update_probes(); /* may update entry */
mutex_lock(&tracepoints_mutex); release_probes(old);
entry = get_tracepoint(name); return 0;
WARN_ON(!entry);
if (entry->rcu_pending)
rcu_barrier_sched();
tracepoint_entry_free_old(entry, old);
end: end:
mutex_unlock(&tracepoints_mutex); mutex_unlock(&tracepoints_mutex);
return ret; return ret;
...@@ -388,25 +359,17 @@ int tracepoint_probe_unregister(const char *name, void *probe) ...@@ -388,25 +359,17 @@ int tracepoint_probe_unregister(const char *name, void *probe)
entry = get_tracepoint(name); entry = get_tracepoint(name);
if (!entry) if (!entry)
goto end; goto end;
if (entry->rcu_pending)
rcu_barrier_sched();
old = tracepoint_entry_remove_probe(entry, probe); old = tracepoint_entry_remove_probe(entry, probe);
if (!old) { if (IS_ERR(old)) {
printk(KERN_WARNING "Warning: Trying to unregister a probe" ret = PTR_ERR(old);
"that doesn't exist\n");
goto end; goto end;
} }
if (!entry->refcount)
remove_tracepoint(entry);
mutex_unlock(&tracepoints_mutex); mutex_unlock(&tracepoints_mutex);
tracepoint_update_probes(); /* may update entry */ tracepoint_update_probes(); /* may update entry */
mutex_lock(&tracepoints_mutex); release_probes(old);
entry = get_tracepoint(name); return 0;
if (!entry)
goto end;
if (entry->rcu_pending)
rcu_barrier_sched();
tracepoint_entry_free_old(entry, old);
remove_tracepoint(name); /* Ignore busy error message */
ret = 0;
end: end:
mutex_unlock(&tracepoints_mutex); mutex_unlock(&tracepoints_mutex);
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment