mirror of
https://github.com/S3NEO/android_kernel_samsung_msm8226.git
synced 2024-11-07 03:47:13 +00:00
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: Lai Jiangshan <laijs@cn.fujitsu.com> Acked-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
45beca08dd
commit
19dba33c43
1 changed files with 37 additions and 74 deletions
|
@ -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 {
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
* 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)
|
||||||
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)
|
||||||
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;
|
||||||
|
|
Loading…
Reference in a new issue