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