Zephyr Project API  3.2.0
A Scalable Open Source RTOS
rtio.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2022 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
32#ifndef ZEPHYR_INCLUDE_RTIO_RTIO_H_
33#define ZEPHYR_INCLUDE_RTIO_RTIO_H_
34
36#include <zephyr/sys/__assert.h>
37#include <zephyr/sys/atomic.h>
38#include <zephyr/device.h>
39#include <zephyr/kernel.h>
40
41#ifdef __cplusplus
42extern "C" {
43#endif
44
45
54struct rtio_iodev;
55
73#define RTIO_PRIO_LOW 0U
74
78#define RTIO_PRIO_NORM 127U
79
83#define RTIO_PRIO_HIGH 255U
84
100#define RTIO_SQE_CHAINED BIT(0)
101
109struct rtio_sqe {
125 void *userdata;
126
127 union {
128 struct {
132 };
133 };
134};
135
142struct rtio_sq {
143 struct rtio_spsc _spsc;
144 struct rtio_sqe buffer[];
145};
146
150struct rtio_cqe {
152 void *userdata;
153};
154
161struct rtio_cq {
162 struct rtio_spsc _spsc;
163 struct rtio_cqe buffer[];
164};
165
166struct rtio;
167
177 int (*submit)(struct rtio *r);
178
182 void (*ok)(struct rtio *r, const struct rtio_sqe *sqe, int result);
183
187 void (*err)(struct rtio *r, const struct rtio_sqe *sqe, int result);
188};
189
211 const struct rtio_executor_api *api;
212};
213
222struct rtio {
223
224 /*
225 * An executor which does the job of working through the submission
226 * queue.
227 */
229
230
231#ifdef CONFIG_RTIO_SUBMIT_SEM
232 /* A wait semaphore which may suspend the calling thread
233 * to wait for some number of completions when calling submit
234 */
235 struct k_sem *submit_sem;
236
237 uint32_t submit_count;
238#endif
239
240#ifdef CONFIG_RTIO_CONSUME_SEM
241 /* A wait semaphore which may suspend the calling thread
242 * to wait for some number of completions while consuming
243 * them from the completion queue
244 */
245 struct k_sem *consume_sem;
246#endif
247
248 /* Number of completions that were unable to be submitted with results
249 * due to the cq spsc being full
250 */
252
253 /* Submission queue */
254 struct rtio_sq *sq;
255
256 /* Completion queue */
257 struct rtio_cq *cq;
258};
259
271 void (*submit)(const struct rtio_sqe *sqe,
272 struct rtio *r);
273
285};
286
287/* IO device submission queue entry */
289 const struct rtio_sqe *sqe;
290 struct rtio *r;
291};
292
299 struct rtio_spsc _spsc;
301};
302
311 /* Function pointer table */
312 const struct rtio_iodev_api *api;
313
314 /* Queue of RTIO contexts with requests */
316};
317
318
320#define RTIO_OP_NOP 0
321
323#define RTIO_OP_RX 1
324
326#define RTIO_OP_TX 2
327
331static inline void rtio_sqe_prep_nop(struct rtio_sqe *sqe,
332 struct rtio_iodev *iodev,
333 void *userdata)
334{
335 sqe->op = RTIO_OP_NOP;
336 sqe->iodev = iodev;
337 sqe->userdata = userdata;
338}
339
343static inline void rtio_sqe_prep_read(struct rtio_sqe *sqe,
344 struct rtio_iodev *iodev,
345 int8_t prio,
346 uint8_t *buf,
347 uint32_t len,
348 void *userdata)
349{
350 sqe->op = RTIO_OP_RX;
351 sqe->prio = prio;
352 sqe->iodev = iodev;
353 sqe->buf_len = len;
354 sqe->buf = buf;
355 sqe->userdata = userdata;
356}
357
361static inline void rtio_sqe_prep_write(struct rtio_sqe *sqe,
362 struct rtio_iodev *iodev,
363 int8_t prio,
364 uint8_t *buf,
365 uint32_t len,
366 void *userdata)
367{
368 sqe->op = RTIO_OP_TX;
369 sqe->prio = prio;
370 sqe->iodev = iodev;
371 sqe->buf_len = len;
372 sqe->buf = buf;
373 sqe->userdata = userdata;
374}
375
382#define RTIO_SQ_DEFINE(name, len) \
383 static RTIO_SPSC_DEFINE(name, struct rtio_sqe, len)
384
391#define RTIO_CQ_DEFINE(name, len) \
392 static RTIO_SPSC_DEFINE(name, struct rtio_cqe, len)
393
402#define RTIO_DEFINE(name, exec, sq_sz, cq_sz) \
403 IF_ENABLED(CONFIG_RTIO_SUBMIT_SEM, (K_SEM_DEFINE(_submit_sem_##name, 0, K_SEM_MAX_LIMIT))) \
404 IF_ENABLED(CONFIG_RTIO_CONSUME_SEM, (K_SEM_DEFINE(_consume_sem_##name, 0, 1))) \
405 RTIO_SQ_DEFINE(_sq_##name, sq_sz); \
406 RTIO_CQ_DEFINE(_cq_##name, cq_sz); \
407 static struct rtio name = { \
408 .executor = (exec), \
409 .xcqcnt = ATOMIC_INIT(0), \
410 IF_ENABLED(CONFIG_RTIO_SUBMIT_SEM, (.submit_sem = &_submit_sem_##name,)) \
411 IF_ENABLED(CONFIG_RTIO_SUBMIT_SEM, (.submit_count = 0,)) \
412 IF_ENABLED(CONFIG_RTIO_CONSUME_SEM, (.consume_sem = &_consume_sem_##name,)) \
413 .sq = (struct rtio_sq *const)&_sq_##name, \
414 .cq = (struct rtio_cq *const)&_cq_##name, \
415 }
416
420static inline void rtio_set_executor(struct rtio *r, struct rtio_executor *exc)
421{
422 r->executor = exc;
423}
424
431static inline void rtio_iodev_submit(const struct rtio_sqe *sqe, struct rtio *r)
432{
433 sqe->iodev->api->submit(sqe, r);
434}
435
452static inline int rtio_submit(struct rtio *r, uint32_t wait_count)
453{
454 int res;
455
456 __ASSERT(r->executor != NULL, "expected rtio submit context to have an executor");
457
458#ifdef CONFIG_RTIO_SUBMIT_SEM
459 /* TODO undefined behavior if another thread calls submit of course
460 */
461 if (wait_count > 0) {
462 __ASSERT(!k_is_in_isr(),
463 "expected rtio submit with wait count to be called from a thread");
464
465 k_sem_reset(r->submit_sem);
466 r->submit_count = wait_count;
467 }
468#endif
469
470 /* Enqueue all prepared submissions */
472
473 /* Submit the queue to the executor which consumes submissions
474 * and produces completions through ISR chains or other means.
475 */
476 res = r->executor->api->submit(r);
477 if (res != 0) {
478 return res;
479 }
480
481 /* TODO could be nicer if we could suspend the thread and not
482 * wake up on each completion here.
483 */
484#ifdef CONFIG_RTIO_SUBMIT_SEM
485
486 if (wait_count > 0) {
487 res = k_sem_take(r->submit_sem, K_FOREVER);
488 __ASSERT(res == 0,
489 "semaphore was reset or timed out while waiting on completions!");
490 }
491#else
492 while (rtio_spsc_consumable(r->cq) < wait_count) {
493#ifdef CONFIG_BOARD_NATIVE_POSIX
494 k_busy_wait(1);
495#else
496 k_yield();
497#endif /* CONFIG_BOARD_NATIVE_POSIX */
498 }
499#endif
500
501 return res;
502}
503
515static inline struct rtio_cqe *rtio_cqe_consume(struct rtio *r)
516{
517 return rtio_spsc_consume(r->cq);
518}
519
530static inline struct rtio_cqe *rtio_cqe_consume_block(struct rtio *r)
531{
532 struct rtio_cqe *cqe;
533
534 /* TODO is there a better way? reset this in submit? */
535#ifdef CONFIG_RTIO_CONSUME_SEM
536 k_sem_reset(r->consume_sem);
537#endif
538 cqe = rtio_spsc_consume(r->cq);
539
540 while (cqe == NULL) {
541 cqe = rtio_spsc_consume(r->cq);
542
543#ifdef CONFIG_RTIO_CONSUME_SEM
544 k_sem_take(r->consume_sem, K_FOREVER);
545#else
546 k_yield();
547#endif
548 }
549
550 return cqe;
551}
552
562static inline void rtio_sqe_ok(struct rtio *r, const struct rtio_sqe *sqe, int result)
563{
564 r->executor->api->ok(r, sqe, result);
565}
566
576static inline void rtio_sqe_err(struct rtio *r, const struct rtio_sqe *sqe, int result)
577{
578 r->executor->api->err(r, sqe, result);
579}
580
591static inline void rtio_cqe_submit(struct rtio *r, int result, void *userdata)
592{
593 struct rtio_cqe *cqe = rtio_spsc_acquire(r->cq);
594
595 if (cqe == NULL) {
596 atomic_inc(&r->xcqcnt);
597 } else {
598 cqe->result = result;
599 cqe->userdata = userdata;
600 rtio_spsc_produce(r->cq);
601 }
602#ifdef CONFIG_RTIO_SUBMIT_SEM
603 if (r->submit_count > 0) {
604 r->submit_count--;
605 if (r->submit_count == 0) {
606 k_sem_give(r->submit_sem);
607 }
608 }
609#endif
610#ifdef CONFIG_RTIO_CONSUME_SEM
611 k_sem_give(r->consume_sem);
612#endif
613}
614
615/* TODO add rtio_sqe_suspend() for suspending a submission chain that must
616 * wait on other in progress submissions or submission chains.
617 */
618
623#ifdef __cplusplus
624}
625#endif
626
627#endif /* ZEPHYR_INCLUDE_RTIO_RTIO_H_ */
workaround assembler barfing for ST r
Definition: asm-macro-32-bit-gnu.h:24
long atomic_t
Definition: atomic.h:22
atomic_val_t atomic_inc(atomic_t *target)
struct result result[2]
Definition: errno.c:42
#define K_FOREVER
Generate infinite timeout delay.
Definition: kernel.h:1236
bool k_is_in_isr(void)
Determine if code is running at interrupt level.
static void rtio_sqe_ok(struct rtio *r, const struct rtio_sqe *sqe, int result)
Inform the executor of a submission completion with success.
Definition: rtio.h:562
static void rtio_sqe_err(struct rtio *r, const struct rtio_sqe *sqe, int result)
Inform the executor of a submissions completion with error.
Definition: rtio.h:576
static int rtio_submit(struct rtio *r, uint32_t wait_count)
Submit I/O requests to the underlying executor.
Definition: rtio.h:452
static void rtio_sqe_prep_read(struct rtio_sqe *sqe, struct rtio_iodev *iodev, int8_t prio, uint8_t *buf, uint32_t len, void *userdata)
Prepare a read op submission.
Definition: rtio.h:343
static void rtio_cqe_submit(struct rtio *r, int result, void *userdata)
Definition: rtio.h:591
#define RTIO_OP_TX
Definition: rtio.h:326
static void rtio_sqe_prep_nop(struct rtio_sqe *sqe, struct rtio_iodev *iodev, void *userdata)
Prepare a nop (no op) submission.
Definition: rtio.h:331
static void rtio_iodev_submit(const struct rtio_sqe *sqe, struct rtio *r)
Perform a submitted operation with an iodev.
Definition: rtio.h:431
static void rtio_set_executor(struct rtio *r, struct rtio_executor *exc)
Set the executor of the rtio context.
Definition: rtio.h:420
static void rtio_sqe_prep_write(struct rtio_sqe *sqe, struct rtio_iodev *iodev, int8_t prio, uint8_t *buf, uint32_t len, void *userdata)
Prepare a write op submission.
Definition: rtio.h:361
#define RTIO_OP_NOP
Definition: rtio.h:320
static struct rtio_cqe * rtio_cqe_consume(struct rtio *r)
Consume a single completion queue event if available.
Definition: rtio.h:515
static struct rtio_cqe * rtio_cqe_consume_block(struct rtio *r)
Wait for and consume a single completion queue event.
Definition: rtio.h:530
#define RTIO_OP_RX
Definition: rtio.h:323
#define rtio_spsc_consume(spsc)
Consume an element from the spsc.
Definition: rtio_spsc.h:203
#define rtio_spsc_acquire(spsc)
Acquire an element to produce from the SPSC.
Definition: rtio_spsc.h:154
#define rtio_spsc_consumable(spsc)
Count of consumables in spsc.
Definition: rtio_spsc.h:231
#define rtio_spsc_produce_all(spsc)
Produce all previously acquired elements to the SPSC.
Definition: rtio_spsc.h:187
#define rtio_spsc_produce(spsc)
Produce one previously acquired element to the SPSC.
Definition: rtio_spsc.h:171
void k_sem_reset(struct k_sem *sem)
Resets a semaphore's count to zero.
void k_sem_give(struct k_sem *sem)
Give a semaphore.
int k_sem_take(struct k_sem *sem, k_timeout_t timeout)
Take a semaphore.
void k_yield(void)
Yield the current thread.
void k_busy_wait(uint32_t usec_to_wait)
Cause the current thread to busy wait.
Public kernel APIs.
A lock-free and type safe power of 2 fixed sized single producer single consumer (SPSC) queue using a...
__UINT32_TYPE__ uint32_t
Definition: stdint.h:90
__INT32_TYPE__ int32_t
Definition: stdint.h:74
__UINT8_TYPE__ uint8_t
Definition: stdint.h:88
__UINT16_TYPE__ uint16_t
Definition: stdint.h:89
__INT8_TYPE__ int8_t
Definition: stdint.h:72
Definition: errno.c:37
Completion queue.
Definition: rtio.h:161
struct rtio_cqe buffer[]
Definition: rtio.h:163
A completion queue event.
Definition: rtio.h:150
void * userdata
Definition: rtio.h:152
int32_t result
Definition: rtio.h:151
Definition: rtio.h:168
void(* err)(struct rtio *r, const struct rtio_sqe *sqe, int result)
SQE fails to complete.
Definition: rtio.h:187
int(* submit)(struct rtio *r)
Submit the request queue to executor.
Definition: rtio.h:177
void(* ok)(struct rtio *r, const struct rtio_sqe *sqe, int result)
SQE completes successfully.
Definition: rtio.h:182
An executor does the work of executing the submissions.
Definition: rtio.h:210
const struct rtio_executor_api * api
Definition: rtio.h:211
API that an RTIO IO device should implement.
Definition: rtio.h:263
void(* submit)(const struct rtio_sqe *sqe, struct rtio *r)
Submission function for a request to the iodev.
Definition: rtio.h:271
IO device submission queue.
Definition: rtio.h:298
struct rtio_iodev_sqe buffer[]
Definition: rtio.h:300
Definition: rtio.h:288
struct rtio * r
Definition: rtio.h:290
const struct rtio_sqe * sqe
Definition: rtio.h:289
An IO device with a function table for submitting requests.
Definition: rtio.h:310
const struct rtio_iodev_api * api
Definition: rtio.h:312
struct rtio_iodev_sq * iodev_sq
Definition: rtio.h:315
Submission queue.
Definition: rtio.h:142
struct rtio_sqe buffer[]
Definition: rtio.h:144
A submission queue event.
Definition: rtio.h:109
void * userdata
Definition: rtio.h:125
uint8_t op
Definition: rtio.h:110
struct rtio_iodev * iodev
Definition: rtio.h:116
uint8_t prio
Definition: rtio.h:112
uint8_t * buf
Definition: rtio.h:131
uint32_t buf_len
Definition: rtio.h:129
uint16_t flags
Definition: rtio.h:114
An RTIO queue pair that both the kernel and application work with.
Definition: rtio.h:222
struct rtio_cq * cq
Definition: rtio.h:257
struct rtio_executor * executor
Definition: rtio.h:228
struct rtio_sq * sq
Definition: rtio.h:254
atomic_t xcqcnt
Definition: rtio.h:251