// SPDX-License-Identifier: GPL-2.0 #include "threads.h" #include "machine.h" #include "thread.h" 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]; } static size_t key_hash(long key, void *ctx __maybe_unused) { /* The table lookup removes low bit entropy, but this is just ignored here. */ return key; } static bool key_equal(long key1, long key2, void *ctx __maybe_unused) { return key1 == key2; } void threads__init(struct threads *threads) { for (int i = 0; i < THREADS__TABLE_SIZE; i++) { struct threads_table_entry *table = &threads->table[i]; hashmap__init(&table->shard, key_hash, key_equal, NULL); init_rwsem(&table->lock); 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]; hashmap__clear(&table->shard); 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 += hashmap__size(&table->shard); 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 thread *res; down_read(&table->lock); res = __threads_table_entry__get_last_match(table, tid); if (!res) { if (hashmap__find(&table->shard, tid, &res)) res = thread__get(res); } 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 thread *res = NULL; *created = false; down_write(&table->lock); res = thread__new(pid, tid); if (res) { if (hashmap__add(&table->shard, tid, res)) { /* Add failed. Assume a race so find other entry. */ thread__put(res); res = NULL; if (hashmap__find(&table->shard, tid, &res)) res = thread__get(res); } else { res = thread__get(res); *created = true; } if (res) __threads_table_entry__set_last_match(table, res); } 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 hashmap_entry *cur, *tmp; size_t bkt; down_write(&table->lock); __threads_table_entry__set_last_match(table, NULL); hashmap__for_each_entry_safe(&table->shard, cur, tmp, bkt) { struct thread *old_value; hashmap__delete(&table->shard, cur->key, /*old_key=*/NULL, &old_value); thread__put(old_value); } up_write(&table->lock); } } void threads__remove(struct threads *threads, struct thread *thread) { struct threads_table_entry *table = threads__table(threads, thread__tid(thread)); struct thread *old_value; down_write(&table->lock); if (table->last_match && RC_CHK_EQUAL(table->last_match, thread)) __threads_table_entry__set_last_match(table, NULL); hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value); thread__put(old_value); 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 hashmap_entry *cur; size_t bkt; down_read(&table->lock); hashmap__for_each_entry(&table->shard, cur, bkt) { int rc = fn((struct thread *)cur->pvalue, data); if (rc != 0) { up_read(&table->lock); return rc; } } up_read(&table->lock); } return 0; }