Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* |
| 3 | * Copyright 2023 Red Hat |
| 4 | */ |
| 5 | |
Mike Snitzer | c2f54aa | 2024-02-09 10:10:03 -0600 | [diff] [blame] | 6 | #include "thread-utils.h" |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 7 | |
Mike Snitzer | 20be466 | 2024-02-13 10:51:19 -0500 | [diff] [blame] | 8 | #include <asm/current.h> |
Mike Snitzer | cae3816d | 2024-02-14 10:25:39 -0500 | [diff] [blame] | 9 | #include <linux/delay.h> |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 10 | #include <linux/kthread.h> |
Mike Snitzer | cb6f8b7 | 2024-02-09 13:17:05 -0600 | [diff] [blame] | 11 | #include <linux/mutex.h> |
| 12 | #include <linux/types.h> |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 13 | |
| 14 | #include "errors.h" |
| 15 | #include "logger.h" |
| 16 | #include "memory-alloc.h" |
| 17 | |
| 18 | static struct hlist_head thread_list; |
| 19 | static struct mutex thread_mutex; |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 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 | |
Matthew Sakai | 7eb30fe | 2024-03-01 18:29:05 -0500 | [diff] [blame] | 29 | void vdo_initialize_threads_mutex(void) |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 30 | { |
| 31 | mutex_init(&thread_mutex); |
| 32 | } |
| 33 | |
| 34 | static int thread_starter(void *arg) |
| 35 | { |
| 36 | struct registered_thread allocating_thread; |
| 37 | struct thread *thread = arg; |
| 38 | |
| 39 | thread->thread_task = current; |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 40 | mutex_lock(&thread_mutex); |
| 41 | hlist_add_head(&thread->thread_links, &thread_list); |
| 42 | mutex_unlock(&thread_mutex); |
Mike Snitzer | 0eea6b6 | 2024-02-13 10:55:50 -0600 | [diff] [blame] | 43 | vdo_register_allocating_thread(&allocating_thread, NULL); |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 44 | thread->thread_function(thread->thread_data); |
Mike Snitzer | 0eea6b6 | 2024-02-13 10:55:50 -0600 | [diff] [blame] | 45 | vdo_unregister_allocating_thread(); |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 46 | complete(&thread->thread_done); |
| 47 | return 0; |
| 48 | } |
| 49 | |
Mike Snitzer | 650e310 | 2024-02-09 12:08:09 -0600 | [diff] [blame] | 50 | int vdo_create_thread(void (*thread_function)(void *), void *thread_data, |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 51 | const char *name, struct thread **new_thread) |
| 52 | { |
| 53 | char *name_colon = strchr(name, ':'); |
| 54 | char *my_name_colon = strchr(current->comm, ':'); |
| 55 | struct task_struct *task; |
| 56 | struct thread *thread; |
| 57 | int result; |
| 58 | |
Mike Snitzer | 0eea6b6 | 2024-02-13 10:55:50 -0600 | [diff] [blame] | 59 | result = vdo_allocate(1, struct thread, __func__, &thread); |
Mike Snitzer | 2de7038 | 2024-02-13 12:06:53 -0600 | [diff] [blame] | 60 | if (result != VDO_SUCCESS) { |
Mike Snitzer | 3584240 | 2024-02-14 09:22:04 -0600 | [diff] [blame] | 61 | vdo_log_warning("Error allocating memory for %s", name); |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 62 | return result; |
| 63 | } |
| 64 | |
| 65 | thread->thread_function = thread_function; |
| 66 | thread->thread_data = thread_data; |
| 67 | init_completion(&thread->thread_done); |
| 68 | /* |
| 69 | * Start the thread, with an appropriate thread name. |
| 70 | * |
| 71 | * If the name supplied contains a colon character, use that name. This causes uds module |
| 72 | * threads to have names like "uds:callbackW" and the main test runner thread to be named |
| 73 | * "zub:runtest". |
| 74 | * |
| 75 | * Otherwise if the current thread has a name containing a colon character, prefix the name |
| 76 | * supplied with the name of the current thread up to (and including) the colon character. |
| 77 | * Thus when the "kvdo0:dedupeQ" thread opens an index session, all the threads associated |
| 78 | * with that index will have names like "kvdo0:foo". |
| 79 | * |
| 80 | * Otherwise just use the name supplied. This should be a rare occurrence. |
| 81 | */ |
| 82 | if ((name_colon == NULL) && (my_name_colon != NULL)) { |
| 83 | task = kthread_run(thread_starter, thread, "%.*s:%s", |
| 84 | (int) (my_name_colon - current->comm), current->comm, |
| 85 | name); |
| 86 | } else { |
| 87 | task = kthread_run(thread_starter, thread, "%s", name); |
| 88 | } |
| 89 | |
| 90 | if (IS_ERR(task)) { |
Mike Snitzer | 0eea6b6 | 2024-02-13 10:55:50 -0600 | [diff] [blame] | 91 | vdo_free(thread); |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 92 | return PTR_ERR(task); |
| 93 | } |
| 94 | |
| 95 | *new_thread = thread; |
Mike Snitzer | 34edf9e | 2024-02-13 13:18:35 -0600 | [diff] [blame] | 96 | return VDO_SUCCESS; |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 97 | } |
| 98 | |
Mike Snitzer | 650e310 | 2024-02-09 12:08:09 -0600 | [diff] [blame] | 99 | void vdo_join_threads(struct thread *thread) |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 100 | { |
Mike Snitzer | cae3816d | 2024-02-14 10:25:39 -0500 | [diff] [blame] | 101 | while (wait_for_completion_interruptible(&thread->thread_done)) |
| 102 | fsleep(1000); |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 103 | |
| 104 | mutex_lock(&thread_mutex); |
| 105 | hlist_del(&thread->thread_links); |
| 106 | mutex_unlock(&thread_mutex); |
Mike Snitzer | 0eea6b6 | 2024-02-13 10:55:50 -0600 | [diff] [blame] | 107 | vdo_free(thread); |
Matthew Sakai | 89f9b70 | 2023-11-16 19:47:35 -0500 | [diff] [blame] | 108 | } |