Line data Source code
1 0 : /*
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 1 : 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 1 : 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 1 : struct k_p4wq_work {
37 : /* Filled out by submitting code */
38 0 : int32_t priority;
39 0 : int32_t deadline;
40 0 : k_p4wq_handler_t handler;
41 0 : bool sync;
42 0 : struct k_sem done_sem;
43 :
44 : /* reserved for implementation */
45 : union {
46 0 : struct rbnode rbnode;
47 0 : sys_dlist_t dlnode;
48 0 : };
49 0 : struct k_thread *thread;
50 0 : struct k_p4wq *queue;
51 : };
52 :
53 0 : #define K_P4WQ_QUEUE_PER_THREAD BIT(0)
54 0 : #define K_P4WQ_DELAYED_START BIT(1)
55 0 : #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 1 : struct k_p4wq {
63 0 : 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 0 : _wait_q_t waitq;
75 :
76 : /* Work items waiting for processing */
77 0 : struct rbtree queue;
78 :
79 : /* Work items in progress */
80 0 : sys_dlist_t active;
81 :
82 : /* K_P4WQ_* flags above */
83 0 : 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 0 : k_p4wq_done_handler_t done_handler;
89 : };
90 :
91 0 : struct k_p4wq_initparam {
92 0 : uint32_t num;
93 0 : uintptr_t stack_size;
94 0 : struct k_p4wq *queue;
95 0 : struct k_thread *threads;
96 0 : struct z_thread_stack_element *stacks;
97 0 : uint32_t flags;
98 0 : 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 1 : #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 1 : #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 1 : #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 1 : #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 1 : 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 1 : 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 1 : 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 1 : 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 1 : int k_p4wq_wait(struct k_p4wq_work *work, k_timeout_t timeout);
244 :
245 0 : 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_ */
|