On Mon, Feb 26, 2024 at 11:07 PM Namhyung Kim <namhyung@xxxxxxxxxx> wrote: > > On Tue, Feb 13, 2024 at 10:37 PM Ian Rogers <irogers@xxxxxxxxxx> wrote: > > > > Move threads out of machine and move thread_rb_node into the C > > file. This hides the implementation of threads from the rest of the > > code allowing for it to be refactored. > > > > Locking discipline is tightened up in this change. > > Doesn't look like a simple code move. Can we split the locking > change from the move to make the reviewer's life a bit easier? :) Not sure I follow. Take threads_nr as an example. The old code is in machine.c, so: -static size_t machine__threads_nr(const struct machine *machine) -{ - size_t nr = 0; - - for (int i = 0; i < THREADS__TABLE_SIZE; i++) - nr += machine->threads[i].nr; - - return nr; -} The new code is in threads.c: +size_t threads__nr(struct threads *threads) +{ + size_t nr = 0; + + for (int i = 0; i < THREADS__TABLE_SIZE; i++) { + struct threads_table_entry *table = &threads->table[i]; + + down_read(&table->lock); + nr += table->nr; + up_read(&table->lock); + } + return nr; +} So it is a copy paste from one file to the other. The only difference is that the old code failed to take a lock when reading "nr" so the locking is added. I wanted to make sure all the functions in threads.c were properly correct wrt locking, semaphore creation and destruction, etc. We could have a broken threads.c and fix it in the next change, but given that's a bug it could make bisection more difficult. Ultimately I thought the locking changes were small enough to not warrant being on their own compared to the advantages of having a sane threads abstraction. Thanks, Ian > Thanks, > Namhyung > > > > > Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx> > > --- > > tools/perf/util/Build | 1 + > > tools/perf/util/bpf_lock_contention.c | 8 +- > > tools/perf/util/machine.c | 287 ++++---------------------- > > tools/perf/util/machine.h | 20 +- > > tools/perf/util/thread.c | 2 +- > > tools/perf/util/thread.h | 6 - > > tools/perf/util/threads.c | 244 ++++++++++++++++++++++ > > tools/perf/util/threads.h | 35 ++++ > > 8 files changed, 325 insertions(+), 278 deletions(-) > > create mode 100644 tools/perf/util/threads.c > > create mode 100644 tools/perf/util/threads.h > > > > diff --git a/tools/perf/util/Build b/tools/perf/util/Build > > index 8027f450fa3e..a0e8cd68d490 100644 > > --- a/tools/perf/util/Build > > +++ b/tools/perf/util/Build > > @@ -71,6 +71,7 @@ perf-y += ordered-events.o > > perf-y += namespaces.o > > perf-y += comm.o > > perf-y += thread.o > > +perf-y += threads.o > > perf-y += thread_map.o > > perf-y += parse-events-flex.o > > perf-y += parse-events-bison.o > > diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c > > index 31ff19afc20c..3992c8a9fd96 100644 > > --- a/tools/perf/util/bpf_lock_contention.c > > +++ b/tools/perf/util/bpf_lock_contention.c > > @@ -210,7 +210,7 @@ static const char *lock_contention_get_name(struct lock_contention *con, > > > > /* do not update idle comm which contains CPU number */ > > if (pid) { > > - struct thread *t = __machine__findnew_thread(machine, /*pid=*/-1, pid); > > + struct thread *t = machine__findnew_thread(machine, /*pid=*/-1, pid); > > > > if (t == NULL) > > return name; > > @@ -302,9 +302,9 @@ int lock_contention_read(struct lock_contention *con) > > return -1; > > > > if (con->aggr_mode == LOCK_AGGR_TASK) { > > - struct thread *idle = __machine__findnew_thread(machine, > > - /*pid=*/0, > > - /*tid=*/0); > > + struct thread *idle = machine__findnew_thread(machine, > > + /*pid=*/0, > > + /*tid=*/0); > > thread__set_comm(idle, "swapper", /*timestamp=*/0); > > } > > > > diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c > > index e072b2115b64..e668a97255f8 100644 > > --- a/tools/perf/util/machine.c > > +++ b/tools/perf/util/machine.c > > @@ -43,9 +43,6 @@ > > #include <linux/string.h> > > #include <linux/zalloc.h> > > > > -static void __machine__remove_thread(struct machine *machine, struct thread_rb_node *nd, > > - struct thread *th, bool lock); > > - > > static struct dso *machine__kernel_dso(struct machine *machine) > > { > > return map__dso(machine->vmlinux_map); > > @@ -58,35 +55,6 @@ static void dsos__init(struct dsos *dsos) > > init_rwsem(&dsos->lock); > > } > > > > -static void machine__threads_init(struct machine *machine) > > -{ > > - int i; > > - > > - for (i = 0; i < THREADS__TABLE_SIZE; i++) { > > - struct threads *threads = &machine->threads[i]; > > - threads->entries = RB_ROOT_CACHED; > > - init_rwsem(&threads->lock); > > - threads->nr = 0; > > - threads->last_match = NULL; > > - } > > -} > > - > > -static int thread_rb_node__cmp_tid(const void *key, const struct rb_node *nd) > > -{ > > - int to_find = (int) *((pid_t *)key); > > - > > - return to_find - (int)thread__tid(rb_entry(nd, struct thread_rb_node, rb_node)->thread); > > -} > > - > > -static struct thread_rb_node *thread_rb_node__find(const struct thread *th, > > - struct rb_root *tree) > > -{ > > - pid_t to_find = thread__tid(th); > > - struct rb_node *nd = rb_find(&to_find, tree, thread_rb_node__cmp_tid); > > - > > - return rb_entry(nd, struct thread_rb_node, rb_node); > > -} > > - > > static int machine__set_mmap_name(struct machine *machine) > > { > > if (machine__is_host(machine)) > > @@ -120,7 +88,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) > > RB_CLEAR_NODE(&machine->rb_node); > > dsos__init(&machine->dsos); > > > > - machine__threads_init(machine); > > + threads__init(&machine->threads); > > > > machine->vdso_info = NULL; > > machine->env = NULL; > > @@ -221,27 +189,11 @@ static void dsos__exit(struct dsos *dsos) > > > > void machine__delete_threads(struct machine *machine) > > { > > - struct rb_node *nd; > > - int i; > > - > > - for (i = 0; i < THREADS__TABLE_SIZE; i++) { > > - struct threads *threads = &machine->threads[i]; > > - down_write(&threads->lock); > > - nd = rb_first_cached(&threads->entries); > > - while (nd) { > > - struct thread_rb_node *trb = rb_entry(nd, struct thread_rb_node, rb_node); > > - > > - nd = rb_next(nd); > > - __machine__remove_thread(machine, trb, trb->thread, false); > > - } > > - up_write(&threads->lock); > > - } > > + threads__remove_all_threads(&machine->threads); > > } > > > > void machine__exit(struct machine *machine) > > { > > - int i; > > - > > if (machine == NULL) > > return; > > > > @@ -254,12 +206,7 @@ void machine__exit(struct machine *machine) > > zfree(&machine->current_tid); > > zfree(&machine->kallsyms_filename); > > > > - machine__delete_threads(machine); > > - for (i = 0; i < THREADS__TABLE_SIZE; i++) { > > - struct threads *threads = &machine->threads[i]; > > - > > - exit_rwsem(&threads->lock); > > - } > > + threads__exit(&machine->threads); > > } > > > > void machine__delete(struct machine *machine) > > @@ -526,7 +473,7 @@ static void machine__update_thread_pid(struct machine *machine, > > if (thread__pid(th) == thread__tid(th)) > > return; > > > > - leader = __machine__findnew_thread(machine, thread__pid(th), thread__pid(th)); > > + leader = machine__findnew_thread(machine, thread__pid(th), thread__pid(th)); > > if (!leader) > > goto out_err; > > > > @@ -560,160 +507,55 @@ static void machine__update_thread_pid(struct machine *machine, > > goto out_put; > > } > > > > -/* > > - * Front-end cache - TID lookups come in blocks, > > - * so most of the time we dont have to look up > > - * the full rbtree: > > - */ > > -static struct thread* > > -__threads__get_last_match(struct threads *threads, struct machine *machine, > > - int pid, int tid) > > -{ > > - struct thread *th; > > - > > - th = threads->last_match; > > - if (th != NULL) { > > - if (thread__tid(th) == tid) { > > - machine__update_thread_pid(machine, th, pid); > > - return thread__get(th); > > - } > > - thread__put(threads->last_match); > > - threads->last_match = NULL; > > - } > > - > > - return NULL; > > -} > > - > > -static struct thread* > > -threads__get_last_match(struct threads *threads, struct machine *machine, > > - int pid, int tid) > > -{ > > - struct thread *th = NULL; > > - > > - if (perf_singlethreaded) > > - th = __threads__get_last_match(threads, machine, pid, tid); > > - > > - return th; > > -} > > - > > -static void > > -__threads__set_last_match(struct threads *threads, struct thread *th) > > -{ > > - thread__put(threads->last_match); > > - threads->last_match = thread__get(th); > > -} > > - > > -static void > > -threads__set_last_match(struct threads *threads, struct thread *th) > > -{ > > - if (perf_singlethreaded) > > - __threads__set_last_match(threads, th); > > -} > > - > > /* > > * Caller must eventually drop thread->refcnt returned with a successful > > * lookup/new thread inserted. > > */ > > -static struct thread *____machine__findnew_thread(struct machine *machine, > > - struct threads *threads, > > - pid_t pid, pid_t tid, > > - bool create) > > +static struct thread *__machine__findnew_thread(struct machine *machine, > > + pid_t pid, > > + pid_t tid, > > + bool create) > > { > > - struct rb_node **p = &threads->entries.rb_root.rb_node; > > - struct rb_node *parent = NULL; > > - struct thread *th; > > - struct thread_rb_node *nd; > > - bool leftmost = true; > > + struct thread *th = threads__find(&machine->threads, tid); > > + bool created; > > > > - th = threads__get_last_match(threads, machine, pid, tid); > > - if (th) > > + if (th) { > > + machine__update_thread_pid(machine, th, pid); > > return th; > > - > > - while (*p != NULL) { > > - parent = *p; > > - th = rb_entry(parent, struct thread_rb_node, rb_node)->thread; > > - > > - if (thread__tid(th) == tid) { > > - threads__set_last_match(threads, th); > > - machine__update_thread_pid(machine, th, pid); > > - return thread__get(th); > > - } > > - > > - if (tid < thread__tid(th)) > > - p = &(*p)->rb_left; > > - else { > > - p = &(*p)->rb_right; > > - leftmost = false; > > - } > > } > > - > > if (!create) > > return NULL; > > > > - th = thread__new(pid, tid); > > - if (th == NULL) > > - return NULL; > > - > > - nd = malloc(sizeof(*nd)); > > - if (nd == NULL) { > > - thread__put(th); > > - return NULL; > > - } > > - nd->thread = th; > > - > > - rb_link_node(&nd->rb_node, parent, p); > > - rb_insert_color_cached(&nd->rb_node, &threads->entries, leftmost); > > - /* > > - * We have to initialize maps separately after rb tree is updated. > > - * > > - * The reason is that we call machine__findnew_thread within > > - * thread__init_maps to find the thread leader and that would screwed > > - * the rb tree. > > - */ > > - if (thread__init_maps(th, machine)) { > > - pr_err("Thread init failed thread %d\n", pid); > > - rb_erase_cached(&nd->rb_node, &threads->entries); > > - RB_CLEAR_NODE(&nd->rb_node); > > - free(nd); > > - thread__put(th); > > - return NULL; > > - } > > - /* > > - * It is now in the rbtree, get a ref > > - */ > > - threads__set_last_match(threads, th); > > - ++threads->nr; > > - > > - return thread__get(th); > > -} > > + th = threads__findnew(&machine->threads, pid, tid, &created); > > + if (created) { > > + /* > > + * We have to initialize maps separately after rb tree is > > + * updated. > > + * > > + * The reason is that we call machine__findnew_thread within > > + * thread__init_maps to find the thread leader and that would > > + * screwed the rb tree. > > + */ > > + if (thread__init_maps(th, machine)) { > > + pr_err("Thread init failed thread %d\n", pid); > > + threads__remove(&machine->threads, th); > > + thread__put(th); > > + return NULL; > > + } > > + } else > > + machine__update_thread_pid(machine, th, pid); > > > > -struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid) > > -{ > > - return ____machine__findnew_thread(machine, machine__threads(machine, tid), pid, tid, true); > > + return th; > > } > > > > -struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, > > - pid_t tid) > > +struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid) > > { > > - struct threads *threads = machine__threads(machine, tid); > > - struct thread *th; > > - > > - down_write(&threads->lock); > > - th = __machine__findnew_thread(machine, pid, tid); > > - up_write(&threads->lock); > > - return th; > > + return __machine__findnew_thread(machine, pid, tid, /*create=*/true); > > } > > > > -struct thread *machine__find_thread(struct machine *machine, pid_t pid, > > - pid_t tid) > > +struct thread *machine__find_thread(struct machine *machine, pid_t pid, pid_t tid) > > { > > - struct threads *threads = machine__threads(machine, tid); > > - struct thread *th; > > - > > - down_read(&threads->lock); > > - th = ____machine__findnew_thread(machine, threads, pid, tid, false); > > - up_read(&threads->lock); > > - return th; > > + return __machine__findnew_thread(machine, pid, tid, /*create=*/false); > > } > > > > /* > > @@ -1127,23 +969,13 @@ static int machine_fprintf_cb(struct thread *thread, void *data) > > return 0; > > } > > > > -static size_t machine__threads_nr(const struct machine *machine) > > -{ > > - size_t nr = 0; > > - > > - for (int i = 0; i < THREADS__TABLE_SIZE; i++) > > - nr += machine->threads[i].nr; > > - > > - return nr; > > -} > > - > > size_t machine__fprintf(struct machine *machine, FILE *fp) > > { > > struct machine_fprintf_cb_args args = { > > .fp = fp, > > .printed = 0, > > }; > > - size_t ret = fprintf(fp, "Threads: %zu\n", machine__threads_nr(machine)); > > + size_t ret = fprintf(fp, "Threads: %zu\n", threads__nr(&machine->threads)); > > > > machine__for_each_thread(machine, machine_fprintf_cb, &args); > > return ret + args.printed; > > @@ -2069,36 +1901,9 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event > > return 0; > > } > > > > -static void __machine__remove_thread(struct machine *machine, struct thread_rb_node *nd, > > - struct thread *th, bool lock) > > -{ > > - struct threads *threads = machine__threads(machine, thread__tid(th)); > > - > > - if (!nd) > > - nd = thread_rb_node__find(th, &threads->entries.rb_root); > > - > > - if (threads->last_match && RC_CHK_EQUAL(threads->last_match, th)) > > - threads__set_last_match(threads, NULL); > > - > > - if (lock) > > - down_write(&threads->lock); > > - > > - BUG_ON(refcount_read(thread__refcnt(th)) == 0); > > - > > - thread__put(nd->thread); > > - rb_erase_cached(&nd->rb_node, &threads->entries); > > - RB_CLEAR_NODE(&nd->rb_node); > > - --threads->nr; > > - > > - free(nd); > > - > > - if (lock) > > - up_write(&threads->lock); > > -} > > - > > void machine__remove_thread(struct machine *machine, struct thread *th) > > { > > - return __machine__remove_thread(machine, NULL, th, true); > > + return threads__remove(&machine->threads, th); > > } > > > > int machine__process_fork_event(struct machine *machine, union perf_event *event, > > @@ -3232,23 +3037,7 @@ int machine__for_each_thread(struct machine *machine, > > int (*fn)(struct thread *thread, void *p), > > void *priv) > > { > > - struct threads *threads; > > - struct rb_node *nd; > > - int rc = 0; > > - int i; > > - > > - for (i = 0; i < THREADS__TABLE_SIZE; i++) { > > - threads = &machine->threads[i]; > > - for (nd = rb_first_cached(&threads->entries); nd; > > - nd = rb_next(nd)) { > > - struct thread_rb_node *trb = rb_entry(nd, struct thread_rb_node, rb_node); > > - > > - rc = fn(trb->thread, priv); > > - if (rc != 0) > > - return rc; > > - } > > - } > > - return rc; > > + return threads__for_each_thread(&machine->threads, fn, priv); > > } > > > > int machines__for_each_thread(struct machines *machines, > > diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h > > index b738ce84817b..e28c787616fe 100644 > > --- a/tools/perf/util/machine.h > > +++ b/tools/perf/util/machine.h > > @@ -7,6 +7,7 @@ > > #include "maps.h" > > #include "dsos.h" > > #include "rwsem.h" > > +#include "threads.h" > > > > struct addr_location; > > struct branch_stack; > > @@ -28,16 +29,6 @@ extern const char *ref_reloc_sym_names[]; > > > > struct vdso_info; > > > > -#define THREADS__TABLE_BITS 8 > > -#define THREADS__TABLE_SIZE (1 << THREADS__TABLE_BITS) > > - > > -struct threads { > > - struct rb_root_cached entries; > > - struct rw_semaphore lock; > > - unsigned int nr; > > - struct thread *last_match; > > -}; > > - > > struct machine { > > struct rb_node rb_node; > > pid_t pid; > > @@ -48,7 +39,7 @@ struct machine { > > char *root_dir; > > char *mmap_name; > > char *kallsyms_filename; > > - struct threads threads[THREADS__TABLE_SIZE]; > > + struct threads threads; > > struct vdso_info *vdso_info; > > struct perf_env *env; > > struct dsos dsos; > > @@ -69,12 +60,6 @@ struct machine { > > bool trampolines_mapped; > > }; > > > > -static inline struct threads *machine__threads(struct machine *machine, pid_t tid) > > -{ > > - /* Cast it to handle tid == -1 */ > > - return &machine->threads[(unsigned int)tid % THREADS__TABLE_SIZE]; > > -} > > - > > /* > > * The main kernel (vmlinux) map > > */ > > @@ -220,7 +205,6 @@ bool machine__is(struct machine *machine, const char *arch); > > bool machine__normalized_is(struct machine *machine, const char *arch); > > int machine__nr_cpus_avail(struct machine *machine); > > > > -struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid); > > struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid); > > > > struct dso *machine__findnew_dso_id(struct machine *machine, const char *filename, struct dso_id *id); > > diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c > > index c59ab4d79163..1aa8962dcf52 100644 > > --- a/tools/perf/util/thread.c > > +++ b/tools/perf/util/thread.c > > @@ -26,7 +26,7 @@ int thread__init_maps(struct thread *thread, struct machine *machine) > > if (pid == thread__tid(thread) || pid == -1) { > > thread__set_maps(thread, maps__new(machine)); > > } else { > > - struct thread *leader = __machine__findnew_thread(machine, pid, pid); > > + struct thread *leader = machine__findnew_thread(machine, pid, pid); > > > > if (leader) { > > thread__set_maps(thread, maps__get(thread__maps(leader))); > > diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h > > index 0df775b5c110..4b8f3e9e513b 100644 > > --- a/tools/perf/util/thread.h > > +++ b/tools/perf/util/thread.h > > @@ -3,7 +3,6 @@ > > #define __PERF_THREAD_H > > > > #include <linux/refcount.h> > > -#include <linux/rbtree.h> > > #include <linux/list.h> > > #include <stdio.h> > > #include <unistd.h> > > @@ -30,11 +29,6 @@ struct lbr_stitch { > > struct callchain_cursor_node *prev_lbr_cursor; > > }; > > > > -struct thread_rb_node { > > - struct rb_node rb_node; > > - struct thread *thread; > > -}; > > - > > DECLARE_RC_STRUCT(thread) { > > /** @maps: mmaps associated with this thread. */ > > struct maps *maps; > > diff --git a/tools/perf/util/threads.c b/tools/perf/util/threads.c > > new file mode 100644 > > index 000000000000..d984ec939c7b > > --- /dev/null > > +++ b/tools/perf/util/threads.c > > @@ -0,0 +1,244 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +#include "threads.h" > > +#include "machine.h" > > +#include "thread.h" > > + > > +struct thread_rb_node { > > + struct rb_node rb_node; > > + struct thread *thread; > > +}; > > + > > +static struct threads_table_entry *threads__table(struct threads *threads, pid_t tid) > > +{ > > + /* Cast it to handle tid == -1 */ > > + return &threads->table[(unsigned int)tid % THREADS__TABLE_SIZE]; > > +} > > + > > +void threads__init(struct threads *threads) > > +{ > > + for (int i = 0; i < THREADS__TABLE_SIZE; i++) { > > + struct threads_table_entry *table = &threads->table[i]; > > + > > + table->entries = RB_ROOT_CACHED; > > + init_rwsem(&table->lock); > > + table->nr = 0; > > + table->last_match = NULL; > > + } > > +} > > + > > +void threads__exit(struct threads *threads) > > +{ > > + threads__remove_all_threads(threads); > > + for (int i = 0; i < THREADS__TABLE_SIZE; i++) { > > + struct threads_table_entry *table = &threads->table[i]; > > + > > + exit_rwsem(&table->lock); > > + } > > +} > > + > > +size_t threads__nr(struct threads *threads) > > +{ > > + size_t nr = 0; > > + > > + for (int i = 0; i < THREADS__TABLE_SIZE; i++) { > > + struct threads_table_entry *table = &threads->table[i]; > > + > > + down_read(&table->lock); > > + nr += table->nr; > > + up_read(&table->lock); > > + } > > + return nr; > > +} > > + > > +/* > > + * Front-end cache - TID lookups come in blocks, > > + * so most of the time we dont have to look up > > + * the full rbtree: > > + */ > > +static struct thread *__threads_table_entry__get_last_match(struct threads_table_entry *table, > > + pid_t tid) > > +{ > > + struct thread *th, *res = NULL; > > + > > + th = table->last_match; > > + if (th != NULL) { > > + if (thread__tid(th) == tid) > > + res = thread__get(th); > > + } > > + return res; > > +} > > + > > +static void __threads_table_entry__set_last_match(struct threads_table_entry *table, > > + struct thread *th) > > +{ > > + thread__put(table->last_match); > > + table->last_match = thread__get(th); > > +} > > + > > +static void threads_table_entry__set_last_match(struct threads_table_entry *table, > > + struct thread *th) > > +{ > > + down_write(&table->lock); > > + __threads_table_entry__set_last_match(table, th); > > + up_write(&table->lock); > > +} > > + > > +struct thread *threads__find(struct threads *threads, pid_t tid) > > +{ > > + struct threads_table_entry *table = threads__table(threads, tid); > > + struct rb_node **p; > > + struct thread *res = NULL; > > + > > + down_read(&table->lock); > > + res = __threads_table_entry__get_last_match(table, tid); > > + if (res) > > + return res; > > + > > + p = &table->entries.rb_root.rb_node; > > + while (*p != NULL) { > > + struct rb_node *parent = *p; > > + struct thread *th = rb_entry(parent, struct thread_rb_node, rb_node)->thread; > > + > > + if (thread__tid(th) == tid) { > > + res = thread__get(th); > > + break; > > + } > > + > > + if (tid < thread__tid(th)) > > + p = &(*p)->rb_left; > > + else > > + p = &(*p)->rb_right; > > + } > > + up_read(&table->lock); > > + if (res) > > + threads_table_entry__set_last_match(table, res); > > + return res; > > +} > > + > > +struct thread *threads__findnew(struct threads *threads, pid_t pid, pid_t tid, bool *created) > > +{ > > + struct threads_table_entry *table = threads__table(threads, tid); > > + struct rb_node **p; > > + struct rb_node *parent = NULL; > > + struct thread *res = NULL; > > + struct thread_rb_node *nd; > > + bool leftmost = true; > > + > > + *created = false; > > + down_write(&table->lock); > > + p = &table->entries.rb_root.rb_node; > > + while (*p != NULL) { > > + struct thread *th; > > + > > + parent = *p; > > + th = rb_entry(parent, struct thread_rb_node, rb_node)->thread; > > + > > + if (thread__tid(th) == tid) { > > + __threads_table_entry__set_last_match(table, th); > > + res = thread__get(th); > > + goto out_unlock; > > + } > > + > > + if (tid < thread__tid(th)) > > + p = &(*p)->rb_left; > > + else { > > + leftmost = false; > > + p = &(*p)->rb_right; > > + } > > + } > > + nd = malloc(sizeof(*nd)); > > + if (nd == NULL) > > + goto out_unlock; > > + res = thread__new(pid, tid); > > + if (!res) > > + free(nd); > > + else { > > + *created = true; > > + nd->thread = thread__get(res); > > + rb_link_node(&nd->rb_node, parent, p); > > + rb_insert_color_cached(&nd->rb_node, &table->entries, leftmost); > > + ++table->nr; > > + __threads_table_entry__set_last_match(table, res); > > + } > > +out_unlock: > > + up_write(&table->lock); > > + return res; > > +} > > + > > +void threads__remove_all_threads(struct threads *threads) > > +{ > > + for (int i = 0; i < THREADS__TABLE_SIZE; i++) { > > + struct threads_table_entry *table = &threads->table[i]; > > + struct rb_node *nd; > > + > > + down_write(&table->lock); > > + __threads_table_entry__set_last_match(table, NULL); > > + nd = rb_first_cached(&table->entries); > > + while (nd) { > > + struct thread_rb_node *trb = rb_entry(nd, struct thread_rb_node, rb_node); > > + > > + nd = rb_next(nd); > > + thread__put(trb->thread); > > + rb_erase_cached(&trb->rb_node, &table->entries); > > + RB_CLEAR_NODE(&trb->rb_node); > > + --table->nr; > > + > > + free(trb); > > + } > > + assert(table->nr == 0); > > + up_write(&table->lock); > > + } > > +} > > + > > +void threads__remove(struct threads *threads, struct thread *thread) > > +{ > > + struct rb_node **p; > > + struct threads_table_entry *table = threads__table(threads, thread__tid(thread)); > > + pid_t tid = thread__tid(thread); > > + > > + down_write(&table->lock); > > + if (table->last_match && RC_CHK_EQUAL(table->last_match, thread)) > > + __threads_table_entry__set_last_match(table, NULL); > > + > > + p = &table->entries.rb_root.rb_node; > > + while (*p != NULL) { > > + struct rb_node *parent = *p; > > + struct thread_rb_node *nd = rb_entry(parent, struct thread_rb_node, rb_node); > > + struct thread *th = nd->thread; > > + > > + if (RC_CHK_EQUAL(th, thread)) { > > + thread__put(nd->thread); > > + rb_erase_cached(&nd->rb_node, &table->entries); > > + RB_CLEAR_NODE(&nd->rb_node); > > + --table->nr; > > + free(nd); > > + break; > > + } > > + > > + if (tid < thread__tid(th)) > > + p = &(*p)->rb_left; > > + else > > + p = &(*p)->rb_right; > > + } > > + up_write(&table->lock); > > +} > > + > > +int threads__for_each_thread(struct threads *threads, > > + int (*fn)(struct thread *thread, void *data), > > + void *data) > > +{ > > + for (int i = 0; i < THREADS__TABLE_SIZE; i++) { > > + struct threads_table_entry *table = &threads->table[i]; > > + struct rb_node *nd; > > + > > + for (nd = rb_first_cached(&table->entries); nd; nd = rb_next(nd)) { > > + struct thread_rb_node *trb = rb_entry(nd, struct thread_rb_node, rb_node); > > + int rc = fn(trb->thread, data); > > + > > + if (rc != 0) > > + return rc; > > + } > > + } > > + return 0; > > + > > +} > > diff --git a/tools/perf/util/threads.h b/tools/perf/util/threads.h > > new file mode 100644 > > index 000000000000..ed67de627578 > > --- /dev/null > > +++ b/tools/perf/util/threads.h > > @@ -0,0 +1,35 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +#ifndef __PERF_THREADS_H > > +#define __PERF_THREADS_H > > + > > +#include <linux/rbtree.h> > > +#include "rwsem.h" > > + > > +struct thread; > > + > > +#define THREADS__TABLE_BITS 8 > > +#define THREADS__TABLE_SIZE (1 << THREADS__TABLE_BITS) > > + > > +struct threads_table_entry { > > + struct rb_root_cached entries; > > + struct rw_semaphore lock; > > + unsigned int nr; > > + struct thread *last_match; > > +}; > > + > > +struct threads { > > + struct threads_table_entry table[THREADS__TABLE_SIZE]; > > +}; > > + > > +void threads__init(struct threads *threads); > > +void threads__exit(struct threads *threads); > > +size_t threads__nr(struct threads *threads); > > +struct thread *threads__find(struct threads *threads, pid_t tid); > > +struct thread *threads__findnew(struct threads *threads, pid_t pid, pid_t tid, bool *created); > > +void threads__remove_all_threads(struct threads *threads); > > +void threads__remove(struct threads *threads, struct thread *thread); > > +int threads__for_each_thread(struct threads *threads, > > + int (*fn)(struct thread *thread, void *data), > > + void *data); > > + > > +#endif /* __PERF_THREADS_H */ > > -- > > 2.43.0.687.g38aa6559b0-goog > >