new file mode 100644
@@ -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 <stddef.h>
+#include <stdbool.h>
+
+/* 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 */
new file mode 100644
@@ -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 <stdio.h>
+#include <errno.h>
+#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);
+}
new file mode 100644
@@ -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 <stdbool.h>
+#include <odp/api/rwlock.h>
+#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
@@ -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
* --------------------------------------------
@@ -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.
@@ -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
@@ -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
@@ -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