|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | * Copyright 2023 Red Hat | 
|  | */ | 
|  |  | 
|  | #include "thread-registry.h" | 
|  |  | 
|  | #include <asm/current.h> | 
|  | #include <linux/rculist.h> | 
|  |  | 
|  | #include "permassert.h" | 
|  |  | 
|  | /* | 
|  | * We need to be careful when using other facilities that may use thread registry functions in | 
|  | * their normal operation. For example, we do not want to invoke the logger while holding a lock. | 
|  | */ | 
|  |  | 
|  | void vdo_initialize_thread_registry(struct thread_registry *registry) | 
|  | { | 
|  | INIT_LIST_HEAD(®istry->links); | 
|  | spin_lock_init(®istry->lock); | 
|  | } | 
|  |  | 
|  | /* Register the current thread and associate it with a data pointer. */ | 
|  | void vdo_register_thread(struct thread_registry *registry, | 
|  | struct registered_thread *new_thread, const void *pointer) | 
|  | { | 
|  | struct registered_thread *thread; | 
|  | bool found_it = false; | 
|  |  | 
|  | INIT_LIST_HEAD(&new_thread->links); | 
|  | new_thread->pointer = pointer; | 
|  | new_thread->task = current; | 
|  |  | 
|  | spin_lock(®istry->lock); | 
|  | list_for_each_entry(thread, ®istry->links, links) { | 
|  | if (thread->task == current) { | 
|  | /* There should be no existing entry. */ | 
|  | list_del_rcu(&thread->links); | 
|  | found_it = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | list_add_tail_rcu(&new_thread->links, ®istry->links); | 
|  | spin_unlock(®istry->lock); | 
|  |  | 
|  | VDO_ASSERT_LOG_ONLY(!found_it, "new thread not already in registry"); | 
|  | if (found_it) { | 
|  | /* Ensure no RCU iterators see it before re-initializing. */ | 
|  | synchronize_rcu(); | 
|  | INIT_LIST_HEAD(&thread->links); | 
|  | } | 
|  | } | 
|  |  | 
|  | void vdo_unregister_thread(struct thread_registry *registry) | 
|  | { | 
|  | struct registered_thread *thread; | 
|  | bool found_it = false; | 
|  |  | 
|  | spin_lock(®istry->lock); | 
|  | list_for_each_entry(thread, ®istry->links, links) { | 
|  | if (thread->task == current) { | 
|  | list_del_rcu(&thread->links); | 
|  | found_it = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | spin_unlock(®istry->lock); | 
|  |  | 
|  | VDO_ASSERT_LOG_ONLY(found_it, "thread found in registry"); | 
|  | if (found_it) { | 
|  | /* Ensure no RCU iterators see it before re-initializing. */ | 
|  | synchronize_rcu(); | 
|  | INIT_LIST_HEAD(&thread->links); | 
|  | } | 
|  | } | 
|  |  | 
|  | const void *vdo_lookup_thread(struct thread_registry *registry) | 
|  | { | 
|  | struct registered_thread *thread; | 
|  | const void *result = NULL; | 
|  |  | 
|  | rcu_read_lock(); | 
|  | list_for_each_entry_rcu(thread, ®istry->links, links) { | 
|  | if (thread->task == current) { | 
|  | result = thread->pointer; | 
|  | break; | 
|  | } | 
|  | } | 
|  | rcu_read_unlock(); | 
|  |  | 
|  | return result; | 
|  | } |