From patchwork Mon Apr 24 13:02:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 126473 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id A79D8429DB; Mon, 24 Apr 2023 15:09:31 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 4B62042D16; Mon, 24 Apr 2023 15:09:18 +0200 (CEST) Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) by mails.dpdk.org (Postfix) with ESMTP id D682041138 for ; Mon, 24 Apr 2023 15:09:13 +0200 (CEST) Received: from dggpeml500024.china.huawei.com (unknown [172.30.72.57]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4Q4lm76VYYzsRJx; Mon, 24 Apr 2023 21:07:35 +0800 (CST) Received: from localhost.localdomain (10.50.163.32) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Mon, 24 Apr 2023 21:09:11 +0800 From: Chengwen Feng To: , CC: Subject: [RFC 1/3] lib/coroutine: add coroutine library Date: Mon, 24 Apr 2023 13:02:06 +0000 Message-ID: <20230424130208.9517-2-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230424130208.9517-1-fengchengwen@huawei.com> References: <20230424130208.9517-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.163.32] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-CFilter-Loop: Reflected X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org 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 --- 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 +#include +#include +#include + +#include +#include + +#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 + +#include + +/** + * 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 +#include +#include + +#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 = [ From patchwork Mon Apr 24 13:02:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 126471 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 425F4429DB; Mon, 24 Apr 2023 15:09:19 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id EFBB042B8E; Mon, 24 Apr 2023 15:09:15 +0200 (CEST) Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) by mails.dpdk.org (Postfix) with ESMTP id A7671410D0 for ; Mon, 24 Apr 2023 15:09:13 +0200 (CEST) Received: from dggpeml500024.china.huawei.com (unknown [172.30.72.57]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4Q4ljY29LxzncKN; Mon, 24 Apr 2023 21:05:21 +0800 (CST) Received: from localhost.localdomain (10.50.163.32) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Mon, 24 Apr 2023 21:09:11 +0800 From: Chengwen Feng To: , CC: Subject: [RFC 2/3] examples/coroutine: support coroutine examples Date: Mon, 24 Apr 2023 13:02:07 +0000 Message-ID: <20230424130208.9517-3-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230424130208.9517-1-fengchengwen@huawei.com> References: <20230424130208.9517-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.163.32] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-CFilter-Loop: Reflected X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org This patch adds coroutine example, usage: 1. start examples: dpdk-coroutine -a 0000:7d:00.2 -l 10-11 2. will output: Start yield coroutine test! I am in yield coroutine 111! I am in yield coroutine 222! I am in yield coroutine 333! I am in yield coroutine 111! I am in yield coroutine 222! I am in yield coroutine 333! ... Start delay coroutine test! I am in delay coroutine 111! I am in delay coroutine 222! I am in delay coroutine 222! I am in delay coroutine 111! I am in delay coroutine 222! I am in delay coroutine 222! ... 3. use ctrl+c to exit example. Signed-off-by: Chengwen Feng --- examples/coroutine/main.c | 153 +++++++++++++++++++++++++++++++++ examples/coroutine/meson.build | 10 +++ examples/meson.build | 1 + 3 files changed, 164 insertions(+) create mode 100644 examples/coroutine/main.c create mode 100644 examples/coroutine/meson.build diff --git a/examples/coroutine/main.c b/examples/coroutine/main.c new file mode 100644 index 0000000000..2704ad1dc9 --- /dev/null +++ b/examples/coroutine/main.c @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static volatile bool force_quit; + +static struct rte_schedule *target_s; + +static void +yield_coroutine_1(void *arg) +{ + RTE_SET_USED(arg); + int i = 10; + while (i--) { + printf("\tI am in yield coroutine 111!\n"); + rte_co_yield(); + } +} + +static void +yield_coroutine_2(void *arg) +{ + RTE_SET_USED(arg); + int i = 10; + while (i--) { + printf("\tI am in yield coroutine 222!\n"); + rte_co_yield(); + } +} + +static void +yield_coroutine_3(void *arg) +{ + RTE_SET_USED(arg); + int i = 10; + while (i--) { + printf("\tI am in yield coroutine 333!\n"); + rte_co_yield(); + } +} + +static void +yield_coroutine_test(void) +{ + printf("Start yield coroutine test!\n"); + rte_co_create(target_s, yield_coroutine_1, NULL, 0); + rte_co_create(target_s, yield_coroutine_2, NULL, 0); + rte_co_create(target_s, yield_coroutine_3, NULL, 0); + sleep(1); +} + +static void +delay_coroutine_1(void *arg) +{ + RTE_SET_USED(arg); + int i = 10; + while (i--) { + printf("\tI am in delay coroutine 111!\n"); + rte_co_delay(100 * 1000); + } +} + +static void +delay_coroutine_2(void *arg) +{ + RTE_SET_USED(arg); + int i = 20; + while (i--) { + printf("\tI am in delay coroutine 222!\n"); + rte_co_delay(50 * 1000); + } +} + +static void +delay_coroutine_test(void) +{ + printf("Start delay coroutine test!\n"); + rte_co_create(target_s, delay_coroutine_1, NULL, 0); + rte_co_create(target_s, delay_coroutine_2, NULL, 0); + sleep(1); +} + +static int +co_main_loop(void *arg) +{ + RTE_SET_USED(arg); + while (!force_quit) + rte_schedule_run(target_s); + return 0; +} + +static void +signal_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) { + printf("\n\nSignal %d received, preparing to exit...\n", + signum); + force_quit = true; + } +} + +int +main(int argc, char **argv) +{ + uint32_t lcore_id = rte_lcore_id(); + int ret; + + /* Init EAL. 8< */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); + + force_quit = false; + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + /* Check if there is enough lcores for all ports. */ + if (rte_lcore_count() < 2) + rte_exit(EXIT_FAILURE, + "There should be at least one worker lcore.\n"); + + target_s = rte_schedule_create("co-sched-test", 128); + if (target_s == NULL) + rte_exit(EXIT_FAILURE, + "Create target scheduler failed!\n"); + + lcore_id = rte_get_next_lcore(lcore_id, true, true); + rte_eal_remote_launch(co_main_loop, NULL, lcore_id); + + yield_coroutine_test(); + delay_coroutine_test(); + + /* force_quit is true when we get here */ + rte_eal_mp_wait_lcore(); + + /* clean up the EAL */ + rte_eal_cleanup(); + + printf("Bye...\n"); + return 0; +} diff --git a/examples/coroutine/meson.build b/examples/coroutine/meson.build new file mode 100644 index 0000000000..c3576fe2f3 --- /dev/null +++ b/examples/coroutine/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 HiSilicon Limited + +allow_experimental_apis = true + +deps += ['coroutine'] + +sources = files( + 'main.c', +) diff --git a/examples/meson.build b/examples/meson.build index 6968c09252..111d628065 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -11,6 +11,7 @@ all_examples = [ 'bbdev_app', 'bond', 'cmdline', + 'coroutine', 'distributor', 'dma', 'ethtool', From patchwork Mon Apr 24 13:02:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 126472 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 41CF9429DB; Mon, 24 Apr 2023 15:09:25 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 2CE5C42D0B; Mon, 24 Apr 2023 15:09:17 +0200 (CEST) Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) by mails.dpdk.org (Postfix) with ESMTP id A93EA410ED for ; Mon, 24 Apr 2023 15:09:13 +0200 (CEST) Received: from dggpeml500024.china.huawei.com (unknown [172.30.72.53]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4Q4lm81PlHzsRTH; Mon, 24 Apr 2023 21:07:36 +0800 (CST) Received: from localhost.localdomain (10.50.163.32) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Mon, 24 Apr 2023 21:09:12 +0800 From: Chengwen Feng To: , CC: Subject: [RFC 3/3] net/hns3: refactor reset process with coroutine Date: Mon, 24 Apr 2023 13:02:08 +0000 Message-ID: <20230424130208.9517-4-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230424130208.9517-1-fengchengwen@huawei.com> References: <20230424130208.9517-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.163.32] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500024.china.huawei.com (7.185.36.10) X-CFilter-Loop: Reflected X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org This patch adds reset mode 1 which use coroutine to refactor the reset process. And this just a demo which only function at PF driver. Using the coroutine will make the reset process more intuitive. Signed-off-by: Chengwen Feng --- drivers/net/hns3/hns3_ethdev.c | 217 +++++++++++++++++++++++++++++++++ drivers/net/hns3/hns3_ethdev.h | 3 + drivers/net/hns3/hns3_intr.c | 38 ++++++ drivers/net/hns3/meson.build | 2 +- 4 files changed, 259 insertions(+), 1 deletion(-) diff --git a/drivers/net/hns3/hns3_ethdev.c b/drivers/net/hns3/hns3_ethdev.c index 36896f8989..06ff0bcae1 100644 --- a/drivers/net/hns3/hns3_ethdev.c +++ b/drivers/net/hns3/hns3_ethdev.c @@ -6487,7 +6487,224 @@ static const struct eth_dev_ops hns3_eth_dev_ops = { .eth_tx_descriptor_dump = hns3_tx_descriptor_dump, }; +#include + +static const char *reset_string[HNS3_MAX_RESET] = { + "flr", "vf_func", "vf_pf_func", "vf_full", "vf_global", + "pf_func", "global", "IMP", "none", +}; + +static void +hns3_clear_reset_level(struct hns3_hw *hw, uint64_t *levels) +{ + uint64_t merge_cnt = hw->reset.stats.merge_cnt; + uint64_t tmp; + + switch (hw->reset.level) { + case HNS3_IMP_RESET: + hns3_atomic_clear_bit(HNS3_IMP_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_GLOBAL_RESET, levels); + merge_cnt = tmp > 0 ? merge_cnt + 1 : merge_cnt; + tmp = hns3_test_and_clear_bit(HNS3_FUNC_RESET, levels); + merge_cnt = tmp > 0 ? merge_cnt + 1 : merge_cnt; + break; + case HNS3_GLOBAL_RESET: + hns3_atomic_clear_bit(HNS3_GLOBAL_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_FUNC_RESET, levels); + merge_cnt = tmp > 0 ? merge_cnt + 1 : merge_cnt; + break; + case HNS3_FUNC_RESET: + hns3_atomic_clear_bit(HNS3_FUNC_RESET, levels); + break; + case HNS3_VF_RESET: + hns3_atomic_clear_bit(HNS3_VF_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_VF_PF_FUNC_RESET, levels); + merge_cnt = tmp > 0 ? merge_cnt + 1 : merge_cnt; + tmp = hns3_test_and_clear_bit(HNS3_VF_FUNC_RESET, levels); + merge_cnt = tmp > 0 ? merge_cnt + 1 : merge_cnt; + break; + case HNS3_VF_FULL_RESET: + hns3_atomic_clear_bit(HNS3_VF_FULL_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_VF_FUNC_RESET, levels); + merge_cnt = tmp > 0 ? merge_cnt + 1 : merge_cnt; + break; + case HNS3_VF_PF_FUNC_RESET: + hns3_atomic_clear_bit(HNS3_VF_PF_FUNC_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_VF_FUNC_RESET, levels); + merge_cnt = tmp > 0 ? merge_cnt + 1 : merge_cnt; + break; + case HNS3_VF_FUNC_RESET: + hns3_atomic_clear_bit(HNS3_VF_FUNC_RESET, levels); + break; + case HNS3_FLR_RESET: + hns3_atomic_clear_bit(HNS3_FLR_RESET, levels); + break; + case HNS3_NONE_RESET: + default: + return; + }; + + if (merge_cnt != hw->reset.stats.merge_cnt) { + hns3_warn(hw, + "No need to do low-level reset after %s reset. " + "merge cnt: %" PRIu64 " total merge cnt: %" PRIu64, + reset_string[hw->reset.level], + hw->reset.stats.merge_cnt - merge_cnt, + hw->reset.stats.merge_cnt); + hw->reset.stats.merge_cnt = merge_cnt; + } +} + +static void hns3_reset_coroutine(void *arg) +{ + struct hns3_hw *hw = (struct hns3_hw *)arg; + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct timeval tv, tv_delta; + int ret, i; + + /* + * calc reset level. + */ + hns3_clock_gettime(&hw->reset.start_time); + hw->reset.level = hns3_get_reset_level(hns, &hw->reset.pending); + if (hw->reset.level == HNS3_NONE_RESET) + hw->reset.level = HNS3_IMP_RESET; + hns3_warn(hw, "start reset level: %s", reset_string[hw->reset.level]); + + + /* + * stop service. + */ + ret = hns3_stop_service(hns); + hns3_clock_gettime(&tv); + if (ret) { + hns3_warn(hw, "Reset step1 down fail=%d time=%ld.%.6ld", + ret, tv.tv_sec, tv.tv_usec); + return; + } + hns3_warn(hw, "Reset step1 down success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + + + /* + * yield CPU to schedule other function's reset. + */ + rte_co_yield(); + + + /* + * prepare reset. + */ + ret = hns3_prepare_reset(hns); + hns3_clock_gettime(&tv); + if (ret) { + hns3_warn(hw, + "Reset step2 prepare wait fail=%d time=%ld.%.6ld", + ret, tv.tv_sec, tv.tv_usec); + return; + } + hns3_warn(hw, "Reset step2 prepare wait success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + + + /* + * delay 100ms which refer the manual of hardware. + */ + rte_co_delay(100 * 1000); + + + /* + * notify IMP reset. + */ + /* inform hardware that preparatory work is done */ + hns3_notify_reset_ready(hw, true); + hns3_clock_gettime(&tv); + hns3_warn(hw, + "Reset step3 request IMP reset success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + + + /* + * Wait hardware reset done. + */ + for (i = 0; i < HNS3_RESET_WAIT_CNT; i++) { + rte_co_delay(HNS3_RESET_WAIT_MS * USEC_PER_MSEC); + if (is_pf_reset_done(hw)) + break; + } + if (i >= HNS3_RESET_WAIT_CNT) { + hns3_clock_gettime(&tv); + hns3_warn(hw, "Reset step4 hardware not ready after reset time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + return; + } + hns3_clock_gettime(&tv); + hns3_warn(hw, "Reset step4 reset wait success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + + + /* + * yield CPU to schedule other function's reset. + */ + rte_co_yield(); + + + /* + * Devinit. + */ + rte_spinlock_lock(&hw->lock); + if (hw->reset.mbuf_deferred_free) { + hns3_dev_release_mbufs(hns); + hw->reset.mbuf_deferred_free = false; + } + ret = hns3_reinit_dev(hns); + rte_spinlock_unlock(&hw->lock); + hns3_clock_gettime(&tv); + if (ret) { + hns3_warn(hw, "Reset step5 devinit fail=%d retries=%d", + ret, hw->reset.retries); + return; + } + hns3_warn(hw, "Reset step5 devinit success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + + + /* + * yield CPU to schedule other function's reset. + */ + rte_co_yield(); + + + /* + * Start service. + */ + /* IMP will wait ready flag before reset */ + hns3_notify_reset_ready(hw, false); + hns3_clear_reset_level(hw, &hw->reset.pending); + rte_spinlock_lock(&hw->lock); + hns3_start_service(hns); + rte_spinlock_unlock(&hw->lock); + hns3_clock_gettime(&tv); + timersub(&tv, &hw->reset.start_time, &tv_delta); + hns3_warn(hw, "%s reset done fail_cnt:%" PRIu64 + " success_cnt:%" PRIu64 " global_cnt:%" PRIu64 + " imp_cnt:%" PRIu64 " request_cnt:%" PRIu64 + " exec_cnt:%" PRIu64 " merge_cnt:%" PRIu64, + reset_string[hw->reset.level], + hw->reset.stats.fail_cnt, hw->reset.stats.success_cnt, + hw->reset.stats.global_cnt, hw->reset.stats.imp_cnt, + hw->reset.stats.request_cnt, hw->reset.stats.exec_cnt, + hw->reset.stats.merge_cnt); + hns3_warn(hw, + "%s reset done delta %" PRIu64 " ms time=%ld.%.6ld", + reset_string[hw->reset.level], + hns3_clock_calctime_ms(&tv_delta), + tv.tv_sec, tv.tv_usec); + hw->reset.level = HNS3_NONE_RESET; +} + static const struct hns3_reset_ops hns3_reset_ops = { + .coroutine = hns3_reset_coroutine, .reset_service = hns3_reset_service, .stop_service = hns3_stop_service, .prepare_reset = hns3_prepare_reset, diff --git a/drivers/net/hns3/hns3_ethdev.h b/drivers/net/hns3/hns3_ethdev.h index 9acc5a3d7e..4029bea133 100644 --- a/drivers/net/hns3/hns3_ethdev.h +++ b/drivers/net/hns3/hns3_ethdev.h @@ -379,6 +379,7 @@ struct hns3_wait_data { }; struct hns3_reset_ops { + void (*coroutine)(void *arg); void (*reset_service)(void *arg); int (*stop_service)(struct hns3_adapter *hns); int (*prepare_reset)(struct hns3_adapter *hns); @@ -396,6 +397,8 @@ enum hns3_schedule { }; struct hns3_reset_data { + struct rte_schedule *reset_sched; + uint32_t mode; enum hns3_reset_stage stage; uint16_t schedule; /* Reset flag, covering the entire reset process */ diff --git a/drivers/net/hns3/hns3_intr.c b/drivers/net/hns3/hns3_intr.c index 44a1119415..327d548bc6 100644 --- a/drivers/net/hns3/hns3_intr.c +++ b/drivers/net/hns3/hns3_intr.c @@ -2393,10 +2393,36 @@ hns3_handle_error(struct hns3_adapter *hns) } } +#include + +static uint32_t thread_run(void *arg) +{ + struct rte_schedule *s = (struct rte_schedule *)arg; + int ret; + + rte_thread_setname(pthread_self(), "co-sched-hns3"); + + while (1) { + ret = rte_schedule_run(s); + if (ret != 0) + break; + } + + return 0; +} + int hns3_reset_init(struct hns3_hw *hw) { rte_spinlock_init(&hw->lock); + hw->reset.mode = 1; /* mode 1 means use coroutine. */ + static struct rte_schedule *s; + if (s == NULL) { + rte_thread_t thread; + s = rte_schedule_create("co-sched-hns3", 128); + rte_thread_create(&thread, NULL, thread_run, (void *)s); + } + hw->reset.reset_sched = s; hw->reset.level = HNS3_NONE_RESET; hw->reset.stage = RESET_STAGE_NONE; hw->reset.request = 0; @@ -2427,6 +2453,12 @@ hns3_schedule_reset(struct hns3_adapter *hns) if (hw->adapter_state >= HNS3_NIC_CLOSED) return; + if (hw->reset.mode == 1) { + rte_co_create(hw->reset.reset_sched, hw->reset.ops->coroutine, + (void *)hw, 128 * 1024); + return; + } + /* Schedule restart alarm if it is not scheduled yet */ if (__atomic_load_n(&hw->reset.schedule, __ATOMIC_RELAXED) == SCHEDULE_REQUESTED) @@ -2453,6 +2485,12 @@ hns3_schedule_delayed_reset(struct hns3_adapter *hns) return; } + if (hw->reset.mode == 1) { + rte_co_create(hw->reset.reset_sched, hw->reset.ops->coroutine, + (void *)hw, 128 * 1024); + return; + } + if (__atomic_load_n(&hw->reset.schedule, __ATOMIC_RELAXED) != SCHEDULE_NONE) return; diff --git a/drivers/net/hns3/meson.build b/drivers/net/hns3/meson.build index 33f61f9883..7abb3d1986 100644 --- a/drivers/net/hns3/meson.build +++ b/drivers/net/hns3/meson.build @@ -37,7 +37,7 @@ require_iova_in_mbuf = false annotate_locks = false -deps += ['hash'] +deps += ['hash', 'coroutine'] if arch_subdir == 'arm' and dpdk_conf.get('RTE_ARCH_64') sources += files('hns3_rxtx_vec.c')