Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191// 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;
}