1 /*
2 * Copyright (c) 2017 Wind River Systems, Inc.
3 * Copyright (c) 2023 Arm Limited (or its affiliates). All rights reserved.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @file
10 *
11 * @brief Kernel asynchronous event polling interface.
12 *
13 * This polling mechanism allows waiting on multiple events concurrently,
14 * either events triggered directly, or from kernel objects or other kernel
15 * constructs.
16 */
17
18 #include <zephyr/kernel.h>
19 #include <zephyr/kernel_structs.h>
20 #include <kernel_internal.h>
21 #include <wait_q.h>
22 #include <ksched.h>
23 #include <zephyr/syscall_handler.h>
24 #include <zephyr/sys/dlist.h>
25 #include <zephyr/sys/util.h>
26 #include <zephyr/sys/__assert.h>
27 #include <stdbool.h>
28
29 /* Single subsystem lock. Locking per-event would be better on highly
30 * contended SMP systems, but the original locking scheme here is
31 * subtle (it relies on releasing/reacquiring the lock in areas for
32 * latency control and it's sometimes hard to see exactly what data is
33 * "inside" a given critical section). Do the synchronization port
34 * later as an optimization.
35 */
36 static struct k_spinlock lock;
37
38 enum POLL_MODE { MODE_NONE, MODE_POLL, MODE_TRIGGERED };
39
40 static int signal_poller(struct k_poll_event *event, uint32_t state);
41 static int signal_triggered_work(struct k_poll_event *event, uint32_t status);
42
k_poll_event_init(struct k_poll_event * event,uint32_t type,int mode,void * obj)43 void k_poll_event_init(struct k_poll_event *event, uint32_t type,
44 int mode, void *obj)
45 {
46 __ASSERT(mode == K_POLL_MODE_NOTIFY_ONLY,
47 "only NOTIFY_ONLY mode is supported\n");
48 __ASSERT(type < (BIT(_POLL_NUM_TYPES)), "invalid type\n");
49 __ASSERT(obj != NULL, "must provide an object\n");
50
51 event->poller = NULL;
52 /* event->tag is left uninitialized: the user will set it if needed */
53 event->type = type;
54 event->state = K_POLL_STATE_NOT_READY;
55 event->mode = mode;
56 event->unused = 0U;
57 event->obj = obj;
58
59 SYS_PORT_TRACING_FUNC(k_poll_api, event_init, event);
60 }
61
62 /* must be called with interrupts locked */
is_condition_met(struct k_poll_event * event,uint32_t * state)63 static inline bool is_condition_met(struct k_poll_event *event, uint32_t *state)
64 {
65 switch (event->type) {
66 case K_POLL_TYPE_SEM_AVAILABLE:
67 if (k_sem_count_get(event->sem) > 0U) {
68 *state = K_POLL_STATE_SEM_AVAILABLE;
69 return true;
70 }
71 break;
72 case K_POLL_TYPE_DATA_AVAILABLE:
73 if (!k_queue_is_empty(event->queue)) {
74 *state = K_POLL_STATE_FIFO_DATA_AVAILABLE;
75 return true;
76 }
77 break;
78 case K_POLL_TYPE_SIGNAL:
79 if (event->signal->signaled != 0U) {
80 *state = K_POLL_STATE_SIGNALED;
81 return true;
82 }
83 break;
84 case K_POLL_TYPE_MSGQ_DATA_AVAILABLE:
85 if (event->msgq->used_msgs > 0) {
86 *state = K_POLL_STATE_MSGQ_DATA_AVAILABLE;
87 return true;
88 }
89 break;
90 #ifdef CONFIG_PIPES
91 case K_POLL_TYPE_PIPE_DATA_AVAILABLE:
92 if (k_pipe_read_avail(event->pipe)) {
93 *state = K_POLL_STATE_PIPE_DATA_AVAILABLE;
94 return true;
95 }
96 #endif
97 case K_POLL_TYPE_IGNORE:
98 break;
99 default:
100 __ASSERT(false, "invalid event type (0x%x)\n", event->type);
101 break;
102 }
103
104 return false;
105 }
106
poller_thread(struct z_poller * p)107 static struct k_thread *poller_thread(struct z_poller *p)
108 {
109 return p ? CONTAINER_OF(p, struct k_thread, poller) : NULL;
110 }
111
add_event(sys_dlist_t * events,struct k_poll_event * event,struct z_poller * poller)112 static inline void add_event(sys_dlist_t *events, struct k_poll_event *event,
113 struct z_poller *poller)
114 {
115 struct k_poll_event *pending;
116
117 pending = (struct k_poll_event *)sys_dlist_peek_tail(events);
118 if ((pending == NULL) ||
119 (z_sched_prio_cmp(poller_thread(pending->poller),
120 poller_thread(poller)) > 0)) {
121 sys_dlist_append(events, &event->_node);
122 return;
123 }
124
125 SYS_DLIST_FOR_EACH_CONTAINER(events, pending, _node) {
126 if (z_sched_prio_cmp(poller_thread(poller),
127 poller_thread(pending->poller)) > 0) {
128 sys_dlist_insert(&pending->_node, &event->_node);
129 return;
130 }
131 }
132
133 sys_dlist_append(events, &event->_node);
134 }
135
136 /* must be called with interrupts locked */
register_event(struct k_poll_event * event,struct z_poller * poller)137 static inline void register_event(struct k_poll_event *event,
138 struct z_poller *poller)
139 {
140 switch (event->type) {
141 case K_POLL_TYPE_SEM_AVAILABLE:
142 __ASSERT(event->sem != NULL, "invalid semaphore\n");
143 add_event(&event->sem->poll_events, event, poller);
144 break;
145 case K_POLL_TYPE_DATA_AVAILABLE:
146 __ASSERT(event->queue != NULL, "invalid queue\n");
147 add_event(&event->queue->poll_events, event, poller);
148 break;
149 case K_POLL_TYPE_SIGNAL:
150 __ASSERT(event->signal != NULL, "invalid poll signal\n");
151 add_event(&event->signal->poll_events, event, poller);
152 break;
153 case K_POLL_TYPE_MSGQ_DATA_AVAILABLE:
154 __ASSERT(event->msgq != NULL, "invalid message queue\n");
155 add_event(&event->msgq->poll_events, event, poller);
156 break;
157 #ifdef CONFIG_PIPES
158 case K_POLL_TYPE_PIPE_DATA_AVAILABLE:
159 __ASSERT(event->pipe != NULL, "invalid pipe\n");
160 add_event(&event->pipe->poll_events, event, poller);
161 break;
162 #endif
163 case K_POLL_TYPE_IGNORE:
164 /* nothing to do */
165 break;
166 default:
167 __ASSERT(false, "invalid event type\n");
168 break;
169 }
170
171 event->poller = poller;
172 }
173
174 /* must be called with interrupts locked */
clear_event_registration(struct k_poll_event * event)175 static inline void clear_event_registration(struct k_poll_event *event)
176 {
177 bool remove_event = false;
178
179 event->poller = NULL;
180
181 switch (event->type) {
182 case K_POLL_TYPE_SEM_AVAILABLE:
183 __ASSERT(event->sem != NULL, "invalid semaphore\n");
184 remove_event = true;
185 break;
186 case K_POLL_TYPE_DATA_AVAILABLE:
187 __ASSERT(event->queue != NULL, "invalid queue\n");
188 remove_event = true;
189 break;
190 case K_POLL_TYPE_SIGNAL:
191 __ASSERT(event->signal != NULL, "invalid poll signal\n");
192 remove_event = true;
193 break;
194 case K_POLL_TYPE_MSGQ_DATA_AVAILABLE:
195 __ASSERT(event->msgq != NULL, "invalid message queue\n");
196 remove_event = true;
197 break;
198 #ifdef CONFIG_PIPES
199 case K_POLL_TYPE_PIPE_DATA_AVAILABLE:
200 __ASSERT(event->pipe != NULL, "invalid pipe\n");
201 remove_event = true;
202 break;
203 #endif
204 case K_POLL_TYPE_IGNORE:
205 /* nothing to do */
206 break;
207 default:
208 __ASSERT(false, "invalid event type\n");
209 break;
210 }
211 if (remove_event && sys_dnode_is_linked(&event->_node)) {
212 sys_dlist_remove(&event->_node);
213 }
214 }
215
216 /* must be called with interrupts locked */
clear_event_registrations(struct k_poll_event * events,int num_events,k_spinlock_key_t key)217 static inline void clear_event_registrations(struct k_poll_event *events,
218 int num_events,
219 k_spinlock_key_t key)
220 {
221 while (num_events--) {
222 clear_event_registration(&events[num_events]);
223 k_spin_unlock(&lock, key);
224 key = k_spin_lock(&lock);
225 }
226 }
227
set_event_ready(struct k_poll_event * event,uint32_t state)228 static inline void set_event_ready(struct k_poll_event *event, uint32_t state)
229 {
230 event->poller = NULL;
231 event->state |= state;
232 }
233
register_events(struct k_poll_event * events,int num_events,struct z_poller * poller,bool just_check)234 static inline int register_events(struct k_poll_event *events,
235 int num_events,
236 struct z_poller *poller,
237 bool just_check)
238 {
239 int events_registered = 0;
240
241 for (int ii = 0; ii < num_events; ii++) {
242 k_spinlock_key_t key;
243 uint32_t state;
244
245 key = k_spin_lock(&lock);
246 if (is_condition_met(&events[ii], &state)) {
247 set_event_ready(&events[ii], state);
248 poller->is_polling = false;
249 } else if (!just_check && poller->is_polling) {
250 register_event(&events[ii], poller);
251 events_registered += 1;
252 } else {
253 /* Event is not one of those identified in is_condition_met()
254 * catching non-polling events, or is marked for just check,
255 * or not marked for polling. No action needed.
256 */
257 ;
258 }
259 k_spin_unlock(&lock, key);
260 }
261
262 return events_registered;
263 }
264
signal_poller(struct k_poll_event * event,uint32_t state)265 static int signal_poller(struct k_poll_event *event, uint32_t state)
266 {
267 struct k_thread *thread = poller_thread(event->poller);
268
269 __ASSERT(thread != NULL, "poller should have a thread\n");
270
271 if (!z_is_thread_pending(thread)) {
272 return 0;
273 }
274
275 z_unpend_thread(thread);
276 arch_thread_return_value_set(thread,
277 state == K_POLL_STATE_CANCELLED ? -EINTR : 0);
278
279 if (!z_is_thread_ready(thread)) {
280 return 0;
281 }
282
283 z_ready_thread(thread);
284
285 return 0;
286 }
287
z_impl_k_poll(struct k_poll_event * events,int num_events,k_timeout_t timeout)288 int z_impl_k_poll(struct k_poll_event *events, int num_events,
289 k_timeout_t timeout)
290 {
291 int events_registered;
292 k_spinlock_key_t key;
293 struct z_poller *poller = &_current->poller;
294
295 poller->is_polling = true;
296 poller->mode = MODE_POLL;
297
298 __ASSERT(!arch_is_in_isr(), "");
299 __ASSERT(events != NULL, "NULL events\n");
300 __ASSERT(num_events >= 0, "<0 events\n");
301
302 SYS_PORT_TRACING_FUNC_ENTER(k_poll_api, poll, events);
303
304 events_registered = register_events(events, num_events, poller,
305 K_TIMEOUT_EQ(timeout, K_NO_WAIT));
306
307 key = k_spin_lock(&lock);
308
309 /*
310 * If we're not polling anymore, it means that at least one event
311 * condition is met, either when looping through the events here or
312 * because one of the events registered has had its state changed.
313 */
314 if (!poller->is_polling) {
315 clear_event_registrations(events, events_registered, key);
316 k_spin_unlock(&lock, key);
317
318 SYS_PORT_TRACING_FUNC_EXIT(k_poll_api, poll, events, 0);
319
320 return 0;
321 }
322
323 poller->is_polling = false;
324
325 if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
326 k_spin_unlock(&lock, key);
327
328 SYS_PORT_TRACING_FUNC_EXIT(k_poll_api, poll, events, -EAGAIN);
329
330 return -EAGAIN;
331 }
332
333 static _wait_q_t wait_q = Z_WAIT_Q_INIT(&wait_q);
334
335 int swap_rc = z_pend_curr(&lock, key, &wait_q, timeout);
336
337 /*
338 * Clear all event registrations. If events happen while we're in this
339 * loop, and we already had one that triggered, that's OK: they will
340 * end up in the list of events that are ready; if we timed out, and
341 * events happen while we're in this loop, that is OK as well since
342 * we've already know the return code (-EAGAIN), and even if they are
343 * added to the list of events that occurred, the user has to check the
344 * return code first, which invalidates the whole list of event states.
345 */
346 key = k_spin_lock(&lock);
347 clear_event_registrations(events, events_registered, key);
348 k_spin_unlock(&lock, key);
349
350 SYS_PORT_TRACING_FUNC_EXIT(k_poll_api, poll, events, swap_rc);
351
352 return swap_rc;
353 }
354
355 #ifdef CONFIG_USERSPACE
z_vrfy_k_poll(struct k_poll_event * events,int num_events,k_timeout_t timeout)356 static inline int z_vrfy_k_poll(struct k_poll_event *events,
357 int num_events, k_timeout_t timeout)
358 {
359 int ret;
360 k_spinlock_key_t key;
361 struct k_poll_event *events_copy = NULL;
362 uint32_t bounds;
363
364 /* Validate the events buffer and make a copy of it in an
365 * allocated kernel-side buffer.
366 */
367 if (Z_SYSCALL_VERIFY(num_events >= 0)) {
368 ret = -EINVAL;
369 goto out;
370 }
371 if (Z_SYSCALL_VERIFY_MSG(!u32_mul_overflow(num_events,
372 sizeof(struct k_poll_event),
373 &bounds),
374 "num_events too large")) {
375 ret = -EINVAL;
376 goto out;
377 }
378 events_copy = z_thread_malloc(bounds);
379 if (!events_copy) {
380 ret = -ENOMEM;
381 goto out;
382 }
383
384 key = k_spin_lock(&lock);
385 if (Z_SYSCALL_MEMORY_WRITE(events, bounds)) {
386 k_spin_unlock(&lock, key);
387 goto oops_free;
388 }
389 (void)memcpy(events_copy, events, bounds);
390 k_spin_unlock(&lock, key);
391
392 /* Validate what's inside events_copy */
393 for (int i = 0; i < num_events; i++) {
394 struct k_poll_event *e = &events_copy[i];
395
396 if (Z_SYSCALL_VERIFY(e->mode == K_POLL_MODE_NOTIFY_ONLY)) {
397 ret = -EINVAL;
398 goto out_free;
399 }
400
401 switch (e->type) {
402 case K_POLL_TYPE_IGNORE:
403 break;
404 case K_POLL_TYPE_SIGNAL:
405 Z_OOPS(Z_SYSCALL_OBJ(e->signal, K_OBJ_POLL_SIGNAL));
406 break;
407 case K_POLL_TYPE_SEM_AVAILABLE:
408 Z_OOPS(Z_SYSCALL_OBJ(e->sem, K_OBJ_SEM));
409 break;
410 case K_POLL_TYPE_DATA_AVAILABLE:
411 Z_OOPS(Z_SYSCALL_OBJ(e->queue, K_OBJ_QUEUE));
412 break;
413 case K_POLL_TYPE_MSGQ_DATA_AVAILABLE:
414 Z_OOPS(Z_SYSCALL_OBJ(e->msgq, K_OBJ_MSGQ));
415 break;
416 #ifdef CONFIG_PIPES
417 case K_POLL_TYPE_PIPE_DATA_AVAILABLE:
418 Z_OOPS(Z_SYSCALL_OBJ(e->pipe, K_OBJ_PIPE));
419 break;
420 #endif
421 default:
422 ret = -EINVAL;
423 goto out_free;
424 }
425 }
426
427 ret = k_poll(events_copy, num_events, timeout);
428 (void)memcpy((void *)events, events_copy, bounds);
429 out_free:
430 k_free(events_copy);
431 out:
432 return ret;
433 oops_free:
434 k_free(events_copy);
435 Z_OOPS(1);
436 }
437 #include <syscalls/k_poll_mrsh.c>
438 #endif
439
440 /* must be called with interrupts locked */
signal_poll_event(struct k_poll_event * event,uint32_t state)441 static int signal_poll_event(struct k_poll_event *event, uint32_t state)
442 {
443 struct z_poller *poller = event->poller;
444 int retcode = 0;
445
446 if (poller != NULL) {
447 if (poller->mode == MODE_POLL) {
448 retcode = signal_poller(event, state);
449 } else if (poller->mode == MODE_TRIGGERED) {
450 retcode = signal_triggered_work(event, state);
451 } else {
452 /* Poller is not poll or triggered mode. No action needed.*/
453 ;
454 }
455
456 poller->is_polling = false;
457
458 if (retcode < 0) {
459 return retcode;
460 }
461 }
462
463 set_event_ready(event, state);
464 return retcode;
465 }
466
z_handle_obj_poll_events(sys_dlist_t * events,uint32_t state)467 void z_handle_obj_poll_events(sys_dlist_t *events, uint32_t state)
468 {
469 struct k_poll_event *poll_event;
470 k_spinlock_key_t key = k_spin_lock(&lock);
471
472 poll_event = (struct k_poll_event *)sys_dlist_get(events);
473 if (poll_event != NULL) {
474 (void) signal_poll_event(poll_event, state);
475 }
476
477 k_spin_unlock(&lock, key);
478 }
479
z_impl_k_poll_signal_init(struct k_poll_signal * sig)480 void z_impl_k_poll_signal_init(struct k_poll_signal *sig)
481 {
482 sys_dlist_init(&sig->poll_events);
483 sig->signaled = 0U;
484 /* signal->result is left uninitialized */
485 z_object_init(sig);
486
487 SYS_PORT_TRACING_FUNC(k_poll_api, signal_init, sig);
488 }
489
490 #ifdef CONFIG_USERSPACE
z_vrfy_k_poll_signal_init(struct k_poll_signal * sig)491 static inline void z_vrfy_k_poll_signal_init(struct k_poll_signal *sig)
492 {
493 Z_OOPS(Z_SYSCALL_OBJ_INIT(sig, K_OBJ_POLL_SIGNAL));
494 z_impl_k_poll_signal_init(sig);
495 }
496 #include <syscalls/k_poll_signal_init_mrsh.c>
497 #endif
498
z_impl_k_poll_signal_reset(struct k_poll_signal * sig)499 void z_impl_k_poll_signal_reset(struct k_poll_signal *sig)
500 {
501 sig->signaled = 0U;
502
503 SYS_PORT_TRACING_FUNC(k_poll_api, signal_reset, sig);
504 }
505
z_impl_k_poll_signal_check(struct k_poll_signal * sig,unsigned int * signaled,int * result)506 void z_impl_k_poll_signal_check(struct k_poll_signal *sig,
507 unsigned int *signaled, int *result)
508 {
509 *signaled = sig->signaled;
510 *result = sig->result;
511
512 SYS_PORT_TRACING_FUNC(k_poll_api, signal_check, sig);
513 }
514
515 #ifdef CONFIG_USERSPACE
z_vrfy_k_poll_signal_check(struct k_poll_signal * sig,unsigned int * signaled,int * result)516 void z_vrfy_k_poll_signal_check(struct k_poll_signal *sig,
517 unsigned int *signaled, int *result)
518 {
519 Z_OOPS(Z_SYSCALL_OBJ(sig, K_OBJ_POLL_SIGNAL));
520 Z_OOPS(Z_SYSCALL_MEMORY_WRITE(signaled, sizeof(unsigned int)));
521 Z_OOPS(Z_SYSCALL_MEMORY_WRITE(result, sizeof(int)));
522 z_impl_k_poll_signal_check(sig, signaled, result);
523 }
524 #include <syscalls/k_poll_signal_check_mrsh.c>
525 #endif
526
z_impl_k_poll_signal_raise(struct k_poll_signal * sig,int result)527 int z_impl_k_poll_signal_raise(struct k_poll_signal *sig, int result)
528 {
529 k_spinlock_key_t key = k_spin_lock(&lock);
530 struct k_poll_event *poll_event;
531
532 sig->result = result;
533 sig->signaled = 1U;
534
535 poll_event = (struct k_poll_event *)sys_dlist_get(&sig->poll_events);
536 if (poll_event == NULL) {
537 k_spin_unlock(&lock, key);
538
539 SYS_PORT_TRACING_FUNC(k_poll_api, signal_raise, sig, 0);
540
541 return 0;
542 }
543
544 int rc = signal_poll_event(poll_event, K_POLL_STATE_SIGNALED);
545
546 SYS_PORT_TRACING_FUNC(k_poll_api, signal_raise, sig, rc);
547
548 z_reschedule(&lock, key);
549 return rc;
550 }
551
552 #ifdef CONFIG_USERSPACE
z_vrfy_k_poll_signal_raise(struct k_poll_signal * sig,int result)553 static inline int z_vrfy_k_poll_signal_raise(struct k_poll_signal *sig,
554 int result)
555 {
556 Z_OOPS(Z_SYSCALL_OBJ(sig, K_OBJ_POLL_SIGNAL));
557 return z_impl_k_poll_signal_raise(sig, result);
558 }
559 #include <syscalls/k_poll_signal_raise_mrsh.c>
560
z_vrfy_k_poll_signal_reset(struct k_poll_signal * sig)561 static inline void z_vrfy_k_poll_signal_reset(struct k_poll_signal *sig)
562 {
563 Z_OOPS(Z_SYSCALL_OBJ(sig, K_OBJ_POLL_SIGNAL));
564 z_impl_k_poll_signal_reset(sig);
565 }
566 #include <syscalls/k_poll_signal_reset_mrsh.c>
567
568 #endif
569
triggered_work_handler(struct k_work * work)570 static void triggered_work_handler(struct k_work *work)
571 {
572 struct k_work_poll *twork =
573 CONTAINER_OF(work, struct k_work_poll, work);
574
575 /*
576 * If callback is not set, the k_work_poll_submit_to_queue()
577 * already cleared event registrations.
578 */
579 if (twork->poller.mode != MODE_NONE) {
580 k_spinlock_key_t key;
581
582 key = k_spin_lock(&lock);
583 clear_event_registrations(twork->events,
584 twork->num_events, key);
585 k_spin_unlock(&lock, key);
586 }
587
588 /* Drop work ownership and execute real handler. */
589 twork->workq = NULL;
590 twork->real_handler(work);
591 }
592
triggered_work_expiration_handler(struct _timeout * timeout)593 static void triggered_work_expiration_handler(struct _timeout *timeout)
594 {
595 struct k_work_poll *twork =
596 CONTAINER_OF(timeout, struct k_work_poll, timeout);
597
598 twork->poller.is_polling = false;
599 twork->poll_result = -EAGAIN;
600 k_work_submit_to_queue(twork->workq, &twork->work);
601 }
602
603 extern int z_work_submit_to_queue(struct k_work_q *queue,
604 struct k_work *work);
605
signal_triggered_work(struct k_poll_event * event,uint32_t status)606 static int signal_triggered_work(struct k_poll_event *event, uint32_t status)
607 {
608 struct z_poller *poller = event->poller;
609 struct k_work_poll *twork =
610 CONTAINER_OF(poller, struct k_work_poll, poller);
611
612 if (poller->is_polling && twork->workq != NULL) {
613 struct k_work_q *work_q = twork->workq;
614
615 z_abort_timeout(&twork->timeout);
616 twork->poll_result = 0;
617 z_work_submit_to_queue(work_q, &twork->work);
618 }
619
620 return 0;
621 }
622
triggered_work_cancel(struct k_work_poll * work,k_spinlock_key_t key)623 static int triggered_work_cancel(struct k_work_poll *work,
624 k_spinlock_key_t key)
625 {
626 /* Check if the work waits for event. */
627 if (work->poller.is_polling && work->poller.mode != MODE_NONE) {
628 /* Remove timeout associated with the work. */
629 z_abort_timeout(&work->timeout);
630
631 /*
632 * Prevent work execution if event arrives while we will be
633 * clearing registrations.
634 */
635 work->poller.mode = MODE_NONE;
636
637 /* Clear registrations and work ownership. */
638 clear_event_registrations(work->events, work->num_events, key);
639 work->workq = NULL;
640 return 0;
641 }
642
643 /*
644 * If we reached here, the work is either being registered in
645 * the k_work_poll_submit_to_queue(), executed or is pending.
646 * Only in the last case we have a chance to cancel it, but
647 * unfortunately there is no public API performing this task.
648 */
649
650 return -EINVAL;
651 }
652
k_work_poll_init(struct k_work_poll * work,k_work_handler_t handler)653 void k_work_poll_init(struct k_work_poll *work,
654 k_work_handler_t handler)
655 {
656 SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_work_poll, init, work);
657
658 *work = (struct k_work_poll) {};
659 k_work_init(&work->work, triggered_work_handler);
660 work->real_handler = handler;
661 z_init_timeout(&work->timeout);
662
663 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_work_poll, init, work);
664 }
665
k_work_poll_submit_to_queue(struct k_work_q * work_q,struct k_work_poll * work,struct k_poll_event * events,int num_events,k_timeout_t timeout)666 int k_work_poll_submit_to_queue(struct k_work_q *work_q,
667 struct k_work_poll *work,
668 struct k_poll_event *events,
669 int num_events,
670 k_timeout_t timeout)
671 {
672 int events_registered;
673 k_spinlock_key_t key;
674
675 __ASSERT(work_q != NULL, "NULL work_q\n");
676 __ASSERT(work != NULL, "NULL work\n");
677 __ASSERT(events != NULL, "NULL events\n");
678 __ASSERT(num_events > 0, "zero events\n");
679
680 SYS_PORT_TRACING_FUNC_ENTER(k_work_poll, submit_to_queue, work_q, work, timeout);
681
682 /* Take ownership of the work if it is possible. */
683 key = k_spin_lock(&lock);
684 if (work->workq != NULL) {
685 if (work->workq == work_q) {
686 int retval;
687
688 retval = triggered_work_cancel(work, key);
689 if (retval < 0) {
690 k_spin_unlock(&lock, key);
691
692 SYS_PORT_TRACING_FUNC_EXIT(k_work_poll, submit_to_queue, work_q,
693 work, timeout, retval);
694
695 return retval;
696 }
697 } else {
698 k_spin_unlock(&lock, key);
699
700 SYS_PORT_TRACING_FUNC_EXIT(k_work_poll, submit_to_queue, work_q,
701 work, timeout, -EADDRINUSE);
702
703 return -EADDRINUSE;
704 }
705 }
706
707
708 work->poller.is_polling = true;
709 work->workq = work_q;
710 work->poller.mode = MODE_NONE;
711 k_spin_unlock(&lock, key);
712
713 /* Save list of events. */
714 work->events = events;
715 work->num_events = num_events;
716
717 /* Clear result */
718 work->poll_result = -EINPROGRESS;
719
720 /* Register events */
721 events_registered = register_events(events, num_events,
722 &work->poller, false);
723
724 key = k_spin_lock(&lock);
725 if (work->poller.is_polling && !K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
726 /*
727 * Poller is still polling.
728 * No event is ready and all are watched.
729 */
730 __ASSERT(num_events == events_registered,
731 "Some events were not registered!\n");
732
733 /* Setup timeout if such action is requested */
734 if (!K_TIMEOUT_EQ(timeout, K_FOREVER)) {
735 z_add_timeout(&work->timeout,
736 triggered_work_expiration_handler,
737 timeout);
738 }
739
740 /* From now, any event will result in submitted work. */
741 work->poller.mode = MODE_TRIGGERED;
742 k_spin_unlock(&lock, key);
743
744 SYS_PORT_TRACING_FUNC_EXIT(k_work_poll, submit_to_queue, work_q, work, timeout, 0);
745
746 return 0;
747 }
748
749 /*
750 * The K_NO_WAIT timeout was specified or at least one event
751 * was ready at registration time or changed state since
752 * registration. Hopefully, the poller mode was not set, so
753 * work was not submitted to workqueue.
754 */
755
756 /*
757 * If poller is still polling, no watched event occurred. This means
758 * we reached here due to K_NO_WAIT timeout "expiration".
759 */
760 if (work->poller.is_polling) {
761 work->poller.is_polling = false;
762 work->poll_result = -EAGAIN;
763 } else {
764 work->poll_result = 0;
765 }
766
767 /* Clear registrations. */
768 clear_event_registrations(events, events_registered, key);
769 k_spin_unlock(&lock, key);
770
771 /* Submit work. */
772 k_work_submit_to_queue(work_q, &work->work);
773
774 SYS_PORT_TRACING_FUNC_EXIT(k_work_poll, submit_to_queue, work_q, work, timeout, 0);
775
776 return 0;
777 }
778
k_work_poll_submit(struct k_work_poll * work,struct k_poll_event * events,int num_events,k_timeout_t timeout)779 int k_work_poll_submit(struct k_work_poll *work,
780 struct k_poll_event *events,
781 int num_events,
782 k_timeout_t timeout)
783 {
784 SYS_PORT_TRACING_FUNC_ENTER(k_work_poll, submit, work, timeout);
785
786 int ret = k_work_poll_submit_to_queue(&k_sys_work_q, work,
787 events, num_events, timeout);
788
789 SYS_PORT_TRACING_FUNC_EXIT(k_work_poll, submit, work, timeout, ret);
790
791 return ret;
792 }
793
k_work_poll_cancel(struct k_work_poll * work)794 int k_work_poll_cancel(struct k_work_poll *work)
795 {
796 k_spinlock_key_t key;
797 int retval;
798
799 SYS_PORT_TRACING_FUNC_ENTER(k_work_poll, cancel, work);
800
801 /* Check if the work was submitted. */
802 if (work == NULL || work->workq == NULL) {
803 SYS_PORT_TRACING_FUNC_EXIT(k_work_poll, cancel, work, -EINVAL);
804
805 return -EINVAL;
806 }
807
808 key = k_spin_lock(&lock);
809 retval = triggered_work_cancel(work, key);
810 k_spin_unlock(&lock, key);
811
812 SYS_PORT_TRACING_FUNC_EXIT(k_work_poll, cancel, work, retval);
813
814 return retval;
815 }
816