diff options
Diffstat (limited to 'drivers/dsp/syslink/multicore_ipc/sharedregion.c')
-rwxr-xr-x | drivers/dsp/syslink/multicore_ipc/sharedregion.c | 800 |
1 files changed, 800 insertions, 0 deletions
diff --git a/drivers/dsp/syslink/multicore_ipc/sharedregion.c b/drivers/dsp/syslink/multicore_ipc/sharedregion.c new file mode 100755 index 000000000000..cfcd1aa1ee53 --- /dev/null +++ b/drivers/dsp/syslink/multicore_ipc/sharedregion.c @@ -0,0 +1,800 @@ +/* + * sharedregion.c + * + * The SharedRegion module is designed to be used in a + * multi-processor environment where there are memory regions + * that are shared and accessed across 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. + */ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <syslink/atomic_linux.h> + +#include <multiproc.h> +#include <nameserver.h> +#include <sharedregion.h> + +/* Macro to make a correct module magic number with refCount */ +#define SHAREDREGION_MAKE_MAGICSTAMP(x) ((SHAREDREGION_MODULEID << 16u) | (x)) + +#define SHAREDREGION_MAX_REGIONS_DEFAULT 4 + +/* + * Module state object + */ +struct sharedregion_module_object { + atomic_t ref_count; /* Reference count */ + struct mutex *gate_handle; + struct sharedregion_info *table; /* Ptr to the table */ + u32 bitOffset; /* Index bit offset */ + u32 region_size; /* Max size of each region */ + struct sharedregion_config cfg; /* Current config values */ + u32 *ref_count_table; /* The number of times each + entry has been added */ +}; + +/* + * Shared region state object variable with default settings + */ +static struct sharedregion_module_object sharedregion_state = { + .cfg.heap_handle = NULL, + .cfg.gate_handle = NULL, + .cfg.max_regions = SHAREDREGION_MAX_REGIONS_DEFAULT +}; + +/* + * ======== sharedregion_get_config ======== + * Purpose: + * This will get sharedregion module configiguration + */ +int sharedregion_get_config(struct sharedregion_config *config) +{ + BUG_ON((config == NULL)); + memcpy(config, &sharedregion_state.cfg, + sizeof(struct sharedregion_config)); + return 0; +} +EXPORT_SYMBOL(sharedregion_get_config); + + +/* + * ======== sharedregion_get_bitoffset ======== + * Purpose: + * This will get get the bit offset + */ +static u32 sharedregion_get_bitoffset(u32 max_regions) +{ + u32 i; + u32 bitoffset = 0; + for (i = ((sizeof(void *) * 8) - 1); i >= 0; i--) { + if (max_regions > (1 << i)) + break; + } + + bitoffset = (((sizeof(void *) * 8) - 1) - i); + return bitoffset; +} + +/* + * ======== sharedregion_setup ======== + * Purpose: + * This will get setup the sharedregion module + */ +int sharedregion_setup(const struct sharedregion_config *config) +{ + struct sharedregion_config *tmpcfg = &sharedregion_state.cfg; + struct sharedregion_info *table = NULL; + u32 i; + u32 j; + s32 retval = 0; + u16 proc_count; + + /* 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(&sharedregion_state.ref_count, + SHAREDREGION_MAKE_MAGICSTAMP(0), + SHAREDREGION_MAKE_MAGICSTAMP(0)); + + if (atomic_inc_return(&sharedregion_state.ref_count) + != SHAREDREGION_MAKE_MAGICSTAMP(1)) { + return 1; + } + + if (config != NULL) { + if (WARN_ON(config->max_regions == 0)) { + retval = -EINVAL; + goto error; + } + memcpy(&sharedregion_state.cfg, config, + sizeof(struct sharedregion_config)); + } + + sharedregion_state.gate_handle = kmalloc(sizeof(struct mutex), + GFP_KERNEL); + if (sharedregion_state.gate_handle == NULL) + goto gate_create_fail; + + sharedregion_state.bitOffset = + sharedregion_get_bitoffset(tmpcfg->max_regions); + sharedregion_state.region_size = (1 << sharedregion_state.bitOffset); + proc_count = multiproc_get_max_processors(); + /* TODO check heap usage & + 1 ? */ + sharedregion_state.table = kmalloc(sizeof(struct sharedregion_info) * + tmpcfg->max_regions * (proc_count + 1), + GFP_KERNEL); + if (sharedregion_state.table == NULL) { + retval = -ENOMEM; + goto table_alloc_fail; + } + + sharedregion_state.ref_count_table = kmalloc(sizeof(u32) * + tmpcfg->max_regions * (proc_count + 1), + GFP_KERNEL); + if (sharedregion_state.ref_count_table == NULL) { + retval = -ENOMEM; + goto table_alloc_fail; + } + + table = sharedregion_state.table; + for (i = 0; i < tmpcfg->max_regions; i++) { + for (j = 0; j < (proc_count + 1); j++) { + (table + (j * tmpcfg->max_regions) + i)->is_valid = + false; + (table + (j * tmpcfg->max_regions) + i)->base = 0; + (table + (j * tmpcfg->max_regions) + i)->len = 0; + sharedregion_state.ref_count_table[(j * + tmpcfg->max_regions) + i] = 0; + } + } + + mutex_init(sharedregion_state.gate_handle); + return 0; + +table_alloc_fail: + kfree(sharedregion_state.gate_handle); + +gate_create_fail: + memset(&sharedregion_state, 0, + sizeof(struct sharedregion_module_object)); + sharedregion_state.cfg.max_regions = SHAREDREGION_MAX_REGIONS_DEFAULT; + +error: + printk(KERN_ERR "sharedregion_setup failed status:%x\n", retval); + return retval; +} +EXPORT_SYMBOL(sharedregion_setup); + +/* + * ======== sharedregion_destroy ======== + * Purpose: + * This will get destroy the sharedregion module + */ +int sharedregion_destroy(void) +{ + s32 retval = 0; + void *gate_handle = NULL; + + if (atomic_cmpmask_and_lt(&(sharedregion_state.ref_count), + SHAREDREGION_MAKE_MAGICSTAMP(0), + SHAREDREGION_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto error; + } + + if (!(atomic_dec_return(&sharedregion_state.ref_count) + == SHAREDREGION_MAKE_MAGICSTAMP(0))) { + retval = 1; /* Syslink is not handling this on 2.0.0.06 */ + goto error; + } + + retval = mutex_lock_interruptible(sharedregion_state.gate_handle); + if (retval) + goto error; + + kfree(sharedregion_state.ref_count_table); + kfree(sharedregion_state.table); + gate_handle = sharedregion_state.gate_handle; /* backup gate handle */ + memset(&sharedregion_state, 0, + sizeof(struct sharedregion_module_object)); + sharedregion_state.cfg.max_regions = SHAREDREGION_MAX_REGIONS_DEFAULT; + mutex_unlock(gate_handle); + kfree(gate_handle); + return 0; + +error: + if (retval < 0) { + printk(KERN_ERR "sharedregion_destroy failed status:%x\n", + retval); + } + return retval; +} +EXPORT_SYMBOL(sharedregion_destroy); + +/* + * ======== sharedregion_add ======== + * Purpose: + * This will add a memory segment to the lookup table + * during runtime by base and length + */ +int sharedregion_add(u32 index, void *base, u32 len) +{ + struct sharedregion_info *entry = NULL; + struct sharedregion_info *table = NULL; + s32 retval = 0; + u32 i; + u16 myproc_id; + bool overlap = false; + bool same = false; + + if (atomic_cmpmask_and_lt(&(sharedregion_state.ref_count), + SHAREDREGION_MAKE_MAGICSTAMP(0), + SHAREDREGION_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto error; + } + + if (index >= sharedregion_state.cfg.max_regions || + sharedregion_state.region_size < len) { + retval = -EINVAL; + goto error; + } + + myproc_id = multiproc_get_id(NULL); + retval = mutex_lock_interruptible(sharedregion_state.gate_handle); + if (retval) + goto error; + + + table = sharedregion_state.table; + /* Check for overlap */ + for (i = 0; i < sharedregion_state.cfg.max_regions; i++) { + entry = (table + + (myproc_id * sharedregion_state.cfg.max_regions) + + i); + if (entry->is_valid) { + /* Handle duplicate entry */ + if ((base == entry->base) && (len == entry->len)) { + same = true; + break; + } + + if ((base >= entry->base) && + (base < (void *)((u32)entry->base + entry->len))) { + overlap = true; + break; + } + + if ((base < entry->base) && + (void *)((u32)base + len) >= entry->base) { + overlap = true; + break; + } + } + } + + if (same) { + retval = 1; + goto success; + } + + if (overlap) { + /* FHACK: FIX ME */ + retval = 1; + goto mem_overlap_error; + } + + entry = (table + + (myproc_id * sharedregion_state.cfg.max_regions) + + index); + if (entry->is_valid == false) { + entry->base = base; + entry->len = len; + entry->is_valid = true; + + } else { + /* FHACK: FIX ME */ + sharedregion_state.ref_count_table[(myproc_id * + sharedregion_state.cfg.max_regions) + + index] += 1; + retval = 1; + goto dup_entry_error; + } + +success: + mutex_unlock(sharedregion_state.gate_handle); + return 0; + +dup_entry_error: /* Fall through */ +mem_overlap_error: + printk(KERN_WARNING "sharedregion_add entry exists status: %x\n", + retval); + mutex_unlock(sharedregion_state.gate_handle); + +error: + if (retval < 0) + printk(KERN_ERR "sharedregion_add failed status:%x\n", retval); + return retval; +} +EXPORT_SYMBOL(sharedregion_add); + +/* + * ======== sharedregion_remove ======== + * Purpose: + * This will removes a memory segment to the lookup table + * during runtime by base and length + */ +int sharedregion_remove(u32 index) +{ + struct sharedregion_info *entry = NULL; + struct sharedregion_info *table = NULL; + u16 myproc_id; + s32 retval = 0; + + if (atomic_cmpmask_and_lt(&(sharedregion_state.ref_count), + SHAREDREGION_MAKE_MAGICSTAMP(0), + SHAREDREGION_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto error; + } + + if (index >= sharedregion_state.cfg.max_regions) { + retval = -EINVAL; + goto error; + } + + retval = mutex_lock_interruptible(sharedregion_state.gate_handle); + if (retval) + goto error; + + myproc_id = multiproc_get_id(NULL); + table = sharedregion_state.table; + entry = (table + + (myproc_id * sharedregion_state.cfg.max_regions) + + index); + + if (sharedregion_state.ref_count_table[(myproc_id * + sharedregion_state.cfg.max_regions) + + index] > 0) + sharedregion_state.ref_count_table[(myproc_id * + sharedregion_state.cfg.max_regions) + + index] -= 1; + else { + entry->is_valid = false; + entry->base = NULL; + entry->len = 0; + } + mutex_unlock(sharedregion_state.gate_handle); + return 0; + +error: + printk(KERN_ERR "sharedregion_remove failed status:%x\n", retval); + return retval; +} +EXPORT_SYMBOL(sharedregion_remove); + +/* + * ======== sharedregion_get_index ======== + * Purpose: + * This will return the index for the specified address pointer. + */ +int sharedregion_get_index(void *addr) +{ + struct sharedregion_info *entry = NULL; + struct sharedregion_info *table = NULL; + bool found = false; + u32 i; + u16 myproc_id; + s32 retval = 0; + + if (WARN_ON(atomic_cmpmask_and_lt(&(sharedregion_state.ref_count), + SHAREDREGION_MAKE_MAGICSTAMP(0), + SHAREDREGION_MAKE_MAGICSTAMP(1)) == true)) { + retval = -ENODEV; + goto exit; + } + + myproc_id = multiproc_get_id(NULL); + retval = mutex_lock_interruptible(sharedregion_state.gate_handle); + if (retval) { + retval = -ENODEV; + goto exit; + } + + table = sharedregion_state.table; + for (i = 0; i < sharedregion_state.cfg.max_regions; i++) { + entry = (table + + (myproc_id * sharedregion_state.cfg.max_regions) + + i); + if ((addr >= entry->base) && + (addr < (void *)((u32)entry->base + (entry->len)))) { + found = true; + break; + } + } + + if (found) + retval = i; + else + retval = -ENOENT; /* No entry found in the table */ + + mutex_unlock(sharedregion_state.gate_handle); + return retval; + +exit: + printk(KERN_ERR "sharedregion_get_index failed index:%x\n", retval); + return retval; +} +EXPORT_SYMBOL(sharedregion_get_index); + +/* + * ======== sharedregion_get_ptr ======== + * Purpose: + * This will return the address pointer associated with the + * shared region pointer + */ +void *sharedregion_get_ptr(u32 *srptr) +{ + struct sharedregion_info *entry = NULL; + void *ptr = NULL; + u16 myproc_id; + s32 retval = 0; + + if (atomic_cmpmask_and_lt(&(sharedregion_state.ref_count), + SHAREDREGION_MAKE_MAGICSTAMP(0), + SHAREDREGION_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto error; + } + + if (srptr == SHAREDREGION_INVALIDSRPTR) + goto error; + + myproc_id = multiproc_get_id(NULL); + retval = mutex_lock_interruptible(sharedregion_state.gate_handle); + if (WARN_ON(retval != 0)) + goto error; + + entry = (sharedregion_state.table + + (myproc_id * sharedregion_state.cfg.max_regions) + + ((u32)srptr >> sharedregion_state.bitOffset)); + /* TO DO check:: is this correct ? */ + ptr = ((void *)(((u32)srptr & + ((1 << sharedregion_state.bitOffset) - 1)) + (u32)entry->base)); + mutex_unlock(sharedregion_state.gate_handle); + return ptr; + +error: + printk(KERN_ERR "sharedregion_get_ptr failed \n"); + return (void *)NULL; + +} +EXPORT_SYMBOL(sharedregion_get_ptr); + +/* + * ======== sharedregion_get_srptr ======== + * Purpose: + * This will return sharedregion pointer associated with the + * an address in a shared region area registered with the + * sharedregion module + */ +u32 *sharedregion_get_srptr(void *addr, s32 index) +{ + struct sharedregion_info *entry = NULL; + u32 *ptr = SHAREDREGION_INVALIDSRPTR ; + u32 myproc_id; + s32 retval = 0; + + if (atomic_cmpmask_and_lt(&(sharedregion_state.ref_count), + SHAREDREGION_MAKE_MAGICSTAMP(0), + SHAREDREGION_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto error; + } + + if (WARN_ON(addr == NULL)) + goto error; + + if (WARN_ON(index >= sharedregion_state.cfg.max_regions)) + goto error; + + retval = mutex_lock_interruptible(sharedregion_state.gate_handle); + if (WARN_ON(retval != 0)) + goto error; + + myproc_id = multiproc_get_id(NULL); + entry = (sharedregion_state.table + + (myproc_id * sharedregion_state.cfg.max_regions) + + index); + ptr = (u32 *) ((index << sharedregion_state.bitOffset) + | ((u32)addr - (u32)entry->base)); + mutex_unlock(sharedregion_state.gate_handle); + return ptr; + +error: + printk(KERN_ERR "sharedregion_get_srptr failed\n"); + return (u32 *)NULL; +} +EXPORT_SYMBOL(sharedregion_get_srptr); + +/* + * ======== sharedregion_get_table_info ======== + * Purpose: + * This will get the table entry information for the + * specified index and id + */ +int sharedregion_get_table_info(u32 index, u16 proc_id, + struct sharedregion_info *info) +{ + struct sharedregion_info *entry = NULL; + struct sharedregion_info *table = NULL; + u16 proc_count; + s32 retval = 0; + + BUG_ON(info == NULL); + if (atomic_cmpmask_and_lt(&(sharedregion_state.ref_count), + SHAREDREGION_MAKE_MAGICSTAMP(0), + SHAREDREGION_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto error; + } + + proc_count = multiproc_get_max_processors(); + if (index >= sharedregion_state.cfg.max_regions || + proc_id >= proc_count) { + retval = -EINVAL; + goto error; + } + + retval = mutex_lock_interruptible(sharedregion_state.gate_handle); + if (retval) + goto error; + + table = sharedregion_state.table; + entry = (table + + (proc_id * sharedregion_state.cfg.max_regions) + + index); + memcpy((void *) info, (void *) entry, sizeof(struct sharedregion_info)); + mutex_unlock(sharedregion_state.gate_handle); + return 0; + +error: + printk(KERN_ERR "sharedregion_get_table_info failed status:%x\n", + retval); + return retval; +} +EXPORT_SYMBOL(sharedregion_get_table_info); + +/* + * ======== sharedregion_set_table_info ======== + * Purpose: + * This will set the table entry information for the + * specified index and id + */ +int sharedregion_set_table_info(u32 index, u16 proc_id, + struct sharedregion_info *info) +{ + struct sharedregion_info *entry = NULL; + struct sharedregion_info *table = NULL; + u16 proc_count; + s32 retval = 0; + + BUG_ON(info != NULL); + if (atomic_cmpmask_and_lt(&(sharedregion_state.ref_count), + SHAREDREGION_MAKE_MAGICSTAMP(0), + SHAREDREGION_MAKE_MAGICSTAMP(1)) == true) { + retval = -ENODEV; + goto error; + } + + proc_count = multiproc_get_max_processors(); + if (index >= sharedregion_state.cfg.max_regions || + proc_id >= proc_count) { + retval = -EINVAL; + goto error; + } + + retval = mutex_lock_interruptible(sharedregion_state.gate_handle); + if (retval) + goto error; + + table = sharedregion_state.table; + entry = (table + + (proc_id * sharedregion_state.cfg.max_regions) + + index); + memcpy((void *) entry, (void *) info, sizeof(struct sharedregion_info)); + mutex_unlock(sharedregion_state.gate_handle); + return 0; + +error: + printk(KERN_ERR "sharedregion_set_table_info failed status:%x\n", + retval); + return retval; +} +EXPORT_SYMBOL(sharedregion_set_table_info); + +/* + * ======== Sharedregion_attach ======== + * Purpose: + * This will attachs the shared region with an proc_id + * + * Application should call this function from the callback + * function registered for device attach to the processor + * manager. All modules which requires some logic setup + * to be done when a device gets attach to the system, + * should export API like this. Please see the below psuedo + * code for example: + * Example + * code + * void function (proc_id, config) { + * NotifyDriver_attach (proc_id, config->ndParams); + * SysMemMgr_attach (proc_id); + * SMM_attach (proc_id); + * NameServerRemoteTransport_attach(proc_id, config->nsrtParams); + * SharedRegion_attach (proc_id); + * SharedMemory_getConfig (&cfg); + * for (i = 0u; i < cfg->maxRegions; i++) { + * SharedRegion_getTableInfo(i, &myinfo, myProcId); + * SharedRegion_getTableInfo(i, &peerinfo, proc_id); + * DMM_map (proc_id, + * PA(myinfo->vaddr), + * peerinfo->vaddr, + * myinfo->len); + * } + * ... + * } + * + * main () { + * # attach callback for device attach only + * ProcMgr_register (function, proc_id, DEV_ATTACH); + * } + * + */ +void sharedregion_attach(u16 proc_id) +{ + struct sharedregion_info *entry = NULL; + struct sharedregion_info *table = NULL; + char *hexstr = "0123456789ABCDEF"; + char tname[80]; + u16 proc_id_list[2]; + u32 addr = 0; + u32 len; + u16 proc_count; + void *nshandle; + s32 retval = 0; + s32 i; + + if (WARN_ON(sharedregion_state.table == NULL)) + goto error; + + proc_count = multiproc_get_max_processors(); + if (WARN_ON(proc_id >= proc_count)) + goto error; + + proc_id_list[0] = proc_id; + proc_id_list[1] = MULTIPROC_INVALIDID; + nshandle = nameserver_get_handle(SHAREDREGION_NAMESERVER); + if (nshandle == NULL) + goto error; + + /* Get Shared region entries from the remote shared region nameserver */ + for (i = 0u; i < sharedregion_state.cfg.max_regions; i++) { + memset(tname, 0, 80); + strcpy(tname, "SHAREDREGION:SRENTRY_ADDR_"); + tname[strlen(tname)] = hexstr[(proc_id >> 4u) & 0xF]; + tname[strlen(tname)] = hexstr[proc_id & 0xF]; + tname[strlen(tname)] = '_'; + tname[strlen(tname)] = hexstr[(i >> 28u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 24u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 20u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 16u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 12u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 8u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 4u) & 0xF]; + tname[strlen(tname)] = hexstr[i & 0xF]; + retval = nameserver_get(nshandle, tname, + &len, sizeof(u32), &proc_id_list[0]); + if (WARN_ON(retval)) + ; + + memset(tname, 0, 80u); + strcpy(tname, "SHAREDREGION:SRENTRY_LEN_"); + tname[strlen(tname)] = hexstr[(proc_id >> 4u) & 0xF]; + tname[strlen(tname)] = hexstr[proc_id & 0xF]; + tname[strlen(tname)] = '_'; + tname[strlen(tname)] = hexstr[(i >> 28u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 24u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 20u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 16u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 12u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 8u) & 0xF]; + tname[strlen(tname)] = hexstr[(i >> 4u) & 0xF]; + tname[strlen(tname)] = hexstr[i & 0xF]; + + /* TO DO : check this */ + retval = nameserver_get(nshandle, tname, + &len, sizeof(u32), &proc_id_list[0]); + if (WARN_ON(retval)) + ; + + /* Found an entry in the remote nameserver */ + /* Add it into the shared region table */ + if (retval == 0) { + retval = mutex_lock_interruptible( + sharedregion_state.gate_handle); + if (WARN_ON(retval)) + break; + + table = sharedregion_state.table; + /* mark entry invalid */ + entry = (table + + (proc_id * sharedregion_state.cfg.max_regions) + + i); + entry->base = (void *)addr; + entry->len = len; + entry->is_valid = false; + mutex_unlock(sharedregion_state.gate_handle); + } + + } + +error: + return; +} +EXPORT_SYMBOL(sharedregion_attach); + +/* + * ======== Sharedregion_detach ======== + * Purpose: + * This will detachs the shared region for an proc_id + * + * Application should call this function from the callback + * function registered for device detach to the processorl + * manager. All modules which requires some logic setup + * to be done when a device gets detach from the system, + * should export API like this. + * Please see the below psuedo code for example: + * @Example + * @code + * void function (proc_id) { + * SharedRegion_detach (proc_id); + * SysMemMgr_detach (proc_id); + * ... + * # Name server must be detached last + * nameserver_detach (proc_id); + * } + * + * main () { + * # attach callback for device detach only + * Processor_register (function, proc_id, DEV_DETACH); + * } + * + */ +void sharedregion_detach(u16 proc_id) +{ + u16 proc_count; + + if (WARN_ON(sharedregion_state.table == NULL)) + goto error; + + proc_count = multiproc_get_max_processors(); + if (WARN_ON(proc_id >= proc_count)) + goto error; + +error: + return; +} +EXPORT_SYMBOL(sharedregion_detach); + |