Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2017 Wind River Systems, Inc. |
| 3 | * |
| 4 | * SPDX-License-Identifier: Apache-2.0 |
| 5 | */ |
| 6 | |
| 7 | /** |
| 8 | * @file |
| 9 | * |
| 10 | * @brief Kernel asynchronous event polling interface. |
| 11 | * |
| 12 | * This polling mechanism allows waiting on multiple events concurrently, |
| 13 | * either events triggered directly, or from kernel objects or other kernel |
| 14 | * constructs. |
| 15 | */ |
| 16 | |
| 17 | #include <kernel.h> |
| 18 | #include <kernel_structs.h> |
| 19 | #include <wait_q.h> |
| 20 | #include <ksched.h> |
| 21 | #include <misc/slist.h> |
| 22 | #include <misc/dlist.h> |
| 23 | #include <misc/__assert.h> |
| 24 | |
Kumar Gala | cc334c7 | 2017-04-21 10:55:34 -0500 | [diff] [blame] | 25 | void k_poll_event_init(struct k_poll_event *event, u32_t type, |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 26 | int mode, void *obj) |
| 27 | { |
| 28 | __ASSERT(mode == K_POLL_MODE_NOTIFY_ONLY, |
| 29 | "only NOTIFY_ONLY mode is supported\n"); |
| 30 | __ASSERT(type < (1 << _POLL_NUM_TYPES), "invalid type\n"); |
| 31 | __ASSERT(obj, "must provide an object\n"); |
| 32 | |
| 33 | event->poller = NULL; |
Benjamin Walsh | 969d4a7 | 2017-02-02 11:25:11 -0500 | [diff] [blame] | 34 | /* event->tag is left uninitialized: the user will set it if needed */ |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 35 | event->type = type; |
| 36 | event->state = K_POLL_STATE_NOT_READY; |
| 37 | event->mode = mode; |
| 38 | event->unused = 0; |
| 39 | event->obj = obj; |
| 40 | } |
| 41 | |
| 42 | /* must be called with interrupts locked */ |
| 43 | static inline void set_polling_state(struct k_thread *thread) |
| 44 | { |
| 45 | _mark_thread_as_polling(thread); |
| 46 | } |
| 47 | |
| 48 | /* must be called with interrupts locked */ |
| 49 | static inline void clear_polling_state(struct k_thread *thread) |
| 50 | { |
| 51 | _mark_thread_as_not_polling(thread); |
| 52 | } |
| 53 | |
| 54 | /* must be called with interrupts locked */ |
Benjamin Walsh | 47503e3 | 2017-02-09 14:52:21 -0500 | [diff] [blame] | 55 | static inline int is_polling(void) |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 56 | { |
Benjamin Walsh | 47503e3 | 2017-02-09 14:52:21 -0500 | [diff] [blame] | 57 | return _is_thread_polling(_current); |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 58 | } |
| 59 | |
| 60 | /* must be called with interrupts locked */ |
Kumar Gala | cc334c7 | 2017-04-21 10:55:34 -0500 | [diff] [blame] | 61 | static inline int is_condition_met(struct k_poll_event *event, u32_t *state) |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 62 | { |
| 63 | switch (event->type) { |
| 64 | case K_POLL_TYPE_SEM_AVAILABLE: |
| 65 | if (k_sem_count_get(event->sem) > 0) { |
| 66 | *state = K_POLL_STATE_SEM_AVAILABLE; |
| 67 | return 1; |
| 68 | } |
| 69 | break; |
Luiz Augusto von Dentz | e5ed88f | 2017-02-21 15:27:20 +0200 | [diff] [blame] | 70 | case K_POLL_TYPE_DATA_AVAILABLE: |
| 71 | if (!k_queue_is_empty(event->queue)) { |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 72 | *state = K_POLL_STATE_FIFO_DATA_AVAILABLE; |
| 73 | return 1; |
| 74 | } |
| 75 | break; |
| 76 | case K_POLL_TYPE_SIGNAL: |
| 77 | if (event->signal->signaled) { |
| 78 | *state = K_POLL_STATE_SIGNALED; |
| 79 | return 1; |
| 80 | } |
| 81 | break; |
| 82 | case K_POLL_TYPE_IGNORE: |
| 83 | return 0; |
| 84 | default: |
| 85 | __ASSERT(0, "invalid event type (0x%x)\n", event->type); |
| 86 | break; |
| 87 | } |
| 88 | |
| 89 | return 0; |
| 90 | } |
| 91 | |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 92 | static inline void add_event(sys_dlist_t *events, struct k_poll_event *event, |
| 93 | struct _poller *poller) |
| 94 | { |
| 95 | struct k_poll_event *pending; |
| 96 | |
| 97 | pending = (struct k_poll_event *)sys_dlist_peek_tail(events); |
| 98 | if (!pending || _is_t1_higher_prio_than_t2(pending->poller->thread, |
| 99 | poller->thread)) { |
| 100 | sys_dlist_append(events, &event->_node); |
| 101 | return; |
| 102 | } |
| 103 | |
| 104 | SYS_DLIST_FOR_EACH_CONTAINER(events, pending, _node) { |
| 105 | if (_is_t1_higher_prio_than_t2(poller->thread, |
| 106 | pending->poller->thread)) { |
| 107 | sys_dlist_insert_before(events, &pending->_node, |
| 108 | &event->_node); |
| 109 | return; |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | sys_dlist_append(events, &event->_node); |
| 114 | } |
| 115 | |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 116 | /* must be called with interrupts locked */ |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 117 | static inline int register_event(struct k_poll_event *event, |
| 118 | struct _poller *poller) |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 119 | { |
| 120 | switch (event->type) { |
| 121 | case K_POLL_TYPE_SEM_AVAILABLE: |
| 122 | __ASSERT(event->sem, "invalid semaphore\n"); |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 123 | add_event(&event->sem->poll_events, event, poller); |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 124 | break; |
Luiz Augusto von Dentz | e5ed88f | 2017-02-21 15:27:20 +0200 | [diff] [blame] | 125 | case K_POLL_TYPE_DATA_AVAILABLE: |
| 126 | __ASSERT(event->queue, "invalid queue\n"); |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 127 | add_event(&event->queue->poll_events, event, poller); |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 128 | break; |
| 129 | case K_POLL_TYPE_SIGNAL: |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 130 | __ASSERT(event->signal, "invalid poll signal\n"); |
| 131 | add_event(&event->signal->poll_events, event, poller); |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 132 | break; |
| 133 | case K_POLL_TYPE_IGNORE: |
| 134 | /* nothing to do */ |
| 135 | break; |
| 136 | default: |
| 137 | __ASSERT(0, "invalid event type\n"); |
| 138 | break; |
| 139 | } |
| 140 | |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 141 | event->poller = poller; |
| 142 | |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 143 | return 0; |
| 144 | } |
| 145 | |
| 146 | /* must be called with interrupts locked */ |
| 147 | static inline void clear_event_registration(struct k_poll_event *event) |
| 148 | { |
| 149 | event->poller = NULL; |
| 150 | |
| 151 | switch (event->type) { |
| 152 | case K_POLL_TYPE_SEM_AVAILABLE: |
| 153 | __ASSERT(event->sem, "invalid semaphore\n"); |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 154 | sys_dlist_remove(&event->_node); |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 155 | break; |
Luiz Augusto von Dentz | e5ed88f | 2017-02-21 15:27:20 +0200 | [diff] [blame] | 156 | case K_POLL_TYPE_DATA_AVAILABLE: |
| 157 | __ASSERT(event->queue, "invalid queue\n"); |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 158 | sys_dlist_remove(&event->_node); |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 159 | break; |
| 160 | case K_POLL_TYPE_SIGNAL: |
| 161 | __ASSERT(event->signal, "invalid poll signal\n"); |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 162 | sys_dlist_remove(&event->_node); |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 163 | break; |
| 164 | case K_POLL_TYPE_IGNORE: |
| 165 | /* nothing to do */ |
| 166 | break; |
| 167 | default: |
| 168 | __ASSERT(0, "invalid event type\n"); |
| 169 | break; |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | /* must be called with interrupts locked */ |
| 174 | static inline void clear_event_registrations(struct k_poll_event *events, |
| 175 | int last_registered, |
| 176 | unsigned int key) |
| 177 | { |
| 178 | for (; last_registered >= 0; last_registered--) { |
| 179 | clear_event_registration(&events[last_registered]); |
| 180 | irq_unlock(key); |
| 181 | key = irq_lock(); |
| 182 | } |
| 183 | } |
| 184 | |
Kumar Gala | cc334c7 | 2017-04-21 10:55:34 -0500 | [diff] [blame] | 185 | static inline void set_event_ready(struct k_poll_event *event, u32_t state) |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 186 | { |
| 187 | event->poller = NULL; |
| 188 | event->state |= state; |
| 189 | } |
| 190 | |
Kumar Gala | cc334c7 | 2017-04-21 10:55:34 -0500 | [diff] [blame] | 191 | int k_poll(struct k_poll_event *events, int num_events, s32_t timeout) |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 192 | { |
| 193 | __ASSERT(!_is_in_isr(), ""); |
| 194 | __ASSERT(events, "NULL events\n"); |
| 195 | __ASSERT(num_events > 0, "zero events\n"); |
| 196 | |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 197 | int last_registered = -1, rc; |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 198 | unsigned int key; |
| 199 | |
| 200 | key = irq_lock(); |
| 201 | set_polling_state(_current); |
| 202 | irq_unlock(key); |
| 203 | |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 204 | struct _poller poller = { .thread = _current }; |
| 205 | |
| 206 | /* find events whose condition is already fulfilled */ |
| 207 | for (int ii = 0; ii < num_events; ii++) { |
Kumar Gala | cc334c7 | 2017-04-21 10:55:34 -0500 | [diff] [blame] | 208 | u32_t state; |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 209 | |
| 210 | key = irq_lock(); |
| 211 | if (is_condition_met(&events[ii], &state)) { |
| 212 | set_event_ready(&events[ii], state); |
| 213 | clear_polling_state(_current); |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 214 | } else if (timeout != K_NO_WAIT && is_polling()) { |
| 215 | rc = register_event(&events[ii], &poller); |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 216 | if (rc == 0) { |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 217 | ++last_registered; |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 218 | } else { |
| 219 | __ASSERT(0, "unexpected return code\n"); |
| 220 | } |
| 221 | } |
| 222 | irq_unlock(key); |
| 223 | } |
| 224 | |
| 225 | key = irq_lock(); |
| 226 | |
| 227 | /* |
| 228 | * If we're not polling anymore, it means that at least one event |
| 229 | * condition is met, either when looping through the events here or |
| 230 | * because one of the events registered has had its state changed, or |
| 231 | * that one of the objects we wanted to poll on already had a thread |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 232 | * polling on it. |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 233 | */ |
Benjamin Walsh | 47503e3 | 2017-02-09 14:52:21 -0500 | [diff] [blame] | 234 | if (!is_polling()) { |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 235 | clear_event_registrations(events, last_registered, key); |
| 236 | irq_unlock(key); |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 237 | return 0; |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 238 | } |
| 239 | |
| 240 | clear_polling_state(_current); |
| 241 | |
| 242 | if (timeout == K_NO_WAIT) { |
| 243 | irq_unlock(key); |
| 244 | return -EAGAIN; |
| 245 | } |
| 246 | |
| 247 | _wait_q_t wait_q = _WAIT_Q_INIT(&wait_q); |
| 248 | |
| 249 | _pend_current_thread(&wait_q, timeout); |
| 250 | |
| 251 | int swap_rc = _Swap(key); |
| 252 | |
| 253 | /* |
| 254 | * Clear all event registrations. If events happen while we're in this |
| 255 | * loop, and we already had one that triggered, that's OK: they will |
| 256 | * end up in the list of events that are ready; if we timed out, and |
| 257 | * events happen while we're in this loop, that is OK as well since |
| 258 | * we've already know the return code (-EAGAIN), and even if they are |
| 259 | * added to the list of events that occurred, the user has to check the |
| 260 | * return code first, which invalidates the whole list of event states. |
| 261 | */ |
| 262 | key = irq_lock(); |
| 263 | clear_event_registrations(events, last_registered, key); |
| 264 | irq_unlock(key); |
| 265 | |
| 266 | return swap_rc; |
| 267 | } |
| 268 | |
| 269 | /* must be called with interrupts locked */ |
Kumar Gala | cc334c7 | 2017-04-21 10:55:34 -0500 | [diff] [blame] | 270 | static int _signal_poll_event(struct k_poll_event *event, u32_t state, |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 271 | int *must_reschedule) |
| 272 | { |
| 273 | *must_reschedule = 0; |
| 274 | |
| 275 | if (!event->poller) { |
| 276 | goto ready_event; |
| 277 | } |
| 278 | |
| 279 | struct k_thread *thread = event->poller->thread; |
| 280 | |
| 281 | __ASSERT(event->poller->thread, "poller should have a thread\n"); |
| 282 | |
| 283 | clear_polling_state(thread); |
| 284 | |
| 285 | if (!_is_thread_pending(thread)) { |
| 286 | goto ready_event; |
| 287 | } |
| 288 | |
| 289 | if (_is_thread_timeout_expired(thread)) { |
| 290 | return -EAGAIN; |
| 291 | } |
| 292 | |
| 293 | _unpend_thread(thread); |
| 294 | _abort_thread_timeout(thread); |
Luiz Augusto von Dentz | fc775a0 | 2017-10-17 15:33:32 +0300 | [diff] [blame] | 295 | _set_thread_return_value(thread, |
| 296 | state == K_POLL_STATE_NOT_READY ? -EINTR : 0); |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 297 | |
| 298 | if (!_is_thread_ready(thread)) { |
| 299 | goto ready_event; |
| 300 | } |
| 301 | |
| 302 | _add_thread_to_ready_q(thread); |
| 303 | *must_reschedule = !_is_in_isr() && _must_switch_threads(); |
| 304 | |
| 305 | ready_event: |
| 306 | set_event_ready(event, state); |
| 307 | return 0; |
| 308 | } |
| 309 | |
| 310 | /* returns 1 if a reschedule must take place, 0 otherwise */ |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 311 | int _handle_obj_poll_events(sys_dlist_t *events, u32_t state) |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 312 | { |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 313 | struct k_poll_event *poll_event; |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 314 | int must_reschedule; |
| 315 | |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 316 | poll_event = (struct k_poll_event *)sys_dlist_get(events); |
| 317 | if (!poll_event) { |
| 318 | return 0; |
| 319 | } |
| 320 | |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 321 | (void)_signal_poll_event(poll_event, state, &must_reschedule); |
| 322 | return must_reschedule; |
| 323 | } |
| 324 | |
Benjamin Walsh | a304f16 | 2017-02-02 16:46:09 -0500 | [diff] [blame] | 325 | void k_poll_signal_init(struct k_poll_signal *signal) |
| 326 | { |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 327 | sys_dlist_init(&signal->poll_events); |
Benjamin Walsh | a304f16 | 2017-02-02 16:46:09 -0500 | [diff] [blame] | 328 | signal->signaled = 0; |
| 329 | /* signal->result is left unitialized */ |
| 330 | } |
| 331 | |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 332 | int k_poll_signal(struct k_poll_signal *signal, int result) |
| 333 | { |
| 334 | unsigned int key = irq_lock(); |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 335 | struct k_poll_event *poll_event; |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 336 | int must_reschedule; |
| 337 | |
| 338 | signal->result = result; |
Benjamin Walsh | 3c1ab5d | 2017-02-09 15:36:29 -0500 | [diff] [blame] | 339 | signal->signaled = 1; |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 340 | |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 341 | poll_event = (struct k_poll_event *)sys_dlist_get(&signal->poll_events); |
| 342 | if (!poll_event) { |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 343 | irq_unlock(key); |
| 344 | return 0; |
| 345 | } |
| 346 | |
Luiz Augusto von Dentz | 7d01c5e | 2017-08-21 10:49:29 +0300 | [diff] [blame] | 347 | int rc = _signal_poll_event(poll_event, K_POLL_STATE_SIGNALED, |
Benjamin Walsh | acc68c1 | 2017-01-29 18:57:45 -0500 | [diff] [blame] | 348 | &must_reschedule); |
| 349 | |
| 350 | if (must_reschedule) { |
| 351 | (void)_Swap(key); |
| 352 | } else { |
| 353 | irq_unlock(key); |
| 354 | } |
| 355 | |
| 356 | return rc; |
| 357 | } |