From patchwork Mon Jul 3 08:00:08 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: 106887 Delivered-To: patch@linaro.org Received: by 10.140.101.44 with SMTP id t41csp5485973qge; Mon, 3 Jul 2017 01:02:29 -0700 (PDT) X-Received: by 10.200.33.209 with SMTP id 17mr1601833qtz.189.1499068949344; Mon, 03 Jul 2017 01:02:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1499068949; cv=none; d=google.com; s=arc-20160816; b=lPdZdd10tSwelLacD8G+8m9pYkrZWx60TvXo2I9xlLHf8za7LBq9iVY8umBPp5RsmA mLOIvTzPS9hXrSSh5hA67naLMp/xFkIG+E1pVRYkrZnx8qBLjgx9MpVdS8a+5AHbziPT XpsvmWbJHdtmtKquT6wRDccIIVR+8CwH4k8KR4yFDWvK/UoAht5Ks47WQhqoCgJo3a+v oFUEm/AZFiZk0uQvdDeW9rvybiFkoDuEBtLy9K7hGxUNXs240vOhg0InzW6B66yyufim bZ21zlutcsS94zlhLJ2kzouIcxlDHJOUfKjFZYVpEIIi2IKlaNFklB+nRz/KzKQ/pexp m66Q== 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=OZ/Zi6XlWBxnDmlQcgNcGZ8mR6j5Pm5vOCQteMZIGbY=; b=iMb5lzSUZBsMJddr6xb781EfewD46jxAOqe+0DZQat4kmcFlvxi12lbxRzT5cHhRbg 1YeN4GbK2cXvWA+Op++Y1ndGiG+3SmgCrBMEJvLXZR1ZyrMPZZEMtKSsTnK0y5DrDCfJ V62hSW9zOOpTJkM1ZW7Fdi0MMbn5i6Y4hHwpOhAB3hZrlJ75TiXIaqi8WIj509B3LdzX aWiaKw9z+Bz5qL9I1xradOuvvztEFPfwkoiayqtet2m1RjqtYcO0azajh3/7LaGX2GTA ICTUg0uEb8Q90O2O+3Qg+QNFNhNZoJxstNE7GwKMLYYXb0YOLnW5o6G0ie+9lLfaGm5G hzeQ== 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 n73si14147562qkl.323.2017.07.03.01.02.29; Mon, 03 Jul 2017 01:02:29 -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 ED980608B8; Mon, 3 Jul 2017 08: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 D6500609F8; Mon, 3 Jul 2017 08:00:52 +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 4DECA6071E; Mon, 3 Jul 2017 08:00:33 +0000 (UTC) Received: from forward4p.cmail.yandex.net (forward4p.cmail.yandex.net [77.88.31.19]) by lists.linaro.org (Postfix) with ESMTPS id AABBD609F8 for ; Mon, 3 Jul 2017 08:00:19 +0000 (UTC) Received: from smtp2m.mail.yandex.net (smtp2m.mail.yandex.net [IPv6:2a02:6b8:0:2519::122]) by forward4p.cmail.yandex.net (Yandex) with ESMTP id B299D20EAE for ; Mon, 3 Jul 2017 11:00:17 +0300 (MSK) Received: from smtp2m.mail.yandex.net (localhost.localdomain [127.0.0.1]) by smtp2m.mail.yandex.net (Yandex) with ESMTP id D99642300D70 for ; Mon, 3 Jul 2017 11:00:16 +0300 (MSK) Received: by smtp2m.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id CsmfCNkM5d-0FNe86kF; Mon, 03 Jul 2017 11:00:15 +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, 3 Jul 2017 11:00:08 +0300 Message-Id: <1499068809-20327-2-git-send-email-odpbot@yandex.ru> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1499068809-20327-1-git-send-email-odpbot@yandex.ru> References: <1499068809-20327-1-git-send-email-odpbot@yandex.ru> Github-pr-num: 65 Subject: [lng-odp] [PATCH CLOUD-DEV v1] [RFC 1/2] 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 runtime 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: f0f96a26d22e16b9299777cd413dfd6ae89a024e **/ modular-framework/list.h | 620 +++++++++++++++++++++ modular-framework/module.c | 158 ++++++ modular-framework/module.h | 205 +++++++ modular-framework/rwlock.h | 88 +++ platform/linux-generic/Makefile.am | 11 + .../include/odp/api/plat/atomic_types.h | 2 + .../include/odp/api/plat/rwlock_types.h | 2 + 7 files changed, 1086 insertions(+) create mode 100644 modular-framework/list.h create mode 100644 modular-framework/module.c create mode 100644 modular-framework/module.h create mode 100644 modular-framework/rwlock.h diff --git a/modular-framework/list.h b/modular-framework/list.h new file mode 100644 index 00000000..366d9f38 --- /dev/null +++ b/modular-framework/list.h @@ -0,0 +1,620 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include + +#if defined(__STDC__) +#define typeof __typeof__ +#endif + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ + +#define LIST_POISON1 ((void *) 0x100) +#define LIST_POISON2 ((void *) 0x200) + +/* + * 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); \ +}) + +#define container_of(ptr, type, member) \ +({ \ + typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type, member) ); \ +}) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + WRITE_ONCE(list->next, list); + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + WRITE_ONCE(prev->next, new); +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + WRITE_ONCE(prev->next, next); +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void list_del(struct list_head *entry) +{ + __list_del_entry(entry); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del_entry(entry); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del_entry(list); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del_entry(list); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return READ_ONCE(head->next) == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +/** + * list_rotate_left - rotate the list to the left + * @head: the head of the list + */ +static inline void list_rotate_left(struct list_head *head) +{ + struct list_head *first; + + if (!list_empty(head)) { + first = head->next; + list_move_tail(first, head); + } +} + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +static inline int list_is_singular(const struct list_head *head) +{ + return !list_empty(head) && (head->next == head->prev); +} + +static inline void __list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + struct list_head *new_first = entry->next; + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; +} + +/** + * list_cut_position - cut a list into two + * @list: a new list to add all removed entries + * @head: a list with entries + * @entry: an entry within head, could be the head itself + * and if so we won't cut the list + * + * This helper moves the initial part of @head, up to and + * including @entry, from @head to @list. You should + * pass on @entry an element you know is on @head. @list + * should be an empty list or a list you do not care about + * losing its data. + * + */ +static inline void list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + if (list_empty(head)) + return; + if (list_is_singular(head) && + (head->next != entry && head != entry)) + return; + if (entry == head) + INIT_LIST_HEAD(list); + else + __list_cut_position(list, head, entry); +} + +static inline void __list_splice(const struct list_head *list, + struct list_head *prev, + struct list_head *next) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +/** + * list_splice - join two lists, this is designed for stacks + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(const struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head, head->next); +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head->prev, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head, head->next); + INIT_LIST_HEAD(list); + } +} + +/** + * list_splice_tail_init - join two lists and reinitialise the emptied list + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head->prev, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_last_entry - get the last element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +/** + * list_first_entry_or_null - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note that if the list is empty, it returns NULL. + */ +#define list_first_entry_or_null(ptr, type, member) ({ \ + struct list_head *head__ = (ptr); \ + struct list_head *pos__ = READ_ONCE(head__->next); \ + pos__ != head__ ? list_entry(pos__, type, member) : NULL; \ +}) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** + * list_prev_entry - get the prev element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, typeof(*(pos)), member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_prev_entry(pos, member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_head within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_prev_entry(pos, member); \ + &pos->member != (head); \ + pos = list_prev_entry(pos, member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_continue - continue list iteration safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_next_entry(pos, member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_from - iterate over list from current point safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member), \ + n = list_prev_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_prev_entry(n, member)) + +/** + * list_safe_reset_next - reset a stale list_for_each_entry_safe loop + * @pos: the loop cursor used in the list_for_each_entry_safe loop + * @n: temporary storage used in list_for_each_entry_safe + * @member: the name of the list_head within the struct. + * + * list_safe_reset_next is not safe to use in general if the list may be + * modified concurrently (eg. the lock is dropped in the loop body). An + * exception to this is if the cursor element (pos) is pinned in the list, + * and list_safe_reset_next is called after re-taking the lock and before + * completing the current iteration of the loop body. + */ +#define list_safe_reset_next(pos, n, member) \ + n = list_next_entry(pos, member) + +#endif diff --git a/modular-framework/module.c b/modular-framework/module.c new file mode 100644 index 00000000..6f80d938 --- /dev/null +++ b/modular-framework/module.c @@ -0,0 +1,158 @@ +/* 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 { + rwlock_t lock; + subsystem_t *subsystem; + module_base_t *module; +} registration = { + .lock = RW_LOCK_UNLOCKED(lock), + .subsystem = NULL, + .module = NULL, +}; + +#define REGISTRATION_SANITY_CHECK(subsystem, module) \ +do { \ + if (subsystem == NULL || module == NULL) \ + return -ENOENT; \ + \ + if (!list_empty(&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 */ + rwlock_write_lock(®istration.lock); + + /* Block the subsystem API calls in load new + * implementation modules. */ + rwlock_write_lock(&subsystem->lock); + module->handler = NULL; /* no DSO handler */ + list_add(&module->list, &subsystem->modules); + rwlock_write_unlock(&subsystem->lock); + + 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 (rwlock_write_trylock(®istration.lock) == 0) { + registration.subsystem = subsystem; + registration.module = module; + return 0; + } + + rwlock_write_unlock(®istration.lock); + return -EACCES; +} + +void module_loader_start(void) +{ + 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; + + rwlock_write_unlock(®istration.lock); +} + +int module_install_dso(void *dso) +{ + /* Bottom halves of the registration, context exclusion + * is guarenteed by module_loader_start() + */ + if (0 == rwlock_write_trylock(®istration.lock)) { + subsystem_t *subsystem = registration.subsystem; + module_base_t *module = registration.module; + + if (subsystem != NULL && module != NULL) { + rwlock_write_lock(&subsystem->lock); + module->handler = dso; + list_add(&module->list, &subsystem->modules); + rwlock_write_unlock(&subsystem->lock); + } + + registration.subsystem = NULL; + registration.module = NULL; + return 0; + } + + rwlock_write_unlock(®istration.lock); + return -EACCES; +} + +int module_abandon_dso(void) +{ + /* Bottom halves of the registration, context exclusion + * is guarenteed by module_loader_start() + */ + if (0 == rwlock_write_trylock(®istration.lock)) { + registration.subsystem = NULL; + registration.module = NULL; + return 0; + } + + 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/modular-framework/module.h b/modular-framework/module.h new file mode 100644 index 00000000..e26ca3aa --- /dev/null +++ b/modular-framework/module.h @@ -0,0 +1,205 @@ +/* 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 "list.h" +#include "rwlock.h" + +typedef struct { + rwlock_t lock; + uint32_t version; + const char *name; + const char *description; + struct list_head modules; + struct list_head *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 = RW_LOCK_UNLOCKED(lock), \ + .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 { \ + rwlock_init(&subsystem(name).lock); \ + INIT_LIST_HEAD(&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) \ + rwlock_ ##access## _lock(&subsystem(name).lock) + +#define subsystem_unlock(access, name) \ + rwlock_ ##access## _unlock(&subsystem(name).lock) + +#define subsystem_foreach_module(name, mod) \ + list_for_each_entry(mod, &subsystem(name).modules, list) + +#define MODULE_CLASS(subsystem) \ + struct subsystem ## _module { \ + struct list_head 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; + +/* 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() : -1; \ + 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 *); +extern int module_abandon_dso(void); + +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); \ +}) + +#endif diff --git a/modular-framework/rwlock.h b/modular-framework/rwlock.h new file mode 100644 index 00000000..108a35b2 --- /dev/null +++ b/modular-framework/rwlock.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2017, ARM Limited. All rights reserved. + * + * Copyright (c) 2017, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RWLOCK_H +#define RWLOCK_H + +/* Get ride on ODP rwlock implementation and in meanwhile + * keep the modular framework as generic as possible, and + * can be re-used in other projects. + * + * Assume that ODP rwlock facility will be available at the + * beginning of the world without any initialization. + */ +#include + +typedef odp_rwlock_t rwlock_t; +#define RW_LOCK_UNLOCKED(lock) ODP_RW_LOCK_UNLOCK(lock) + +#if defined(BUILD_ODP_RWLOCK) + /* only defined in odp rwlock source to link the implementation */ + #define _ALIAS(impl, api) __attribute__((weak, alias(#impl))) api + #define ODP_ALIAS(api) _ALIAS(odp_ ## api, api) +#else + /* other objects only see declarations */ + #define ODP_ALIAS(api) api +#endif + +/** + * Initialize a reader/writer lock. + * + * @param rwlock Pointer to a reader/writer lock + */ +void ODP_ALIAS(rwlock_init)(rwlock_t *rwlock); + +/** + * Acquire read permission on a reader/writer lock. + * + * @param rwlock Pointer to a reader/writer lock + */ +void ODP_ALIAS(rwlock_read_lock)(rwlock_t *rwlock); + +/** + * Try to acquire read permission to a reader/writer lock. + * + * @param rwlock Pointer to a reader/writer lock + * + * @retval 0 Lock was not available for read access + * @retval !0 Read access to lock acquired + */ +int ODP_ALIAS(rwlock_read_trylock)(rwlock_t *rwlock); + +/** + * Release read permission on a reader/writer lock. + * + * @param rwlock Pointer to a reader/writer lock + */ +void ODP_ALIAS(rwlock_read_unlock)(rwlock_t *rwlock); + +/** + * Acquire write permission on a reader/writer lock. + * + * @param rwlock Pointer to a reader/writer lock + */ +void ODP_ALIAS(rwlock_write_lock)(rwlock_t *rwlock); + +/** + * Try to acquire write permission to a reader/writer lock. + * + * @param rwlock Pointer to a reader/writer lock + * + * @retval 0 Lock was not available for write access + * @retval !0 Write access to lock acquired + */ +int ODP_ALIAS(rwlock_write_trylock)(rwlock_t *rwlock); + +/** + * Release write permission on a reader/writer lock. + * + * @param rwlock Pointer to a reader/writer lock + */ +void ODP_ALIAS(rwlock_write_unlock)(rwlock_t *rwlock); + +#endif /* RWLOCK_H */ diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am index ba4a31ea..3fc032da 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)/modular-framework AM_CFLAGS += -I$(top_srcdir)/include/odp/arch/@ARCH_ABI@ AM_CFLAGS += -I$(top_builddir)/include AM_CFLAGS += -Iinclude @@ -292,6 +293,16 @@ if HAVE_PCAP __LIB__libodp_linux_la_SOURCES += pktio/pcap.c endif +modularframeworkdir = $(top_srcdir)/modular-framework +noinst_HEADERS += $(modularframeworkdir)/list.h \ + $(modularframeworkdir)/rwlock.h \ + $(modularframeworkdir)/module.h + +__LIB__libodp_linux_la_SOURCES += ../../modular-framework/module.c + +odp_rwlock.o odp_rwlock.lo : \ + CFLAGS += -DBUILD_ODP_RWLOCK -include $(modularframeworkdir)/rwlock.h + __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..a69a5f00 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_RW_LOCK_UNLOCK(lock) { .cnt = ODP_ATOMIC_INIT(0) } + #ifdef __cplusplus } #endif