191 lines · c
1// SPDX-License-Identifier: GPL-2.02#include "threads.h"3#include "machine.h"4#include "thread.h"5 6static struct threads_table_entry *threads__table(struct threads *threads, pid_t tid)7{8 /* Cast it to handle tid == -1 */9 return &threads->table[(unsigned int)tid % THREADS__TABLE_SIZE];10}11 12static size_t key_hash(long key, void *ctx __maybe_unused)13{14 /* The table lookup removes low bit entropy, but this is just ignored here. */15 return key;16}17 18static bool key_equal(long key1, long key2, void *ctx __maybe_unused)19{20 return key1 == key2;21}22 23void threads__init(struct threads *threads)24{25 for (int i = 0; i < THREADS__TABLE_SIZE; i++) {26 struct threads_table_entry *table = &threads->table[i];27 28 hashmap__init(&table->shard, key_hash, key_equal, NULL);29 init_rwsem(&table->lock);30 table->last_match = NULL;31 }32}33 34void threads__exit(struct threads *threads)35{36 threads__remove_all_threads(threads);37 for (int i = 0; i < THREADS__TABLE_SIZE; i++) {38 struct threads_table_entry *table = &threads->table[i];39 40 hashmap__clear(&table->shard);41 exit_rwsem(&table->lock);42 }43}44 45size_t threads__nr(struct threads *threads)46{47 size_t nr = 0;48 49 for (int i = 0; i < THREADS__TABLE_SIZE; i++) {50 struct threads_table_entry *table = &threads->table[i];51 52 down_read(&table->lock);53 nr += hashmap__size(&table->shard);54 up_read(&table->lock);55 }56 return nr;57}58 59/*60 * Front-end cache - TID lookups come in blocks,61 * so most of the time we dont have to look up62 * the full rbtree:63 */64static struct thread *__threads_table_entry__get_last_match(struct threads_table_entry *table,65 pid_t tid)66{67 struct thread *th, *res = NULL;68 69 th = table->last_match;70 if (th != NULL) {71 if (thread__tid(th) == tid)72 res = thread__get(th);73 }74 return res;75}76 77static void __threads_table_entry__set_last_match(struct threads_table_entry *table,78 struct thread *th)79{80 thread__put(table->last_match);81 table->last_match = thread__get(th);82}83 84static void threads_table_entry__set_last_match(struct threads_table_entry *table,85 struct thread *th)86{87 down_write(&table->lock);88 __threads_table_entry__set_last_match(table, th);89 up_write(&table->lock);90}91 92struct thread *threads__find(struct threads *threads, pid_t tid)93{94 struct threads_table_entry *table = threads__table(threads, tid);95 struct thread *res;96 97 down_read(&table->lock);98 res = __threads_table_entry__get_last_match(table, tid);99 if (!res) {100 if (hashmap__find(&table->shard, tid, &res))101 res = thread__get(res);102 }103 up_read(&table->lock);104 if (res)105 threads_table_entry__set_last_match(table, res);106 return res;107}108 109struct thread *threads__findnew(struct threads *threads, pid_t pid, pid_t tid, bool *created)110{111 struct threads_table_entry *table = threads__table(threads, tid);112 struct thread *res = NULL;113 114 *created = false;115 down_write(&table->lock);116 res = thread__new(pid, tid);117 if (res) {118 if (hashmap__add(&table->shard, tid, res)) {119 /* Add failed. Assume a race so find other entry. */120 thread__put(res);121 res = NULL;122 if (hashmap__find(&table->shard, tid, &res))123 res = thread__get(res);124 } else {125 res = thread__get(res);126 *created = true;127 }128 if (res)129 __threads_table_entry__set_last_match(table, res);130 }131 up_write(&table->lock);132 return res;133}134 135void threads__remove_all_threads(struct threads *threads)136{137 for (int i = 0; i < THREADS__TABLE_SIZE; i++) {138 struct threads_table_entry *table = &threads->table[i];139 struct hashmap_entry *cur, *tmp;140 size_t bkt;141 142 down_write(&table->lock);143 __threads_table_entry__set_last_match(table, NULL);144 hashmap__for_each_entry_safe((&table->shard), cur, tmp, bkt) {145 struct thread *old_value;146 147 hashmap__delete(&table->shard, cur->key, /*old_key=*/NULL, &old_value);148 thread__put(old_value);149 }150 up_write(&table->lock);151 }152}153 154void threads__remove(struct threads *threads, struct thread *thread)155{156 struct threads_table_entry *table = threads__table(threads, thread__tid(thread));157 struct thread *old_value;158 159 down_write(&table->lock);160 if (table->last_match && RC_CHK_EQUAL(table->last_match, thread))161 __threads_table_entry__set_last_match(table, NULL);162 163 hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value);164 thread__put(old_value);165 up_write(&table->lock);166}167 168int threads__for_each_thread(struct threads *threads,169 int (*fn)(struct thread *thread, void *data),170 void *data)171{172 for (int i = 0; i < THREADS__TABLE_SIZE; i++) {173 struct threads_table_entry *table = &threads->table[i];174 struct hashmap_entry *cur;175 size_t bkt;176 177 down_read(&table->lock);178 hashmap__for_each_entry((&table->shard), cur, bkt) {179 int rc = fn((struct thread *)cur->pvalue, data);180 181 if (rc != 0) {182 up_read(&table->lock);183 return rc;184 }185 }186 up_read(&table->lock);187 }188 return 0;189 190}191