Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dm vdo: add thread and synchronization utilities

This patch adds utilities for managing and using named threads, as well as
several locking and synchronization utilities. These utilities help dm-vdo
minimize thread transitions and manage interactions between threads.

Co-developed-by: J. corwin Coburn <corwin@hurlbutnet.net>
Signed-off-by: J. corwin Coburn <corwin@hurlbutnet.net>
Co-developed-by: Michael Sclafani <dm-devel@lists.linux.dev>
Signed-off-by: Michael Sclafani <dm-devel@lists.linux.dev>
Co-developed-by: Thomas Jaskiewicz <tom@jaskiewicz.us>
Signed-off-by: Thomas Jaskiewicz <tom@jaskiewicz.us>
Co-developed-by: Bruce Johnston <bjohnsto@redhat.com>
Signed-off-by: Bruce Johnston <bjohnsto@redhat.com>
Co-developed-by: Ken Raeburn <raeburn@redhat.com>
Signed-off-by: Ken Raeburn <raeburn@redhat.com>
Signed-off-by: Matthew Sakai <msakai@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@kernel.org>

authored by

Matthew Sakai and committed by
Mike Snitzer
89f9b701 4fcb4290

+524
+46
drivers/md/dm-vdo/thread-cond-var.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2023 Red Hat 4 + */ 5 + 6 + #include <linux/jiffies.h> 7 + #include <linux/minmax.h> 8 + 9 + #include "errors.h" 10 + #include "time-utils.h" 11 + #include "uds-threads.h" 12 + 13 + int uds_init_cond(struct cond_var *cv) 14 + { 15 + init_waitqueue_head(&cv->wait_queue); 16 + return UDS_SUCCESS; 17 + } 18 + 19 + int uds_signal_cond(struct cond_var *cv) 20 + { 21 + wake_up(&cv->wait_queue); 22 + return UDS_SUCCESS; 23 + } 24 + 25 + int uds_broadcast_cond(struct cond_var *cv) 26 + { 27 + wake_up_all(&cv->wait_queue); 28 + return UDS_SUCCESS; 29 + } 30 + 31 + int uds_wait_cond(struct cond_var *cv, struct mutex *mutex) 32 + { 33 + DEFINE_WAIT(__wait); 34 + 35 + prepare_to_wait(&cv->wait_queue, &__wait, TASK_IDLE); 36 + uds_unlock_mutex(mutex); 37 + schedule(); 38 + finish_wait(&cv->wait_queue, &__wait); 39 + uds_lock_mutex(mutex); 40 + return UDS_SUCCESS; 41 + } 42 + 43 + int uds_destroy_cond(struct cond_var *cv) 44 + { 45 + return UDS_SUCCESS; 46 + }
+36
drivers/md/dm-vdo/thread-device.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2023 Red Hat 4 + */ 5 + 6 + #include "thread-device.h" 7 + 8 + #include "thread-registry.h" 9 + 10 + /* A registry of threads associated with device id numbers. */ 11 + static struct thread_registry device_id_thread_registry; 12 + 13 + /* Any registered thread must be unregistered. */ 14 + void uds_register_thread_device_id(struct registered_thread *new_thread, 15 + unsigned int *id_ptr) 16 + { 17 + uds_register_thread(&device_id_thread_registry, new_thread, id_ptr); 18 + } 19 + 20 + void uds_unregister_thread_device_id(void) 21 + { 22 + uds_unregister_thread(&device_id_thread_registry); 23 + } 24 + 25 + int uds_get_thread_device_id(void) 26 + { 27 + const unsigned int *pointer; 28 + 29 + pointer = uds_lookup_thread(&device_id_thread_registry); 30 + return (pointer != NULL) ? *pointer : -1; 31 + } 32 + 33 + void uds_initialize_thread_device_registry(void) 34 + { 35 + uds_initialize_thread_registry(&device_id_thread_registry); 36 + }
+20
drivers/md/dm-vdo/thread-device.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright 2023 Red Hat 4 + */ 5 + 6 + #ifndef UDS_THREAD_DEVICE_H 7 + #define UDS_THREAD_DEVICE_H 8 + 9 + #include "thread-registry.h" 10 + 11 + void uds_register_thread_device_id(struct registered_thread *new_thread, 12 + unsigned int *id_ptr); 13 + 14 + void uds_unregister_thread_device_id(void); 15 + 16 + int uds_get_thread_device_id(void); 17 + 18 + void uds_initialize_thread_device_registry(void); 19 + 20 + #endif /* UDS_THREAD_DEVICE_H */
+92
drivers/md/dm-vdo/thread-registry.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2023 Red Hat 4 + */ 5 + 6 + #include "thread-registry.h" 7 + 8 + #include <linux/rculist.h> 9 + 10 + #include "permassert.h" 11 + 12 + /* 13 + * We need to be careful when using other facilities that may use thread registry functions in 14 + * their normal operation. For example, we do not want to invoke the logger while holding a lock. 15 + */ 16 + 17 + void uds_initialize_thread_registry(struct thread_registry *registry) 18 + { 19 + INIT_LIST_HEAD(&registry->links); 20 + spin_lock_init(&registry->lock); 21 + } 22 + 23 + /* Register the current thread and associate it with a data pointer. */ 24 + void uds_register_thread(struct thread_registry *registry, 25 + struct registered_thread *new_thread, const void *pointer) 26 + { 27 + struct registered_thread *thread; 28 + bool found_it = false; 29 + 30 + INIT_LIST_HEAD(&new_thread->links); 31 + new_thread->pointer = pointer; 32 + new_thread->task = current; 33 + 34 + spin_lock(&registry->lock); 35 + list_for_each_entry(thread, &registry->links, links) { 36 + if (thread->task == current) { 37 + /* There should be no existing entry. */ 38 + list_del_rcu(&thread->links); 39 + found_it = true; 40 + break; 41 + } 42 + } 43 + list_add_tail_rcu(&new_thread->links, &registry->links); 44 + spin_unlock(&registry->lock); 45 + 46 + ASSERT_LOG_ONLY(!found_it, "new thread not already in registry"); 47 + if (found_it) { 48 + /* Ensure no RCU iterators see it before re-initializing. */ 49 + synchronize_rcu(); 50 + INIT_LIST_HEAD(&thread->links); 51 + } 52 + } 53 + 54 + void uds_unregister_thread(struct thread_registry *registry) 55 + { 56 + struct registered_thread *thread; 57 + bool found_it = false; 58 + 59 + spin_lock(&registry->lock); 60 + list_for_each_entry(thread, &registry->links, links) { 61 + if (thread->task == current) { 62 + list_del_rcu(&thread->links); 63 + found_it = true; 64 + break; 65 + } 66 + } 67 + spin_unlock(&registry->lock); 68 + 69 + ASSERT_LOG_ONLY(found_it, "thread found in registry"); 70 + if (found_it) { 71 + /* Ensure no RCU iterators see it before re-initializing. */ 72 + synchronize_rcu(); 73 + INIT_LIST_HEAD(&thread->links); 74 + } 75 + } 76 + 77 + const void *uds_lookup_thread(struct thread_registry *registry) 78 + { 79 + struct registered_thread *thread; 80 + const void *result = NULL; 81 + 82 + rcu_read_lock(); 83 + list_for_each_entry_rcu(thread, &registry->links, links) { 84 + if (thread->task == current) { 85 + result = thread->pointer; 86 + break; 87 + } 88 + } 89 + rcu_read_unlock(); 90 + 91 + return result; 92 + }
+32
drivers/md/dm-vdo/thread-registry.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright 2023 Red Hat 4 + */ 5 + 6 + #ifndef UDS_THREAD_REGISTRY_H 7 + #define UDS_THREAD_REGISTRY_H 8 + 9 + #include <linux/list.h> 10 + #include <linux/spinlock.h> 11 + 12 + struct thread_registry { 13 + struct list_head links; 14 + spinlock_t lock; 15 + }; 16 + 17 + struct registered_thread { 18 + struct list_head links; 19 + const void *pointer; 20 + struct task_struct *task; 21 + }; 22 + 23 + void uds_initialize_thread_registry(struct thread_registry *registry); 24 + 25 + void uds_register_thread(struct thread_registry *registry, 26 + struct registered_thread *new_thread, const void *pointer); 27 + 28 + void uds_unregister_thread(struct thread_registry *registry); 29 + 30 + const void *uds_lookup_thread(struct thread_registry *registry); 31 + 32 + #endif /* UDS_THREAD_REGISTRY_H */
+183
drivers/md/dm-vdo/uds-threads.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2023 Red Hat 4 + */ 5 + 6 + #include "uds-threads.h" 7 + 8 + #include <linux/completion.h> 9 + #include <linux/err.h> 10 + #include <linux/kthread.h> 11 + #include <linux/sched.h> 12 + 13 + #include "errors.h" 14 + #include "logger.h" 15 + #include "memory-alloc.h" 16 + 17 + static struct hlist_head thread_list; 18 + static struct mutex thread_mutex; 19 + static atomic_t thread_once = ATOMIC_INIT(0); 20 + 21 + struct thread { 22 + void (*thread_function)(void *thread_data); 23 + void *thread_data; 24 + struct hlist_node thread_links; 25 + struct task_struct *thread_task; 26 + struct completion thread_done; 27 + }; 28 + 29 + enum { 30 + ONCE_NOT_DONE = 0, 31 + ONCE_IN_PROGRESS = 1, 32 + ONCE_COMPLETE = 2, 33 + }; 34 + 35 + /* Run a function once only, and record that fact in the atomic value. */ 36 + void uds_perform_once(atomic_t *once, void (*function)(void)) 37 + { 38 + for (;;) { 39 + switch (atomic_cmpxchg(once, ONCE_NOT_DONE, ONCE_IN_PROGRESS)) { 40 + case ONCE_NOT_DONE: 41 + function(); 42 + atomic_set_release(once, ONCE_COMPLETE); 43 + return; 44 + case ONCE_IN_PROGRESS: 45 + cond_resched(); 46 + break; 47 + case ONCE_COMPLETE: 48 + return; 49 + default: 50 + return; 51 + } 52 + } 53 + } 54 + 55 + static void thread_init(void) 56 + { 57 + mutex_init(&thread_mutex); 58 + } 59 + 60 + static int thread_starter(void *arg) 61 + { 62 + struct registered_thread allocating_thread; 63 + struct thread *thread = arg; 64 + 65 + thread->thread_task = current; 66 + uds_perform_once(&thread_once, thread_init); 67 + mutex_lock(&thread_mutex); 68 + hlist_add_head(&thread->thread_links, &thread_list); 69 + mutex_unlock(&thread_mutex); 70 + uds_register_allocating_thread(&allocating_thread, NULL); 71 + thread->thread_function(thread->thread_data); 72 + uds_unregister_allocating_thread(); 73 + complete(&thread->thread_done); 74 + return 0; 75 + } 76 + 77 + int uds_create_thread(void (*thread_function)(void *), void *thread_data, 78 + const char *name, struct thread **new_thread) 79 + { 80 + char *name_colon = strchr(name, ':'); 81 + char *my_name_colon = strchr(current->comm, ':'); 82 + struct task_struct *task; 83 + struct thread *thread; 84 + int result; 85 + 86 + result = uds_allocate(1, struct thread, __func__, &thread); 87 + if (result != UDS_SUCCESS) { 88 + uds_log_warning("Error allocating memory for %s", name); 89 + return result; 90 + } 91 + 92 + thread->thread_function = thread_function; 93 + thread->thread_data = thread_data; 94 + init_completion(&thread->thread_done); 95 + /* 96 + * Start the thread, with an appropriate thread name. 97 + * 98 + * If the name supplied contains a colon character, use that name. This causes uds module 99 + * threads to have names like "uds:callbackW" and the main test runner thread to be named 100 + * "zub:runtest". 101 + * 102 + * Otherwise if the current thread has a name containing a colon character, prefix the name 103 + * supplied with the name of the current thread up to (and including) the colon character. 104 + * Thus when the "kvdo0:dedupeQ" thread opens an index session, all the threads associated 105 + * with that index will have names like "kvdo0:foo". 106 + * 107 + * Otherwise just use the name supplied. This should be a rare occurrence. 108 + */ 109 + if ((name_colon == NULL) && (my_name_colon != NULL)) { 110 + task = kthread_run(thread_starter, thread, "%.*s:%s", 111 + (int) (my_name_colon - current->comm), current->comm, 112 + name); 113 + } else { 114 + task = kthread_run(thread_starter, thread, "%s", name); 115 + } 116 + 117 + if (IS_ERR(task)) { 118 + uds_free(thread); 119 + return PTR_ERR(task); 120 + } 121 + 122 + *new_thread = thread; 123 + return UDS_SUCCESS; 124 + } 125 + 126 + int uds_join_threads(struct thread *thread) 127 + { 128 + while (wait_for_completion_interruptible(&thread->thread_done) != 0) 129 + /* empty loop */ 130 + ; 131 + 132 + mutex_lock(&thread_mutex); 133 + hlist_del(&thread->thread_links); 134 + mutex_unlock(&thread_mutex); 135 + uds_free(thread); 136 + return UDS_SUCCESS; 137 + } 138 + 139 + int uds_initialize_barrier(struct barrier *barrier, unsigned int thread_count) 140 + { 141 + int result; 142 + 143 + result = uds_initialize_semaphore(&barrier->mutex, 1); 144 + if (result != UDS_SUCCESS) 145 + return result; 146 + 147 + barrier->arrived = 0; 148 + barrier->thread_count = thread_count; 149 + return uds_initialize_semaphore(&barrier->wait, 0); 150 + } 151 + 152 + int uds_destroy_barrier(struct barrier *barrier) 153 + { 154 + int result; 155 + 156 + result = uds_destroy_semaphore(&barrier->mutex); 157 + if (result != UDS_SUCCESS) 158 + return result; 159 + 160 + return uds_destroy_semaphore(&barrier->wait); 161 + } 162 + 163 + int uds_enter_barrier(struct barrier *barrier) 164 + { 165 + bool last_thread; 166 + 167 + uds_acquire_semaphore(&barrier->mutex); 168 + last_thread = (++barrier->arrived == barrier->thread_count); 169 + if (last_thread) { 170 + int i; 171 + 172 + for (i = 1; i < barrier->thread_count; i++) 173 + uds_release_semaphore(&barrier->wait); 174 + 175 + barrier->arrived = 0; 176 + uds_release_semaphore(&barrier->mutex); 177 + } else { 178 + uds_release_semaphore(&barrier->mutex); 179 + uds_acquire_semaphore(&barrier->wait); 180 + } 181 + 182 + return UDS_SUCCESS; 183 + }
+115
drivers/md/dm-vdo/uds-threads.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright 2023 Red Hat 4 + */ 5 + 6 + #ifndef UDS_THREADS_H 7 + #define UDS_THREADS_H 8 + 9 + #include <linux/atomic.h> 10 + #include <linux/delay.h> 11 + #include <linux/jiffies.h> 12 + #include <linux/mutex.h> 13 + #include <linux/semaphore.h> 14 + #include <linux/wait.h> 15 + 16 + #include "errors.h" 17 + #include "time-utils.h" 18 + 19 + /* Thread and synchronization utilities for UDS */ 20 + 21 + struct cond_var { 22 + wait_queue_head_t wait_queue; 23 + }; 24 + 25 + struct thread; 26 + 27 + struct barrier { 28 + /* Mutex for this barrier object */ 29 + struct semaphore mutex; 30 + /* Semaphore for threads waiting at the barrier */ 31 + struct semaphore wait; 32 + /* Number of threads which have arrived */ 33 + int arrived; 34 + /* Total number of threads using this barrier */ 35 + int thread_count; 36 + }; 37 + 38 + int __must_check uds_create_thread(void (*thread_function)(void *), void *thread_data, 39 + const char *name, struct thread **new_thread); 40 + 41 + void uds_perform_once(atomic_t *once_state, void (*function) (void)); 42 + 43 + int uds_join_threads(struct thread *thread); 44 + 45 + int __must_check uds_initialize_barrier(struct barrier *barrier, 46 + unsigned int thread_count); 47 + int uds_destroy_barrier(struct barrier *barrier); 48 + int uds_enter_barrier(struct barrier *barrier); 49 + 50 + int __must_check uds_init_cond(struct cond_var *cond); 51 + int uds_signal_cond(struct cond_var *cond); 52 + int uds_broadcast_cond(struct cond_var *cond); 53 + int uds_wait_cond(struct cond_var *cond, struct mutex *mutex); 54 + int uds_destroy_cond(struct cond_var *cond); 55 + 56 + static inline int __must_check uds_init_mutex(struct mutex *mutex) 57 + { 58 + mutex_init(mutex); 59 + return UDS_SUCCESS; 60 + } 61 + 62 + static inline int uds_destroy_mutex(struct mutex *mutex) 63 + { 64 + return UDS_SUCCESS; 65 + } 66 + 67 + static inline void uds_lock_mutex(struct mutex *mutex) 68 + { 69 + mutex_lock(mutex); 70 + } 71 + 72 + static inline void uds_unlock_mutex(struct mutex *mutex) 73 + { 74 + mutex_unlock(mutex); 75 + } 76 + 77 + static inline int __must_check uds_initialize_semaphore(struct semaphore *semaphore, 78 + unsigned int value) 79 + { 80 + sema_init(semaphore, value); 81 + return UDS_SUCCESS; 82 + } 83 + 84 + static inline int uds_destroy_semaphore(struct semaphore *semaphore) 85 + { 86 + return UDS_SUCCESS; 87 + } 88 + 89 + static inline void uds_acquire_semaphore(struct semaphore *semaphore) 90 + { 91 + /* 92 + * Do not use down(semaphore). Instead use down_interruptible so that 93 + * we do not get 120 second stall messages in kern.log. 94 + */ 95 + while (down_interruptible(semaphore) != 0) { 96 + /* 97 + * If we're called from a user-mode process (e.g., "dmsetup 98 + * remove") while waiting for an operation that may take a 99 + * while (e.g., UDS index save), and a signal is sent (SIGINT, 100 + * SIGUSR2), then down_interruptible will not block. If that 101 + * happens, sleep briefly to avoid keeping the CPU locked up in 102 + * this loop. We could just call cond_resched, but then we'd 103 + * still keep consuming CPU time slices and swamp other threads 104 + * trying to do computational work. [VDO-4980] 105 + */ 106 + fsleep(1000); 107 + } 108 + } 109 + 110 + static inline void uds_release_semaphore(struct semaphore *semaphore) 111 + { 112 + up(semaphore); 113 + } 114 + 115 + #endif /* UDS_THREADS_H */