[CLOUD-DEV,v4,3/3] odp: add modular framework

Message ID 1501736410-15618-4-git-send-email-odpbot@yandex.ru
State New
Headers show
Series
  • [CLOUD-DEV,v4,1/3] example: ddf_app: add .gitignore
Related show

Commit Message

Github ODP bot Aug. 3, 2017, 5 a.m.
From: Yi He <yi.he@linaro.org>


Add modular programming framework to support selectable
implementations for variant software subsystems.

Signed-off-by: Yi He <yi.he@linaro.org>

---
/** 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: bf1d072772b57f96a842e5d2b57e3f9f9eed0107
 **/
 frameworks/modular/list.h                          | 271 +++++++++++++++++++
 frameworks/modular/odp_module.c                    | 179 +++++++++++++
 frameworks/modular/odp_module.h                    | 296 +++++++++++++++++++++
 include/odp/api/spec/atomic.h                      |   6 +
 include/odp/api/spec/rwlock.h                      |   5 +
 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, 769 insertions(+)
 create mode 100644 frameworks/modular/list.h
 create mode 100644 frameworks/modular/odp_module.c
 create mode 100644 frameworks/modular/odp_module.h

Patch

diff --git a/frameworks/modular/list.h b/frameworks/modular/list.h
new file mode 100644
index 00000000..cc42b714
--- /dev/null
+++ b/frameworks/modular/list.h
@@ -0,0 +1,271 @@ 
+/* 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.
+ *
+ * Copyright (c) 2017, ARM Limited. All rights reserved.
+ *
+ * Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#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
+
+/**
+ * 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_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;
+	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;
+	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 (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 (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. */
+	n->next = NULL;
+	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/odp_module.c b/frameworks/modular/odp_module.c
new file mode 100644
index 00000000..89b7cb0d
--- /dev/null
+++ b/frameworks/modular/odp_module.c
@@ -0,0 +1,179 @@ 
+/* 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 "odp_module.h"
+
+#define MODULE_FRAMEWORK_VERSION 0x00010000UL
+ODP_SUBSYSTEM_DEFINE(module, "module framework", MODULE_FRAMEWORK_VERSION);
+
+/* Bootstrap log facility, enable if ODP_DEBUG_PRINT flag is set. */
+#define DBG(format, ...)					\
+	do {							\
+		if (ODP_DEBUG_PRINT == 1)			\
+			fprintf(stderr, format, ##__VA_ARGS__);	\
+	} while (0)
+
+/* Keep it simple, allow one registration session at a time. */
+static struct {
+	odp_rwlock_t lock;
+	odp_subsystem_t *subsystem;
+	odp_module_base_t *module;
+} registration = {
+	.lock = ODP_RWLOCK_UNLOCKED,
+	.subsystem = NULL,
+	.module = NULL,
+};
+
+static inline int registration_sanity_check(
+	odp_subsystem_t *subsystem, odp_module_base_t *module)
+{
+	if (subsystem == NULL || module == NULL)
+		return -ENOENT;
+
+	if (!list_node_detached(&module->list)) {
+		DBG("module %s was already registered.\n", module->name);
+		return -EAGAIN;
+	}
+
+	return 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(
+	odp_subsystem_t *subsystem, odp_module_base_t *module)
+{
+	int sanity = registration_sanity_check(subsystem, module);
+
+	if (sanity < 0) /* sanity check errors */
+		return sanity;
+
+	/* Allow one registration session at a time */
+	odp_rwlock_write_lock(&registration.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(&registration.lock);
+	return 0;
+}
+
+static int (*do_register_module)(odp_subsystem_t *, odp_module_base_t *)
+		= &linker_register_module;
+
+static int loader_register_module(
+	odp_subsystem_t *subsystem, odp_module_base_t *module)
+{
+	int sanity = registration_sanity_check(subsystem, module);
+
+	if (sanity < 0) /* sanity check errors */
+		return sanity;
+
+	/* Registration session lock must be held by
+	 * module_loader_start(). */
+	if (odp_rwlock_write_trylock(&registration.lock) == 0) {
+		registration.subsystem = subsystem;
+		registration.module = module;
+		return 0;
+	}
+
+	odp_rwlock_write_unlock(&registration.lock);
+	return -EACCES;
+}
+
+void odp_module_loader_start(void)
+{
+	odp_rwlock_write_lock(&registration.lock);
+
+	if (registration.module != NULL ||
+	    registration.subsystem != NULL) {
+		DBG("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 odp_module_loader_end(void)
+{
+	if (registration.module != NULL ||
+	    registration.subsystem != NULL) {
+		DBG("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(&registration.lock);
+}
+
+int odp_module_install(void *dso, bool active)
+{
+	/* Bottom halves of the registration, context exclusion
+	 * is guaranteed by module_loader_start()
+	 */
+	if (odp_rwlock_write_trylock(&registration.lock) == 0) {
+		odp_subsystem_t *subsystem = registration.subsystem;
+		odp_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;
+
+			odp_rwlock_write_unlock(&subsystem->lock);
+		}
+
+		registration.subsystem = NULL;
+		registration.module = NULL;
+		return 0;
+	}
+
+	odp_rwlock_write_unlock(&registration.lock);
+	return -EACCES;
+}
+
+int odp_module_abandon(void)
+{
+	/* Bottom halves of the registration, context exclusion
+	 * is guaranteed by module_loader_start()
+	 */
+	if (odp_rwlock_write_trylock(&registration.lock) == 0) {
+		registration.subsystem = NULL;
+		registration.module = NULL;
+		return 0;
+	}
+
+	odp_rwlock_write_unlock(&registration.lock);
+	return -EACCES;
+}
+
+int __subsystem_register_module(
+	odp_subsystem_t *subsystem, odp_module_base_t *module)
+{
+	return do_register_module(subsystem, module);
+}
diff --git a/frameworks/modular/odp_module.h b/frameworks/modular/odp_module.h
new file mode 100644
index 00000000..1a9e178c
--- /dev/null
+++ b/frameworks/modular/odp_module.h
@@ -0,0 +1,296 @@ 
+/* Copyright (c) 2017, ARM Limited. All rights reserved.
+ *
+ * Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * Modular framework solves the problem of choosing a module
+ * from multiple modules of a subsystem.
+ *
+ * The choice is available during compile time or during runtime
+ * or initialization. The runtime choice could be using shared
+ * libraries or dynamic loadable libraries.
+ *
+ * Multiple modules of the same subsystem can be built into
+ * individual static libraries(.a), shared libraries(.so) to be
+ * dynamically linked or loaded, and use constructor functions
+ * to register themselves.
+ *
+ * A subsystem can choose one active module and provide APIs to
+ * switch between modules.
+ *
+ * Alternatively, subsystem can load multiple modules and
+ * determine the APIs route in runtime.
+ *
+ * In order to gain full possible performance, the subsystem
+ * allows for choosing a specific module at compile time.
+ * This eliminates the need to choose the module using function
+ * pointer table.
+ *
+ * This framework tries to minimizes dependencies to the linked
+ * list and rwlock facilities only.
+ */
+
+#ifndef ODP_MODULE_H_
+#define ODP_MODULE_H_
+
+#include <stdbool.h>
+#include <odp/api/rwlock.h>
+#include "list.h"
+
+/* Forward declaration */
+typedef struct odp_module_base odp_module_base_t;
+
+/* Subsystem */
+typedef struct odp_subsystem {
+	odp_rwlock_t lock;
+	uint32_t version;
+	const char *name;
+	const char *description;
+	struct list_head modules;
+	odp_module_base_t *active;
+} odp_subsystem_t;
+
+/* Internally construct subsystem instance name */
+#define __subsystem(name) odp_ ## name ## _subsystem
+
+/* Declare an ODP subsystem in header file */
+#define ODP_SUBSYSTEM_DECLARE(name) \
+	extern odp_subsystem_t __subsystem(name)
+
+/* Define an ODP subsystem in source file */
+#define ODP_SUBSYSTEM_DEFINE(_name, _description, _version)	\
+	odp_subsystem_t __subsystem(_name) =			\
+	{							\
+		.lock = ODP_RWLOCK_UNLOCKED,			\
+		.name = # _name,				\
+		.version = _version,				\
+		.description = _description,			\
+	}
+
+/* Internally construct subsystem API name */
+#define __odp_api(subsystem, api) odp_ ## subsystem ##_## api
+
+/* Subsystem API prototype name */
+#define odp_api_proto(subsystem, api) __odp_api(subsystem, api ## _proto_t)
+
+/* Subsystem API declaration */
+#define ODP_SUBSYSTEM_API(name, _return, api, ...)		\
+	extern _return __odp_api(name, api)(__VA_ARGS__);	\
+	typedef _return (*odp_api_proto(name, api))(__VA_ARGS__)
+
+/* Subsystem API stubs are weak */
+#define ODP_SUBSYSTEM_API_STUB(name, api)			\
+	__attribute__((weak)) __odp_api(name, api)
+
+/* In case a subsystem module are built as static libraries(.a)
+ * or preload dynamic libraries(.so), the module can use this
+ * macro to override the APIs weak stubs.
+ */
+#define ODP_SUBSYSTEM_API_OVERRIDE(name, api, _alias)		\
+	__attribute__((alias(#_alias))) __odp_api(name, api)
+
+#define odp_subsystem_constructor(name)				\
+	do {							\
+		odp_rwlock_init(&__subsystem(name).lock);	\
+		list_head_init(&__subsystem(name).modules);	\
+		__subsystem(name).active = NULL;		\
+	} while (0)
+
+#define ODP_SUBSYSTEM_CONSTRUCTOR(name)				\
+	static void __attribute__((constructor(101)))		\
+		odp_ ## name ## _subsystem_constructor(void)
+
+#define odp_subsystem_lock(access, name)			\
+	odp_rwlock_ ## access ## _lock(&__subsystem(name).lock)
+
+#define odp_subsystem_unlock(access, name)			\
+	odp_rwlock_ ## access ## _unlock(&__subsystem(name).lock)
+
+/* Base class to all inherited subsystem module classes */
+struct odp_module_base {
+	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);
+};
+
+/* It is required to define subsystem module class with the
+ * base class as its 1st member and named as "base", and also
+ * use ODP_MODULE_CLASS(subsystem) to create the association
+ * between module class name and subsystem name, like:
+ *
+ * typedef ODP_MODULE_CLASS(subsystem) {
+ *	odp_module_base_t base;
+ *	...new members...
+ * } new_module_t; // Here pick the name you like freely
+ *
+ * It also supports forward declaration like:
+ *
+ * // Forward declaration
+ * typedef ODP_MODULE_CLASS(subsystem) new_module_t;
+ * // Laterly comes the definition
+ * ODP_MODULE_CLASS(subsystem) {
+ *	odp_module_base_t base;
+ *	...new members...
+ * }
+ *
+ * Then in preprocessor macros when we have the subsystem name
+ * we can recover the module class type information, like:
+ *
+ * #define MACRO(subsystem)
+ * do {
+ *	ODP_MODULE_CLASS(subsystem) *mod = NULL;
+ *	odp_subsystem_foreach_module(subsystem, mod) {
+ *		mod->xxx; // access the module class
+ *	}
+ * } while(0)
+ */
+#define ODP_MODULE_CLASS(subsystem) struct odp_ ## subsystem ## _module
+
+/* Below macros assume that all subsystem module classes have
+ * odp_module_base_t as their 1st member named "base".
+ *
+ * This greatly reduces the complexity for 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 __foreach_module(pos, head)				\
+	for (pos = __force_cast(pos, (head)->node.next);	\
+	     pos != __force_cast(pos, head);			\
+	     pos = __force_cast(pos, (pos)->base.list.next))
+
+#define __foreach_module_safe(pos, n, head)			\
+	for (pos = __force_cast(pos, (head)->node.next),	\
+	     n = __force_cast(pos, (pos)->base.list.next);	\
+	     pos != __force_cast(pos, head);			\
+	     pos = n, n = __force_cast(next, (next)->base.list.next))
+
+#define odp_subsystem_active_module(name, mod)			\
+	__force_cast(mod, __subsystem(name).active)
+
+#define odp_subsystem_foreach_module(name, mod)			\
+	__foreach_module(mod, &__subsystem(name).modules)
+
+#define odp_subsystem_foreach_module_safe(name, mod, next)	\
+	__foreach_module_safe(mod, next, &__subsystem(name).modules)
+
+#define odp_module_constructor(mod) list_node_init(&(mod)->base.list)
+
+/* Module constructors should be later 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 ODP_MODULE_CONSTRUCTOR(name)				\
+	static void __attribute__((constructor(102)))		\
+		odp_ ## name ## _module_constructor(void)
+
+/* All subsystems' initialization and termination routines are
+ * the same, provide template to help generate similar routines
+ * automatically, examples:
+ *
+ * ODP_SUBSYSTEM_FOREACH_TEMPLATE(subsystem, init_global, DBG)
+ * will generate a function walk through all the modules of the
+ * subsystem and invoke init_global method for each.
+ */
+#define ODP_SUBSYSTEM_FOREACH_TEMPLATE(subs, method, print)	\
+static int odp_ ## subs ##_## method(bool continue_on_errors)	\
+{								\
+	int result = 0;						\
+	ODP_MODULE_CLASS(subs) * mod = NULL;			\
+								\
+	odp_subsystem_lock(read, subs);				\
+	odp_subsystem_foreach_module(subs, mod) {		\
+		result = mod->base.method ?			\
+				mod->base.method() : 0;		\
+		if (result < 0) {				\
+			print("error %d to %s subsystem %s "	\
+			      "module %s.\n", result, #method,	\
+			      __subsystem(subs).name,		\
+			      mod->base.name);			\
+								\
+			if (continue_on_errors)			\
+				continue;			\
+			else					\
+				goto done;			\
+		}						\
+	}							\
+done:								\
+	odp_subsystem_unlock(read, subs);			\
+	return result;						\
+}
+
+/* Subsystem Modules Registration
+ *
+ * odp_subsystem_register_module() are called by all modules in their
+ * constructors, whereas the modules could be:
+ *
+ * 1) built as static libraries(.a) and linked statically, or
+ *    built as shared libraries(.so) and linked dynamically.
+ *
+ *    odp_subsystem_register_module() should complete the whole
+ *    registration session and link the module into subsystem's
+ *    module array.
+ *
+ * 2) built as shared libraries(.so) and 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:
+ *	odp_module_loader_start();
+ *	......
+ *	for each module
+ *		handler = dlopen(module);
+ *		// The module constructor runs before dlopen() returns
+ *		// which in turn calls odp_subsystem_register_module()
+ *		if (handler is valid)
+ *			odp_module_install(handler);
+ *		else
+ *			odp_module_abandon();
+ *      ......
+ *	odp_module_loader_end();
+ */
+
+void odp_module_loader_start(void);
+void odp_module_loader_end(void);
+
+int odp_module_install(void *, bool active);
+int odp_module_abandon(void);
+
+#define __maybe_unused __attribute__((unused))
+static inline void __subsystem_set_active(
+	odp_subsystem_t *subsystem __maybe_unused,
+		odp_module_base_t *module __maybe_unused)
+{
+#if defined(IM_ACTIVE_MODULE)
+	subsystem->active = module;
+#endif
+}
+
+int __subsystem_register_module(
+	odp_subsystem_t *, odp_module_base_t *);
+
+/* Macro to allow polymorphism on module classes */
+#define odp_subsystem_register_module(name, module)		\
+({								\
+	odp_module_base_t *base = &(module)->base;		\
+	__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..54a174ea 100644
--- a/include/odp/api/spec/atomic.h
+++ b/include/odp/api/spec/atomic.h
@@ -59,6 +59,12 @@  extern "C" {
  * Atomic 32-bit unsigned integer
  */
 
+/**
+ * @def ODP_ATOMIC_INIT
+ * Compile-time atomic initializer:
+ * odp_atomic_xx_t atomic = ODP_ATOMIC_INIT(v);
+ */
+
 /*
  * 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..8816b6e7 100644
--- a/include/odp/api/spec/rwlock.h
+++ b/include/odp/api/spec/rwlock.h
@@ -36,6 +36,11 @@  extern "C" {
  * ODP reader/writer lock
  */
 
+/**
+ * @def ODP_RWLOCK_UNLOCKED
+ * Compile-time RW lock initializer:
+ * odp_rwlock_t lock = ODP_RWLOCK_UNLOCKED;
+ */
 
 /**
  * Initialize a reader/writer lock.
diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index ba4a31ea..4a7b77b8 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)/odp_module.h
+
+__LIB__libodp_linux_la_SOURCES += ../../frameworks/modular/odp_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