BAL and Maple Release 2.2
Signed-off-by: Shad Ansari <developer@Carbon.local>
diff --git a/bal_release/src/common/db_engine/Makefile b/bal_release/src/common/db_engine/Makefile
new file mode 100644
index 0000000..23b64f6
--- /dev/null
+++ b/bal_release/src/common/db_engine/Makefile
@@ -0,0 +1,36 @@
+###############################################################################
+#
+# <:copyright-BRCM:2016:DUAL/GPL:standard
+#
+# Copyright (c) 2016 Broadcom
+# All Rights Reserved
+#
+# Unless you and Broadcom execute a separate written software license
+# agreement governing use of this software, this software is licensed
+# to you under the terms of the GNU General Public License version 2
+# (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+# with the following added to such license:
+#
+# As a special exception, the copyright holders of this software give
+# you permission to link this software with independent modules, and
+# to copy and distribute the resulting executable under terms of your
+# choice, provided that you also meet, for each linked independent
+# module, the terms and conditions of the license of that module.
+# An independent module is a module which is not derived from this
+# software. The special exception does not apply to any modifications
+# of the software.
+#
+# Not withstanding the above, under no circumstances may you combine
+# this software in any way with any other Broadcom software provided
+# under a license other than the GPL, without Broadcom's express prior
+# written consent.
+#
+# :>
+#
+###############################################################################
+# Data Base engine
+#
+MOD_NAME = db
+MOD_TYPE = lib
+
+srcs = bcm_db_engine.c
diff --git a/bal_release/src/common/db_engine/bcm_db_engine.c b/bal_release/src/common/db_engine/bcm_db_engine.c
new file mode 100644
index 0000000..dc4f19a
--- /dev/null
+++ b/bal_release/src/common/db_engine/bcm_db_engine.c
@@ -0,0 +1,1606 @@
+/******************************************************************************
+ *
+ * <:copyright-BRCM:2016:DUAL/GPL:standard
+ *
+ * Copyright (c) 2016 Broadcom
+ * All Rights Reserved
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed
+ * to you under the terms of the GNU General Public License version 2
+ * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+ * with the following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give
+ * you permission to link this software with independent modules, and
+ * to copy and distribute the resulting executable under terms of your
+ * choice, provided that you also meet, for each linked independent
+ * module, the terms and conditions of the license of that module.
+ * An independent module is a module which is not derived from this
+ * software. The special exception does not apply to any modifications
+ * of the software.
+ *
+ * Not withstanding the above, under no circumstances may you combine
+ * this software in any way with any other Broadcom software provided
+ * under a license other than the GPL, without Broadcom's express prior
+ * written consent.
+ *
+ * :>
+ *
+ *****************************************************************************/
+
+/*
+ * Data Base Engine
+ *
+ * Proprietary and confidential.
+ */
+#include <bcmos_system.h>
+#include <bcm_db_engine.h>
+
+/* DB trace level */
+bcmos_trace_level bcmdb_trace_level = BCMOS_TRACE_LEVEL_ERROR;
+
+#define BCMDB_TRACE(level, fmt, args...) \
+ do { \
+ if (level <= bcmdb_trace_level) \
+ bcmos_trace(level, "%s#%d> " fmt, __FUNCTION__, __LINE__, ## args); \
+ } while (0)
+
+/* Print error trace conditionally, depending on the current trace level */
+#define BCMDB_TRACE_ERROR(fmt, args...) \
+ BCMDB_TRACE(BCMOS_TRACE_LEVEL_ERROR, "DB ERR: %s#%d> " fmt, __FUNCTION__, __LINE__, ## args)
+
+/* Print warning trace conditionally, depending on the current trace level */
+#define BCMDB_TRACE_WARNING(fmt, args...) \
+ BCMDB_TRACE(BCMOS_TRACE_LEVEL_WARNING, "DB WARN: %s#%d> " fmt, __FUNCTION__, __LINE__, ## args)
+
+/* Print info trace conditionally, depending on the current trace level */
+#define BCMDB_TRACE_INFO(fmt, args...) \
+ BCMDB_TRACE(BCMOS_TRACE_LEVEL_INFO, "DB INFO: %s#%d> " fmt, __FUNCTION__, __LINE__, ## args)
+
+/* Enable debug prints */
+#define BCMDB_DEBUG
+
+#ifdef BCMDB_DEBUG
+
+/* Print debug trace conditionally, depending on the current trace level */
+#define BCMDB_TRACE_DEBUG(fmt, args...) \
+ BCMDB_TRACE(BCMOS_TRACE_LEVEL_DEBUG, "DB DBG: %s#%d> " fmt, __FUNCTION__, __LINE__, ## args)
+
+#else
+
+#define BCMDB_TRACE_DEBUG(fmt, args...)
+
+#endif
+
+
+
+/** Data base entry
+ * \ingroup bcmdb
+ */
+struct bcmdb_entry
+{
+ void *data; /* Set or record */
+ uint8_t flags;
+#define BCMDB_FLAG_VALID 0x01 /**< Entry is valid */
+#define BCMDB_FLAG_RECORD 0x02 /**< Record */
+#define BCMDB_FLAG_SOS 0x10 /**< Set of sets */
+ uint8_t read_count;
+ uint8_t write_pending;
+ uint8_t ffu;
+};
+
+/** Set/Record change notification
+ * \ingroup bcmdb
+ */
+typedef struct bcmdb_notify
+{
+ struct bcmdb_notify *next;
+ bcmdb_notify_cb cb;
+ long cb_priv;
+} bcmdb_notify;
+
+/** Data Base Set control block
+ * \ingroup bcmdb
+ */
+struct bcmdb_set
+{
+ bcmdb_entry entry; /**< Common fields for set and record */
+ char *name; /**< Set name */
+ bcmdb_set *parent; /**< Set parent */
+ bcmdb_key my_key; /**< Key in the parent set */
+ int max_entries; /**< Max number of elements in the set. -1=inlimited */
+ int num_entries; /**< Current number of elements in the set */
+ int entry_size; /**< Set entry size. */
+ int magic; /**< Magic number */
+#define BCMDB_MAGIC_ACTIVE_SET (('a'<<24) | ('s'<<16) | ('e'<<8) | 't')
+#define BCMDB_MAGIC_FREE_SET (('f'<<24) | ('s'<<16) | ('e'<<8) | 't')
+
+ /** Get next element */
+ bcmdb_key (*entry_get_next)(const bcmdb_set *this, bcmdb_key cur);
+
+ /** Add new entry. returns 0 if ok */
+ int (*entry_new)(bcmdb_set *this, bcmdb_key key, const void *data);
+
+ /** Delete entry */
+ int (*entry_delete)(bcmdb_set *this, bcmdb_entry *entry);
+
+ /*
+ * Handle – key mapping
+ */
+
+ /** Convert entry handle to entry key */
+ bcmdb_key (*handle_to_key)(const bcmdb_set *this, const bcmdb_entry *entry);
+
+ /** Convert entry key to entry handle */
+ bcmdb_entry *(*key_to_handle)(const bcmdb_set *this, bcmdb_key key);
+
+ /*
+ * Set/Record locking
+ * entry is handle of the set or record to be locked/unlocked
+ */
+
+ /** Lock the whole set for reading */
+ void (*lock_set_read)(bcmdb_set *set);
+
+ /** Unlock set locked for reading */
+ void (*unlock_set_read)(bcmdb_set *set);
+
+ /** Lock the whole set for update */
+ long (*lock_set_modify)(bcmdb_set *set);
+
+ /** Unlock set locked for update */
+ void (*unlock_set_modify)(bcmdb_set *set, long fastlock_flags);
+
+ /** Lock the set recursively for update (SoS only) */
+ void (*lock_set_recursively_modify)(bcmdb_set *set);
+
+ /** Unlock recursively set locked for update (SoS only) */
+ void (*unlock_set_recursively_modify)(bcmdb_set *set);
+
+ /** Lock record for reading */
+ void *(*lock_record_read)(bcmdb_set *set, bcmdb_key key);
+
+ /** Release read lock */
+ void (*unlock_record_read)(bcmdb_set *set, bcmdb_key key);
+
+ /** Lock record for modification. */
+ void *(*lock_record_write)(bcmdb_set *set, bcmdb_key key, int is_deletion);
+
+ /** Release modify lock */
+ void (*unlock_record_write)(bcmdb_set *set, int is_deletion, int is_cancellation);
+
+ /** Format function that converts record data to human-readable form */
+ bcmdb_format_cb format;
+
+ /** Update notification mechanism.\n
+ * Note that notification functions are called before the actual update of the data base, so that
+ * there is an option to abort the update if needed.
+ */
+ bcmdb_notify *notify_list_head;
+
+ /** Shadow data record */
+ void *shadow_data;
+
+ /** holds the pointer to a write-locked entry */
+ bcmdb_entry *write_locked_entry ;
+
+ /** mutex that prevents multiple simultaneous updates */
+ bcmos_mutex mutex_update;
+
+ /** semaphore that holds update while there are unfinished reads */
+ bcmos_sem sem_wait_read_to_finish;
+
+ /** fastlock */
+ bcmos_fastlock fastlock;
+};
+
+
+/**< Data base record
+ * \ingroup bcmdb
+ */
+struct bcmdb_record
+{
+ struct bcmdb_entry e; /**< Entry - common part for set and record */
+};
+
+/*
+ * DB backend callbacks
+ */
+
+/*
+ * Array-based backend
+ */
+
+/** Get next element */
+static bcmdb_key _bcmdb_array_entry_get_next(const bcmdb_set *this, bcmdb_key key)
+{
+ BUG_ON(!this->entry.data);
+
+ if (key < 0)
+ key = 0;
+ else
+ ++key;
+
+ while((unsigned)key<this->max_entries)
+ {
+ bcmdb_entry *entry = (bcmdb_entry *)this->entry.data + key;
+ if ((entry->flags & BCMDB_FLAG_VALID))
+ break;
+ ++key;
+ }
+
+ if ((unsigned)key >= this->max_entries)
+ return BCMDB_KEY_NO_MORE; /* no more */
+
+ return key;
+}
+
+/*
+ * Handle – key mapping
+ */
+
+/** Convert entry handle to entry key */
+static inline bcmdb_key _bcmdb_array_handle_to_key(const bcmdb_set *this, const bcmdb_entry *entry)
+{
+ bcmdb_key key;
+
+ BUG_ON(!this);
+ BUG_ON(!entry);
+ BUG_ON(!this->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!this->entry.data);
+
+ key = entry - (bcmdb_entry *)this->entry.data;
+ if ((unsigned)key >= this->max_entries)
+ return BCMDB_KEY_INVAL;
+
+ return key;
+}
+
+
+/** Convert entry key to entry handle */
+static inline bcmdb_entry *_bcmdb_array_key_to_handle(const bcmdb_set *this, bcmdb_key key)
+{
+ BUG_ON(!this);
+ BUG_ON(!this->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!this->entry.data);
+
+ if ((unsigned long)key >= this->max_entries)
+ return NULL;
+
+ return (bcmdb_entry *)this->entry.data + key;
+}
+
+
+/** sem-based set read-lock */
+static void bcmdb_set_lock_read_sem(bcmdb_set *set)
+{
+ bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+}
+
+
+/** sem-based set read-unlock */
+static void bcmdb_set_unlock_read_sem(bcmdb_set *set)
+{
+ bcmos_mutex_unlock(&set->mutex_update);
+}
+
+
+/** sem-based set modify-lock */
+static long bcmdb_set_lock_modify_sem(bcmdb_set *set)
+{
+ bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+ return 0;
+}
+
+
+/** sem-based set modify-unlock */
+static void bcmdb_set_unlock_modify_sem(bcmdb_set *set, long fastlock_flags)
+{
+ bcmos_mutex_unlock(&set->mutex_update);
+}
+
+
+/** helper function which recursively locks all subsets of SoS
+ * for modify */
+static void bcmdb_recursive_subsets_lock_modify(bcmdb_set *sos)
+{
+ int key;
+ int left_entries = sos->num_entries;
+ bcmdb_entry *entry;
+ bcmdb_set *subset;
+
+ BUG_ON(!(sos->entry.flags & BCMDB_FLAG_SOS));
+
+ for (key = 0; key < sos->max_entries; ++key)
+ {
+ entry = sos->key_to_handle(sos, key);
+
+ if ((entry->flags & BCMDB_FLAG_VALID))
+ {
+ subset = (bcmdb_set *)entry->data;
+ subset->lock_set_recursively_modify(subset);
+
+ --left_entries;
+ /* if we handled all subsets we can break the "for" */
+ if (left_entries==0)
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+/** helper function which recursively unlocks all subsets of SoS
+ * for modify */
+static void bcmdb_recursive_subsets_unlock_modify(bcmdb_set *sos)
+{
+ int key;
+ int left_entries = sos->num_entries;
+ bcmdb_entry *entry;
+ bcmdb_set *subset;
+
+ BUG_ON(!(sos->entry.flags & BCMDB_FLAG_SOS));
+
+ for (key = 0; key < sos->max_entries; ++key)
+ {
+ entry = sos->key_to_handle(sos, key);
+
+ if ((entry->flags & BCMDB_FLAG_VALID))
+ {
+ subset = (bcmdb_set *)entry->data;
+ subset->unlock_set_recursively_modify(subset);
+
+ --left_entries;
+ /* if we handled all subsets we can break the "for" */
+ if (left_entries==0)
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+/** sem-based set modify-lock recursively */
+static void bcmdb_set_lock_recursively_modify_sem(bcmdb_set *set)
+{
+ BCMDB_TRACE_DEBUG("lock set modify recursively: %s\n", set->name);
+ bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+ BCMDB_TRACE_DEBUG("mutex was locked\n");
+
+ if ((set->entry.flags & BCMDB_FLAG_SOS))
+ {
+ BCMDB_TRACE_DEBUG("going to lock the subsets\n");
+ bcmdb_recursive_subsets_lock_modify(set);
+ }
+}
+
+
+/** sem-based set modify-unlock recursively */
+static void bcmdb_set_unlock_recursively_modify_sem(bcmdb_set *set)
+{
+ BCMDB_TRACE_DEBUG("unlock set modify recursively: %s\n", set->name);
+
+ if ((set->entry.flags & BCMDB_FLAG_SOS))
+ {
+ BCMDB_TRACE_DEBUG("going to unlock the subsets\n");
+ bcmdb_recursive_subsets_unlock_modify(set);
+ }
+
+ bcmos_mutex_unlock(&set->mutex_update);
+ BCMDB_TRACE_DEBUG("mutex was unlocked\n");
+}
+
+
+/** NB-read-sem-write policy: set read lock */
+static void bcmdb_set_lock_read__nb_read_sem_write(bcmdb_set *set)
+{
+ long flags;
+
+ BCMDB_TRACE_DEBUG("lock set read: %s\n", set->name);
+ flags = bcmos_fastlock_lock(&set->fastlock);
+ ++set->entry.read_count;
+ BCMDB_TRACE_DEBUG("read_count is now: %u\n", set->entry.read_count);
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+}
+
+
+/** NB-read-sem-write policy: set read unlock */
+static void bcmdb_set_unlock_read__nb_read_sem_write(bcmdb_set *set)
+{
+ long flags;
+
+ BCMDB_TRACE_DEBUG("unlock set read: %s\n", set->name);
+
+ flags = bcmos_fastlock_lock(&set->fastlock);
+ BUG_ON(!set->entry.read_count);
+ if (!(--set->entry.read_count) &&
+ set->entry.write_pending)
+ {
+ BCMDB_TRACE_DEBUG("going to wake pending writer\n");
+
+ set->entry.write_pending = 0;
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+ bcmos_sem_post(&set->sem_wait_read_to_finish);
+ }
+ else
+ {
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+ }
+}
+
+
+/** NB-read-sem-write policy: set modify lock */
+static long bcmdb_set_lock_modify__nb_read_sem_write(bcmdb_set *set)
+{
+ long flags;
+
+ BCMDB_TRACE_DEBUG("lock set modify: %s\n", set->name);
+ bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+ BCMDB_TRACE_DEBUG("mutex was locked\n");
+
+ while(1)
+ {
+ flags = bcmos_fastlock_lock(&set->fastlock);
+ if (!set->entry.read_count)
+ break;
+ /* Wait until read is completed */
+ set->entry.write_pending = 1;
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+ bcmos_sem_wait(&set->sem_wait_read_to_finish, BCMOS_WAIT_FOREVER);
+ }
+ /* At this point fastlock is taken and there are no pending reads */
+
+ BCMDB_TRACE_DEBUG("fastlock is taken, no active reads\n");
+
+ return flags;
+}
+
+
+/** NB-read-sem-write policy: set modify unlock */
+static void bcmdb_set_unlock_modify__nb_read_sem_write(bcmdb_set *set, long fastlock_flags)
+{
+ BCMDB_TRACE_DEBUG("unlock set modify: %s\n", set->name);
+ bcmos_fastlock_unlock(&set->fastlock, fastlock_flags);
+ bcmos_mutex_unlock(&set->mutex_update);
+ BCMDB_TRACE_DEBUG("mutex was unlocked\n");
+}
+
+
+/** sem-read/sem-write policy: lock entry */
+static void *bcmdb_sem_read_sem_write_lock(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+ bcmdb_entry *entry;
+ bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+
+ if (is_deletion)
+ {
+ /* there is nothing to return in deletion case */
+ return NULL;
+ }
+
+ entry = set->key_to_handle(set, key);
+ if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+ {
+ bcmos_mutex_unlock(&set->mutex_update);
+ return NULL;
+ }
+ return entry->data;
+}
+
+/** sem-read/sem-write policy: unlock entry */
+static void bcmdb_sem_read_sem_write_unlock(bcmdb_set *set)
+{
+ bcmos_mutex_unlock(&set->mutex_update);
+}
+
+/** sem-read/sem-write policy: lock entry for reading */
+static void *bcmdb_sem_read_sem_write_lock_read(bcmdb_set *set, bcmdb_key key)
+{
+ return bcmdb_sem_read_sem_write_lock(set, key, 0) ;
+}
+
+/** sem-read/sem-write policy: unlock entry for reading */
+static void bcmdb_sem_read_sem_write_unlock_read(bcmdb_set *set, bcmdb_key key)
+{
+ bcmdb_sem_read_sem_write_unlock(set);
+}
+
+/** sem-read/sem-write policy: lock entry for writing */
+static void *bcmdb_sem_read_sem_write_lock_write(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+ return bcmdb_sem_read_sem_write_lock(set, key, is_deletion) ;
+}
+
+/** sem-read/sem-write policy: unlock entry for writing */
+static void bcmdb_sem_read_sem_write_unlock_write(bcmdb_set *set, int is_deletion, int is_cancellation)
+{
+ bcmdb_sem_read_sem_write_unlock(set);
+}
+
+/** non-blocking-read/shadow write policy: Lock entry for reading */
+static void *bcmdb_nb_read_shadow_write_lock_read(bcmdb_set *set, bcmdb_key key)
+{
+ bcmdb_entry *entry;
+ long flags;
+
+ flags = bcmos_fastlock_lock(&set->fastlock);
+ entry = set->key_to_handle(set, key);
+ if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+ {
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+ return NULL;
+ }
+ ++entry->read_count;
+
+ BCMDB_TRACE_DEBUG("lock record read: %s, key=%d new_read_count=%d\n", set->name, key, entry->read_count);
+
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+ return entry->data;
+}
+
+
+/** non-blocking-read/shadow write policy: Unlock entry for reading */
+static void bcmdb_nb_read_shadow_write_unlock_read(bcmdb_set *set, bcmdb_key key)
+{
+ bcmdb_entry *entry=set->key_to_handle(set, key);
+ long flags;
+ BUG_ON(!entry);
+
+ BCMDB_TRACE_DEBUG("unlock record read: %s, key=%d\n", set->name, key);
+
+ flags = bcmos_fastlock_lock(&set->fastlock);
+ /* If write is pending - finish it and release write lock */
+ BUG_ON(!entry->read_count);
+ if (!(--entry->read_count) && set->entry.write_pending)
+ {
+ BCMDB_TRACE_DEBUG("going to wake pending writer\n");
+
+ /* Write was pending. Release write task */
+ set->entry.write_pending = 0;
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+ bcmos_sem_post(&set->sem_wait_read_to_finish);
+ }
+ else
+ {
+ BCMDB_TRACE_DEBUG("read_count is now: %u\n", entry->read_count);
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+ }
+}
+
+/** non-blocking-read/shadow write policy: Lock entry for
+ * writing/deletion.
+ * returned value of NULL means error only in case that
+ * is_deletion is 0 */
+static void *bcmdb_nb_read_shadow_write_lock_write(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+ bcmdb_entry *entry;
+
+ BCMDB_TRACE_DEBUG("lock record write: %s, key=%d\n", set->name, key);
+
+ bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+ BCMDB_TRACE_DEBUG("mutex was locked\n");
+
+ if (is_deletion)
+ {
+ /* there is nothing to return in deletion case */
+ return NULL;
+ }
+
+ /* this check is needed since mutex_update is task-aware.
+ it is not allowed for a task to lock for writing a 2nd record before unlocking the first one. */
+ if (set->write_locked_entry)
+ {
+ BCMDB_TRACE_ERROR("there is already an entry locked for writing\n");
+ bcmos_mutex_unlock(&set->mutex_update);
+ return NULL;
+ }
+
+ entry = set->key_to_handle(set, key);
+ if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+ {
+ bcmos_mutex_unlock(&set->mutex_update);
+ return NULL;
+ }
+ /* Copy data to shadow entry */
+ memcpy(set->shadow_data, entry->data, set->entry_size);
+ set->write_locked_entry = entry;
+ return set->shadow_data;
+}
+
+/** non-blocking-read/shadow write policy: Unlock entry for
+ * writing/deletion */
+static void bcmdb_nb_read_shadow_write_unlock_write(bcmdb_set *set, int is_deletion, int is_cancellation)
+{
+ bcmdb_entry *entry = set->write_locked_entry;
+ long flags;
+ void *old_data;
+
+ /* no entry is locked */
+ BUG_ON(!entry);
+
+ BCMDB_TRACE_DEBUG("unlock record write: %s\n", set->name);
+
+ /* cancellation: no need to update the entry from the shadow (or to delete the entry in case of deletion). */
+ if (is_cancellation)
+ {
+ set->write_locked_entry = NULL;
+ bcmos_mutex_unlock(&set->mutex_update);
+ return;
+ }
+
+ while(1)
+ {
+ flags = bcmos_fastlock_lock(&set->fastlock);
+ /* Wait until neither record nor set are locked for reading */
+ if (!entry->read_count && !set->entry.read_count)
+ break;
+
+ /* Read lock is active. wait */
+
+ BCMDB_TRACE_DEBUG("read lock is active. going to sleep.\n");
+
+ set->entry.write_pending = 1;
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+ bcmos_sem_wait(&set->sem_wait_read_to_finish, BCMOS_WAIT_FOREVER);
+ }
+
+ /* At this point there is no read lock and fastlock is taken. */
+
+ BCMDB_TRACE_DEBUG("fastlock is taken, no active reads\n");
+
+ if (is_deletion)
+ {
+ /* delete the entry */
+ set->entry_delete(set, entry);
+ }
+ else
+ {
+ /* Exchange record data with shadow and release all locks. */
+ old_data = entry->data;
+ entry->data = set->shadow_data;
+ set->shadow_data = old_data;
+ set->write_locked_entry = NULL;
+ }
+
+ bcmos_fastlock_unlock(&set->fastlock, flags);
+ bcmos_mutex_unlock(&set->mutex_update);
+ BCMDB_TRACE_DEBUG("mutex was unlocked\n");
+}
+
+/** none policy: set read-lock */
+static inline void bcmdb_set_lock_read_dummy(bcmdb_set *set)
+{
+}
+
+/** none policy: set read-unlock */
+static inline void bcmdb_set_unlock_read_dummy(bcmdb_set *set)
+{
+}
+
+/** none policy: set modify-lock */
+static inline long bcmdb_set_lock_modify_dummy(bcmdb_set *set)
+{
+ return 0;
+}
+
+/** none policy: set modify-unlock */
+static inline void bcmdb_set_unlock_modify_dummy(bcmdb_set *set, long fastlock_flags)
+{
+}
+
+/** none policy: set modify-lock recursively */
+static void bcmdb_set_lock_recursively_modify_dummy(bcmdb_set *set)
+{
+}
+
+/** none policy: set modify-unlock recursively */
+static void bcmdb_set_unlock_recursively_modify_dummy(bcmdb_set *set)
+{
+}
+
+/** none policy: record lock */
+static inline void *bcmdb_dummy_lock(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+ bcmdb_entry *entry;
+
+ /* there is nothing to return in deletion case */
+ if (is_deletion)
+ return NULL;
+
+ entry = set->key_to_handle(set, key);
+ if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+ return NULL;
+ return entry->data;
+}
+
+/** none policy: record unlock */
+static inline void bcmdb_dummy_unlock(bcmdb_set *set)
+{
+}
+
+/** none policy: record lock for reading */
+static inline void *bcmdb_dummy_lock_read(bcmdb_set *set, bcmdb_key key)
+{
+ return bcmdb_dummy_lock(set, key, 0);
+}
+
+/** none policy: record unlock for reading */
+static inline void bcmdb_dummy_unlock_read(bcmdb_set *set, bcmdb_key key)
+{
+ bcmdb_dummy_unlock(set);
+}
+
+/** none policy: record lock for writing */
+static inline void *bcmdb_dummy_lock_write(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+ return bcmdb_dummy_lock(set, key, is_deletion);
+}
+
+/** none policy: record unlock for writing */
+static inline void bcmdb_dummy_unlock_write(bcmdb_set *set, int is_deletion, int is_cancellation)
+{
+ bcmdb_dummy_unlock(set);
+}
+
+
+
+/** Add new sub-set. returns 0 if ok
+ * data contains new set handle
+ */
+static int bcmdb_set_new(bcmdb_set *this, bcmdb_key key, const void *data)
+{
+ /* Although this callback takes "const void *" parameter,
+ * it is just for compatibility fith SoR->new_entry interface.
+ * For SoS this parameter is not constant on application level
+ * (see bcmdb_set_add())
+ */
+ bcmdb_entry *entry = this->key_to_handle(this, key);
+ bcmdb_set *new_set = (bcmdb_set *)(long)data;
+
+ if (!entry)
+ return BCM_ERR_PARM;
+ if ((entry->flags & BCMDB_FLAG_VALID))
+ return BCM_ERR_ALREADY;
+ ++this->num_entries;
+ entry->data = (void *)(long)data;
+ entry->flags |= BCMDB_FLAG_VALID;
+ new_set->my_key = key;
+ new_set->parent = this;
+ return 0;
+}
+
+/** Add new record. returns 0 if ok
+ * data contains record data pointer
+ */
+static int bcmdb_record_new(bcmdb_set *this, bcmdb_key key, const void *data)
+{
+ bcmdb_record *record = (bcmdb_record *)this->key_to_handle(this, key);
+ if (!record || !record->e.data)
+ return BCM_ERR_PARM;
+ if ((record->e.flags & BCMDB_FLAG_VALID))
+ return BCM_ERR_ALREADY;
+ ++this->num_entries;
+ memcpy(record->e.data, data, this->entry_size);
+ record->e.flags |= BCMDB_FLAG_VALID;
+ return 0;
+}
+
+/** Delete entry */
+static int bcmdb_entry_delete(bcmdb_set *this, bcmdb_entry *entry)
+{
+ if (!entry)
+ return BCM_ERR_PARM;
+ if (!(entry->flags & BCMDB_FLAG_VALID))
+ return BCM_ERR_ALREADY;
+ entry->flags &= ~BCMDB_FLAG_VALID;
+ --this->num_entries;
+ return 0;
+}
+
+
+/*
+ * External APIs
+ */
+
+
+/** Initialize data base engine
+ *
+ * \return
+ * 0 - OK\n
+ * <0 - error code
+ * \ingroup bcmdb
+ */
+int bcmdb_module_init(void)
+{
+ return 0;
+}
+
+
+/** Make set-of-sets control block.
+ *
+ * Helper function that creates a set of sets with reasonable defaults for all callbacks and fields.
+ * Once created, the set control block can be tuned before adding the new set to its parent set.
+ * \param[in] init set parameters
+ * \param[out] new_set set control block
+ * \return
+ * 0 - OK\n
+ * <0 - error code
+ */
+int bcmdb_make_set_of_sets(const bcmdb_sos_init *init, bcmdb_set **new_set)
+{
+ bcmdb_set *sos;
+ bcmdb_entry *entries;
+ int rc;
+
+ /* Parameter check */
+ if (!init || !init->name || !new_set)
+ return BCM_ERR_PARM;
+ if ((init->backend_type == BCMDB_BACKEND_ARRAY) && !init->max_entries)
+ return BCM_ERR_PARM;
+
+ /* Allocate set control block and set records */
+ sos = bcmos_calloc(sizeof(bcmdb_set) + strlen(init->name) + 1);
+ if (!sos)
+ return BCM_ERR_NOMEM;
+ sos->name = (char *)(sos + 1);
+ strcpy(sos->name, init->name);
+ sos->entry_size = sizeof(bcmdb_set);
+ sos->max_entries = init->max_entries;
+ sos->my_key = BCMDB_KEY_INVAL;
+ sos->entry.flags = BCMDB_FLAG_SOS;
+ sos->magic = BCMDB_MAGIC_ACTIVE_SET;
+
+ /* Set backend callbacks */
+ switch(init->backend_type)
+ {
+ case BCMDB_BACKEND_ARRAY:
+ entries = bcmos_calloc(sizeof(bcmdb_set)*init->max_entries);
+ if (!entries)
+ {
+ bcmos_free(sos);
+ return BCM_ERR_NOMEM;
+ }
+ sos->entry.data = entries;
+ sos->entry_get_next = _bcmdb_array_entry_get_next;
+ sos->handle_to_key = _bcmdb_array_handle_to_key;
+ sos->key_to_handle = _bcmdb_array_key_to_handle;
+ sos->entry_new = bcmdb_set_new;
+ sos->entry_delete = bcmdb_entry_delete;
+ break;
+
+ default:
+ printf("Only array-based DB backend is supported\n");
+ bcmos_free(sos);
+ return BCM_ERR_NOT_SUPPORTED;
+ }
+
+ /* Set locking callbacks. SoS locking policy is always SEMAPHORE */
+
+ /* in SoS, locking for read is same as for write (and is done recursively). */
+ sos->lock_set_read = bcmdb_set_lock_recursively_modify_sem;
+ sos->unlock_set_read = bcmdb_set_unlock_recursively_modify_sem;
+ sos->lock_set_modify = bcmdb_set_lock_modify_sem;
+ sos->unlock_set_modify = bcmdb_set_unlock_modify_sem;
+ sos->lock_set_recursively_modify = bcmdb_set_lock_recursively_modify_sem ;
+ sos->unlock_set_recursively_modify = bcmdb_set_unlock_recursively_modify_sem ;
+
+ /* create mutex_update */
+ rc = bcmos_mutex_create(&sos->mutex_update, init->os_flags);
+ if (rc)
+ {
+ bcmos_free(entries);
+ bcmos_free(sos);
+ return BCM_ERR_NOMEM;
+ }
+
+ bcmos_fastlock_init(&sos->fastlock, init->os_flags);
+
+ *new_set = sos;
+
+ return 0;
+}
+
+
+
+
+/** Make set-of-records control block.
+ *
+ * Helper function that creates a set of records with reasonable defaults for all callbacks and fields.
+ * Once created, the set control block can be tuned before adding the new set to its parent set.
+ * \param[in] init set parameters
+ * \param[in] alloc_records true (1) - allocate memory for all records.
+ * \param[out] new_set set control block
+ * \return
+ * 0 - OK\n
+ * <0 - error code
+ */
+int bcmdb_make_set_of_records(const bcmdb_sor_init *init, int alloc_records, bcmdb_set **new_set)
+{
+ bcmdb_set *sor;
+ bcmdb_entry *entries = NULL;
+ void *data = NULL;
+ int i;
+ int rc ;
+
+ /* Parameter check */
+ if (!init || !init->name)
+ return BCM_ERR_PARM;
+ if ((init->backend_type == BCMDB_BACKEND_ARRAY) && !init->max_entries)
+ return BCM_ERR_PARM;
+ if (!init->record_size)
+ return BCM_ERR_PARM;
+
+ /* Allocate set control block and set records */
+ sor = bcmos_calloc(sizeof(bcmdb_set) + strlen(init->name) + 1);
+ if (!sor)
+ return BCM_ERR_NOMEM;
+ sor->name = (char *)(sor + 1);
+ strcpy(sor->name, init->name);
+ sor->entry_size = init->record_size;
+ sor->max_entries = init->max_entries;
+ sor->my_key = BCMDB_KEY_INVAL;
+ sor->magic = BCMDB_MAGIC_ACTIVE_SET;
+ sor->format = init->format;
+
+ /* Set backend callbacks */
+ switch(init->backend_type)
+ {
+ case BCMDB_BACKEND_ARRAY:
+ entries = bcmos_calloc(sizeof(bcmdb_entry)*init->max_entries);
+ if (!entries)
+ {
+ bcmos_free(sor);
+ return BCM_ERR_NOMEM;
+ }
+ sor->entry.data = entries;
+ sor->entry_get_next = _bcmdb_array_entry_get_next;
+ sor->handle_to_key = _bcmdb_array_handle_to_key;
+ sor->key_to_handle = _bcmdb_array_key_to_handle;
+ sor->entry_new = bcmdb_record_new;
+ sor->entry_delete = bcmdb_entry_delete;
+
+ /* Preallocate data */
+ if (alloc_records)
+ {
+ int size = init->max_entries * init->record_size;
+ if (init->lock_policy == BCMDB_LOCK_NB_READ_SHADOW_WRITE)
+ size += init->record_size; /* room for shadow entry */
+ /* Allocate data + 1 extra for shadow area */
+ data = bcmos_calloc(size);
+ if (!data)
+ {
+ bcmos_free(entries);
+ bcmos_free(sor);
+ return BCM_ERR_NOMEM;
+ }
+ for(i=0; i<init->max_entries; i++)
+ {
+ bcmdb_entry *entry = (bcmdb_entry *)sor->entry.data + i;
+ entry->data = (void *)((long)data + i * init->record_size);
+ }
+ if (init->lock_policy == BCMDB_LOCK_NB_READ_SHADOW_WRITE)
+ {
+ sor->shadow_data = (void *)((long)data + i * init->record_size);
+ }
+ }
+
+ /* Initialize records */
+ for(i=0; i<init->max_entries; i++)
+ {
+ bcmdb_entry *entry = (bcmdb_entry *)sor->entry.data + i;
+ entry->flags = BCMDB_FLAG_RECORD;
+ }
+ break;
+
+ default:
+ printf("Only array-based DB backend is supported\n");
+ bcmos_free(sor);
+ return BCM_ERR_NOT_SUPPORTED;
+ }
+
+ /* Set locking callbacks based on locking policy */
+ switch(init->lock_policy)
+ {
+ case BCMDB_LOCK_SEM_READ_SEM_WRITE:
+ sor->lock_record_write = bcmdb_sem_read_sem_write_lock_write;
+ sor->lock_record_read = bcmdb_sem_read_sem_write_lock_read;
+ sor->unlock_record_write = bcmdb_sem_read_sem_write_unlock_write;
+ sor->unlock_record_read = bcmdb_sem_read_sem_write_unlock_read;
+ sor->lock_set_read = bcmdb_set_lock_read_sem;
+ sor->unlock_set_read = bcmdb_set_unlock_read_sem;
+ sor->lock_set_modify = bcmdb_set_lock_modify_sem;
+ sor->unlock_set_modify = bcmdb_set_unlock_modify_sem;
+ sor->lock_set_recursively_modify = bcmdb_set_lock_recursively_modify_sem ;
+ sor->unlock_set_recursively_modify = bcmdb_set_unlock_recursively_modify_sem ;
+ break;
+
+ case BCMDB_LOCK_NONE:
+ case BCMDB_LOCK_OTHER:
+ sor->lock_record_write = bcmdb_dummy_lock_write;
+ sor->lock_record_read = bcmdb_dummy_lock_read;
+ sor->unlock_record_write = bcmdb_dummy_unlock_write;
+ sor->unlock_record_read = bcmdb_dummy_unlock_read;
+ sor->lock_set_read = bcmdb_set_lock_read_dummy;
+ sor->unlock_set_read = bcmdb_set_unlock_read_dummy;
+ sor->lock_set_modify = bcmdb_set_lock_modify_dummy;
+ sor->unlock_set_modify = bcmdb_set_unlock_modify_dummy;
+ sor->lock_set_recursively_modify = bcmdb_set_lock_recursively_modify_dummy ;
+ sor->unlock_set_recursively_modify = bcmdb_set_unlock_recursively_modify_dummy ;
+ break;
+
+ case BCMDB_LOCK_NB_READ_SHADOW_WRITE:
+ sor->lock_record_write = bcmdb_nb_read_shadow_write_lock_write;
+ sor->lock_record_read = bcmdb_nb_read_shadow_write_lock_read;
+ sor->unlock_record_write = bcmdb_nb_read_shadow_write_unlock_write;
+ sor->unlock_record_read = bcmdb_nb_read_shadow_write_unlock_read;
+ sor->lock_set_read = bcmdb_set_lock_read__nb_read_sem_write;
+ sor->unlock_set_read = bcmdb_set_unlock_read__nb_read_sem_write;
+ sor->lock_set_modify = bcmdb_set_lock_modify__nb_read_sem_write;
+ sor->unlock_set_modify = bcmdb_set_unlock_modify__nb_read_sem_write;
+ sor->lock_set_recursively_modify = bcmdb_set_lock_recursively_modify_sem ;
+ sor->unlock_set_recursively_modify = bcmdb_set_unlock_recursively_modify_sem ;
+ break;
+
+ default:
+ printf("Lock policy %d is not supported\n", init->lock_policy);
+ if (data)
+ bcmos_free(data);
+ if (entries)
+ bcmos_free(entries);
+ bcmos_free(sor);
+ return BCM_ERR_NOT_SUPPORTED;
+ }
+
+ /* create mutex_update */
+ rc = bcmos_mutex_create(&sor->mutex_update, init->os_flags);
+ if (rc)
+ {
+ if (data)
+ bcmos_free(data);
+ if (entries)
+ bcmos_free(entries);
+ bcmos_free(sor);
+ return BCM_ERR_NOMEM;
+ }
+
+ /* create sem_wait_read_to_finish. it is initialized to be taken */
+ rc = bcmos_sem_create(&sor->sem_wait_read_to_finish, 0, init->os_flags);
+ if (rc)
+ {
+ /* no point to check here the error code of bcmos_mutex_destroy */
+ bcmos_mutex_destroy(&sor->mutex_update);
+ if (data)
+ bcmos_free(data);
+ if (entries)
+ bcmos_free(entries);
+ bcmos_free(sor);
+ return BCM_ERR_NOMEM;
+ }
+
+ bcmos_fastlock_init(&sor->fastlock, init->os_flags);
+
+ *new_set = sor;
+
+ return 0;
+}
+
+
+/** Lock data set for reading. When set is locked - it can't be
+ * modified.
+ *
+ * \param[in] set data base set to be locked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_lock_read(bcmdb_set *set)
+{
+ BUG_ON(!set);
+ BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!set->lock_set_read);
+ set->lock_set_read(set);
+}
+
+
+/** Release data set lock
+ *
+ * Unlock set locked by \ref bcmdb_set_lock_read
+ *
+ * \param[in] set data base set to be unlocked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_unlock_read(bcmdb_set *set)
+{
+ BUG_ON(!set);
+ BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!set->unlock_set_read);
+ set->unlock_set_read(set);
+}
+
+
+/** Lock data set for modify. If the set is SoS, the locking
+ * will be recursive.
+ *
+ * \param[in] set data base set to be locked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_lock_modify(bcmdb_set *set)
+{
+ BUG_ON(!set);
+ BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!set->lock_set_recursively_modify);
+ set->lock_set_recursively_modify(set);
+}
+
+
+/** Release data set lock
+ *
+ * Unlock set locked by \ref bcmdb_set_lock_modify
+ *
+ * \param[in] set data base set to be unlocked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_unlock_modify(bcmdb_set *set)
+{
+ BUG_ON(!set);
+ BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!set->unlock_set_recursively_modify);
+ set->unlock_set_recursively_modify(set);
+}
+
+
+/** Add set to the parent set with specific key.
+ *
+ * The function adds set to the parent set creating data base hierarchy.
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ * \param[in] sos parent set of sets
+ * \param[in] key key to add new set at
+ * \param[in] new_set set control block
+ * \return
+ * =0 - OK\n
+ * <0 - error code
+ * \ingroup bcmdb
+ */
+int bcmdb_set_add(bcmdb_set *sos, bcmdb_key key, bcmdb_set *new_set)
+{
+ int rc;
+ long fastlock_flags;
+ BUG_ON(!sos);
+ BUG_ON(!sos->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!(sos->entry.flags & BCMDB_FLAG_SOS));
+ BUG_ON(!new_set);
+ BUG_ON(!new_set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!new_set->my_key == BCMDB_KEY_INVAL);
+ fastlock_flags = sos->lock_set_modify(sos);
+ rc = sos->entry_new(sos, key, new_set);
+ sos->unlock_set_modify(sos, fastlock_flags);
+ return rc;
+}
+
+
+/** Get set handle given its key.
+ *
+ * \param[in] sos parent set of sets
+ * \param[in] key set key.
+ * \return
+ * !=0 - set handle
+ * NULL- doesn't exist
+ * \ingroup bcmdb
+ */
+bcmdb_set *bcmdb_set_handle(const bcmdb_set *sos, bcmdb_key key)
+{
+ bcmdb_entry *entry;
+ BUG_ON(!sos);
+ BUG_ON(!sos->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!(sos->entry.flags & BCMDB_FLAG_SOS));
+ entry = sos->key_to_handle(sos, key);
+ if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+ return NULL;
+ return (bcmdb_set *)entry->data;
+}
+
+
+/** Get set key given its handle.
+ *
+ * \param[in] set set handle
+ * \param[in] key set key.
+ * \return
+ * !=BCMDB_KEY_INVAL - set key
+ * BCMDB_KEY_INVAL - error
+ * \ingroup bcmdb
+ */
+bcmdb_key bcmdb_set_key(const bcmdb_set *set)
+{
+ BUG_ON(!set);
+ BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ return set->my_key;
+}
+
+
+/** Get set name
+ *
+ * \param[in] set set handle
+ * \return set name
+ * \ingroup bcmdb
+ */
+const char *bcmdb_set_name(const bcmdb_set *set)
+{
+ BUG_ON(!set);
+ BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ return set->name;
+}
+
+
+/** Get number of records in the set.
+ *
+ * \param[in] set set handle
+ * \return number of active records in the set
+ * \ingroup bcmdb
+ */
+int bcmdb_set_num_records(const bcmdb_set *set)
+{
+ BUG_ON(!set);
+ BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ return set->num_entries;
+}
+
+
+/** Get entry size
+ *
+ * \param[in] set set handle
+ * \return set entry size
+ * \ingroup bcmdb
+ */
+int bcmdb_set_entry_size(const bcmdb_set *set)
+{
+ BUG_ON(!set);
+ BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ return set->entry_size;
+}
+
+
+/** Add record to the parent set with specific key.
+ *
+ * The function creates a new record and adds it to the parent set with specific key.
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ * \param[in] sor parent set of records
+ * \param[in] key key to add new set at
+ * \param[in] data record data. Data size is defined at parent SOR registration time.
+ * \param[out] p_record new record handle
+ * \return
+ * =0 - OK\n
+ * <0 - error code
+ * \ingroup bcmdb
+ */
+int bcmdb_record_add(bcmdb_set *sor, bcmdb_key key, const void *data)
+{
+ int rc;
+ long fastlock_flags;
+
+ BUG_ON(!sor);
+ BUG_ON(!data);
+ BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+
+ fastlock_flags = sor->lock_set_modify(sor);
+ rc=sor->entry_new(sor, key, data);
+ sor->unlock_set_modify(sor, fastlock_flags);
+ return rc;
+}
+
+
+/** Delete record from the parent SoR given the record key.
+ *
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key.
+ */
+void bcmdb_record_delete(bcmdb_set *sor, bcmdb_key key)
+{
+ BUG_ON(!sor);
+ BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+
+ sor->lock_record_write(sor, key, 1);
+ sor->unlock_record_write(sor, 1, 0);
+}
+
+
+/** Get record data pointer without locking.
+ *
+ * The function returns pointer to data structure stored in data base record.\n
+ * Attention! The caller is required to aquire read or write lock - as appropriate
+ * before calling this function and release the lock when processing is finished.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \return
+ * data pointer. NULL if there is no record matching the key.
+ * \ingroup bcmdb
+ */
+void *bcmdb_record_getraw_nolock(bcmdb_set *sor, bcmdb_key key)
+{
+ bcmdb_entry *entry;
+ BUG_ON(!sor);
+ BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+ entry = sor->key_to_handle(sor, key);
+ if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+ return NULL;
+ return entry->data;
+}
+
+
+/** Lock record for reading and return record data pointer.
+ *
+ * The function aquires read-lock and returns pointer to data structure stored in data base record.\n
+ * read-lock must be released separately when the pointer is no longer in use.
+ * Note that the default record-read lock is non-blocking and counting.
+ * That means that multiple processes cam read-lock the same record without blocking.
+ * The function is low-level. It is recommended to use macro \ref bcmdb_record_get_read instead.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \return
+ * data pointer. NULL if there is no record matching the key.
+ * \ingroup bcmdb
+ */
+const void *bcmdb_record_getraw_read(bcmdb_set *sor, bcmdb_key key)
+{
+ BUG_ON(!sor);
+ BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+ return sor->lock_record_read(sor, key);
+}
+
+
+/** Unlock record locked for reading.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_record_unlock_read(bcmdb_set *sor, bcmdb_key key)
+{
+ BUG_ON(!sor);
+ BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+ sor->unlock_record_read(sor, key);
+}
+
+
+/** Read record data into user area.
+ *
+ * The function aquires read-lock, reads data into user area and releases read-lock.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \param[in] offset offset in data record
+ * \param[in] size data size. Note that offset+size must be <= record_size
+ * \param[in] data data pointer.
+ * \return
+ * =0-OK\n
+ * <0-error code
+ * \ingroup bcmdb
+ */
+int bcmdb_record_read(bcmdb_set *sor, bcmdb_key key, int offset, int size, void *data)
+{
+ const void *d = bcmdb_record_getraw_read(sor, key);
+ if (!d)
+ return BCM_ERR_PARM;
+ if ((unsigned)offset + (unsigned)size > sor->entry_size)
+ {
+ bcmdb_record_unlock_read(sor, key);
+ return BCM_ERR_PARM;
+ }
+ memcpy(data, (const char *)d+(unsigned)offset, (unsigned)size);
+ bcmdb_record_unlock_read(sor, key);
+ return 0;
+}
+
+
+/** Lock record for writing and return record data pointer.
+ *
+ * The function aquires write-lock and returns pointer to data structure stored in data base record.\n
+ * write-lock must be released separately when the pointer is no longer in use.
+ * The function is low-level. It is recommended to use macro \ref bcmdb_record_get_write instead.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \return
+ * data pointer. NULL if there is no record matching the key.
+ * \ingroup bcmdb
+ */
+void *bcmdb_record_getraw_write(bcmdb_set *sor, bcmdb_key key)
+{
+ BUG_ON(!sor);
+ BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+ return sor->lock_record_write(sor, key, 0);
+}
+
+
+/** Unlock record locked for writing.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] is_cancellation TRUE=cancel transaction
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_record_unlock_write(bcmdb_set *sor, int is_cancellation)
+{
+ BUG_ON(!sor);
+ BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+ sor->unlock_record_write(sor, 0, is_cancellation);
+}
+
+
+/** Write record data.
+ *
+ * The function aquires modify-lock, replaces data stored in data base record
+ * and releses the lock.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \param[in] offset offset in data record
+ * \param[in] size data size. Note that offset+size must be <= record_size
+ * \param[in] data data pointer.
+ * \return
+ * =0-OK\n
+ * <0-error code
+ * \ingroup bcmdb
+ */
+int bcmdb_record_write(bcmdb_set *sor, bcmdb_key key, int offset, int size, const void *data)
+{
+ void *d=bcmdb_record_getraw_write(sor, key);
+ if (!d)
+ return BCM_ERR_PARM;
+ if ((unsigned)offset + (unsigned)size > sor->entry_size)
+ {
+ bcmdb_record_unlock_write(sor, 0);
+ return BCM_ERR_PARM;
+ }
+ memcpy((char *)d+(unsigned)offset, data, (unsigned)size);
+ bcmdb_record_unlock_write(sor, 0);
+ return 0;
+}
+
+
+/** Register notification function to get informed
+ * when data base set is modified.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] cb callback function pointer
+ * \param[in] cb_priv private data that should be passed to the callback
+ * \return
+ * =0 - OK\n
+ * <0 - error code
+ * \ingroup bcmdb
+ */
+int bcmdb_set_notify_register(bcmdb_set *sor, bcmdb_notify_cb cb, long cb_priv)
+{
+ bcmdb_notify *nf_new, *nf, *nf_prev = NULL;
+
+ BUG_ON(!sor);
+ BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+ BUG_ON(!cb);
+
+ nf_new = bcmos_calloc(sizeof(bcmdb_notify));
+ if (!nf_new)
+ return BCM_ERR_NOMEM;
+ nf_new->cb = cb;
+ nf_new->cb_priv = cb_priv;
+
+ /* Add to set's notification list */
+ nf = sor->notify_list_head;
+ while(nf)
+ {
+ nf_prev = nf;
+ nf = nf->next;
+ }
+ if (nf_prev)
+ nf_prev->next = nf_new;
+ else
+ sor->notify_list_head = nf_new;
+
+ return 0;
+}
+
+
+/** Data base iterator
+ *
+ * \param[in] set data base set
+ * \param[in] prev last entry. BCMDB_KEY_ANY=start from the beginning
+ * \return data base entry key following prev or BCMDB_KEY_NO_MORE if end is reached.\n
+ * BCMDB_KEY_INVAL is reqturned if prev key is invalid
+ * \ingroup bcmdb
+ */
+bcmdb_key bcmdb_set_iterate(const bcmdb_set *set, bcmdb_key prev)
+{
+ BUG_ON(!set);
+ BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+ return set->entry_get_next(set, prev);
+}
+
+
+/* Print database structure */
+static void _bcmdb_print_structure(const bcmdb_set *set, int level)
+{
+ int i;
+
+ if (!set)
+ return;
+
+ /* Indentation */
+ for(i=0; i<level; i++)
+ printf("\t");
+
+ if ((set->entry.flags & BCMDB_FLAG_SOS))
+ {
+ bcmdb_key key = bcmdb_set_iterate(set, BCMDB_KEY_ANY);
+ printf("%-16s SoS max_entries=%d entries=%d\n", set->name, set->max_entries, set->num_entries);
+ while(key >= 0)
+ {
+ _bcmdb_print_structure(bcmdb_set_handle(set, key), level+1);
+ key = bcmdb_set_iterate(set, key);
+ }
+ }
+ else
+ {
+ printf("%-16s SoR max_entries=%d entries=%d record_size=%d total_size=%d\n",
+ set->name, set->max_entries, set->num_entries, set->entry_size,
+ set->entry_size*set->max_entries);
+ }
+}
+
+
+/** Print database structure.
+ *
+ * \param[in] set root set
+ * \ingroup bcmdb
+ */
+void bcmdb_set_print_structure(const bcmdb_set *set)
+{
+ _bcmdb_print_structure(set, 0);
+}
+
+
+/** Format record for printing.
+ *
+ * The function converts record data to human-readible format.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \param[out] buffer output buffer
+ * \param[in] size buffer size
+ * \return
+ * >=0-amount of data placed in the buffer\n
+ * <0-error code
+ */
+int bcmdb_record_read_formatted(bcmdb_set *sor, bcmdb_key key, char *buffer, int size)
+{
+ const void *data;
+ int len;
+ if (!buffer || !size)
+ return BCM_ERR_PARM;
+ if (!sor->format)
+ return BCM_ERR_NOT_SUPPORTED;
+ *buffer=0;
+ data = bcmdb_record_getraw_read(sor, key);
+ if (!data)
+ return 0;
+ len = sor->format(data, buffer, size);
+ bcmdb_record_unlock_read(sor, key);
+ return len;
+}
diff --git a/bal_release/src/common/db_engine/bcm_db_engine.h b/bal_release/src/common/db_engine/bcm_db_engine.h
new file mode 100644
index 0000000..8d77022
--- /dev/null
+++ b/bal_release/src/common/db_engine/bcm_db_engine.h
@@ -0,0 +1,622 @@
+/******************************************************************************
+ *
+ * <:copyright-BRCM:2016:DUAL/GPL:standard
+ *
+ * Copyright (c) 2016 Broadcom
+ * All Rights Reserved
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed
+ * to you under the terms of the GNU General Public License version 2
+ * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+ * with the following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give
+ * you permission to link this software with independent modules, and
+ * to copy and distribute the resulting executable under terms of your
+ * choice, provided that you also meet, for each linked independent
+ * module, the terms and conditions of the license of that module.
+ * An independent module is a module which is not derived from this
+ * software. The special exception does not apply to any modifications
+ * of the software.
+ *
+ * Not withstanding the above, under no circumstances may you combine
+ * this software in any way with any other Broadcom software provided
+ * under a license other than the GPL, without Broadcom's express prior
+ * written consent.
+ *
+ * :>
+ *
+ *****************************************************************************/
+
+/*
+ * bcm_db_engine.c
+ *
+ * Data base engine
+ */
+#ifndef BCMDB_ENGINE_H
+
+#define BCMDB_ENGINE_H
+
+/** \defgroup bcmdb_e Data Base Engine Module
+Hierarchical data base is built from 3 types of objects:
+- set: consists of control info and dynamic array of other sets OR of records of the same kind.
+ - Mixing subsets and records in the same set is not supported.
+ - Sets are objects that perform operations like access, locking, adding or removing elements,
+ etc., via methods that can differ for every set.
+ - Set elements are addressed using a single key.
+ - Most sets are internally organized as arrays. However, other organizations (e.g., lists, hash tables)
+ are also possible because each set can have different set of methods for element access.
+- record: is a container for storing information.
+ - Record consists of control info and a structure containing fields.
+ - Record is the smallest DB element that has a handle and can be individually locked.
+ - Record size is fixed at time when the set containing records is created.
+- field: is a convenience element.
+ - DB API includes field access macros for convenience and traceability.
+ Apart from that, record layout is transparent to the DB engine.
+ - DB user has an option of accessing record fields directly (as C structure fields), without using DB API
+ @{
+*/
+
+#define bcmdb_error_print(rc,format,args...) BCMOS_TRACE_ERR("status:%s :" format, bcmos_strerror(rc), ##args)
+
+/** Data base backend type
+ */
+typedef enum
+{
+ BCMDB_BACKEND_ARRAY, /**< Array-based backend */
+ BCMDB_BACKEND_HASH, /**< Hash-based backend */
+ BCMDB_BACKEND_OTHER /**< User-defined backend */
+} bcmdb_backend_type;
+
+
+/** Data locking policy
+ */
+typedef enum
+{
+ BCMDB_LOCK_NONE, /**< No record-level locking. Can be used for records containing independent fields */
+ BCMDB_LOCK_NB_READ_SHADOW_WRITE,/**< Non-blocking read, write using shadow area (default) */
+ BCMDB_LOCK_SEM_READ_SEM_WRITE, /**< Strong locking. Both read and write locks use semaphores */
+ BCMDB_LOCK_OTHER /**< User-defined locking policy */
+} bcmdb_lock_policy;
+
+
+/** Data base key
+ * Valid values >= 0
+ */
+typedef int bcmdb_key;
+
+
+/** Any key
+ */
+#define BCMDB_KEY_ANY (-1)
+
+/** Invalid key
+ */
+#define BCMDB_KEY_INVAL (-2)
+
+/** No more records
+ */
+#define BCMDB_KEY_NO_MORE (-3)
+
+
+ /** Data Base Set control block */
+typedef struct bcmdb_set bcmdb_set;
+
+/** Data Base Record control block */
+typedef struct bcmdb_record bcmdb_record;
+
+/** Data Base Set or Record */
+typedef struct bcmdb_entry bcmdb_entry;
+
+
+/** Data base operations for notifications.
+ */
+typedef enum
+{
+ BCMDB_OPER_ADD, /**< Entry has been added */
+ BCMDB_OPER_DELETE, /**< Entry has been deleted */
+ BCMDB_OPER_UPDATE /**< Entry has been modified */
+} bcmdb_oper_t;
+
+
+/** Data base update notification callback.
+ */
+typedef void (*bcmdb_notify_cb)(bcmdb_set *set, bcmdb_key key, bcmdb_oper_t oper, void *new_data);
+
+
+/** Format callback. Used by bcmdb_record_read_formatted to convert record data to human-readible format */
+typedef int (*bcmdb_format_cb)(const void *data, char *buffer, int buffer_size);
+
+
+/** Set-of-Sets init structure.
+ */
+typedef struct bcmdb_sos_init
+{
+ const char *name; /**< Set name */
+ bcmdb_backend_type backend_type; /**< Backend type */
+ uint32_t max_entries; /**< Max number of entries. 0=unlimited (not supported for array backend) */
+ uint32_t os_flags; /**< OS flags. Control whether set can be accessed by multiple cores. See bcmos_mutex_create() */
+} bcmdb_sos_init;
+
+
+/** Set-of-Records init structure.
+ */
+typedef struct bcmdb_sor_init
+{
+ const char *name; /**< Set name */
+ bcmdb_backend_type backend_type; /**< Backend type */
+ bcmdb_lock_policy lock_policy; /**< Set locking policy */
+ uint32_t max_entries; /**< Max number of entries. 0=unlimited (not supported for array backend) */
+ uint32_t record_size; /**< Record size > 0 */
+ bcmdb_format_cb format; /**< callback that converts record data to human-readable form */
+ uint32_t os_flags; /**< OS flags. Control whether set can be accessed by multiple cores. See bcmos_mutex_create() */
+} bcmdb_sor_init;
+
+
+/** Initialize data base engine
+ *
+ * \return
+ * 0 - OK\n
+ * <0 - error code
+ */
+int bcmdb_module_init(void);
+
+
+/** Make set-of-sets control block.
+ *
+ * Helper function that creates a set of sets with reasonable defaults for all callbacks and fields.
+ * Once created, the set control block can be tuned before adding the new set to its parent set.
+ * \param[in] init set parameters
+ * \param[out] new_set set control block
+ * \return
+ * 0 - OK\n
+ * <0 - error code
+ */
+int bcmdb_make_set_of_sets(const bcmdb_sos_init *init, bcmdb_set **new_set);
+
+
+/** Make set-of-sets control block macro.
+ *
+ * Calls \ref bcmdb_make_set_of_sets.
+ * Prints error message and jumps to error_label in case of failure.
+ * For parameter description see \ref bcmdb_make_set_of_sets
+ */
+#define BCMDB_MAKE_SOS(_name,_backend,_max_entries,_p_handle,_rc,_error_label) \
+({\
+ bcmdb_sos_init _init = { .name=_name, .max_entries=_max_entries, .backend_type=_backend};\
+ _rc = bcmdb_make_set_of_sets(&_init, _p_handle);\
+ if (_rc)\
+ {\
+ bcmdb_error_print(_rc, "failed to create set %s.\n", _name);\
+ goto _error_label;\
+ }\
+})
+
+
+/** Make set-of-records control block.
+ *
+ * Helper function that creates a set of records with reasonable defaults for all callbacks and fields.
+ * Once created, the set control block can be tuned before adding the new set to its parent set.
+ * \param[in] init set parameters
+ * \param[in] alloc_records true (1) - allocate memory for all records.
+ * \param[out] new_set set control block
+ * \return
+ * 0 - OK\n
+ * <0 - error code
+ */
+int bcmdb_make_set_of_records(const bcmdb_sor_init *init, int alloc_records, bcmdb_set **new_set);
+
+
+/** Make set-of-records control block macro.
+ *
+ * Calls \ref bcmdb_make_set_of_records.
+ * Prints error message and jumps to error_label in case of failure.
+ * For parameter description see \ref bcmdb_make_set_of_records
+ */
+#define BCMDB_MAKE_SOR(_name,_backend,_lock,_max_entries,_rec_size,_is_alloc,_format,_p_handle,_rc,_error_label) \
+({\
+ bcmdb_sor_init _init = { .name=_name, .max_entries=_max_entries, .backend_type=_backend,\
+ .lock_policy=_lock, .record_size=_rec_size,.format=_format};\
+ _rc = bcmdb_make_set_of_records(&_init,_is_alloc,_p_handle);\
+ if (_rc)\
+ {\
+ bcmdb_error_print(_rc, "failed to create record set %s.\n", _name);\
+ goto _error_label;\
+ }\
+})
+
+
+/** Lock data set. When set is locked - it can't be modified.
+ *
+ * \param[in] set data base set to be locked
+ *
+ */
+void bcmdb_set_lock_read(bcmdb_set *set);
+
+
+/** Release data set lock
+ *
+ * Unlock set locked by \ref bcmdb_set_lock_read
+ *
+ * \param[in] set data base set to be unlocked
+ */
+void bcmdb_set_unlock_read(bcmdb_set *set);
+
+
+/** Lock data set for modify. If the set is SoS, the locking
+ * will be recursive.
+ *
+ * \param[in] set data base set to be locked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_lock_modify(bcmdb_set *set);
+
+
+/** Release data set lock
+ *
+ * Unlock set locked by \ref bcmdb_set_lock_modify
+ *
+ * \param[in] set data base set to be unlocked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_unlock_modify(bcmdb_set *set);
+
+
+/** Add set to the parent set.
+ *
+ * The function adds set to the parent set creating data base hierarchy.
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ * \param[in] sos parent set of sets
+ * \param[in] key key to add new set at
+ * \param[in] new_set set control block
+ * \return
+ * =0 - OK\n
+ * <0 - error code
+ */
+int bcmdb_set_add(bcmdb_set *sos, bcmdb_key key, bcmdb_set *new_set);
+
+
+/** Add set to the parent set with specific key macro.
+ *
+ * Calls \ref bcmdb_set_add.
+ * Prints error message and jumps to error_label in case of failure.
+ * For parameter description see \ref bcmdb_set_add
+ */
+#define BCMDB_SET_ADD(_parent,_key,_set,_rc,_error_label) \
+({\
+ _rc = bcmdb_set_add(_parent,_key,_set);\
+ if (_rc)\
+ {\
+ bcmdb_error_print(_rc, "failed to add set %s to %s.\n", bcmdb_set_name(_set), bcmdb_set_name(_parent));\
+ goto _error_label;\
+ }\
+})
+
+
+/** Get set handle given its key.
+ *
+ * \param[in] sos parent set of sets
+ * \param[in] key set key.
+ * \return
+ * !=0 - set handle
+ * NULL- doesn't exist
+ */
+bcmdb_set *bcmdb_set_handle(const bcmdb_set *sos, bcmdb_key key);
+
+
+/** Get set key given its handle.
+ *
+ * \param[in] set set handle
+ * \return
+ * !=BCMDB_KEY_INVAL - set key\n
+ * BCMDB_KEY_INVAL - error
+ */
+bcmdb_key bcmdb_set_key(const bcmdb_set *set);
+
+
+/** Get set name
+ *
+ * \param[in] set set handle
+ * \return set name
+ */
+const char *bcmdb_set_name(const bcmdb_set *set);
+
+
+/** Get number of records in the set.
+ *
+ * \param[in] set set handle
+ * \return number of active records in the set
+ */
+int bcmdb_set_num_records(const bcmdb_set *set);
+
+
+/** Get entry size
+ *
+ * \param[in] set set handle
+ * \return set entry size
+ */
+int bcmdb_set_entry_size(const bcmdb_set *set);
+
+
+/** Add record to the parent set.
+ *
+ * The function creates a new record and adds it to the parent set with specific key.
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \param[in] data record data. Data size is defined at parent SOR registration time.
+ * \return
+ * =0 - OK\n
+ * <0 - error code
+ */
+int bcmdb_record_add(bcmdb_set *sor, bcmdb_key key, const void *data);
+
+
+/** Delete record from the parent SoR given the record key.
+ *
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key.
+ */
+void bcmdb_record_delete(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Get record data pointer without locking.
+ *
+ * The function returns pointer to data structure stored in data base record.\n
+ * Attention! The caller is required to aquire read or write lock - as appropriate
+ * before calling this function and release the lock when processing is finished.
+ * The function is low-level. It is recommended to use \ref bcmdb_record_get_nolock instead.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \return
+ * data pointer. NULL if there is no record matching the key.
+ */
+void *bcmdb_record_getraw_nolock(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Get record data pointer without locking.
+ *
+ * The function returns pointer to data structure stored in data base record.\n
+ * Attention! The caller is required to aquire read or write lock - as appropriate
+ * before calling this function and release the lock when processing is finished.
+ *
+ * \param[in] _sor parent set of records
+ * \param[in] _key record key
+ * \param[in] _record_type underlying data type.
+ * \return
+ * data pointer casted to the underlying data type\n
+ * NULL if there is no record matching the key.
+ */
+#define bcmdb_record_get_nolock(_sor, _key, _record_type) \
+ ({ \
+ assert(sizeof(_record_type)==bcmdb_set_entry_size(_sor)); \
+ (_record_type *)bcmdb_record_getraw_nolock(_sor, _key); \
+ })
+
+
+/** Lock record for reading and return record data pointer.
+ *
+ * The function aquires read-lock and returns pointer to data structure stored in data base record.\n
+ * read-lock must be released separately when the pointer is no longer in use.
+ * Note that the default record-read lock is non-blocking and counting.
+ * That means that multiple processes can read-lock the same record without blocking.
+ * The function is low-level. It is recommended to use macro \ref bcmdb_record_get_read instead.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \return
+ * data pointer. NULL if there is no record matching the key.
+ */
+const void *bcmdb_record_getraw_read(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Lock record for reading and return record data pointer.
+ *
+ * The macro returns pointer to data structure stored in data base record.\n
+ * The read-lock must be released separately when the pointer is no longer in use.
+ * Note that the default record-read lock is non-blocking and counting.
+ * That means that multiple processes can read-lock the same record without blocking.
+ *
+ * \param[in] _sor parent set of records
+ * \param[in] _key record key
+ * \param[in] _record_type underlying data type.
+ * \return
+ * data pointer casted to the underlying data type
+ */
+#define bcmdb_record_get_read(_sor, _key, _record_type) \
+ ({ \
+ assert(sizeof(_record_type)==bcmdb_set_entry_size(_sor)); \
+ (const _record_type *)bcmdb_record_getraw_read(_sor, _key);\
+ })
+
+
+/** Unlock record locked for reading.
+ *
+ * This function must be called after \ref bcmdb_record_get_read or
+ * \ref bcmdb_record_getraw_read. Following bcmdb_record_read_unlock
+ * call pointer returned by \ref bcmdb_record_get_read becomes invalid.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ *
+ */
+void bcmdb_record_unlock_read(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Read record data into user area.
+ *
+ * The function aquires read-lock, reads data into user area and releases read-lock.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \param[in] offset offset in data record
+ * \param[in] size data size. Note that offset+size must be <= record_size
+ * \param[in] data data pointer.
+ * \return
+ * =0-OK\n
+ * <0-error code
+ */
+int bcmdb_record_read(bcmdb_set *sor, bcmdb_key key, int offset, int size, void *data);
+
+
+/** Get record field.
+ *
+ * The macro returns record field value.
+ *
+ * \param[in] _sor parent set of records
+ * \param[in] _key record key
+ * \param[in] _record_type type of the underlying data structure.
+ * \param[in] _field_name data structure field name
+ * \param[out] _p_field_value pointer of variable where data structure field value should be returned
+ * \return
+ * =0-OK\n
+ * <0-error code
+ */
+#define bcmdb_record_read_field(_sor, _key, _record_type, _field_name, _p_field_value) \
+ bcmdb_record_read(_sor, _key, offsetof(_record_type, _field_name), \
+ sizeof(*(_p_field_value)), _p_field_value);
+
+
+/** Lock record for writing and return record data pointer.
+ *
+ * The function aquires write-lock and returns pointer to data structure stored in data base record.\n
+ * write-lock must be released separately when the pointer is no longer in use.
+ * The function is low-level. It is recommended to use macro \ref bcmdb_record_get_write instead.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \return
+ * data pointer. NULL if there is no record matching the key.
+ */
+void *bcmdb_record_getraw_write(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Lock record for writing and return record data pointer.
+ *
+ * The function aquires write-lock and returns pointer to data structure stored in data base record.\n
+ * write-lock must be released separately when the pointer is no longer in use.
+ *
+ * \param[in] _sor parent set of records
+ * \param[in] _key record key
+ * \param[in] _record_type underlying data type.
+ * \return
+ * data pointer casted to the underlying data type
+ */
+#define bcmdb_record_get_write(_sor, _key, _record_type) \
+ ({ \
+ assert(sizeof(_record_type)==bcmdb_set_entry_size(_sor)); \
+ (_record_type *)bcmdb_record_getraw_write(_sor, _key);\
+ })
+
+
+/** Unlock record locked for writing.
+ *
+ * This function must be called after \ref bcmdb_record_get_write or
+ * \ref bcmdb_record_getraw_write. Following bcmdb_record_unlock_write
+ * call pointer returned by \ref bcmdb_record_get_write becomes invalid.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] is_cancellation TRUE=cancel transaction
+ *
+ */
+void bcmdb_record_unlock_write(bcmdb_set *sor, int is_cancellation);
+
+
+
+/** Write record data.
+ *
+ * The function aquires modify-lock, replaces data stored in data base record
+ * and releses the lock.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \param[in] offset offset in data record
+ * \param[in] size data size. Note that offset+size must be <= record_size
+ * \param[in] data data pointer.
+ * \return
+ * =0-OK\n
+ * <0-error code
+ */
+int bcmdb_record_write(bcmdb_set *sor, bcmdb_key key, int offset, int size, const void *data);
+
+
+/** Write record field.
+ *
+ * The macro updates record field value.\n
+ * The macro aquires and releases record-modify lock.
+ *
+ * \param[in] _sor parent set of records
+ * \param[in] _key record key
+ * \param[in] _record_type type of the underlying data structure.
+ * \param[in] _field_name data structure field name
+ * \param[in] _field_value field value
+ * \return
+ * =0-OK\n
+ * <0-error code
+ */
+#define bcmdb_record_write_field(_sor, _key, _record_type, _field_name, _field_value) \
+ ({ \
+ typeof(((_record_type *)0)->_field_name) _f = _field_value;\
+ bcmdb_record_write(_sor, _key, offsetof(_record_type, _field_name), sizeof(_f), &_f);\
+ });
+
+
+/** Register notification function to get informed
+ * when data base set is modified.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] cb callback function pointer
+ * \param[in] cb_priv private data that should be passed to the callback
+ * \return
+ * =0 - OK\n
+ * <0 - error code
+ */
+int bcmdb_set_notify_register(bcmdb_set *sor, bcmdb_notify_cb cb, long cb_priv);
+
+
+/** Data base iterator
+ *
+ * \param[in] set data base set
+ * \param[in] prev last entry. BCMDB_KEY_ANY=start from the beginning
+ * \return data base entry key following prev or BCMDB_KEY_NO_MORE if end is reached.\n
+ * BCMDB_KEY_INVAL is reqturned if prev key is invalid
+ */
+bcmdb_key bcmdb_set_iterate(const bcmdb_set *set, bcmdb_key prev);
+
+
+/** Print database structure.
+ *
+ * \param[in] set root set
+ */
+void bcmdb_set_print_structure(const bcmdb_set *set);
+
+
+/** Format record for printing.
+ *
+ * The function converts record data to human-readable format.
+ *
+ * \param[in] sor parent set of records
+ * \param[in] key record key
+ * \param[out] buffer output buffer
+ * \param[in] size buffer size
+ * \return
+ * >=0-amount of data placed in the buffer\n
+ * <0-error code
+ */
+int bcmdb_record_read_formatted(bcmdb_set *sor, bcmdb_key key, char *buffer, int size);
+
+
+/** @} end of bcmdb_e group */
+
+
+#endif /* #ifndef BCMDB_ENGINE_H */
+
diff --git a/bal_release/src/common/db_engine/unitest.c b/bal_release/src/common/db_engine/unitest.c
new file mode 100644
index 0000000..ca638ce
--- /dev/null
+++ b/bal_release/src/common/db_engine/unitest.c
@@ -0,0 +1,490 @@
+/******************************************************************************
+ *
+ * <:copyright-BRCM:2016:DUAL/GPL:standard
+ *
+ * Copyright (c) 2016 Broadcom
+ * All Rights Reserved
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed
+ * to you under the terms of the GNU General Public License version 2
+ * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+ * with the following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give
+ * you permission to link this software with independent modules, and
+ * to copy and distribute the resulting executable under terms of your
+ * choice, provided that you also meet, for each linked independent
+ * module, the terms and conditions of the license of that module.
+ * An independent module is a module which is not derived from this
+ * software. The special exception does not apply to any modifications
+ * of the software.
+ *
+ * Not withstanding the above, under no circumstances may you combine
+ * this software in any way with any other Broadcom software provided
+ * under a license other than the GPL, without Broadcom's express prior
+ * written consent.
+ *
+ * :>
+ *
+ *****************************************************************************/
+
+/*
+ * unitest.c
+ *
+ * Created on: 2013-12-10
+ * Author: swallace
+ */
+
+#include "bcmos_system.h"
+#include "bcm_db_engine.h"
+
+/* EPON LLID data structure subset */
+typedef enum
+ {
+ /* Free entry in the LLID management table, available for assignment on an
+ MPCP register request. */
+ unassigned_llid,
+ /* Locked out waiting on a timer to release the LLID */
+ not_registered_llid,
+ /* Waiting for permission to register from host */
+ ignored_llid,
+ /* LLID has been assigned to an ONU MAC but not registered. Intermediate
+ state before the ONU returns a registration ack. */
+ wait_reg_ack_llid,
+ /* OLT link is in-service; user traffic is allowed */
+ inservice_llid,
+ wait_no_reports_llid,
+ /* The following state only applies to multicast/flood links */
+ in_service_mcast_llid,
+ /* We want a "Reserved" state for things like Optical monitoring */
+ reserved_llid,
+ /* We have detected a rogue ONU on this LLID - don't use it! */
+ quarantined_llid,
+ llid_state_count,
+ } epon_olt_llid_state;
+
+static char *get_llid_state_string(epon_olt_llid_state llid_state)
+{
+ static char *llid_state_strings[]= {
+ "unassigned",
+ "not_registered",
+ "ignored",
+ "wait_reg_ack",
+ "inservice",
+ "wait_no_reports",
+ "in_service_mcast",
+ "reserved",
+ "quarantined"
+ };
+
+ return llid_state_strings[(uint16_t)(llid_state)];
+}
+
+#define MAX_LINKS_PER_PORT 512
+#define MAX_PORTS_PER_HALF_CHIP 8
+#define MAX_LINKS_PER_HALF_CHIP ((MAX_LINKS_PER_PORT)*(MAX_PORTS_PER_HALF_CHIP))
+
+typedef uint8_t core_epon;
+typedef uint16_t hw_link_index;
+
+typedef struct epon_db_olt_llid_rec
+ {
+ epon_olt_llid_state state;
+ core_epon epon;
+ hw_link_index index;
+ } epon_db_olt_llid_rec;
+
+
+typedef struct epon_msg_olt_llid_rec
+{
+ uint16_t index;
+ epon_db_olt_llid_rec llid_rec;
+} epon_msg_olt_llid_rec;
+
+typedef enum
+ {
+ enabled,
+ disabled,
+ } epon_olt_port_state;
+
+static char *get_port_state_string(epon_olt_port_state port_state)
+{
+ static char *port_state_strings[]= {
+ "enabled",
+ "disabled",
+ };
+
+ return port_state_strings[(uint16_t)(port_state)];
+}
+
+
+typedef struct epon_db_olt_port_rec
+ {
+ epon_olt_port_state state;
+ } epon_db_olt_port_rec;
+
+typedef struct epon_msg_olt_port_rec
+{
+ uint16_t index;
+ epon_db_olt_port_rec port_rec;
+} epon_msg_olt_port_rec;
+
+typedef enum
+{
+ epon_olt_link_record,
+ epon_olt_port_record,
+ num_db_tables,
+} db_tables;
+
+
+/* Master database handle */
+static bcmdb_set *db_sos_set;
+
+static bcmdb_set* epon_get_db_handle(void)
+{
+ return db_sos_set;
+}
+
+#define LINK_REC_DB() bcmdb_set *db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_link_record)
+
+#define PORT_REC_DB bcmdb_set *db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_port_record)
+
+/* Database test messages - */
+typedef enum
+ {
+ update_link_db = 20,
+ update_port_db = 21,
+ dump_db = 30,
+ } dbtest_msgid;
+
+static inline const epon_db_olt_llid_rec *epon_olt_get_llid_rec_read(uint16_t index)
+ {
+ LINK_REC_DB();
+ return bcmdb_record_get_read(db_set, index, epon_db_olt_llid_rec);
+ };
+
+static inline void epon_db_olt_unlock_llid_rec(uint16_t index)
+ {
+ LINK_REC_DB();
+ bcmdb_record_unlock_read(db_set, index);
+ }
+
+
+#define OltGetLlidRecWrite(index) \
+ ({ \
+ LINK_REC_DB(); \
+ bcmdb_record_get_write(db_set, index, epon_db_olt_llid_rec);\
+ })
+
+#define OltCommitLlidRec(index) \
+ ({ \
+ LINK_REC_DB(); \
+ bcmdb_record_unlock_write(db_set, BCMOS_FALSE);\
+ })
+
+
+static void ut_dump_db(void)
+{
+ uint16_t index;
+ bcmdb_set *db_set;
+
+ db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_port_record);
+ for (index = 0; index < MAX_PORTS_PER_HALF_CHIP; index++)
+ {
+ const epon_db_olt_port_rec *port_rec;
+ port_rec = bcmdb_record_get_read(db_set, index, epon_db_olt_port_rec);
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO,
+ "Record %d, state %s\n", index,
+ get_port_state_string(port_rec->state));
+ bcmdb_record_unlock_read(db_set, index);
+ }
+
+ for (index = 0; index < MAX_LINKS_PER_HALF_CHIP; index++)
+ {
+ const epon_db_olt_llid_rec *llid_rec;
+ llid_rec = epon_olt_get_llid_rec_read(index);
+ if (llid_rec->state != unassigned_llid)
+ {
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO,
+ "Record %d, port %d, state %s\n", llid_rec->index,
+ llid_rec->epon, get_llid_state_string(llid_rec->state));
+ }
+ epon_db_olt_unlock_llid_rec(index);
+ }
+
+
+}
+
+static void ut_update_link_db(epon_msg_olt_llid_rec *rec)
+{
+ epon_db_olt_llid_rec *llid_rec;
+ llid_rec=OltGetLlidRecWrite(rec->index);
+ llid_rec->state=rec->llid_rec.state;
+
+ OltCommitLlidRec(index);
+}
+
+static void ut_update_port_db(epon_msg_olt_port_rec *rec)
+{
+ bcmdb_set *db_set;
+ epon_db_olt_port_rec *port_rec;
+
+ db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_port_record);
+ port_rec = bcmdb_record_get_write(db_set, rec->index, epon_db_olt_port_rec);
+
+ port_rec->state=rec->port_rec.state;
+
+ bcmdb_record_unlock_write(db_set, BCMOS_FALSE);
+}
+
+
+static void ut_msg_handler(dbtest_msgid id, void *data)
+ {
+ switch (id)
+ {
+ case update_link_db:
+ ut_update_link_db((epon_msg_olt_llid_rec*)data);
+ break;
+
+ case update_port_db:
+ ut_update_port_db((epon_msg_olt_port_rec*)data);
+ break;
+
+ case dump_db:
+ ut_dump_db();
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+/* Database engine unit test functions */
+static uint16_t epon_db_data_init(void)
+ {
+ uint16_t index;
+ bcmdb_set *db_set;
+ int rc = 0;
+
+ db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_link_record);
+ for ( index = 0;
+ (index < MAX_LINKS_PER_HALF_CHIP) && (rc >= 0) && (db_set != NULL) ;
+ index++)
+ {
+ epon_db_olt_llid_rec llid_rec;
+
+ llid_rec.state = unassigned_llid;
+ llid_rec.epon = index/MAX_LINKS_PER_PORT;
+ llid_rec.index = index;
+ rc = bcmdb_record_add(db_set, index, (void *)&llid_rec);
+ }
+
+ db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_port_record);
+ for ( index = 0;
+ (index < MAX_PORTS_PER_HALF_CHIP) && (rc >= 0) && (db_set != NULL) ;
+ index++)
+ {
+ epon_db_olt_port_rec port_rec;
+ port_rec.state = disabled;
+ rc = bcmdb_record_add(db_set, index, (void *)&port_rec);
+ }
+ return rc;
+ }
+
+static int epon_db_instance_init(void)
+ {
+ bcmdb_sos_init db_sos_inst;
+ bcmdb_sor_init db_sor_inst;
+ const char* db_name = "EPON STACK";
+ const char* db_llid_name = "EPON LINK REC";
+ const char* db_eport_name = "EPON PORT REC";
+ bcmdb_set *db_sor_set;
+ int rc;
+
+ db_sos_inst.name = db_name;
+ db_sos_inst.backend_type = BCMDB_BACKEND_ARRAY;
+ db_sos_inst.max_entries = num_db_tables;
+ rc = bcmdb_make_set_of_sets(&db_sos_inst, &db_sos_set);
+
+ if (rc >= 0)
+ {
+ db_sor_inst.name = db_llid_name;
+ db_sor_inst.backend_type = BCMDB_BACKEND_ARRAY;
+ db_sor_inst.lock_policy = BCMDB_LOCK_NB_READ_SHADOW_WRITE;
+ db_sor_inst.max_entries = MAX_LINKS_PER_HALF_CHIP;
+ db_sor_inst.record_size = sizeof(epon_db_olt_llid_rec);
+ db_sor_inst.format = NULL;
+ bcmdb_make_set_of_records(&db_sor_inst, BCMOS_TRUE, &db_sor_set);
+
+ rc = bcmdb_set_add(epon_get_db_handle(),
+ epon_olt_link_record, db_sor_set);
+ }
+ if (rc >= 0)
+ {
+ db_sor_inst.name = db_eport_name;
+ db_sor_inst.backend_type = BCMDB_BACKEND_ARRAY;
+ db_sor_inst.lock_policy = BCMDB_LOCK_NB_READ_SHADOW_WRITE;
+ db_sor_inst.max_entries = MAX_PORTS_PER_HALF_CHIP;
+ db_sor_inst.record_size = sizeof(epon_db_olt_port_rec);
+ db_sor_inst.format = NULL;
+ rc = bcmdb_make_set_of_records(&db_sor_inst, BCMOS_TRUE, &db_sor_set);
+ }
+
+ rc = bcmdb_set_add(epon_get_db_handle(), epon_olt_port_record, db_sor_set);
+
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "database creation returnd %d\n", rc);
+
+ if (rc >= 0)
+ {
+ rc = epon_db_data_init();
+ }
+ return rc;
+ }
+
+/* Thread handlers - so that the DB accesses can be tested across multiple
+ threads. */
+static int task1_handler(long data)
+{
+ bcmos_msg_queue *q = (bcmos_msg_queue *)data;
+ bcmos_msg *msg;
+
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "traditional task handler\n");
+
+ while (1)
+ {
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "Waiting for message\n");
+
+ bcmos_msg_recv(q, BCMOS_WAIT_FOREVER, &msg);
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO,
+ "Received message ID %d, data %p\n",
+ msg->type, msg->data);
+
+ ut_msg_handler(msg->type, msg->data);
+ bcmos_usleep(100000);
+ }
+
+ return 0;
+}
+
+
+static bcmos_errno mod1_init(long data)
+{
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "%ld\n", data);
+ return BCM_ERR_OK;
+}
+
+static void mod_msg_handler(bcmos_module_id module_id, bcmos_msg *msg)
+{
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "module %d msg %d data %p\n",
+ module_id, msg->type, msg->data);
+
+ ut_msg_handler(msg->type, msg->data);
+}
+
+
+
+/* Unit test function - */
+int main(int argc, char *argv[])
+{
+ bcmos_task_parm tp = {};
+ bcmos_msg_queue_parm qp = {};
+ bcmos_module_parm mp = {};
+ bcmos_msg msg1 = {};
+ bcmos_msg msg2 = {};
+
+ bcmos_task t1;
+ bcmos_task t2;
+ bcmos_msg_queue q1;
+ bcmos_errno rc;
+ epon_msg_olt_llid_rec link_rec1, link_rec2;
+ epon_msg_olt_port_rec port_msg1, port_msg2;
+
+ bcmos_init();
+ bcmos_trace_level_set(BCMOS_TRACE_LEVEL_DEBUG);
+
+ if (epon_db_instance_init() < 0)
+ {
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_ERROR,
+ "Could not instantiate a Database\n");
+ return BCM_ERR_NOMEM;
+ }
+
+ BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "Database set %p\n",
+ bcmdb_set_handle(db_sos_set, epon_olt_link_record));
+
+ /* Create message queue */
+ qp.name = "msg queue1";
+ qp.size = 16;
+ qp.high_wm = 14;
+ qp.low_wm = 12;
+ rc = bcmos_msg_queue_create(&q1, &qp);
+
+ /* Create a couple of threads */
+ tp.name = "task1";
+ tp.handler = task1_handler;
+ tp.data = (long)&q1;
+ rc = bcmos_task_create(&t1, &tp);
+
+ tp.name = "task2";
+ tp.handler = NULL;
+ tp.data = 0;
+ rc = bcmos_task_create(&t2, &tp);
+
+ /* Register a module */
+ mp.qparm.name = "module1";
+ mp.qparm.size = 16;
+ mp.init = mod1_init;
+ bcmos_module_create(BCMOS_MODULE_ID_TEST1, &t2, &mp);
+
+ /* Wait some */
+ bcmos_usleep(2000000);
+
+ /* Send a message to update the DB - enable a port*/
+ port_msg1.index=5;
+ port_msg1.port_rec.state=enabled;
+ msg1.type = update_port_db;
+ msg1.data = &port_msg1;
+ bcmos_msg_send(&q1, &msg1, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+ /* Send a message to update the DB - enable a port*/
+ port_msg2.index=3;
+ port_msg2.port_rec.state=enabled;
+ msg2.type = update_port_db;
+ msg2.data = &port_msg2;
+ bcmos_msg_send(&q1, &msg2, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+ /* Wait some */
+ bcmos_usleep(2000000);
+
+ /* Send a message to update the DB - put a link In Service*/
+ link_rec1.index=14;
+ link_rec1.llid_rec.state=inservice_llid;
+ msg1.type = update_link_db;
+ msg1.data = &link_rec1;
+ bcmos_msg_send(&q1, &msg1, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+ /* Send a message to update the DB - quarantine a link */
+ link_rec2.index=22;
+ link_rec2.llid_rec.state=quarantined_llid;
+ msg2.type = update_link_db;
+ msg2.data = &link_rec2;
+ msg2.handler = mod_msg_handler;
+ bcmos_msg_send_to_module(BCMOS_MODULE_ID_TEST1, &msg2, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+ /* Wait some */
+ bcmos_usleep(2000000);
+
+ /* Send a message to dump the DB */
+ msg1.type = dump_db;
+ msg1.handler = mod_msg_handler;
+ msg1.data = NULL;
+ bcmos_msg_send_to_module(BCMOS_MODULE_ID_TEST1, &msg1, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+
+ /* Wait some */
+ bcmos_usleep(2000000);
+
+ return rc;
+}