aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorLai Jiangshan <laijs@cn.fujitsu.com>2008-10-27 22:51:49 -0400
committerIngo Molnar <mingo@elte.hu>2008-11-03 04:28:30 -0500
commit19dba33c43a2f0f2aa727ae075ec3b11330775ef (patch)
treea1e60ef0e892ad7d87938b1caeeadc605e2d487a /kernel
parent45beca08dd8b6d6a65c5ffd730af2eac7a2c7a03 (diff)
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>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/tracepoint.c111
1 files changed, 37 insertions, 74 deletions
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c
index af8c85664882..3e22867184e3 100644
--- a/kernel/tracepoint.c
+++ b/kernel/tracepoint.c
@@ -43,6 +43,7 @@ static DEFINE_MUTEX(tracepoints_mutex);
43 */ 43 */
44#define TRACEPOINT_HASH_BITS 6 44#define TRACEPOINT_HASH_BITS 6
45#define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS) 45#define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS)
46static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
46 47
47/* 48/*
48 * Note about RCU : 49 * Note about RCU :
@@ -54,40 +55,40 @@ struct tracepoint_entry {
54 struct hlist_node hlist; 55 struct hlist_node hlist;
55 void **funcs; 56 void **funcs;
56 int refcount; /* Number of times armed. 0 if disarmed. */ 57 int refcount; /* Number of times armed. 0 if disarmed. */
57 struct rcu_head rcu;
58 void *oldptr;
59 unsigned char rcu_pending:1;
60 char name[0]; 58 char name[0];
61}; 59};
62 60
63static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; 61struct tp_probes {
62 struct rcu_head rcu;
63 void *probes[0];
64};
64 65
65static void free_old_closure(struct rcu_head *head) 66static inline void *allocate_probes(int count)
66{ 67{
67 struct tracepoint_entry *entry = container_of(head, 68 struct tp_probes *p = kmalloc(count * sizeof(void *)
68 struct tracepoint_entry, rcu); 69 + sizeof(struct tp_probes), GFP_KERNEL);
69 kfree(entry->oldptr); 70 return p == NULL ? NULL : p->probes;
70 /* Make sure we free the data before setting the pending flag to 0 */
71 smp_wmb();
72 entry->rcu_pending = 0;
73} 71}
74 72
75static void tracepoint_entry_free_old(struct tracepoint_entry *entry, void *old) 73static void rcu_free_old_probes(struct rcu_head *head)
76{ 74{
77 if (!old) 75 kfree(container_of(head, struct tp_probes, rcu));
78 return; 76}
79 entry->oldptr = old; 77
80 entry->rcu_pending = 1; 78static inline void release_probes(void *old)
81 /* write rcu_pending before calling the RCU callback */ 79{
82 smp_wmb(); 80 if (old) {
83 call_rcu_sched(&entry->rcu, free_old_closure); 81 struct tp_probes *tp_probes = container_of(old,
82 struct tp_probes, probes[0]);
83 call_rcu(&tp_probes->rcu, rcu_free_old_probes);
84 }
84} 85}
85 86
86static void debug_print_probes(struct tracepoint_entry *entry) 87static void debug_print_probes(struct tracepoint_entry *entry)
87{ 88{
88 int i; 89 int i;
89 90
90 if (!tracepoint_debug) 91 if (!tracepoint_debug || !entry->funcs)
91 return; 92 return;
92 93
93 for (i = 0; entry->funcs[i]; i++) 94 for (i = 0; entry->funcs[i]; i++)
@@ -111,12 +112,13 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe)
111 return ERR_PTR(-EEXIST); 112 return ERR_PTR(-EEXIST);
112 } 113 }
113 /* + 2 : one for new probe, one for NULL func */ 114 /* + 2 : one for new probe, one for NULL func */
114 new = kzalloc((nr_probes + 2) * sizeof(void *), GFP_KERNEL); 115 new = allocate_probes(nr_probes + 2);
115 if (new == NULL) 116 if (new == NULL)
116 return ERR_PTR(-ENOMEM); 117 return ERR_PTR(-ENOMEM);
117 if (old) 118 if (old)
118 memcpy(new, old, nr_probes * sizeof(void *)); 119 memcpy(new, old, nr_probes * sizeof(void *));
119 new[nr_probes] = probe; 120 new[nr_probes] = probe;
121 new[nr_probes + 1] = NULL;
120 entry->refcount = nr_probes + 1; 122 entry->refcount = nr_probes + 1;
121 entry->funcs = new; 123 entry->funcs = new;
122 debug_print_probes(entry); 124 debug_print_probes(entry);
@@ -132,7 +134,7 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe)
132 old = entry->funcs; 134 old = entry->funcs;
133 135
134 if (!old) 136 if (!old)
135 return NULL; 137 return ERR_PTR(-ENOENT);
136 138
137 debug_print_probes(entry); 139 debug_print_probes(entry);
138 /* (N -> M), (N > 1, M >= 0) probes */ 140 /* (N -> M), (N > 1, M >= 0) probes */
@@ -151,13 +153,13 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe)
151 int j = 0; 153 int j = 0;
152 /* N -> M, (N > 1, M > 0) */ 154 /* N -> M, (N > 1, M > 0) */
153 /* + 1 for NULL */ 155 /* + 1 for NULL */
154 new = kzalloc((nr_probes - nr_del + 1) 156 new = allocate_probes(nr_probes - nr_del + 1);
155 * sizeof(void *), GFP_KERNEL);
156 if (new == NULL) 157 if (new == NULL)
157 return ERR_PTR(-ENOMEM); 158 return ERR_PTR(-ENOMEM);
158 for (i = 0; old[i]; i++) 159 for (i = 0; old[i]; i++)
159 if ((probe && old[i] != probe)) 160 if ((probe && old[i] != probe))
160 new[j++] = old[i]; 161 new[j++] = old[i];
162 new[nr_probes - nr_del] = NULL;
161 entry->refcount = nr_probes - nr_del; 163 entry->refcount = nr_probes - nr_del;
162 entry->funcs = new; 164 entry->funcs = new;
163 } 165 }
@@ -215,7 +217,6 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
215 memcpy(&e->name[0], name, name_len); 217 memcpy(&e->name[0], name, name_len);
216 e->funcs = NULL; 218 e->funcs = NULL;
217 e->refcount = 0; 219 e->refcount = 0;
218 e->rcu_pending = 0;
219 hlist_add_head(&e->hlist, head); 220 hlist_add_head(&e->hlist, head);
220 return e; 221 return e;
221} 222}
@@ -224,32 +225,10 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
224 * Remove the tracepoint from the tracepoint hash table. Must be called with 225 * Remove the tracepoint from the tracepoint hash table. Must be called with
225 * mutex_lock held. 226 * mutex_lock held.
226 */ 227 */
227static int remove_tracepoint(const char *name) 228static inline void remove_tracepoint(struct tracepoint_entry *e)
228{ 229{
229 struct hlist_head *head;
230 struct hlist_node *node;
231 struct tracepoint_entry *e;
232 int found = 0;
233 size_t len = strlen(name) + 1;
234 u32 hash = jhash(name, len-1, 0);
235
236 head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
237 hlist_for_each_entry(e, node, head, hlist) {
238 if (!strcmp(name, e->name)) {
239 found = 1;
240 break;
241 }
242 }
243 if (!found)
244 return -ENOENT;
245 if (e->refcount)
246 return -EBUSY;
247 hlist_del(&e->hlist); 230 hlist_del(&e->hlist);
248 /* Make sure the call_rcu_sched has been executed */
249 if (e->rcu_pending)
250 rcu_barrier_sched();
251 kfree(e); 231 kfree(e);
252 return 0;
253} 232}
254 233
255/* 234/*
@@ -343,25 +322,17 @@ int tracepoint_probe_register(const char *name, void *probe)
343 goto end; 322 goto end;
344 } 323 }
345 } 324 }
346 /*
347 * If we detect that a call_rcu_sched is pending for this tracepoint,
348 * make sure it's executed now.
349 */
350 if (entry->rcu_pending)
351 rcu_barrier_sched();
352 old = tracepoint_entry_add_probe(entry, probe); 325 old = tracepoint_entry_add_probe(entry, probe);
353 if (IS_ERR(old)) { 326 if (IS_ERR(old)) {
327 if (!entry->refcount)
328 remove_tracepoint(entry);
354 ret = PTR_ERR(old); 329 ret = PTR_ERR(old);
355 goto end; 330 goto end;
356 } 331 }
357 mutex_unlock(&tracepoints_mutex); 332 mutex_unlock(&tracepoints_mutex);
358 tracepoint_update_probes(); /* may update entry */ 333 tracepoint_update_probes(); /* may update entry */
359 mutex_lock(&tracepoints_mutex); 334 release_probes(old);
360 entry = get_tracepoint(name); 335 return 0;
361 WARN_ON(!entry);
362 if (entry->rcu_pending)
363 rcu_barrier_sched();
364 tracepoint_entry_free_old(entry, old);
365end: 336end:
366 mutex_unlock(&tracepoints_mutex); 337 mutex_unlock(&tracepoints_mutex);
367 return ret; 338 return ret;
@@ -388,25 +359,17 @@ int tracepoint_probe_unregister(const char *name, void *probe)
388 entry = get_tracepoint(name); 359 entry = get_tracepoint(name);
389 if (!entry) 360 if (!entry)
390 goto end; 361 goto end;
391 if (entry->rcu_pending)
392 rcu_barrier_sched();
393 old = tracepoint_entry_remove_probe(entry, probe); 362 old = tracepoint_entry_remove_probe(entry, probe);
394 if (!old) { 363 if (IS_ERR(old)) {
395 printk(KERN_WARNING "Warning: Trying to unregister a probe" 364 ret = PTR_ERR(old);
396 "that doesn't exist\n");
397 goto end; 365 goto end;
398 } 366 }
367 if (!entry->refcount)
368 remove_tracepoint(entry);
399 mutex_unlock(&tracepoints_mutex); 369 mutex_unlock(&tracepoints_mutex);
400 tracepoint_update_probes(); /* may update entry */ 370 tracepoint_update_probes(); /* may update entry */
401 mutex_lock(&tracepoints_mutex); 371 release_probes(old);
402 entry = get_tracepoint(name); 372 return 0;
403 if (!entry)
404 goto end;
405 if (entry->rcu_pending)
406 rcu_barrier_sched();
407 tracepoint_entry_free_old(entry, old);
408 remove_tracepoint(name); /* Ignore busy error message */
409 ret = 0;
410end: 373end:
411 mutex_unlock(&tracepoints_mutex); 374 mutex_unlock(&tracepoints_mutex);
412 return ret; 375 return ret;