| /****************************************************************************** |
| * |
| * <: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; |
| } |