| /* |
| * Copyright (C) 2010-2011, 2013-2014, 2016 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. |
| * |
| * A copy of the licence is included with the program, and can also be obtained from Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "mali_kernel_common.h" |
| #include "mali_osk.h" |
| #include "ump_osk.h" |
| #include "ump_kernel_common.h" |
| #include "ump_kernel_types.h" |
| #include "ump_kernel_random_mapping.h" |
| |
| #include <linux/random.h> |
| #include <linux/rbtree.h> |
| #include <linux/sched.h> |
| #include <linux/jiffies.h> |
| |
| |
| static ump_dd_mem *search(struct rb_root *root, int id) |
| { |
| struct rb_node *node = root->rb_node; |
| |
| while (node) { |
| ump_dd_mem *e = container_of(node, ump_dd_mem, node); |
| |
| if (id < e->secure_id) { |
| node = node->rb_left; |
| } else if (id > e->secure_id) { |
| node = node->rb_right; |
| } else { |
| return e; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static mali_bool insert(struct rb_root *root, int id, ump_dd_mem *mem) |
| { |
| struct rb_node **new = &(root->rb_node); |
| struct rb_node *parent = NULL; |
| |
| while (*new) { |
| ump_dd_mem *this = container_of(*new, ump_dd_mem, node); |
| |
| parent = *new; |
| if (id < this->secure_id) { |
| new = &((*new)->rb_left); |
| } else if (id > this->secure_id) { |
| new = &((*new)->rb_right); |
| } else { |
| printk(KERN_ERR "UMP: ID already used %x\n", id); |
| return MALI_FALSE; |
| } |
| } |
| |
| rb_link_node(&mem->node, parent, new); |
| rb_insert_color(&mem->node, root); |
| |
| return MALI_TRUE; |
| } |
| |
| |
| ump_random_mapping *ump_random_mapping_create(void) |
| { |
| ump_random_mapping *map = _mali_osk_calloc(1, sizeof(ump_random_mapping)); |
| |
| if (NULL == map) |
| return NULL; |
| |
| map->lock = _mali_osk_mutex_rw_init(_MALI_OSK_LOCKFLAG_ORDERED, |
| _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP); |
| if (NULL != map->lock) { |
| map->root = RB_ROOT; |
| #if UMP_RANDOM_MAP_DELAY |
| map->failed.count = 0; |
| map->failed.timestamp = jiffies; |
| #endif |
| return map; |
| } |
| return NULL; |
| } |
| |
| void ump_random_mapping_destroy(ump_random_mapping *map) |
| { |
| _mali_osk_mutex_rw_term(map->lock); |
| _mali_osk_free(map); |
| } |
| |
| int ump_random_mapping_insert(ump_random_mapping *map, ump_dd_mem *mem) |
| { |
| _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); |
| |
| while (1) { |
| u32 id; |
| |
| get_random_bytes(&id, sizeof(id)); |
| |
| /* Try a new random number if id happened to be the invalid |
| * secure ID (-1). */ |
| if (unlikely(id == UMP_INVALID_SECURE_ID)) |
| continue; |
| |
| /* Insert into the tree. If the id was already in use, get a |
| * new random id and try again. */ |
| if (insert(&map->root, id, mem)) { |
| mem->secure_id = id; |
| break; |
| } |
| } |
| _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); |
| |
| return 0; |
| } |
| |
| ump_dd_mem *ump_random_mapping_get(ump_random_mapping *map, int id) |
| { |
| ump_dd_mem *mem = NULL; |
| #if UMP_RANDOM_MAP_DELAY |
| int do_delay = 0; |
| #endif |
| |
| DEBUG_ASSERT(map); |
| |
| _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO); |
| mem = search(&map->root, id); |
| |
| if (unlikely(NULL == mem)) { |
| #if UMP_RANDOM_MAP_DELAY |
| map->failed.count++; |
| |
| if (time_is_before_jiffies(map->failed.timestamp + |
| UMP_FAILED_LOOKUP_DELAY * HZ)) { |
| /* If it is a long time since last failure, reset |
| * the counter and skip the delay this time. */ |
| map->failed.count = 0; |
| } else if (map->failed.count > UMP_FAILED_LOOKUPS_ALLOWED) { |
| do_delay = 1; |
| } |
| |
| map->failed.timestamp = jiffies; |
| #endif /* UMP_RANDOM_MAP_DELAY */ |
| } else { |
| ump_dd_reference_add(mem); |
| } |
| _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO); |
| |
| #if UMP_RANDOM_MAP_DELAY |
| if (do_delay) { |
| /* Apply delay */ |
| schedule_timeout_killable(UMP_FAILED_LOOKUP_DELAY); |
| } |
| #endif /* UMP_RANDOM_MAP_DELAY */ |
| |
| return mem; |
| } |
| |
| static ump_dd_mem *ump_random_mapping_remove_internal(ump_random_mapping *map, int id) |
| { |
| ump_dd_mem *mem = NULL; |
| |
| mem = search(&map->root, id); |
| |
| if (mem) { |
| rb_erase(&mem->node, &map->root); |
| } |
| |
| return mem; |
| } |
| |
| void ump_random_mapping_put(ump_dd_mem *mem) |
| { |
| int new_ref; |
| |
| _mali_osk_mutex_rw_wait(device.secure_id_map->lock, _MALI_OSK_LOCKMODE_RW); |
| |
| new_ref = _ump_osk_atomic_dec_and_read(&mem->ref_count); |
| DBG_MSG(5, ("Memory reference decremented. ID: %u, new value: %d\n", |
| mem->secure_id, new_ref)); |
| |
| if (0 == new_ref) { |
| DBG_MSG(3, ("Final release of memory. ID: %u\n", mem->secure_id)); |
| |
| #ifdef CONFIG_DMA_SHARED_BUFFER |
| if (mem->import_attach) { |
| struct dma_buf_attachment *attach = mem->import_attach; |
| struct dma_buf *dma_buf; |
| |
| if (mem->sgt) |
| dma_buf_unmap_attachment(attach, mem->sgt, |
| DMA_BIDIRECTIONAL); |
| |
| dma_buf = attach->dmabuf; |
| dma_buf_detach(attach->dmabuf, attach); |
| dma_buf_put(dma_buf); |
| |
| } |
| #endif |
| ump_random_mapping_remove_internal(device.secure_id_map, mem->secure_id); |
| |
| mem->release_func(mem->ctx, mem); |
| _mali_osk_free(mem); |
| } |
| |
| _mali_osk_mutex_rw_signal(device.secure_id_map->lock, _MALI_OSK_LOCKMODE_RW); |
| } |
| |
| ump_dd_mem *ump_random_mapping_remove(ump_random_mapping *map, int descriptor) |
| { |
| ump_dd_mem *mem; |
| |
| _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); |
| mem = ump_random_mapping_remove_internal(map, descriptor); |
| _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); |
| |
| return mem; |
| } |