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