1 /* 2 * Copyright (c) 2020 Intel Corporation 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 #ifndef ZEPHYR_INCLUDE_SYS_P4WQ_H_ 7 #define ZEPHYR_INCLUDE_SYS_P4WQ_H_ 8 9 #include <zephyr/kernel.h> 10 #include <zephyr/sys/iterable_sections.h> 11 12 /* Zephyr Pooled Parallel Preemptible Priority-based Work Queues */ 13 14 struct k_p4wq_work; 15 16 /** 17 * P4 Queue handler callback 18 */ 19 typedef void (*k_p4wq_handler_t)(struct k_p4wq_work *work); 20 21 /** 22 * Optional P4 Queue done callback. 23 * Will be called after the memory structure is not used anymore by the p4wq. 24 * If it is not used it must be set to NULL. 25 */ 26 typedef void (*k_p4wq_done_handler_t)(struct k_p4wq_work *work); 27 28 /** 29 * @brief P4 Queue Work Item 30 * 31 * User-populated struct representing a single work item. The 32 * priority and deadline fields are interpreted as thread scheduling 33 * priorities, exactly as per k_thread_priority_set() and 34 * k_thread_deadline_set(). 35 */ 36 struct k_p4wq_work { 37 /* Filled out by submitting code */ 38 int32_t priority; 39 int32_t deadline; 40 k_p4wq_handler_t handler; 41 bool sync; 42 struct k_sem done_sem; 43 44 /* reserved for implementation */ 45 union { 46 struct rbnode rbnode; 47 sys_dlist_t dlnode; 48 }; 49 struct k_thread *thread; 50 struct k_p4wq *queue; 51 }; 52 53 #define K_P4WQ_QUEUE_PER_THREAD BIT(0) 54 #define K_P4WQ_DELAYED_START BIT(1) 55 #define K_P4WQ_USER_CPU_MASK BIT(2) 56 57 /** 58 * @brief P4 Queue 59 * 60 * Kernel pooled parallel preemptible priority-based work queue 61 */ 62 struct k_p4wq { 63 struct k_spinlock lock; 64 65 /* Pending threads waiting for work items 66 * 67 * FIXME: a waitq isn't really the right data structure here. 68 * Wait queues are priority-sorted, but we don't want that 69 * sorting overhead since we're effectively doing it ourselves 70 * by directly mutating the priority when a thread is 71 * unpended. We just want "blocked threads on a list", but 72 * there's no clean scheduler API for that. 73 */ 74 _wait_q_t waitq; 75 76 /* Work items waiting for processing */ 77 struct rbtree queue; 78 79 /* Work items in progress */ 80 sys_dlist_t active; 81 82 /* K_P4WQ_* flags above */ 83 uint32_t flags; 84 85 /* done handler which is called every time after work was successfully executed 86 * and k_p4wq_work is not needed by p4wq anymore 87 */ 88 k_p4wq_done_handler_t done_handler; 89 }; 90 91 struct k_p4wq_initparam { 92 uint32_t num; 93 uintptr_t stack_size; 94 struct k_p4wq *queue; 95 struct k_thread *threads; 96 struct z_thread_stack_element *stacks; 97 uint32_t flags; 98 k_p4wq_done_handler_t done_handler; 99 }; 100 101 /** 102 * @brief Statically initialize a P4 Work Queue 103 * 104 * Statically defines a struct k_p4wq object with the specified number 105 * of threads which will be initialized at boot and ready for use on 106 * entry to main(). 107 * 108 * @param name Symbol name of the struct k_p4wq that will be defined 109 * @param n_threads Number of threads in the work queue pool 110 * @param stack_sz Requested stack size of each thread, in bytes 111 * @param dn_handler Function pointer to handler of type k_p4wq_done_handler_t 112 */ 113 #define K_P4WQ_DEFINE_WITH_DONE_HANDLER(name, n_threads, stack_sz, dn_handler) \ 114 static K_THREAD_STACK_ARRAY_DEFINE(_p4stacks_##name, \ 115 n_threads, stack_sz); \ 116 static struct k_thread _p4threads_##name[n_threads]; \ 117 static struct k_p4wq name; \ 118 static const STRUCT_SECTION_ITERABLE(k_p4wq_initparam, \ 119 _init_##name) = { \ 120 .num = n_threads, \ 121 .stack_size = stack_sz, \ 122 .threads = _p4threads_##name, \ 123 .stacks = &(_p4stacks_##name[0][0]), \ 124 .queue = &name, \ 125 .flags = 0, \ 126 .done_handler = dn_handler, \ 127 } 128 129 /** 130 * @brief Statically initialize a P4 Work Queue 131 * 132 * Same like K_P4WQ_DEFINE_WITH_DONE_HANDLER but without an 133 * optional handler which is called everytime when work is executed 134 * and not used anymore by the p4wq 135 */ 136 #define K_P4WQ_DEFINE(name, n_threads, stack_sz) \ 137 K_P4WQ_DEFINE_WITH_DONE_HANDLER(name, n_threads, stack_sz, NULL) 138 139 /** 140 * @brief Statically initialize an array of P4 Work Queues 141 * 142 * Statically defines an array of struct k_p4wq objects with the specified 143 * number of threads which will be initialized at boot and ready for use on 144 * entry to main(). 145 * 146 * @param name Symbol name of the struct k_p4wq array that will be defined 147 * @param n_threads Number of threads and work queues 148 * @param stack_sz Requested stack size of each thread, in bytes 149 * @param flg Flags 150 * @param dn_handler Function pointer to handler of type k_p4wq_done_handler_t 151 */ 152 #define K_P4WQ_ARRAY_DEFINE_WITH_DONE_HANDLER(name, n_threads, stack_sz, flg, dn_handler) \ 153 static K_THREAD_STACK_ARRAY_DEFINE(_p4stacks_##name, \ 154 n_threads, stack_sz); \ 155 static struct k_thread _p4threads_##name[n_threads]; \ 156 static struct k_p4wq name[n_threads]; \ 157 static const STRUCT_SECTION_ITERABLE(k_p4wq_initparam, \ 158 _init_##name) = { \ 159 .num = n_threads, \ 160 .stack_size = stack_sz, \ 161 .threads = _p4threads_##name, \ 162 .stacks = &(_p4stacks_##name[0][0]), \ 163 .queue = name, \ 164 .flags = K_P4WQ_QUEUE_PER_THREAD | flg, \ 165 .done_handler = dn_handler, \ 166 } 167 168 /** 169 * @brief Statically initialize an array of P4 Work Queues 170 * 171 * Same like K_P4WQ_ARRAY_DEFINE_WITH_DONE_HANDLER but without an 172 * optional handler which is called everytime when work is executed 173 * and not used anymore by the p4wq 174 */ 175 #define K_P4WQ_ARRAY_DEFINE(name, n_threads, stack_sz, flg) \ 176 K_P4WQ_ARRAY_DEFINE_WITH_DONE_HANDLER(name, n_threads, stack_sz, flg, NULL) 177 178 /** 179 * @brief Initialize P4 Queue 180 * 181 * Initializes a P4 Queue object. These objects must be initialized 182 * via this function (or statically using K_P4WQ_DEFINE) before any 183 * other API calls are made on it. 184 * 185 * @param queue P4 Queue to initialize 186 */ 187 void k_p4wq_init(struct k_p4wq *queue); 188 189 /** 190 * @brief Dynamically add a thread object to a P4 Queue pool 191 * 192 * Adds a thread to the pool managed by a P4 queue. The thread object 193 * must not be in use. If k_thread_create() has previously been 194 * called on it, it must be aborted before being given to the queue. 195 * 196 * @param queue P4 Queue to which to add the thread 197 * @param thread Uninitialized/aborted thread object to add 198 * @param stack Thread stack memory 199 * @param stack_size Thread stack size 200 */ 201 void k_p4wq_add_thread(struct k_p4wq *queue, struct k_thread *thread, 202 k_thread_stack_t *stack, 203 size_t stack_size); 204 205 /** 206 * @brief Submit work item to a P4 queue 207 * 208 * Submits the specified work item to the queue. The caller must have 209 * already initialized the relevant fields of the struct. The queue 210 * will execute the handler when CPU time is available and when no 211 * higher-priority work items are available. The handler may be 212 * invoked on any CPU. 213 * 214 * The caller must not mutate the struct while it is stored in the 215 * queue. The memory should remain unchanged until k_p4wq_cancel() is 216 * called or until the entry to the handler function. 217 * 218 * @note This call is a scheduling point, so if the submitted item (or 219 * any other ready thread) has a higher priority than the current 220 * thread and the current thread has a preemptible priority then the 221 * caller will yield. 222 * 223 * @param queue P4 Queue to which to submit 224 * @param item P4 work item to be submitted 225 */ 226 void k_p4wq_submit(struct k_p4wq *queue, struct k_p4wq_work *item); 227 228 /** 229 * @brief Cancel submitted P4 work item 230 * 231 * Cancels a previously-submitted work item and removes it from the 232 * queue. Returns true if the item was found in the queue and 233 * removed. If the function returns false, either the item was never 234 * submitted, has already been executed, or is still running. 235 * 236 * @return true if the item was successfully removed, otherwise false 237 */ 238 bool k_p4wq_cancel(struct k_p4wq *queue, struct k_p4wq_work *item); 239 240 /** 241 * @brief Regain ownership of the work item, wait for completion if it's synchronous 242 */ 243 int k_p4wq_wait(struct k_p4wq_work *work, k_timeout_t timeout); 244 245 void k_p4wq_enable_static_thread(struct k_p4wq *queue, struct k_thread *thread, 246 uint32_t cpu_mask); 247 248 #endif /* ZEPHYR_INCLUDE_SYS_P4WQ_H_ */ 249