From patchwork Mon Jul 24 06:00:11 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Github ODP bot X-Patchwork-Id: 108544 Delivered-To: patch@linaro.org Received: by 10.140.101.44 with SMTP id t41csp3691506qge; Sun, 23 Jul 2017 23:02:28 -0700 (PDT) X-Received: by 10.55.74.2 with SMTP id x2mr18695308qka.316.1500876148334; Sun, 23 Jul 2017 23:02:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1500876148; cv=none; d=google.com; s=arc-20160816; b=lwimv4HyjW9IMl5VEgXZZcePgamQI4b018EWvS33OSjeBBjBxOzeXvMN3ZkQjzvyz+ htZ85s9LciPyAEWL0lVD5Pl17gat2LlGI/m1QqjpgNIhwDC4QPnH2S+b+MrJjcPG9Qa+ sWO3WviIFEH8TX4uLbEAJSKJd+Cc+7/soHV8UsXp7hMFLD0elJnZakW0asgPAwoWUEDY aukgzRMjhqbc9dUkL1xl40fqolA+tdQPP9EoJmYSABMxLzCHLU4TElDCCnYoMCKgZm/d ZGRyKd19UsvpXNWMx49UjSRYQ6K0T6EsrTCZSlUeleDtJTsg5mjgtTj/H31szLPLgnZu /p5Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:subject:github-pr-num :references:in-reply-to:message-id:date:to:from:delivered-to :arc-authentication-results; bh=XYM3DTTrGSivk+pUhkIw3OEbkH4+QbN+qFW1l/OY+88=; b=y2+ziOqYQ4FI40teZ1C6SJ9lFmR4p28+C87VlA41jUeM1Qguz/HBza8ygIT84i+t9+ AH6YB4xCwuGm16FhUlaTTnVQB5v7L6E/SVqcAkA3QtEW4jboVOeF7ZO40ivMLXzMlLoU 3KujF24lM/anc81hyN5eHGUet4euUReDQJ29vJt7/uuWGbVKEjTN5AhWfMJxqlf2SHET Ik5TqYe0QieeEv0/MjtxqXz8tR9P2EZpT/tw51gCPAEtZpb04JL8ZVMuJX1XNEnBjRaV uH7qBsFKlvrlBd7mFavKW30FmPD58hxv00mqvThZIwMAu5qKx2OLGRDbKd7ZpiafZlcc n3Lw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) smtp.mailfrom=lng-odp-bounces@lists.linaro.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=yandex.ru Return-Path: Received: from lists.linaro.org (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTP id m31si6385098qtb.331.2017.07.23.23.02.28; Sun, 23 Jul 2017 23:02:28 -0700 (PDT) Received-SPF: pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) client-ip=54.225.227.206; Authentication-Results: mx.google.com; spf=pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) smtp.mailfrom=lng-odp-bounces@lists.linaro.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=yandex.ru Received: by lists.linaro.org (Postfix, from userid 109) id 0C97260826; Mon, 24 Jul 2017 06:02:28 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on ip-10-142-244-252 X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, URIBL_BLOCKED, URIBL_SBL, URIBL_SBL_A autolearn=disabled version=3.4.0 Received: from [127.0.0.1] (localhost [127.0.0.1]) by lists.linaro.org (Postfix) with ESMTP id 2204F60889; Mon, 24 Jul 2017 06:01:41 +0000 (UTC) X-Original-To: lng-odp@lists.linaro.org Delivered-To: lng-odp@lists.linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id CBC656081D; Mon, 24 Jul 2017 06:01:28 +0000 (UTC) Received: from forward2j.cmail.yandex.net (forward2j.cmail.yandex.net [5.255.227.20]) by lists.linaro.org (Postfix) with ESMTPS id E0DC26068B for ; Mon, 24 Jul 2017 06:00:53 +0000 (UTC) Received: from smtp1p.mail.yandex.net (smtp1p.mail.yandex.net [IPv6:2a02:6b8:0:1472:2741:0:8b6:6]) by forward2j.cmail.yandex.net (Yandex) with ESMTP id 7F14B20C9B for ; Mon, 24 Jul 2017 09:00:52 +0300 (MSK) Received: from smtp1p.mail.yandex.net (localhost.localdomain [127.0.0.1]) by smtp1p.mail.yandex.net (Yandex) with ESMTP id 8EF961780B68 for ; Mon, 24 Jul 2017 09:00:33 +0300 (MSK) Received: by smtp1p.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id fIdlDeIN3D-0WWChouf; Mon, 24 Jul 2017 09:00:32 +0300 (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (Client certificate not present) X-Yandex-Suid-Status: 1 0 From: Github ODP bot To: lng-odp@lists.linaro.org Date: Mon, 24 Jul 2017 09:00:11 +0300 Message-Id: <1500876011-20467-4-git-send-email-odpbot@yandex.ru> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1500876011-20467-1-git-send-email-odpbot@yandex.ru> References: <1500876011-20467-1-git-send-email-odpbot@yandex.ru> Github-pr-num: 65 Subject: [lng-odp] [PATCH CLOUD-DEV v2 3/3] odp: add modular framework X-BeenThere: lng-odp@lists.linaro.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: "The OpenDataPlane \(ODP\) List" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: lng-odp-bounces@lists.linaro.org Sender: "lng-odp" From: Yi He Add modular programming framework to support selectable implementations for variant software subsystems. Signed-off-by: Yi He --- /** Email created from pull request 65 (heyi-linaro:modular-framework) ** https://github.com/Linaro/odp/pull/65 ** Patch: https://github.com/Linaro/odp/pull/65.patch ** Base sha: 1ba26aa5650c05718c177842178de6d0f70b7fc1 ** Merge commit sha: e7e984619ed4a5d5b5dba8e68b79fb1161583050 **/ frameworks/modular/list.h | 301 +++++++++++++++++++++ frameworks/modular/module.c | 164 +++++++++++ frameworks/modular/module.h | 249 +++++++++++++++++ include/odp/api/spec/atomic.h | 5 + include/odp/api/spec/rwlock.h | 4 + platform/linux-generic/Makefile.am | 8 + .../include/odp/api/plat/atomic_types.h | 2 + .../include/odp/api/plat/rwlock_types.h | 2 + 8 files changed, 735 insertions(+) create mode 100644 frameworks/modular/list.h create mode 100644 frameworks/modular/module.c create mode 100644 frameworks/modular/module.h diff --git a/frameworks/modular/list.h b/frameworks/modular/list.h new file mode 100644 index 00000000..e6c2d732 --- /dev/null +++ b/frameworks/modular/list.h @@ -0,0 +1,301 @@ +/* Adopted and modified Rusty Russell CCAN Project + * https://github.com/rustyrussell/ccan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef CCAN_LIST_H +#define CCAN_LIST_H + +#include +#include + +/* Always assume the availabilities of typeof or __typeof__ */ +#if defined(__STDC__) +#define typeof __typeof__ +#endif + +/* + * Prevent the compiler from merging or refetching reads or writes. The + * compiler is also forbidden from reordering successive instances of + * READ_ONCE, WRITE_ONCE and ACCESS_ONCE (see below), but only when the + * compiler is aware of some particular ordering. One way to make the + * compiler aware of ordering is to put the two invocations of READ_ONCE, + * WRITE_ONCE or ACCESS_ONCE() in different C statements. + */ + +#define READ_ONCE(x) \ +({ \ + volatile typeof(x) *__p = &(x); \ + *__p; \ +}) + +#define WRITE_ONCE(x, val) \ +({ \ + volatile typeof(x) *__p = &(x); \ + *__p = (typeof(x))(val); \ +}) + +/** + * struct list_node - an entry in a doubly-linked list + * @next: next entry (self if empty) + * @prev: previous entry (self if empty) + * + * This is used as an entry in a linked list. + * Example: + * struct child { + * const char *name; + * // Linked list of all us children. + * struct list_node list; + * }; + */ +struct list_node { + struct list_node *next, *prev; +}; + +/** + * struct list_head - the head of a doubly-linked list + * @node: the head node + * + * This is used as the head of a linked list. + * Example: + * struct parent { + * const char *name; + * struct list_head children; + * unsigned int num_children; + * }; + */ +struct list_head { + struct list_node node; +}; + +/** + * LIST_HEAD_INIT - initializer for an empty list_head + * @name: the name of the list. + * + * Explicit initializer for an empty list. + * + * See also: + * LIST_HEAD, list_head_init() + * + * Example: + * static struct list_head my_list = LIST_HEAD_INIT(my_list); + */ +#define LIST_HEAD_INIT(name) { { &(name).node, &(name).node } } + +/** + * LIST_HEAD - define and initialize an empty list_head + * @name: the name of the list. + * + * The LIST_HEAD macro defines a list_head and initializes it to an empty + * list. It can be prepended by "static" to define a static list_head. + * + * See also: + * LIST_HEAD_INIT, list_head_init() + * + * Example: + * static LIST_HEAD(my_global_list); + */ +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +/** + * list_head_init - initialize a list_head + * @h: the list_head to set to the empty list + * + * Example: + * ... + * struct parent *parent = malloc(sizeof(*parent)); + * + * list_head_init(&parent->children); + * parent->num_children = 0; + */ +static inline void list_head_init(struct list_head *h) +{ + h->node.next = &h->node; + h->node.prev = &h->node; +} + +/** + * list_node_init - initialize a list_node + * @n: the list_node to link to itself. + * + * You don't need to use this normally! But it lets you list_del(@n) + * safely. + */ +static inline void list_node_init(struct list_node *n) +{ + n->next = n; + n->prev = n; +} + +/** + * list_add_after - add an entry after an existing node in a linked list + * @p: the existing list_node to add the node after + * @n: the new list_node to add to the list. + * + * The existing list_node must already be a member of the list. + * The new list_node does not need to be initialized; it will be overwritten. + * + * Example: + * struct child c1, c2, c3; + * LIST_HEAD(h); + * + * list_add_tail(&h, &c1.list); + * list_add_tail(&h, &c3.list); + * list_add_after(&c1.list, &c2.list); + */ +static inline void list_add_after(struct list_node *p, + struct list_node *n) +{ + n->next = p->next; + n->prev = p; + p->next->prev = n; + WRITE_ONCE(p->next, n); +} + +/** + * list_add - add an entry at the start of a linked list. + * @h: the list_head to add the node to + * @n: the list_node to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * struct child *child = malloc(sizeof(*child)); + * + * child->name = "marvin"; + * list_add(&parent->children, &child->list); + * parent->num_children++; + */ +static inline void list_add(struct list_head *h, + struct list_node *n) +{ + list_add_after(&h->node, n); +} + +/** + * list_add_before - add an entry before an existing node in a linked list + * @p: the existing list_node to add the node before + * @n: the new list_node to add to the list. + * + * The existing list_node must already be a member of the list. + * The new list_node does not need to be initialized; it will be overwritten. + * + * Example: + * list_head_init(&h); + * list_add_tail(&h, &c1.list); + * list_add_tail(&h, &c3.list); + * list_add_before(&c3.list, &c2.list); + */ +static inline void list_add_before(struct list_node *p, + struct list_node *n) +{ + n->next = p; + n->prev = p->prev; + p->prev->next = n; + WRITE_ONCE(p->prev, n); +} + +/** + * list_add_tail - add an entry at the end of a linked list. + * @h: the list_head to add the node to + * @n: the list_node to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * list_add_tail(&parent->children, &child->list); + * parent->num_children++; + */ +static inline void list_add_tail(struct list_head *h, + struct list_node *n) +{ + list_add_before(&h->node, n); +} + +/** + * list_empty - is a list empty? + * @h: the list_head + * + * If the list is empty, returns true. + * + * Example: + * assert(list_empty(&parent->children) == (parent->num_children == 0)); + */ +static inline bool list_empty(const struct list_head *h) +{ + return READ_ONCE(h->node.next) == &h->node; +} + +/** + * list_node_detached - is a node detached from any lists? + * @n: the list_node + * + * If the list node is initialized and detached, return true. + * Always use list_node_init() and list_del_init() on list nodes. + */ +static inline bool list_node_detached(const struct list_node *n) +{ + return READ_ONCE(n->next) == n; +} + +/** + * list_del - delete an entry from an (unknown) linked list. + * @n: the list_node to delete from the list. + * + * Note that this leaves @n in an undefined state; it can be added to + * another list, but not deleted again. + * + * See also: + * list_del_from(), list_del_init() + * + * Example: + * list_del(&child->list); + * parent->num_children--; + */ +static inline void list_del(struct list_node *n) +{ + n->next->prev = n->prev; + n->prev->next = n->next; + + /* Catch use-after-del. */ + WRITE_ONCE(n->next, NULL); + WRITE_ONCE(n->prev, NULL); +} + +/** + * list_del_init - delete a node, and reset it so it can be deleted again. + * @n: the list_node to be deleted. + * + * list_del(@n) or list_del_init() again after this will be safe, + * which can be useful in some cases. + * + * See also: + * list_del_from(), list_del() + * + * Example: + * list_del_init(&child->list); + * parent->num_children--; + */ +static inline void list_del_init(struct list_node *n) +{ + list_del(n); + list_node_init(n); +} + +#endif /* CCAN_LIST_H */ diff --git a/frameworks/modular/module.c b/frameworks/modular/module.c new file mode 100644 index 00000000..15dd968d --- /dev/null +++ b/frameworks/modular/module.c @@ -0,0 +1,164 @@ +/* Copyright (c) 2017, ARM Limited. All rights reserved. + * + * Copyright (c) 2017, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "module.h" + +#define MODULE_FRAMEWORK_VERSION 0x00010000UL +SUBSYSTEM(module, "module framework", MODULE_FRAMEWORK_VERSION); + +/* Keep it simple, allow one registration session at a time. */ +static struct { + odp_rwlock_t lock; + subsystem_t *subsystem; + module_base_t *module; +} registration = { + .lock = ODP_RWLOCK_UNLOCKED, + .subsystem = NULL, + .module = NULL, +}; + +#define REGISTRATION_SANITY_CHECK(subsystem, module) \ +do { \ + if (subsystem == NULL || module == NULL) \ + return -ENOENT; \ + \ + if (!list_node_detached(&module->list)) { \ + printf("module %s was already registered.\n", \ + module->name); \ + return -EAGAIN; \ + } \ +} while (0) + +/* Module is linked statically or dynamically, and are loaded by + * program loader (execve) or dynamic linker/loader (ld.so) + * + * subsystem_register_module() should complete the whole registration + * session and link the module into subsystem's module array. + */ +static int linker_register_module( + subsystem_t *subsystem, module_base_t *module) +{ + REGISTRATION_SANITY_CHECK(subsystem, module); + + /* Allow one registration session at a time */ + odp_rwlock_write_lock(®istration.lock); + + /* Block the subsystem API calls in load new + * implementation modules. */ + odp_rwlock_write_lock(&subsystem->lock); + module->handler = NULL; /* no DSO handler */ + list_add_tail(&subsystem->modules, &module->list); + odp_rwlock_write_unlock(&subsystem->lock); + + odp_rwlock_write_unlock(®istration.lock); + return 0; +} + +static int (*do_register_module)(subsystem_t *, module_base_t *) + = &linker_register_module; + +static int loader_register_module( + subsystem_t *subsystem, module_base_t *module) +{ + REGISTRATION_SANITY_CHECK(subsystem, module); + + /* Registration session lock must be held by + * module_loader_start(). */ + if (odp_rwlock_write_trylock(®istration.lock) == 0) { + registration.subsystem = subsystem; + registration.module = module; + return 0; + } + + odp_rwlock_write_unlock(®istration.lock); + return -EACCES; +} + +void module_loader_start(void) +{ + odp_rwlock_write_lock(®istration.lock); + + if (registration.module != NULL || + registration.subsystem != NULL) { + printf("module loader start warn, A previous " + "registration did not complete yet.\n"); + } + + registration.module = NULL; + registration.subsystem = NULL; + do_register_module = &loader_register_module; +} + +void module_loader_end(void) +{ + if (registration.module != NULL || + registration.subsystem != NULL) { + printf("module loader end warn, A previous " + "registration did not complete yet.\n"); + } + + registration.module = NULL; + registration.subsystem = NULL; + do_register_module = &linker_register_module; + + odp_rwlock_write_unlock(®istration.lock); +} + +int module_install_dso(void *dso, bool active) +{ + /* Bottom halves of the registration, context exclusion + * is guaranteed by module_loader_start() + */ + if (0 == odp_rwlock_write_trylock(®istration.lock)) { + subsystem_t *subsystem = registration.subsystem; + module_base_t *module = registration.module; + + if (subsystem != NULL && module != NULL) { + odp_rwlock_write_lock(&subsystem->lock); + + module->handler = dso; + list_add_tail(&subsystem->modules, &module->list); + + /* install as active implementation */ + if (active) /* warn: replaceable */ + subsystem->active = &module->list; + + odp_rwlock_write_unlock(&subsystem->lock); + } + + registration.subsystem = NULL; + registration.module = NULL; + return 0; + } + + odp_rwlock_write_unlock(®istration.lock); + return -EACCES; +} + +int module_abandon_dso(void) +{ + /* Bottom halves of the registration, context exclusion + * is guaranteed by module_loader_start() + */ + if (0 == odp_rwlock_write_trylock(®istration.lock)) { + registration.subsystem = NULL; + registration.module = NULL; + return 0; + } + + odp_rwlock_write_unlock(®istration.lock); + return -EACCES; +} + +int __subsystem_register_module( + subsystem_t *subsystem, module_base_t *module) +{ + return do_register_module(subsystem, module); +} diff --git a/frameworks/modular/module.h b/frameworks/modular/module.h new file mode 100644 index 00000000..0c0a08d8 --- /dev/null +++ b/frameworks/modular/module.h @@ -0,0 +1,249 @@ +/* Copyright (c) 2017, ARM Limited. All rights reserved. + * + * Copyright (c) 2017, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * Modular programming framework supports runtime selectable + * implementations for variant software subsystems. + * + * Multiple implementations of the same subsystem can be built + * into individual static libraries or loadable DSOs, and use + * constructor functions to register themselves. + * + * A subsystem can choose one active implementation and provide + * APIs to switch between implementations. + * + * Alternatively, subsystem can load multiple implementations + * and determine the APIs route in runtime. + + * Also in need to pursue extreme performance the subsystem + * can choose one specific implementation module and build it + * to override subsystem API symbols directly, thus eliminate + * one level indirection of API calls through function pointers. + * + * This framework tries to minimizes dependencies to the linked + * list and rwlock facilities only. + */ + +#ifndef MODULE_H +#define MODULE_H + +#include +#include +#include "list.h" + +typedef struct { + odp_rwlock_t lock; + uint32_t version; + const char *name; + const char *description; + struct list_head modules; + struct list_node *active; +} subsystem_t; + +/* Subsystem instance name */ +#define subsystem(name) name ## _subsystem + +/* The trick is to use macro SUBSYSTEM() for both subsystem + * declaration and definition. ARGC() macro chooses either + * SUBSYSTEM_DEFINE() or SUBSYSTEM_DECLARE() depends on argument + * number, + */ +#define _ARGC(_0, _1, _2, _3, ...) _3 +#define ARGC(...) _ARGC(__VA_ARGS__, DEFINE, 2, DECLARE, 0) + +#define _OVERLOAD(M, S, ...) M ## _ ## S(__VA_ARGS__) +#define OVERLOAD(M, S, ...) _OVERLOAD(M, S, __VA_ARGS__) + +#define SUBSYSTEM_DEFINE(_name, _description, _version) \ + subsystem_t subsystem(_name) = { \ + .lock = ODP_RWLOCK_UNLOCKED, \ + .name = # _name, \ + .version = _version, \ + .description = _description, \ + } + +#define SUBSYSTEM_DECLARE(name) subsystem_t subsystem(name) +#define SUBSYSTEM(...) OVERLOAD(SUBSYSTEM, ARGC(__VA_ARGS__), __VA_ARGS__) + +/* Subsystem API prototype name */ +#define api_proto(subsystem, api) subsystem ##_## api ## _proto_t + +/* Subsystem API declaration */ +#define SUBSYSTEM_API(name, _return, api, ...) \ + extern _return name ##_## api(__VA_ARGS__); \ + typedef _return (*api_proto(name, api))(__VA_ARGS__) \ + +/* Subsystem API stubs are weak */ +#define SUBSYSTEM_API_STUB(name, api) \ + __attribute__((weak)) name ##_## api + +/* In case subsystem API implementations are built as static + * libraries or preload DSOs, one implementation can use this + * macro to override the APIs weak stubs. + */ +#define SUBSYSTEM_API_OVERRIDE(name, api, _alias) \ + __attribute__((alias(#_alias))) name ##_## api + +#define subsystem_constructor(name) \ + do { \ + odp_rwlock_init(&subsystem(name).lock); \ + list_head_init(&subsystem(name).modules); \ + subsystem(name).active = NULL; \ + } while (0) + +#define SUBSYSTEM_CONSTRUCTOR(name) \ + static void __attribute__((constructor(101))) \ + name ## _subsystem_constructor(void) + +#define subsystem_lock(access, name) \ + odp_rwlock_ ##access## _lock(&subsystem(name).lock) + +#define subsystem_unlock(access, name) \ + odp_rwlock_ ##access## _unlock(&subsystem(name).lock) + +/* Below macros assume that all module classes derive from + * module_base_t class by using MODULE_CLASS macro in their + * typedefs and have list_node as their 1st member named "list". + * + * This greatly reduces the complexity for subsystem's module + * list iteration and module pointer recovery from its list_node + * member by a forced type conversion instead of complex calls to + * container_of() etc. + */ +#define __force_cast(module, node) \ + ((typeof(module))((void *)(node))) + +#define subsystem_active_module(name, mod) \ + __force_cast(mod, subsystem(name).active) + +#define __foreach_module(pos, head) \ + for (pos = __force_cast(pos, (head)->node.next); \ + pos != __force_cast(pos, head); \ + pos = __force_cast(pos, (pos)->list.next)) + +#define __foreach_module_safe(pos, n, head) \ + for (pos = __force_cast(pos, (head)->node.next), \ + n = __force_cast(pos, (pos)->list.next); \ + pos != __force_cast(pos, head); \ + pos = n, n = __force_cast(next, (next)->list.next)) + +#define subsystem_foreach_module(name, mod) \ + __foreach_module(mod, &subsystem(name).modules) + +#define subsystem_foreach_module_safe(name, mod, next) \ + __foreach_module_safe(mod, next, &subsystem(name).modules) + +#define MODULE_CLASS(subsystem) \ + struct subsystem ## _module { \ + struct list_node list; \ + const char *name; \ + void *handler; /* DSO */ \ + int (*init_local)(void); \ + int (*term_local)(void); \ + int (*init_global)(void); \ + int (*term_global)(void); \ + +/* Base class to all inherited subsystem module classes */ +typedef MODULE_CLASS(base) } module_base_t; + +#define module_constructor(mod) list_node_init(&(mod)->list) + +/* Module constructors should be late than subsystem constructors, + * in statically linked scenarios (both subsystems and modules are + * linked statically). thus the priority 102 compared to the above + * subsystem constructor priority 101. + */ +#define MODULE_CONSTRUCTOR(name) \ + static void __attribute__((constructor(102))) \ + name ## _module_constructor(void) + +/* All subsystems' initialization and termination routines are + * the same, provide template to instantiation. + */ +#define SUBSYSTEM_INITERM_TEMPLATE(subs, method, print) \ +static inline int subs ## _subsystem ##_## method(void) \ +{ \ + module_base_t *mod = NULL; \ + \ + subsystem_lock(read, subs); \ + subsystem_foreach_module(subs, mod) { \ + int result = mod->method ? mod->method() : 0; \ + if (result < 0) { \ + subsystem_unlock(read, subs); \ + print("error %d to %s subsystem %s " \ + "module %s.\n", result, # method, \ + subsystem(subs).name, mod->name); \ + return result; \ + } \ + } \ + subsystem_unlock(read, subs); \ + return 0; \ +} + +/* Subsystem Modules Registration + * + * subsystem_register_module() are called by all modules in their + * constructors, whereas the modules could be: + * + * 1) Linked statically or dynamically, and are loaded by program + * loader (execve) or dynamic linker/loader (ld.so) + * + * subsystem_register_module() should complete the whole + * registration session and link the module into subsystem's + * module array. + * + * 2) Loaded by a module loader in runtime with libdl APIs + * + * The whole registration session needs to be split to aim the + * module loader to properly handle dlopen() returns, and save + * the DSO handler into module's data structure. + * + * The module loader should program in this way: + * module_loader_start(); + * ...... + * for each module + * handler = dlopen(module) + * -- the module constructor calls register_module() + * if (handler is valid) + * install_dso(handler); + * else + * abandon_dso(); + * ...... + * module_loader_end(); + */ + +extern void module_loader_start(void); +extern void module_loader_end(void); + +extern int module_install_dso(void *, bool active); +extern int module_abandon_dso(void); + +#define __maybe_unused __attribute__((unused)) +static inline void __subsystem_set_active( + subsystem_t *subsystem __maybe_unused, + module_base_t *module __maybe_unused) +{ +#if defined(IM_ACTIVE_MODULE) + subsystem->active = &module->list; +#endif +} + +extern int __subsystem_register_module( + subsystem_t *, module_base_t *); + +/* Macro to allow polymorphism on module classes */ +#define subsystem_register_module(name, module) \ +({ \ + module_base_t *base = (module_base_t *)module; \ + __subsystem_register_module(&subsystem(name), base); \ + __subsystem_set_active(&subsystem(name), base); \ +}) + +#endif diff --git a/include/odp/api/spec/atomic.h b/include/odp/api/spec/atomic.h index 408829df..027d9f93 100644 --- a/include/odp/api/spec/atomic.h +++ b/include/odp/api/spec/atomic.h @@ -59,6 +59,11 @@ extern "C" { * Atomic 32-bit unsigned integer */ +/** + * @def ODP_ATOMIC_INIT + * Used to initialize static atomic variables without a constructor + */ + /* * 32-bit operations in RELAXED memory ordering * -------------------------------------------- diff --git a/include/odp/api/spec/rwlock.h b/include/odp/api/spec/rwlock.h index ff8a3f27..400eda47 100644 --- a/include/odp/api/spec/rwlock.h +++ b/include/odp/api/spec/rwlock.h @@ -36,6 +36,10 @@ extern "C" { * ODP reader/writer lock */ +/** + * @def ODP_RWLOCK_UNLOCKED + * Used to initialize static rwlock variables without a constructor + */ /** * Initialize a reader/writer lock. diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am index ba4a31ea..7794d0b4 100644 --- a/platform/linux-generic/Makefile.am +++ b/platform/linux-generic/Makefile.am @@ -6,6 +6,7 @@ include $(top_srcdir)/platform/@with_platform@/Makefile.inc AM_CFLAGS += -I$(srcdir)/include AM_CFLAGS += -I$(top_srcdir)/include +AM_CFLAGS += -I$(top_srcdir)/frameworks/modular AM_CFLAGS += -I$(top_srcdir)/include/odp/arch/@ARCH_ABI@ AM_CFLAGS += -I$(top_builddir)/include AM_CFLAGS += -Iinclude @@ -292,6 +293,13 @@ if HAVE_PCAP __LIB__libodp_linux_la_SOURCES += pktio/pcap.c endif +# Build modular framework into odp-linux library +modularframeworkdir = $(top_srcdir)/frameworks/modular +noinst_HEADERS += $(modularframeworkdir)/list.h \ + $(modularframeworkdir)/module.h + +__LIB__libodp_linux_la_SOURCES += ../../frameworks/modular/module.c + __LIB__libodp_linux_la_LIBADD = $(ATOMIC_LIBS) # Create symlink for ABI header files. Application does not need to use the arch diff --git a/platform/linux-generic/include/odp/api/plat/atomic_types.h b/platform/linux-generic/include/odp/api/plat/atomic_types.h index a674ac99..86c9e5b0 100644 --- a/platform/linux-generic/include/odp/api/plat/atomic_types.h +++ b/platform/linux-generic/include/odp/api/plat/atomic_types.h @@ -81,6 +81,8 @@ typedef struct odp_atomic_u64_s odp_atomic_u64_t; typedef struct odp_atomic_u32_s odp_atomic_u32_t; +#define ODP_ATOMIC_INIT(a) { .v = a } + #ifdef __cplusplus } #endif diff --git a/platform/linux-generic/include/odp/api/plat/rwlock_types.h b/platform/linux-generic/include/odp/api/plat/rwlock_types.h index f7dc0449..2c1f3660 100644 --- a/platform/linux-generic/include/odp/api/plat/rwlock_types.h +++ b/platform/linux-generic/include/odp/api/plat/rwlock_types.h @@ -30,6 +30,8 @@ struct odp_rwlock_s { typedef struct odp_rwlock_s odp_rwlock_t; +#define ODP_RWLOCK_UNLOCKED { .cnt = ODP_ATOMIC_INIT(0) } + #ifdef __cplusplus } #endif