diff options
author | Lai Jiangshan <laijs@cn.fujitsu.com> | 2008-10-27 22:51:49 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-11-03 04:28:30 -0500 |
commit | 19dba33c43a2f0f2aa727ae075ec3b11330775ef (patch) | |
tree | a1e60ef0e892ad7d87938b1caeeadc605e2d487a | |
parent | 45beca08dd8b6d6a65c5ffd730af2eac7a2c7a03 (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>
-rw-r--r-- | kernel/tracepoint.c | 111 |
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) |
46 | static 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 | ||
63 | static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; | 61 | struct tp_probes { |
62 | struct rcu_head rcu; | ||
63 | void *probes[0]; | ||
64 | }; | ||
64 | 65 | ||
65 | static void free_old_closure(struct rcu_head *head) | 66 | static 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 | ||
75 | static void tracepoint_entry_free_old(struct tracepoint_entry *entry, void *old) | 73 | static 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; | 78 | static 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 | ||
86 | static void debug_print_probes(struct tracepoint_entry *entry) | 87 | static 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 | */ |
227 | static int remove_tracepoint(const char *name) | 228 | static 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); | ||
365 | end: | 336 | end: |
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; | ||
410 | end: | 373 | end: |
411 | mutex_unlock(&tracepoints_mutex); | 374 | mutex_unlock(&tracepoints_mutex); |
412 | return ret; | 375 | return ret; |