From: Chengwen Feng <fengchengwen@huawei.com>
To: <thomas@monjalon.net>, <ferruh.yigit@amd.com>
Cc: <dev@dpdk.org>
Subject: [RFC 1/3] lib/coroutine: add coroutine library
Date: Mon, 24 Apr 2023 13:02:06 +0000 [thread overview]
Message-ID: <20230424130208.9517-2-fengchengwen@huawei.com> (raw)
In-Reply-To: <20230424130208.9517-1-fengchengwen@huawei.com>
This patch adds coroutine library. The main elements are:
1. scheduler: container of coroutines, which is responsible for
scheduling coroutine.
2. coroutine: Minimum scheduling unit, it should associated to one
scheduler.
In the coroutine callback, application could invoke rte_co_yield() to
give up the CPU, and invoke rte_co_delay() to delay the specified
microsecond.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
lib/coroutine/meson.build | 8 ++
lib/coroutine/rte_coroutine.c | 190 ++++++++++++++++++++++++++++++
lib/coroutine/rte_coroutine.h | 110 +++++++++++++++++
lib/coroutine/rte_coroutine_imp.h | 46 ++++++++
lib/coroutine/version.map | 11 ++
lib/meson.build | 1 +
6 files changed, 366 insertions(+)
create mode 100644 lib/coroutine/meson.build
create mode 100644 lib/coroutine/rte_coroutine.c
create mode 100644 lib/coroutine/rte_coroutine.h
create mode 100644 lib/coroutine/rte_coroutine_imp.h
create mode 100644 lib/coroutine/version.map
diff --git a/lib/coroutine/meson.build b/lib/coroutine/meson.build
new file mode 100644
index 0000000000..2064fb1909
--- /dev/null
+++ b/lib/coroutine/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 HiSilicon Limited.
+
+sources = files('rte_coroutine.c')
+headers = files('rte_coroutine.h')
+indirect_headers += files('rte_coroutine_imp.h')
+
+deps += ['ring']
diff --git a/lib/coroutine/rte_coroutine.c b/lib/coroutine/rte_coroutine.c
new file mode 100644
index 0000000000..07c79fc901
--- /dev/null
+++ b/lib/coroutine/rte_coroutine.c
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ucontext.h>
+
+#include <rte_alarm.h>
+#include <rte_ring.h>
+
+#include "rte_coroutine.h"
+#include "rte_coroutine_imp.h"
+
+#define FATAL(fmt, args...) printf("[FATAL] %s() %d: " fmt "\n", __func__, __LINE__, ##args)
+
+static __thread struct rte_schedule *co_schedule;
+
+struct rte_schedule *
+rte_schedule_create(const char *name, uint32_t max_coroutines)
+{
+ struct rte_schedule *s = calloc(1, sizeof(struct rte_schedule));
+ if (s == NULL)
+ return NULL;
+
+ s->ring = rte_ring_create(name, max_coroutines, rte_socket_id(),
+ RING_F_SC_DEQ);
+ if (s->ring == NULL) {
+ free(s);
+ return NULL;
+ }
+
+ s->max_coroutines = max_coroutines;
+
+ return s;
+}
+
+static void
+co_run_func(uint32_t low, uint32_t hi)
+{
+ uintptr_t ptr = (uint64_t)low | ((uint64_t)hi << 32);
+ struct rte_cocontext *co = (struct rte_cocontext *)ptr;
+ co->cb(co->arg);
+ /* Run complete, so free it. */
+ free(co->stack);
+ free(co);
+}
+
+int
+rte_schedule_run(struct rte_schedule *s)
+{
+ struct rte_cocontext *co = NULL;
+ uintptr_t ptr;
+
+ /* Set local thread variable as input argument. */
+ co_schedule = s;
+
+ while (!rte_ring_empty(s->ring)) {
+ rte_ring_dequeue(s->ring, (void **)&co);
+ if (co->state == COROUTINE_READY) {
+ getcontext(&co->ctx);
+ co->ctx.uc_stack.ss_sp = co->stack;
+ co->ctx.uc_stack.ss_size = co->stack_sz;
+ co->ctx.uc_link = &s->main;
+ co->state = COROUTINE_RUNNING;
+ s->running = co;
+ ptr = (uintptr_t)co;
+ makecontext(&co->ctx, (void (*)(void))co_run_func, 2,
+ (uint32_t)ptr, (uint32_t)(ptr >> 32));
+ swapcontext(&s->main, &co->ctx);
+ } else if (co->state == COROUTINE_SUSPEND) {
+ co->state = COROUTINE_RUNNING;
+ s->running = co;
+ swapcontext(&s->main, &co->ctx);
+ } else {
+ FATAL("invalid state!");
+ }
+ }
+
+ while (s->yield_head != NULL) {
+ co = s->yield_head;
+ s->yield_head = co->yield_next;
+ if (co->state == COROUTINE_YIELD) {
+ co->state = COROUTINE_RUNNING;
+ s->running = co;
+ swapcontext(&s->main, &co->ctx);
+ } else {
+ FATAL("invalid yield state!");
+ }
+ }
+
+ return 0;
+}
+
+int
+rte_co_create(struct rte_schedule *s, coroutine_callback_t cb, void *arg, uint32_t stack_sz)
+{
+ struct rte_cocontext *co = calloc(1, sizeof(struct rte_cocontext));
+ int ret;
+ if (co == NULL)
+ return -ENOMEM;
+
+ co->owner = s;
+ co->state = COROUTINE_READY;
+ co->cb = cb;
+ co->arg = arg;
+ if (stack_sz < MIN_STACK_SIZE)
+ stack_sz = MIN_STACK_SIZE;
+ co->stack_sz = stack_sz;
+ co->stack = calloc(1, stack_sz);
+ if (co->stack == NULL) {
+ free(co);
+ return -ENOMEM;
+ }
+
+ ret = rte_ring_enqueue(s->ring, co);
+ if (ret != 0) {
+ free(co->stack);
+ free(co);
+ }
+
+ return ret;
+}
+
+static inline void
+co_addto_yield_list(struct rte_schedule *s, struct rte_cocontext *co)
+{
+ co->yield_next = NULL;
+ if (s->yield_head == NULL) {
+ s->yield_head = s->yield_tail = co;
+ } else {
+ s->yield_tail->yield_next = co;
+ s->yield_tail = co;
+ }
+}
+
+void
+rte_co_yield(void)
+{
+ struct rte_schedule *s = co_schedule;
+ struct rte_cocontext *co;
+ if (s == NULL) {
+ FATAL("thread co_schedule is NULL!");
+ return;
+ }
+ co = s->running;
+ if (co == NULL) {
+ FATAL("running is NULL!");
+ return;
+ }
+ co->state = COROUTINE_YIELD;
+ s->running = NULL;
+ co_addto_yield_list(s, co);
+ swapcontext(&co->ctx, &s->main);
+}
+
+static void
+co_delay_imp(void *arg)
+{
+ struct rte_cocontext *co = (struct rte_cocontext *)arg;
+ int ret;
+
+ ret = rte_ring_enqueue(co->owner->ring, (void *)co);
+ if (ret != 0)
+ FATAL("enqueue failed!");
+}
+
+void
+rte_co_delay(unsigned int us)
+{
+ struct rte_schedule *s = co_schedule;
+ struct rte_cocontext *co;
+
+ if (s == NULL) {
+ FATAL("thread co_schedule is NULL!");
+ return;
+ }
+
+ co = s->running;
+ if (co == NULL) {
+ FATAL("running is NULL!");
+ return;
+ }
+
+ rte_eal_alarm_set(us, co_delay_imp, (void *)co);
+ co->state = COROUTINE_SUSPEND;
+ s->running = NULL;
+ swapcontext(&co->ctx, &s->main);
+}
diff --git a/lib/coroutine/rte_coroutine.h b/lib/coroutine/rte_coroutine.h
new file mode 100644
index 0000000000..71ac9488b6
--- /dev/null
+++ b/lib/coroutine/rte_coroutine.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#ifndef RTE_COROUTINE_H
+#define RTE_COROUTINE_H
+
+#include <stdint.h>
+
+#include <rte_compat.h>
+
+/**
+ * Callback prototype of coroutine.
+ *
+ * @param arg
+ * An arg pointer coming from the caller.
+ */
+typedef void (*coroutine_callback_t)(void *arg);
+
+struct rte_schedule;
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create coroutine scheduler.
+ *
+ * The scheduler is a coroutines container, which could schedule coroutine
+ * running.
+ *
+ * @param name
+ * The unique name of scheduler.
+ * @param max_coroutines
+ * Maximum number of coroutines that can be processed by the scheduler.
+ *
+ * @return
+ * Non-NULL on success. Otherwise NULL value is returned.
+ */
+__rte_experimental
+struct rte_schedule *rte_schedule_create(const char *name, uint32_t max_coroutines);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Run the coroutine scheduler.
+ *
+ * This function will schedule all associated coroutine, it will return zero if
+ * no coroutines are active after scheduled.
+ *
+ * @param s
+ * The pointer of the scheduler.
+ *
+ * @return
+ * 0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_schedule_run(struct rte_schedule *s);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create one coroutine.
+ *
+ * This function will create one coroutine which associated target scheduler
+ * 's'.
+ *
+ * @param s
+ * The pointer of the scheduler.
+ * @param cb
+ * The callback function.
+ * @param arg
+ * The argument parameter for callback function.
+ * @param stack_sz
+ * The stack size which associated to coroutine. The value zero indicates that
+ * the default size (which is 8KB) is used.
+ *
+ * @return
+ * 0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_co_create(struct rte_schedule *s, coroutine_callback_t cb, void *arg, uint32_t stack_sz);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Coroutine yield the CPU.
+ *
+ * This function yield the cpu of current coroutine.
+ */
+__rte_experimental
+void rte_co_yield(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Coroutine delay function.
+ *
+ * This function support delay microseconds of current coroutine.
+ *
+ * @param us
+ * The time in microseconds which need delay.
+ */
+__rte_experimental
+void rte_co_delay(unsigned int us);
+
+#endif /* RTE_COROUTINE_H */
diff --git a/lib/coroutine/rte_coroutine_imp.h b/lib/coroutine/rte_coroutine_imp.h
new file mode 100644
index 0000000000..70b4f19670
--- /dev/null
+++ b/lib/coroutine/rte_coroutine_imp.h
@@ -0,0 +1,46 @@
+#ifndef RTE_COROUTINE_IMP_H
+#define RTE_COROUTINE_IMP_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <ucontext.h>
+
+#include "rte_coroutine.h"
+
+#define MIN_STACK_SIZE 8192
+
+enum rte_costate {
+ COROUTINE_INVALID,
+ COROUTINE_READY,
+ COROUTINE_RUNNING,
+ COROUTINE_YIELD,
+ COROUTINE_SUSPEND,
+};
+
+struct rte_cocontext {
+ struct rte_schedule *owner; /**< Which scheduler this coroutine belongs. */
+ int state; /**< The current coroutine state. */
+ ucontext_t ctx;
+
+ coroutine_callback_t cb; /**< The coroutine callback function. */
+ void *arg; /**< The coroutine callback function's input argument. */
+
+ struct rte_cocontext *yield_next;
+
+ void *stack; /**< The allocated stack pointer. */
+ uint32_t stack_sz; /**< The allocated stack size. */
+};
+
+struct rte_schedule {
+ uint32_t max_coroutines; /**< Max coroutines which this scheduler supports. */
+
+ ucontext_t main;
+ struct rte_cocontext *running; /**< Current running coroutine. */
+
+ struct rte_ring *ring; /**< Command ring for schedule. */
+
+ struct rte_cocontext *yield_head; /**< Yield coroutine list for schedule. */
+ struct rte_cocontext *yield_tail;
+};
+
+#endif /* RTE_COROUTINE_IMP_H */
diff --git a/lib/coroutine/version.map b/lib/coroutine/version.map
new file mode 100644
index 0000000000..393b8979a6
--- /dev/null
+++ b/lib/coroutine/version.map
@@ -0,0 +1,11 @@
+EXPERIMENTAL {
+ global:
+
+ rte_co_create;
+ rte_co_delay;
+ rte_co_yield;
+ rte_schedule_create;
+ rte_schedule_run;
+
+ local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index dc8aa4ac84..50e41f1511 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -64,6 +64,7 @@ libraries = [
'flow_classify', # flow_classify lib depends on pkt framework table lib
'graph',
'node',
+ 'coroutine',
]
optional_libs = [
--
2.17.1
next prev parent reply other threads:[~2023-04-24 13:09 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-24 13:02 [RFC 0/3] introduce " Chengwen Feng
2023-04-24 13:02 ` Chengwen Feng [this message]
2023-04-26 11:28 ` [RFC 1/3] lib/coroutine: add " Ferruh Yigit
2023-04-24 13:02 ` [RFC 2/3] examples/coroutine: support coroutine examples Chengwen Feng
2023-04-24 13:02 ` [RFC 3/3] net/hns3: refactor reset process with coroutine Chengwen Feng
2023-04-24 16:08 ` [RFC 0/3] introduce coroutine library Stephen Hemminger
2023-04-25 2:11 ` fengchengwen
2023-04-25 2:16 ` Stephen Hemminger
2023-04-25 2:50 ` fengchengwen
2023-04-25 2:59 ` Garrett D'Amore
2023-04-25 21:06 ` Stephen Hemminger
2023-04-26 11:27 ` Ferruh Yigit
2023-04-28 7:20 ` fengchengwen
2023-04-25 9:27 ` Mattias Rönnblom
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230424130208.9517-2-fengchengwen@huawei.com \
--to=fengchengwen@huawei.com \
--cc=dev@dpdk.org \
--cc=ferruh.yigit@amd.com \
--cc=thomas@monjalon.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).