diff options
Diffstat (limited to 'drivers/dsp/syslink/multicore_ipc/listmp_sharedmemory.c')
-rw-r--r-- | drivers/dsp/syslink/multicore_ipc/listmp_sharedmemory.c | 1492 |
1 files changed, 1492 insertions, 0 deletions
diff --git a/drivers/dsp/syslink/multicore_ipc/listmp_sharedmemory.c b/drivers/dsp/syslink/multicore_ipc/listmp_sharedmemory.c new file mode 100644 index 000000000000..eb44bc29fede --- /dev/null +++ b/drivers/dsp/syslink/multicore_ipc/listmp_sharedmemory.c @@ -0,0 +1,1492 @@ +/* + * listmp_sharedmemory.c + * + * The listmp_sharedmemory is a linked-list based module designed to be + * used in a multi-processor environment. It is designed to + * provide a means of communication between different processors. + * + * Copyright (C) 2008-2009 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. + */ + +/*! + * This module is instance based. Each instance requires a small + * piece of shared memory. This is specified via the #shared_addr + * parameter to the create. The proper #shared_addr_size parameter + * can be determined via the #shared_memreq call. Note: the + * parameters to this function must be the same that will used to + * create or open the instance. + * The listmp_sharedmemory module uses a #NameServer instance + * to store instance information when an instance is created and + * the name parameter is non-NULL. If a name is supplied, it must + * be unique for all listmp_sharedmemory instances. + * The #create also initializes the shared memory as needed. The + * shared memory must be initialized to 0 or all ones + * (e.g. 0xFFFFFFFFF) before the listmp_sharedmemory instance + * is created. + * Once an instance is created, an open can be performed. The #open + * is used to gain access to the same listmp_sharedmemory instance. + * Generally an instance is created on one processor and opened + * on the other processor. + * The open returns a listmp_sharedmemory instance handle like the + * create, however the open does not modify the shared memory. + * Generally an instance is created on one processor and opened + * on the other processor. + * There are two options when opening the instance: + * @li Supply the same name as specified in the create. The + * listmp_sharedmemory module queries the NameServer to get the + * needed information. + * @li Supply the same #shared_addr value as specified in the + * create. + * If the open is called before the instance is created, open + * returns NULL. + * There is currently a list of restrictions for the module: + * @li Both processors must have the same endianness. Endianness + * conversion may supported in a future version of + * listmp_sharedmemory. + * @li The module will be made a gated module + */ + + +/* Standard headers */ +#include <linux/types.h> +#include <linux/module.h> + +/* Utilities headers */ +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/string.h> + +/* Syslink headers */ +#include <syslink/atomic_linux.h> + +/* Module level headers */ +#include <nameserver.h> +#include <sharedregion.h> +#include <multiproc.h> +#include "_listmp.h" +#include <listmp.h> +#include <gatepeterson.h> +#include <listmp_sharedmemory.h> + + +/* ============================================================================= + * Globals + * ============================================================================= + */ +/*! @brief Macro to make a correct module magic number with refCount */ +#define LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(x) \ + ((LISTMPSHAREDMEMORY_MODULEID << 12u) | (x)) + +/*! + * @brief Name of the reserved NameServer used for listmp_sharedmemory. + */ +#define LISTMP_SHAREDMEMORY_NAMESERVER "ListMPSharedMemory" + +/*! + * @brief Cache size + */ +#define LISTMP_SHAREDMEMORY_CACHESIZE 128 + + + +/* ============================================================================= + * Structures and Enums + * ============================================================================= + */ +/*! @brief structure for listmp_sharedmemory module state */ +struct listmp_sharedmemory_module_object { + atomic_t ref_count; + /*!< Reference count */ + void *ns_handle; + /*!< Handle to the local NameServer used for storing GP objects */ + struct list_head obj_list; + /*!< List holding created listmp_sharedmemory objects */ + struct mutex *local_gate; + /*!< Handle to lock for protecting obj_list */ + struct listmp_config cfg; + /*!< Current config values */ + struct listmp_config default_cfg; + /*!< Default config values */ + listmp_sharedmemory_params default_inst_params; + /*!< Default instance creation parameters */ +}; + +/*! + * @var listmp_sharedmemory_nameServer + * + * @brief Name of the reserved NameServer used for listmp_sharedmemory. + */ +static +struct listmp_sharedmemory_module_object listmp_sharedmemory_state = { + .default_cfg.max_name_len = 32, + .default_cfg.use_name_server = true, + .default_inst_params.shared_addr = 0, + .default_inst_params.shared_addr_size = 0, + .default_inst_params.name = NULL, + .default_inst_params.gate = NULL, + .default_inst_params.list_type = listmp_type_SHARED}; + +/*! + * @brief Structure for the internal Handle for the listmp_sharedmemory. + */ +struct listmp_sharedmemory_obj{ + struct list_head list_elem; + /*!< Used for creating a linked list */ + VOLATILE struct listmp_elem *listmp_elem; + /*!< Used for storing listmp_sharedmemory element */ + struct listmp_proc_attrs *owner; + /*!< Creator's attributes associated with an instance */ + listmp_sharedmemory_params params; + /*!< the parameter structure */ + void *ns_key; + u32 index; + /*!< the index for SrPtr */ + VOLATILE struct listmp_attrs *attrs; + /*!< Shared memory attributes */ + void *top; + /*!< Pointer to the top Object */ +}; + + +/* ============================================================================= + * Function definitions + * ============================================================================= + */ +/* + * ======== _listmp_sharedmemory_create ======== + * Purpose: + * Creates a new instance of listmp_sharedmemory module. This is an internal + * function because both listmp_sharedmemory_create and + * listmp_sharedmemory_open call use the same functionality. + */ +static listmp_sharedmemory_handle +_listmp_sharedmemory_create(listmp_sharedmemory_params *params, + u32 create_flag); + + +/* ============================================================================= + * Function API's + * ============================================================================= + */ +/* + * ======== listmp_sharedmemory_get_config ======== + * Purpose: + * Function to get configuration parameters to setup the + * the listmp_sharedmemory module. + */ +int listmp_sharedmemory_get_config(struct listmp_config *cfgParams) +{ + int status = 0; + + if (WARN_ON(cfgParams == NULL)) { + status = -EINVAL; + goto exit; + } + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) + /* If setup has not yet been called) */ + memcpy(cfgParams, &listmp_sharedmemory_state.default_cfg, + sizeof(struct listmp_config)); + else + memcpy(cfgParams, &listmp_sharedmemory_state.cfg, + sizeof(struct listmp_config)); + return 0; + +exit: + printk(KERN_ERR "listmp_sharedmemory_get_config failed: " + "status = 0x%x\n", status); + return status; +} + +/* + * ======== listmp_sharedmemory_setup ======== + * Purpose: + * Function to setup the listmp_sharedmemory module. + */ +int listmp_sharedmemory_setup(struct listmp_config *config) +{ + int status = 0; + int status1 = 0; + void *nshandle = NULL; + struct nameserver_params params; + struct listmp_config tmp_cfg; + + /* This sets the refCount variable is not initialized, upper 16 bits is + * written with module Id to ensure correctness of refCount variable. + */ + atomic_cmpmask_and_set(&listmp_sharedmemory_state.ref_count, + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0)); + + if (atomic_inc_return(&listmp_sharedmemory_state.ref_count) + != LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) { + return 1; + } + + if (config == NULL) { + listmp_sharedmemory_get_config(&tmp_cfg); + config = &tmp_cfg; + } + + if (WARN_ON(config->max_name_len == 0)) { + status = -EINVAL; + goto exit; + } + + if (likely((config->use_name_server == true))) { + /* Initialize the parameters */ + nameserver_params_init(¶ms); + params.max_value_len = 4; + params.max_name_len = config->max_name_len; + /* Create the nameserver for modules */ + nshandle = nameserver_create( + LISTMP_SHAREDMEMORY_NAMESERVER, ¶ms); + if (unlikely(nshandle == NULL)) { + status = LISTMPSHAREDMEMORY_E_FAIL; + goto exit; + } + } + + listmp_sharedmemory_state.ns_handle = nshandle; + /* Construct the list object */ + INIT_LIST_HEAD(&listmp_sharedmemory_state.obj_list); + /* Create a lock for protecting list object */ + listmp_sharedmemory_state.local_gate = \ + kmalloc(sizeof(struct mutex), GFP_KERNEL); + if (listmp_sharedmemory_state.local_gate == NULL) { + status = -ENOMEM; + goto clean_nameserver; + } + + mutex_init(listmp_sharedmemory_state.local_gate); + /* Copy the cfg */ + memcpy(&listmp_sharedmemory_state.cfg, config, + sizeof(struct listmp_config)); + return 0; + +clean_nameserver: + printk(KERN_ERR "listmp_sharedmemory_setup: Failed to create the local " + "gate! status = 0x%x\n", status); + atomic_set(&listmp_sharedmemory_state.ref_count, + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0)); + status1 = nameserver_delete + (&(listmp_sharedmemory_state.ns_handle)); + BUG_ON(status1 < 0); + +exit: + printk(KERN_ERR "listmp_sharedmemory_setup failed! status = 0x%x\n", + status); + return status; +} + +/* + * ======== listmp_sharedmemory_destroy ======== + * Purpose: + * Function to destroy the listmp_sharedmemory module. + */ +int listmp_sharedmemory_destroy(void) +{ + int status = 0; + int status1 = 0; + struct list_head *elem = NULL; + struct list_head *head = &listmp_sharedmemory_state.obj_list; + struct list_head *next; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + + if (atomic_dec_return(&listmp_sharedmemory_state.ref_count) + == LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0)) { + /* Temporarily increment refCount here. */ + atomic_set(&listmp_sharedmemory_state.ref_count, + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)); + /* Check if any listmp_sharedmemory instances have not been + * deleted so far. If not, delete them. */ + for (elem = (head)->next; elem != (head); elem = next) { + /* Retain the next pointer so it doesn't + get overwritten */ + next = elem->next; + if (((struct listmp_sharedmemory_obj *) elem)->owner + ->proc_id == multiproc_get_id(NULL)) { + status1 = listmp_sharedmemory_delete( + (listmp_sharedmemory_handle *) + &(((struct listmp_sharedmemory_obj *) \ + elem)->top)); + WARN_ON(status1 < 0); + } else { + status1 = listmp_sharedmemory_close( + (listmp_sharedmemory_handle) + (((struct listmp_sharedmemory_obj *) \ + elem)->top)); + WARN_ON(status1 < 0); + } + } + + /* Again reset refCount. */ + atomic_set(&listmp_sharedmemory_state.ref_count, + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0)); + if (likely(listmp_sharedmemory_state.cfg.use_name_server + == true)) { + /* Delete the nameserver for modules */ + status = nameserver_delete( + &(listmp_sharedmemory_state.ns_handle)); + BUG_ON(status < 0); + } + + /* Destruct the list object */ + list_del(&listmp_sharedmemory_state.obj_list); + /* Delete the list lock */ + kfree(listmp_sharedmemory_state.local_gate); + listmp_sharedmemory_state.local_gate = NULL; + memset(&listmp_sharedmemory_state.cfg, 0, + sizeof(struct listmp_config)); + /* Decrease the refCount */ + atomic_set(&listmp_sharedmemory_state.ref_count, + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0)); + } + +exit: + if (status < 0) { + printk(KERN_ERR "listmp_sharedmemory_destroy failed! " + "status = 0x%x\n", status); + } + return status; +} + +/* + * ======== listmp_sharedmemory_params_init ======== + * Purpose: + * Function to initialize the config-params structure with supplier-specified + * defaults before instance creation. + */ +void listmp_sharedmemory_params_init(listmp_sharedmemory_handle handle, + listmp_sharedmemory_params *params) +{ + s32 status = 0; + struct listmp_sharedmemory_obj *obj = NULL; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + + if (WARN_ON(params == NULL)) { + status = -EINVAL; + goto exit; + } + + if (handle == NULL) { + memcpy(params, + &(listmp_sharedmemory_state.default_inst_params), + sizeof(listmp_sharedmemory_params)); + } else { + obj = (struct listmp_sharedmemory_obj *) + (((listmp_sharedmemory_object *) handle)->obj); + + memcpy((void *)&obj->params, + (void *)params, + sizeof(listmp_sharedmemory_params)); + } + return; + +exit: + printk(KERN_ERR "listmp_sharedmemory_params_init failed! " + "status = 0x%x\n", status); + return; +} + +/* + * ======== listmp_sharedmemory_create ======== + * Purpose: + * Creates a new instance of listmp_sharedmemory module. + */ +listmp_sharedmemory_handle listmp_sharedmemory_create( + listmp_sharedmemory_params *params) +{ + s32 status = 0; + listmp_sharedmemory_object *handle = NULL; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + + if (WARN_ON(params == NULL)) { + status = -EINVAL; + goto exit; + } + + if (WARN_ON((params->shared_addr == (u32)NULL) + && (params->list_type != listmp_type_FAST))) { + status = -EINVAL; + goto exit; + } + if (WARN_ON(params->shared_addr_size + < listmp_sharedmemory_shared_memreq(params))) { + status = -EINVAL; + goto exit; + } + + handle = (listmp_sharedmemory_object *) + _listmp_sharedmemory_create(params, true); + +exit: + if (status < 0) { + printk(KERN_ERR "listmp_sharedmemory_create failed! " + "status = 0x%x\n", status); + } + return (listmp_sharedmemory_handle) handle; +} + +/* + * ======== listmp_sharedmemory_delete ======== + * Purpose: + * Deletes a instance of listmp_sharedmemory instance object. + */ +int listmp_sharedmemory_delete(listmp_sharedmemory_handle *listmp_handleptr) +{ + int status = 0; + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + listmp_sharedmemory_params *params = NULL; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + if (WARN_ON(listmp_handleptr == NULL)) { + status = -EINVAL; + goto exit; + } + if (WARN_ON(*listmp_handleptr == NULL)) { + status = -EINVAL; + goto exit; + } + + handle = (listmp_sharedmemory_object *) (*listmp_handleptr); + obj = (struct listmp_sharedmemory_obj *) handle->obj; + if (WARN_ON(obj == NULL)) { + status = -EINVAL; + goto exit; + } + + params = (listmp_sharedmemory_params *) &obj->params; + + if (obj->owner->proc_id != multiproc_get_id(NULL)) { + status = -EPERM; + goto exit; + } + + if (obj->owner->open_count > 1) { + status = -EBUSY; + goto exit; + } + + if (obj->owner->open_count != 1) { + status = -EBUSY; + goto exit; + } + + /* Remove from the local list */ + status = mutex_lock_interruptible( + listmp_sharedmemory_state.local_gate); + if (status) + goto exit; + list_del(&obj->list_elem); + mutex_unlock(listmp_sharedmemory_state.local_gate); + + if (likely(params->name != NULL)) { + /* Free memory for the name */ + kfree(params->name); + /* Remove from the name server */ + if (likely(listmp_sharedmemory_state.cfg.use_name_server)) { + if (obj->ns_key != NULL) { + nameserver_remove_entry( + listmp_sharedmemory_state.ns_handle, + obj->ns_key); + obj->ns_key = NULL; + } + } + } + + /* Free memory for the processor info's */ + if (obj) + kfree(obj->owner); + /* Now free the handle */ + kfree(obj); + obj = NULL; + + /* Now free the handle */ + kfree(handle); + handle = NULL; + *listmp_handleptr = NULL; + +exit: + if (status < 0) { + printk(KERN_ERR "listmp_sharedmemory_delete failed! " + "status = 0x%x\n", status); + } + return status; + +} + + +/* + * ======== listmp_sharedmemory_shared_memreq ======== + * Purpose: + * Function to return the amount of shared memory required for creation of + * each instance. + */ +int listmp_sharedmemory_shared_memreq(listmp_sharedmemory_params *params) +{ + int retval = 0; + + if (WARN_ON(params == NULL)) { + retval = -EINVAL; + goto exit; + } + + if (params->list_type == listmp_type_SHARED) + retval = (LISTMP_SHAREDMEMORY_CACHESIZE * 2); + +exit: + if (retval < 0) { + printk(KERN_ERR "listmp_sharedmemory_shared_memreq failed! " + "retval = 0x%x\n", retval); + } + /*! @retval ((1 * sizeof(struct listmp_elem)) + *! + 1 * sizeof(struct listmp_attrs)) if params is null */ + /*! @retval (2 * cacheSize) if params is provided */ + /*! @retval (0) if hardware queue */ + return retval; +} + +/* + * ======== listmp_sharedmemory_open ======== + * Purpose: + * Function to open a listmp_sharedmemory instance + */ +int listmp_sharedmemory_open(listmp_sharedmemory_handle *listmp_handleptr, + listmp_sharedmemory_params *params) +{ + int status = 0; + bool done_flag = false; + struct list_head *elem; + u32 shared_shm_base; + struct listmp_attrs *attrs; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + if (WARN_ON(listmp_handleptr == NULL)) { + status = -EINVAL; + goto exit; + } + if (WARN_ON(params == NULL)) { + status = -EINVAL; + goto exit; + } + + if (WARN_ON((listmp_sharedmemory_state.cfg.use_name_server == false) + && (params->shared_addr == (u32)NULL))) { + status = -EINVAL; + goto exit; + } + + if (WARN_ON((listmp_sharedmemory_state.cfg.use_name_server == true) + && (params->shared_addr == (u32)NULL)) + && (params->name == NULL)) { + status = -EINVAL; + goto exit; + } + + /* First check in the local list */ + list_for_each(elem, &listmp_sharedmemory_state.obj_list) { + if (((struct listmp_sharedmemory_obj *)elem)->params.shared_addr + == params->shared_addr) { + status = mutex_lock_interruptible( + listmp_sharedmemory_state.local_gate); + if (status) + goto exit; + if (((struct listmp_sharedmemory_obj *)elem) + ->owner->proc_id + == multiproc_get_id(NULL)) + ((struct listmp_sharedmemory_obj *)elem) + ->owner->open_count++; + mutex_unlock( + listmp_sharedmemory_state.local_gate); + *listmp_handleptr = \ + (((struct listmp_sharedmemory_obj *) + elem)->top); + done_flag = true; + break; + } else if ((params->name != NULL) + && (((struct listmp_sharedmemory_obj *)elem) \ + ->params.name != NULL)){ + if (strcmp(((struct listmp_sharedmemory_obj *)elem) + ->params.name, params->name) == 0) { + status = mutex_lock_interruptible( + listmp_sharedmemory_state.local_gate); + if (status) + goto exit; + if (((struct listmp_sharedmemory_obj *)elem) + ->owner->proc_id + == multiproc_get_id(NULL)) + ((struct listmp_sharedmemory_obj *)elem) + ->owner->open_count++; + mutex_unlock( + listmp_sharedmemory_state.local_gate); + *listmp_handleptr = \ + (((struct listmp_sharedmemory_obj *) + elem)->top); + done_flag = true; + break; + } + } + } + + if (likely(done_flag == false)) { + if (unlikely(params->shared_addr == NULL)) { + if (likely(listmp_sharedmemory_state.cfg.use_name_server + == true)){ + /* Find in name server */ + status = + nameserver_get( + listmp_sharedmemory_state.ns_handle, + params->name, + &shared_shm_base, + sizeof(u32), + NULL); + if (status >= 0) + params->shared_addr = + sharedregion_get_ptr( + (u32 *)shared_shm_base); + if (params->shared_addr == NULL) { + status = + LISTMPSHAREDMEMORY_E_NOTCREATED; + goto exit; + } + } + } + } + + if (status >= 0) { + attrs = (struct listmp_attrs *) (params->shared_addr); + if (unlikely(attrs->status != (LISTMP_SHAREDMEMORY_CREATED))) + status = LISTMPSHAREDMEMORY_E_NOTCREATED; + else if (unlikely(attrs->version != + (LISTMP_SHAREDMEMORY_VERSION))) + status = LISTMPSHAREDMEMORY_E_VERSION; + } + + if (likely(status >= 0)) + *listmp_handleptr = (listmp_sharedmemory_handle) + _listmp_sharedmemory_create(params, false); + +exit: + if (status < 0) { + printk(KERN_ERR "listmp_sharedmemory_open failed! " + "status = 0x%x\n", status); + } + return status; +} + +/* + * ======== listmp_sharedmemory_close ======== + * Purpose: + * Function to close a previously opened instance + */ +int listmp_sharedmemory_close(listmp_sharedmemory_handle listmp_handle) +{ + int status = 0; + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + listmp_sharedmemory_params *params = NULL; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + + if (WARN_ON(listmp_handle == NULL)) { + status = -EINVAL; + goto exit; + } + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + if (obj == NULL) { + status = -EINVAL; + goto exit; + } + + status = mutex_lock_interruptible( + listmp_sharedmemory_state.local_gate); + if (status) + goto exit; + if (obj->owner->proc_id == multiproc_get_id(NULL)) + (obj)->owner->open_count--; + + params = (listmp_sharedmemory_params *) &obj->params; + /* Check if ListMP is opened on same processor*/ + if (((struct listmp_sharedmemory_obj *)obj)->owner->creator == false) { + list_del(&obj->list_elem); + /* remove from the name server */ + if (params->name != NULL) + /* Free memory for the name */ + kfree(params->name); + + kfree(obj->owner); + kfree(obj); + obj = NULL; + kfree(handle); + handle = NULL; + } + + mutex_unlock(listmp_sharedmemory_state.local_gate); + +exit: + if (status < 0) { + printk(KERN_ERR "listmp_sharedmemory_close failed! " + "status = 0x%x\n", status); + } + return status; +} + +/* + * ======== listmp_sharedmemory_empty ======== + * Purpose: + * Function to check if the shared memory list is empty + */ +bool listmp_sharedmemory_empty(listmp_sharedmemory_handle listmp_handle) +{ + + int status = 0; + bool is_empty = false; + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + s32 retval = 0; + struct listmp_elem *sharedHead; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + + if (WARN_ON(listmp_handle == NULL)) { + status = -EINVAL; + goto exit; + } + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + + if (obj->params.gate != NULL) { + retval = gatepeterson_enter(obj->params.gate); + if (retval < 0) { + status = -EINVAL; + goto exit; + } + } + + /*! @retval true if list is empty */ + sharedHead = (struct listmp_elem *)(sharedregion_get_srptr( + (void *)obj->listmp_elem, obj->index)); + dsb(); + if (obj->listmp_elem->next == sharedHead) + is_empty = true; + + if (obj->params.gate != NULL) + gatepeterson_leave(obj->params.gate, 0); + +exit: + return is_empty; +} + +/* + * ======== listmp_sharedmemory_get_head ======== + * Purpose: + * Function to get head element from a shared memory list + */ +void *listmp_sharedmemory_get_head(listmp_sharedmemory_handle listmp_handle) +{ + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + struct listmp_elem *elem = NULL; + struct listmp_elem *localHeadNext = NULL; + struct listmp_elem *localNext = NULL; + struct listmp_elem *sharedHead = NULL; + s32 retval = 0; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) + goto exit; + + if (WARN_ON(listmp_handle == NULL)) { + /*! @retval NULL if listmp_handle passed is NULL */ + elem = NULL; + goto exit; + } + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + + if (obj->params.gate != NULL) { + retval = gatepeterson_enter(obj->params.gate); + if (retval < 0) + goto exit; + } + + localHeadNext = sharedregion_get_ptr((u32 *)obj->listmp_elem->next); + dsb(); + /* See if the listmp_sharedmemory_object was empty */ + if (localHeadNext == (struct listmp_elem *)obj->listmp_elem) { + /*! @retval NULL if list is empty */ + elem = NULL ; + } else { + /* Elem to return */ + elem = localHeadNext; + dsb(); + localNext = sharedregion_get_ptr((u32 *)elem->next); + sharedHead = (struct listmp_elem *) sharedregion_get_srptr( + (void *)obj->listmp_elem, obj->index); + + /* Fix the head of the list next pointer */ + obj->listmp_elem->next = elem->next; + dsb(); + /* Fix the prev pointer of the new first elem on the + list */ + localNext->prev = sharedHead; + } + + if (obj->params.gate != NULL) + gatepeterson_leave(obj->params.gate, 0); + +exit: + return elem; +} + +/* + * ======== listmp_sharedmemory_get_tail ======== + * Purpose: + * Function to get tail element from a shared memory list + */ +void *listmp_sharedmemory_get_tail(listmp_sharedmemory_handle listmp_handle) +{ + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + struct listmp_elem *elem = NULL; + struct listmp_elem *localHeadNext = NULL; + struct listmp_elem *localHeadPrev = NULL; + struct listmp_elem *localPrev = NULL; + struct listmp_elem *sharedHead = NULL; + s32 retval = 0; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) + goto exit; + + if (WARN_ON(listmp_sharedmemory_state.ns_handle == NULL)) { + /*! @retval NULL if Module was not initialized */ + elem = NULL; + goto exit; + } + if (WARN_ON(listmp_handle == NULL)) { + /*! @retval NULL if listmp_handle passed is NULL */ + elem = NULL; + goto exit; + } + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + + if (obj->params.gate != NULL) { + retval = gatepeterson_enter(obj->params.gate); + if (retval < 0) + goto exit; + } + + localHeadNext = sharedregion_get_ptr((u32 *)obj->listmp_elem->next); + localHeadPrev = sharedregion_get_ptr((u32 *)obj->listmp_elem->prev); + + /* See if the listmp_sharedmemory_object was empty */ + if (localHeadNext == (struct listmp_elem *)obj->listmp_elem) { + /* Empty, return NULL */ + elem = NULL ; + } else { + /* Elem to return */ + elem = localHeadPrev; + localPrev = sharedregion_get_ptr((u32 *)elem->prev); + sharedHead = (struct listmp_elem *) sharedregion_get_srptr( + (void *)obj->listmp_elem, obj->index); + obj->listmp_elem->prev = elem->prev; + localPrev->next = sharedHead; + } + + if (obj->params.gate != NULL) + gatepeterson_leave(obj->params.gate, 0); + +exit: + return elem; +} + +/* + * ======== listmp_sharedmemory_put_head ======== + * Purpose: + * Function to put head element into a shared memory list + */ +int listmp_sharedmemory_put_head(listmp_sharedmemory_handle listmp_handle, + struct listmp_elem *elem) +{ + int status = 0; + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + struct listmp_elem *localNextElem = NULL; + struct listmp_elem *sharedElem = NULL; + struct listmp_elem *sharedHead = NULL; + s32 retval = 0; + u32 index; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + + if (WARN_ON(listmp_handle == NULL)) { + status = -EINVAL; + goto exit; + } + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + dsb(); + index = sharedregion_get_index(elem); + sharedElem = (struct listmp_elem *) sharedregion_get_srptr(elem, index); + sharedHead = (struct listmp_elem *)sharedregion_get_srptr( + (void *)obj->listmp_elem, obj->index); + dsb(); + if (obj->params.gate != NULL) { + retval = gatepeterson_enter(obj->params.gate); + if (retval < 0) { + status = -EINVAL; + goto exit; + } + } + /* Add the new elem into the list */ + elem->next = obj->listmp_elem->next; + elem->prev = sharedHead; + dsb(); + localNextElem = sharedregion_get_ptr((u32 *)elem->next); + localNextElem->prev = sharedElem; + obj->listmp_elem->next = sharedElem; + + if (obj->params.gate != NULL) + gatepeterson_leave(obj->params.gate, 0); + +exit: + if (status < 0) { + printk(KERN_ERR "listmp_sharedmemory_put_head failed! " + "status = 0x%x\n", status); + } + return status; +} + +/* + * ======== listmp_sharedmemory_put_tail ======== + * Purpose: + * Function to put tail element into a shared memory list + */ +int listmp_sharedmemory_put_tail(listmp_sharedmemory_handle listmp_handle, + struct listmp_elem *elem) +{ + int status = 0; + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + struct listmp_elem *localPrevElem = NULL; + struct listmp_elem *sharedElem = NULL; + struct listmp_elem *sharedHead = NULL; + s32 retval = 0; + u32 index; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + + if (WARN_ON(elem == NULL)) { + status = -EINVAL; + goto exit; + } + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + dsb(); + /* Safe to do outside the gate */ + index = sharedregion_get_index(elem); + sharedElem = (struct listmp_elem *) + sharedregion_get_srptr(elem, index); + sharedHead = (struct listmp_elem *)sharedregion_get_srptr + ((void *)obj->listmp_elem, + obj->index); + + if (obj->params.gate != NULL) { + retval = gatepeterson_enter(obj->params.gate); + if (retval < 0) { + status = -EINVAL; + goto exit; + } + } + /* Add the new elem into the list */ + elem->next = sharedHead; + elem->prev = obj->listmp_elem->prev; + dsb(); + localPrevElem = sharedregion_get_ptr((u32 *)elem->prev); + dsb(); + localPrevElem->next = sharedElem; + obj->listmp_elem->prev = sharedElem; + + if (obj->params.gate != NULL) + gatepeterson_leave(obj->params.gate, 0); + +exit: + if (status < 0) { + printk(KERN_ERR "listmp_sharedmemory_put_tail failed! " + "status = 0x%x\n", status); + } + return status; +} + +/* + * ======== listmp_sharedmemory_insert ======== + * Purpose: + * Function to insert an element into a shared memory list + */ +int listmp_sharedmemory_insert(listmp_sharedmemory_handle listmp_handle, + struct listmp_elem *new_elem, + struct listmp_elem *cur_elem) +{ + int status = 0; + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + struct listmp_elem *localPrevElem = NULL; + struct listmp_elem *sharedNewElem = NULL; + struct listmp_elem *sharedCurElem = NULL; + s32 retval = 0; + u32 index; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + + if (WARN_ON(new_elem == NULL)) { + status = -EINVAL; + goto exit; + } + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + + if (obj->params.gate != NULL) { + retval = gatepeterson_enter(obj->params.gate); + if (retval < 0) { + status = -EINVAL; + goto exit; + } + } + + /* If NULL change cur_elem to the obj */ + if (cur_elem == NULL) + cur_elem = (struct listmp_elem *)obj->listmp_elem->next; + + /* Get SRPtr for new_elem */ + index = sharedregion_get_index(new_elem); + sharedNewElem = (struct listmp_elem *) + sharedregion_get_srptr(new_elem, index); + dsb(); + /* Get SRPtr for cur_elem */ + index = sharedregion_get_index(cur_elem); + sharedCurElem = (struct listmp_elem *) + sharedregion_get_srptr(cur_elem, index); + + /* Get SRPtr for cur_elem->prev */ + localPrevElem = sharedregion_get_ptr((u32 *)cur_elem->prev); + dsb(); + new_elem->next = sharedCurElem; + new_elem->prev = cur_elem->prev; + localPrevElem->next = sharedNewElem; + dsb(); + cur_elem->prev = sharedNewElem; + + if (obj->params.gate != NULL) + gatepeterson_leave(obj->params.gate, 0); + +exit: + if (status < 0) { + printk(KERN_ERR "listmp_sharedmemory_insert failed! " + "status = 0x%x\n", status); + } + return status; +} + +/* + * ======== listmp_sharedmemory_remove ======== + * Purpose: + * Function to remove a element from a shared memory list + */ +int listmp_sharedmemory_remove(listmp_sharedmemory_handle listmp_handle, + struct listmp_elem *elem) +{ + int status = 0; + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + struct listmp_elem *localPrevElem = NULL; + struct listmp_elem *localNextElem = NULL; + s32 retval = 0; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) { + status = -ENODEV; + goto exit; + } + + if (WARN_ON(elem == NULL)) { + status = -EINVAL; + goto exit; + } + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + + if (obj->params.gate != NULL) { + retval = gatepeterson_enter(obj->params.gate); + if (retval < 0) { + status = -EINVAL; + goto exit; + } + } + + localPrevElem = sharedregion_get_ptr((u32 *)elem->prev); + localNextElem = sharedregion_get_ptr((u32 *)elem->next); + dsb(); + localPrevElem->next = elem->next; + localNextElem->prev = elem->prev; + + if (obj->params.gate != NULL) + gatepeterson_leave(obj->params.gate, 0); + +exit: + if (status < 0) { + printk(KERN_ERR "listmp_sharedmemory_remove failed! " + "status = 0x%x\n", status); + } + return status; +} + +/* + * ======== listmp_sharedmemory_next ======== + * Purpose: + * Function to traverse to next element in shared memory list + */ +void *listmp_sharedmemory_next(listmp_sharedmemory_handle listmp_handle, + struct listmp_elem *elem) +{ + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + struct listmp_elem *retElem = NULL; + struct listmp_elem *sharedNextElem = NULL; + s32 retval = 0; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) + goto exit; + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + + if (obj->params.gate != NULL) { + retval = gatepeterson_enter(obj->params.gate); + if (retval < 0) + goto exit; + } + + /* If element is NULL start at head */ + if (elem == NULL) + sharedNextElem = (struct listmp_elem *) obj->listmp_elem->next; + else + sharedNextElem = (struct listmp_elem *)elem->next; + + retElem = sharedregion_get_ptr((u32 *)sharedNextElem); + + /*! @retval NULL if list is empty */ + if (retElem == (struct listmp_elem *)obj->listmp_elem) + retElem = NULL; + if (obj->params.gate != NULL) + gatepeterson_leave(obj->params.gate, 0); + +exit: + return retElem; +} + +/* + * ======== listmp_sharedmemory_prev ======== + * Purpose: + * Function to traverse to prev element in shared memory list + */ +void *listmp_sharedmemory_prev(listmp_sharedmemory_handle listmp_handle, + struct listmp_elem *elem) +{ + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + struct listmp_elem *retElem = NULL; + struct listmp_elem *sharedPrevElem = NULL; + s32 retval = 0; + + if (atomic_cmpmask_and_lt(&(listmp_sharedmemory_state.ref_count), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(0), + LISTMPSHAREDMEMORY_MAKE_MAGICSTAMP(1)) == true) + goto exit; + + handle = (listmp_sharedmemory_object *)listmp_handle; + obj = (struct listmp_sharedmemory_obj *) handle->obj; + + if (obj->params.gate != NULL) { + retval = gatepeterson_enter(obj->params.gate); + if (retval < 0) + goto exit; + } + + /* If elem is NULL use head */ + if (elem == NULL) + sharedPrevElem = (struct listmp_elem *) + obj->listmp_elem->prev; + else + sharedPrevElem = (struct listmp_elem *)elem->prev; + + retElem = sharedregion_get_ptr((u32 *)sharedPrevElem); + + /*! @retval NULL if list is empty */ + if (retElem == (struct listmp_elem *)(obj->listmp_elem)) + retElem = NULL; + + if (obj->params.gate != NULL) + gatepeterson_leave(obj->params.gate, 0); + +exit: + return retElem; +} + +/* + * ======== _listmp_sharedmemory_create ======== + * Purpose: + * Creates a new instance of listmp_sharedmemory module. This is an internal + * function because both listmp_sharedmemory_create and + * listmp_sharedmemory_open call use the same functionality. + */ +listmp_sharedmemory_handle _listmp_sharedmemory_create( + listmp_sharedmemory_params *params, u32 create_flag) +{ + int status = 0; + listmp_sharedmemory_object *handle = NULL; + struct listmp_sharedmemory_obj *obj = NULL; + u32 key; + u32 shm_index; + u32 *shared_shm_base; + + BUG_ON(params == NULL); + + /* Allow local lock not being provided. Don't do protection if local + * lock is not provided. + */ + /* Create the handle */ + handle = kzalloc(sizeof(listmp_sharedmemory_object), GFP_KERNEL); + if (handle == NULL) { + status = -ENOMEM; + goto exit; + } + + obj = kzalloc(sizeof(struct listmp_sharedmemory_obj), + GFP_KERNEL); + if (obj == NULL) { + /*! @retval NULL if Memory allocation failed for + * internal object "Memory allocation failed for handle" + * "of type struct listmp_sharedmemory_obj"); */ + status = -ENOMEM; + goto exit; + } + + handle->obj = (struct listmp_sharedmemory_obj *)obj ; + handle->empty = &listmp_sharedmemory_empty; + handle->get_head = &listmp_sharedmemory_get_head; + handle->get_tail = &listmp_sharedmemory_get_tail; + handle->put_head = &listmp_sharedmemory_put_head; + handle->put_tail = &listmp_sharedmemory_put_tail; + handle->insert = &listmp_sharedmemory_insert; + handle->remove = &listmp_sharedmemory_remove; + handle->next = &listmp_sharedmemory_next; + handle->prev = &listmp_sharedmemory_prev; + + /* Update attrs */ + obj->attrs = (struct listmp_attrs *) params->shared_addr; + /* Assign the memory with proper cache line padding */ + obj->listmp_elem = (void *) ((u32)obj->attrs + \ + LISTMP_SHAREDMEMORY_CACHESIZE); + obj->index = sharedregion_get_index(params->shared_addr); + + if (create_flag == true) { + obj->attrs->shared_addr_size = params->shared_addr_size; + obj->attrs->version = LISTMP_SHAREDMEMORY_VERSION; + obj->listmp_elem->next = obj->listmp_elem->prev = + (struct listmp_elem *) + (sharedregion_get_srptr((void *)obj->listmp_elem, + obj->index)); + } + + /* Populate the params member */ + memcpy((void *)&obj->params, (void *)params, + sizeof(listmp_sharedmemory_params)); + + if (likely(listmp_sharedmemory_state.cfg.use_name_server + == true)) { + if (obj->params.name != NULL) { + /* Copy the name */ + obj->params.name = kmalloc(strlen(params->name) + 1, + GFP_KERNEL); + + if (obj->params.name == NULL) { + /*! @retval NULL if Memory allocation failed for + name */ + status = -ENOMEM; + } else { + strncpy(obj->params.name, params->name, + strlen(params->name) + 1); + } + } + } + + /* Update processor information */ + obj->owner = kmalloc(sizeof(struct listmp_proc_attrs), + GFP_KERNEL); + if (obj->owner == NULL) { + printk(KERN_ERR "_listmp_sharedmemory_create: Memory " + "allocation failed for processor information!\n"); + status = -ENOMEM; + } else { + /* Update owner and opener details */ + if (create_flag == true) { + obj->owner->creator = true; + obj->owner->open_count = 1; + obj->owner->proc_id = multiproc_get_id(NULL); + obj->top = handle; + obj->attrs->status = \ + LISTMP_SHAREDMEMORY_CREATED; + } else { + obj->owner->creator = false; + obj->owner->open_count = 0; + obj->owner->proc_id = MULTIPROC_INVALIDID; + obj->top = handle; + } + + /* Put in the local list */ + key = mutex_lock_interruptible( + listmp_sharedmemory_state.local_gate); + INIT_LIST_HEAD(&obj->list_elem); + list_add_tail((&obj->list_elem), + &listmp_sharedmemory_state.obj_list); + mutex_unlock(listmp_sharedmemory_state.local_gate); + + if (create_flag == true) { + + /* We will store a shared pointer in the + * NameServer + */ + shm_index = sharedregion_get_index(params->shared_addr); + shared_shm_base = sharedregion_get_srptr( + params->shared_addr, shm_index); + /* Add list instance to name server */ + if (obj->params.name != NULL) { + obj->ns_key = nameserver_add_uint32( + listmp_sharedmemory_state.ns_handle, + params->name, + (u32) (shared_shm_base)); + if (obj->ns_key == NULL) + status = -EFAULT; + } + } + } + + if (status < 0) { + /* Remove from the local list */ + key = mutex_lock_interruptible( + listmp_sharedmemory_state.local_gate); + list_del(&obj->list_elem); + mutex_unlock(listmp_sharedmemory_state.local_gate); + + if (likely(listmp_sharedmemory_state.cfg.use_name_server + == true)) { + if ((obj->params.name != NULL) && (status != -EFAULT)) + nameserver_remove_entry( + listmp_sharedmemory_state.ns_handle, + obj->ns_key); + } + if (obj->owner != NULL) + kfree(obj->owner); + + if (obj->params.name != NULL) + kfree(obj->params.name); + + if (obj != NULL) { + kfree(obj); + obj = NULL; + } + if (handle != NULL) { + kfree(handle); + handle = NULL; + } + } + +exit: + if (status < 0) { + printk(KERN_ERR "_listmp_sharedmemory_create failed! " + "status = 0x%x\n", status); + } + return (listmp_sharedmemory_handle) handle; +} |