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