diff options
author | Kan Liang <kan.liang@intel.com> | 2017-09-10 22:23:14 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2017-09-18 08:40:19 -0400 |
commit | 91e467bc568f15da2eac688e131010601e889184 (patch) | |
tree | d6e2441af518536d534eacde9a4e549ffeb8ec42 /tools/perf/util/machine.c | |
parent | 56de5b63ffaff859f75c19aff057ee10f20c6c07 (diff) |
perf machine: Use hashtable for machine threads
To process any events, it needs to find the thread in the machine first.
The machine maintains a rb tree to store all threads. The rb tree is
protected by a rw lock.
It is not a problem for current perf which serially processing events.
However, it will have scalability performance issue to process events in
parallel, especially on a heavy load system which have many threads.
Introduce a hashtable to divide the big rb tree into many samll rb tree
for threads. The index is thread id % hashtable size. It can reduce the
lock contention.
Committer notes:
Renamed some variables and function names to reduce semantic confusion:
'struct threads' pointers: thread -> threads
threads hastable index: tid -> hash_bucket
struct threads *machine__thread() -> machine__threads()
Cast tid to (unsigned int) to handle -1 in machine__threads() (Kan Liang)
Signed-off-by: Kan Liang <kan.liang@intel.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Lukasz Odzioba <lukasz.odzioba@intel.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/1505096603-215017-2-git-send-email-kan.liang@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util/machine.c')
-rw-r--r-- | tools/perf/util/machine.c | 136 |
1 files changed, 85 insertions, 51 deletions
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index df709363ef69..f4f926753209 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -33,6 +33,20 @@ static void dsos__init(struct dsos *dsos) | |||
33 | pthread_rwlock_init(&dsos->lock, NULL); | 33 | pthread_rwlock_init(&dsos->lock, NULL); |
34 | } | 34 | } |
35 | 35 | ||
36 | static void machine__threads_init(struct machine *machine) | ||
37 | { | ||
38 | int i; | ||
39 | |||
40 | for (i = 0; i < THREADS__TABLE_SIZE; i++) { | ||
41 | struct threads *threads = &machine->threads[i]; | ||
42 | threads->entries = RB_ROOT; | ||
43 | pthread_rwlock_init(&threads->lock, NULL); | ||
44 | threads->nr = 0; | ||
45 | INIT_LIST_HEAD(&threads->dead); | ||
46 | threads->last_match = NULL; | ||
47 | } | ||
48 | } | ||
49 | |||
36 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | 50 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) |
37 | { | 51 | { |
38 | memset(machine, 0, sizeof(*machine)); | 52 | memset(machine, 0, sizeof(*machine)); |
@@ -40,11 +54,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
40 | RB_CLEAR_NODE(&machine->rb_node); | 54 | RB_CLEAR_NODE(&machine->rb_node); |
41 | dsos__init(&machine->dsos); | 55 | dsos__init(&machine->dsos); |
42 | 56 | ||
43 | machine->threads = RB_ROOT; | 57 | machine__threads_init(machine); |
44 | pthread_rwlock_init(&machine->threads_lock, NULL); | ||
45 | machine->nr_threads = 0; | ||
46 | INIT_LIST_HEAD(&machine->dead_threads); | ||
47 | machine->last_match = NULL; | ||
48 | 58 | ||
49 | machine->vdso_info = NULL; | 59 | machine->vdso_info = NULL; |
50 | machine->env = NULL; | 60 | machine->env = NULL; |
@@ -141,27 +151,37 @@ static void dsos__exit(struct dsos *dsos) | |||
141 | void machine__delete_threads(struct machine *machine) | 151 | void machine__delete_threads(struct machine *machine) |
142 | { | 152 | { |
143 | struct rb_node *nd; | 153 | struct rb_node *nd; |
154 | int i; | ||
144 | 155 | ||
145 | pthread_rwlock_wrlock(&machine->threads_lock); | 156 | for (i = 0; i < THREADS__TABLE_SIZE; i++) { |
146 | nd = rb_first(&machine->threads); | 157 | struct threads *threads = &machine->threads[i]; |
147 | while (nd) { | 158 | pthread_rwlock_wrlock(&threads->lock); |
148 | struct thread *t = rb_entry(nd, struct thread, rb_node); | 159 | nd = rb_first(&threads->entries); |
160 | while (nd) { | ||
161 | struct thread *t = rb_entry(nd, struct thread, rb_node); | ||
149 | 162 | ||
150 | nd = rb_next(nd); | 163 | nd = rb_next(nd); |
151 | __machine__remove_thread(machine, t, false); | 164 | __machine__remove_thread(machine, t, false); |
165 | } | ||
166 | pthread_rwlock_unlock(&threads->lock); | ||
152 | } | 167 | } |
153 | pthread_rwlock_unlock(&machine->threads_lock); | ||
154 | } | 168 | } |
155 | 169 | ||
156 | void machine__exit(struct machine *machine) | 170 | void machine__exit(struct machine *machine) |
157 | { | 171 | { |
172 | int i; | ||
173 | |||
158 | machine__destroy_kernel_maps(machine); | 174 | machine__destroy_kernel_maps(machine); |
159 | map_groups__exit(&machine->kmaps); | 175 | map_groups__exit(&machine->kmaps); |
160 | dsos__exit(&machine->dsos); | 176 | dsos__exit(&machine->dsos); |
161 | machine__exit_vdso(machine); | 177 | machine__exit_vdso(machine); |
162 | zfree(&machine->root_dir); | 178 | zfree(&machine->root_dir); |
163 | zfree(&machine->current_tid); | 179 | zfree(&machine->current_tid); |
164 | pthread_rwlock_destroy(&machine->threads_lock); | 180 | |
181 | for (i = 0; i < THREADS__TABLE_SIZE; i++) { | ||
182 | struct threads *threads = &machine->threads[i]; | ||
183 | pthread_rwlock_destroy(&threads->lock); | ||
184 | } | ||
165 | } | 185 | } |
166 | 186 | ||
167 | void machine__delete(struct machine *machine) | 187 | void machine__delete(struct machine *machine) |
@@ -382,7 +402,8 @@ static struct thread *____machine__findnew_thread(struct machine *machine, | |||
382 | pid_t pid, pid_t tid, | 402 | pid_t pid, pid_t tid, |
383 | bool create) | 403 | bool create) |
384 | { | 404 | { |
385 | struct rb_node **p = &machine->threads.rb_node; | 405 | struct threads *threads = machine__threads(machine, tid); |
406 | struct rb_node **p = &threads->entries.rb_node; | ||
386 | struct rb_node *parent = NULL; | 407 | struct rb_node *parent = NULL; |
387 | struct thread *th; | 408 | struct thread *th; |
388 | 409 | ||
@@ -391,14 +412,14 @@ static struct thread *____machine__findnew_thread(struct machine *machine, | |||
391 | * so most of the time we dont have to look up | 412 | * so most of the time we dont have to look up |
392 | * the full rbtree: | 413 | * the full rbtree: |
393 | */ | 414 | */ |
394 | th = machine->last_match; | 415 | th = threads->last_match; |
395 | if (th != NULL) { | 416 | if (th != NULL) { |
396 | if (th->tid == tid) { | 417 | if (th->tid == tid) { |
397 | machine__update_thread_pid(machine, th, pid); | 418 | machine__update_thread_pid(machine, th, pid); |
398 | return thread__get(th); | 419 | return thread__get(th); |
399 | } | 420 | } |
400 | 421 | ||
401 | machine->last_match = NULL; | 422 | threads->last_match = NULL; |
402 | } | 423 | } |
403 | 424 | ||
404 | while (*p != NULL) { | 425 | while (*p != NULL) { |
@@ -406,7 +427,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine, | |||
406 | th = rb_entry(parent, struct thread, rb_node); | 427 | th = rb_entry(parent, struct thread, rb_node); |
407 | 428 | ||
408 | if (th->tid == tid) { | 429 | if (th->tid == tid) { |
409 | machine->last_match = th; | 430 | threads->last_match = th; |
410 | machine__update_thread_pid(machine, th, pid); | 431 | machine__update_thread_pid(machine, th, pid); |
411 | return thread__get(th); | 432 | return thread__get(th); |
412 | } | 433 | } |
@@ -423,7 +444,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine, | |||
423 | th = thread__new(pid, tid); | 444 | th = thread__new(pid, tid); |
424 | if (th != NULL) { | 445 | if (th != NULL) { |
425 | rb_link_node(&th->rb_node, parent, p); | 446 | rb_link_node(&th->rb_node, parent, p); |
426 | rb_insert_color(&th->rb_node, &machine->threads); | 447 | rb_insert_color(&th->rb_node, &threads->entries); |
427 | 448 | ||
428 | /* | 449 | /* |
429 | * We have to initialize map_groups separately | 450 | * We have to initialize map_groups separately |
@@ -434,7 +455,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine, | |||
434 | * leader and that would screwed the rb tree. | 455 | * leader and that would screwed the rb tree. |
435 | */ | 456 | */ |
436 | if (thread__init_map_groups(th, machine)) { | 457 | if (thread__init_map_groups(th, machine)) { |
437 | rb_erase_init(&th->rb_node, &machine->threads); | 458 | rb_erase_init(&th->rb_node, &threads->entries); |
438 | RB_CLEAR_NODE(&th->rb_node); | 459 | RB_CLEAR_NODE(&th->rb_node); |
439 | thread__put(th); | 460 | thread__put(th); |
440 | return NULL; | 461 | return NULL; |
@@ -443,8 +464,8 @@ static struct thread *____machine__findnew_thread(struct machine *machine, | |||
443 | * It is now in the rbtree, get a ref | 464 | * It is now in the rbtree, get a ref |
444 | */ | 465 | */ |
445 | thread__get(th); | 466 | thread__get(th); |
446 | machine->last_match = th; | 467 | threads->last_match = th; |
447 | ++machine->nr_threads; | 468 | ++threads->nr; |
448 | } | 469 | } |
449 | 470 | ||
450 | return th; | 471 | return th; |
@@ -458,21 +479,24 @@ struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid | |||
458 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, | 479 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, |
459 | pid_t tid) | 480 | pid_t tid) |
460 | { | 481 | { |
482 | struct threads *threads = machine__threads(machine, tid); | ||
461 | struct thread *th; | 483 | struct thread *th; |
462 | 484 | ||
463 | pthread_rwlock_wrlock(&machine->threads_lock); | 485 | pthread_rwlock_wrlock(&threads->lock); |
464 | th = __machine__findnew_thread(machine, pid, tid); | 486 | th = __machine__findnew_thread(machine, pid, tid); |
465 | pthread_rwlock_unlock(&machine->threads_lock); | 487 | pthread_rwlock_unlock(&threads->lock); |
466 | return th; | 488 | return th; |
467 | } | 489 | } |
468 | 490 | ||
469 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, | 491 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, |
470 | pid_t tid) | 492 | pid_t tid) |
471 | { | 493 | { |
494 | struct threads *threads = machine__threads(machine, tid); | ||
472 | struct thread *th; | 495 | struct thread *th; |
473 | pthread_rwlock_rdlock(&machine->threads_lock); | 496 | |
497 | pthread_rwlock_rdlock(&threads->lock); | ||
474 | th = ____machine__findnew_thread(machine, pid, tid, false); | 498 | th = ____machine__findnew_thread(machine, pid, tid, false); |
475 | pthread_rwlock_unlock(&machine->threads_lock); | 499 | pthread_rwlock_unlock(&threads->lock); |
476 | return th; | 500 | return th; |
477 | } | 501 | } |
478 | 502 | ||
@@ -719,21 +743,24 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) | |||
719 | 743 | ||
720 | size_t machine__fprintf(struct machine *machine, FILE *fp) | 744 | size_t machine__fprintf(struct machine *machine, FILE *fp) |
721 | { | 745 | { |
722 | size_t ret; | ||
723 | struct rb_node *nd; | 746 | struct rb_node *nd; |
747 | size_t ret; | ||
748 | int i; | ||
724 | 749 | ||
725 | pthread_rwlock_rdlock(&machine->threads_lock); | 750 | for (i = 0; i < THREADS__TABLE_SIZE; i++) { |
726 | 751 | struct threads *threads = &machine->threads[i]; | |
727 | ret = fprintf(fp, "Threads: %u\n", machine->nr_threads); | 752 | pthread_rwlock_rdlock(&threads->lock); |
728 | 753 | ||
729 | for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { | 754 | ret = fprintf(fp, "Threads: %u\n", threads->nr); |
730 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | ||
731 | 755 | ||
732 | ret += thread__fprintf(pos, fp); | 756 | for (nd = rb_first(&threads->entries); nd; nd = rb_next(nd)) { |
733 | } | 757 | struct thread *pos = rb_entry(nd, struct thread, rb_node); |
734 | 758 | ||
735 | pthread_rwlock_unlock(&machine->threads_lock); | 759 | ret += thread__fprintf(pos, fp); |
760 | } | ||
736 | 761 | ||
762 | pthread_rwlock_unlock(&threads->lock); | ||
763 | } | ||
737 | return ret; | 764 | return ret; |
738 | } | 765 | } |
739 | 766 | ||
@@ -1479,23 +1506,25 @@ out_problem: | |||
1479 | 1506 | ||
1480 | static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock) | 1507 | static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock) |
1481 | { | 1508 | { |
1482 | if (machine->last_match == th) | 1509 | struct threads *threads = machine__threads(machine, th->tid); |
1483 | machine->last_match = NULL; | 1510 | |
1511 | if (threads->last_match == th) | ||
1512 | threads->last_match = NULL; | ||
1484 | 1513 | ||
1485 | BUG_ON(refcount_read(&th->refcnt) == 0); | 1514 | BUG_ON(refcount_read(&th->refcnt) == 0); |
1486 | if (lock) | 1515 | if (lock) |
1487 | pthread_rwlock_wrlock(&machine->threads_lock); | 1516 | pthread_rwlock_wrlock(&threads->lock); |
1488 | rb_erase_init(&th->rb_node, &machine->threads); | 1517 | rb_erase_init(&th->rb_node, &threads->entries); |
1489 | RB_CLEAR_NODE(&th->rb_node); | 1518 | RB_CLEAR_NODE(&th->rb_node); |
1490 | --machine->nr_threads; | 1519 | --threads->nr; |
1491 | /* | 1520 | /* |
1492 | * Move it first to the dead_threads list, then drop the reference, | 1521 | * Move it first to the dead_threads list, then drop the reference, |
1493 | * if this is the last reference, then the thread__delete destructor | 1522 | * if this is the last reference, then the thread__delete destructor |
1494 | * will be called and we will remove it from the dead_threads list. | 1523 | * will be called and we will remove it from the dead_threads list. |
1495 | */ | 1524 | */ |
1496 | list_add_tail(&th->node, &machine->dead_threads); | 1525 | list_add_tail(&th->node, &threads->dead); |
1497 | if (lock) | 1526 | if (lock) |
1498 | pthread_rwlock_unlock(&machine->threads_lock); | 1527 | pthread_rwlock_unlock(&threads->lock); |
1499 | thread__put(th); | 1528 | thread__put(th); |
1500 | } | 1529 | } |
1501 | 1530 | ||
@@ -2140,21 +2169,26 @@ int machine__for_each_thread(struct machine *machine, | |||
2140 | int (*fn)(struct thread *thread, void *p), | 2169 | int (*fn)(struct thread *thread, void *p), |
2141 | void *priv) | 2170 | void *priv) |
2142 | { | 2171 | { |
2172 | struct threads *threads; | ||
2143 | struct rb_node *nd; | 2173 | struct rb_node *nd; |
2144 | struct thread *thread; | 2174 | struct thread *thread; |
2145 | int rc = 0; | 2175 | int rc = 0; |
2176 | int i; | ||
2146 | 2177 | ||
2147 | for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { | 2178 | for (i = 0; i < THREADS__TABLE_SIZE; i++) { |
2148 | thread = rb_entry(nd, struct thread, rb_node); | 2179 | threads = &machine->threads[i]; |
2149 | rc = fn(thread, priv); | 2180 | for (nd = rb_first(&threads->entries); nd; nd = rb_next(nd)) { |
2150 | if (rc != 0) | 2181 | thread = rb_entry(nd, struct thread, rb_node); |
2151 | return rc; | 2182 | rc = fn(thread, priv); |
2152 | } | 2183 | if (rc != 0) |
2184 | return rc; | ||
2185 | } | ||
2153 | 2186 | ||
2154 | list_for_each_entry(thread, &machine->dead_threads, node) { | 2187 | list_for_each_entry(thread, &threads->dead, node) { |
2155 | rc = fn(thread, priv); | 2188 | rc = fn(thread, priv); |
2156 | if (rc != 0) | 2189 | if (rc != 0) |
2157 | return rc; | 2190 | return rc; |
2191 | } | ||
2158 | } | 2192 | } |
2159 | return rc; | 2193 | return rc; |
2160 | } | 2194 | } |