2 * Copyright (C) 2010-2012 ARM Limited. All rights reserved.
4 * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
7 * A copy of the licence is included with the program, and can also be obtained from Free Software
8 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
10 #include "mali_kernel_common.h"
11 #include "mali_kernel_core.h"
12 #include "mali_kernel_memory_engine.h"
13 #include "mali_block_allocator.h"
16 #define MALI_BLOCK_SIZE (256UL * 1024UL) /* 256 kB, remember to keep the ()s */
18 typedef struct block_info
20 struct block_info * next;
23 /* The structure used as the handle produced by block_allocator_allocate,
24 * and removed by block_allocator_release */
25 typedef struct block_allocator_allocation
27 /* The list will be released in reverse order */
28 block_info *last_allocated;
29 mali_allocation_engine * engine;
30 mali_memory_allocation * descriptor;
33 } block_allocator_allocation;
36 typedef struct block_allocator
38 _mali_osk_lock_t *mutex;
39 block_info * all_blocks;
40 block_info * first_free;
46 MALI_STATIC_INLINE u32 get_phys(block_allocator * info, block_info * block);
47 static mali_physical_memory_allocation_result block_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info);
48 static void block_allocator_release(void * ctx, void * handle);
49 static mali_physical_memory_allocation_result block_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block);
50 static void block_allocator_release_page_table_block( mali_page_table_block *page_table_block );
51 static void block_allocator_destroy(mali_physical_memory_allocator * allocator);
52 static u32 block_allocator_stat(mali_physical_memory_allocator * allocator);
54 mali_physical_memory_allocator * mali_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size, const char *name)
56 mali_physical_memory_allocator * allocator;
57 block_allocator * info;
61 usable_size = size & ~(MALI_BLOCK_SIZE - 1);
62 MALI_DEBUG_PRINT(3, ("Mali block allocator create for region starting at 0x%08X length 0x%08X\n", base_address, size));
63 MALI_DEBUG_PRINT(4, ("%d usable bytes\n", usable_size));
64 num_blocks = usable_size / MALI_BLOCK_SIZE;
65 MALI_DEBUG_PRINT(4, ("which becomes %d blocks\n", num_blocks));
69 MALI_DEBUG_PRINT(1, ("Memory block of size %d is unusable\n", size));
73 allocator = _mali_osk_malloc(sizeof(mali_physical_memory_allocator));
74 if (NULL != allocator)
76 info = _mali_osk_malloc(sizeof(block_allocator));
79 info->mutex = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED, 0, _MALI_OSK_LOCK_ORDER_MEM_INFO);
80 if (NULL != info->mutex)
82 info->all_blocks = _mali_osk_malloc(sizeof(block_info) * num_blocks);
83 if (NULL != info->all_blocks)
86 info->first_free = NULL;
87 info->num_blocks = num_blocks;
89 info->base = base_address;
90 info->cpu_usage_adjust = cpu_usage_adjust;
92 for ( i = 0; i < num_blocks; i++)
94 info->all_blocks[i].next = info->first_free;
95 info->first_free = &info->all_blocks[i];
98 allocator->allocate = block_allocator_allocate;
99 allocator->allocate_page_table_block = block_allocator_allocate_page_table_block;
100 allocator->destroy = block_allocator_destroy;
101 allocator->stat = block_allocator_stat;
102 allocator->ctx = info;
103 allocator->name = name;
107 _mali_osk_lock_term(info->mutex);
109 _mali_osk_free(info);
111 _mali_osk_free(allocator);
117 static void block_allocator_destroy(mali_physical_memory_allocator * allocator)
119 block_allocator * info;
120 MALI_DEBUG_ASSERT_POINTER(allocator);
121 MALI_DEBUG_ASSERT_POINTER(allocator->ctx);
122 info = (block_allocator*)allocator->ctx;
124 _mali_osk_free(info->all_blocks);
125 _mali_osk_lock_term(info->mutex);
126 _mali_osk_free(info);
127 _mali_osk_free(allocator);
130 MALI_STATIC_INLINE u32 get_phys(block_allocator * info, block_info * block)
132 return info->base + ((block - info->all_blocks) * MALI_BLOCK_SIZE);
135 static mali_physical_memory_allocation_result block_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info)
137 block_allocator * info;
139 block_info * last_allocated = NULL;
140 mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_NONE;
141 block_allocator_allocation *ret_allocation;
143 MALI_DEBUG_ASSERT_POINTER(ctx);
144 MALI_DEBUG_ASSERT_POINTER(descriptor);
145 MALI_DEBUG_ASSERT_POINTER(offset);
146 MALI_DEBUG_ASSERT_POINTER(alloc_info);
148 info = (block_allocator*)ctx;
149 left = descriptor->size - *offset;
150 MALI_DEBUG_ASSERT(0 != left);
152 if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE;
154 ret_allocation = _mali_osk_malloc( sizeof(block_allocator_allocation) );
156 if ( NULL == ret_allocation )
158 /* Failure; try another allocator by returning MALI_MEM_ALLOC_NONE */
159 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
163 ret_allocation->start_offset = *offset;
164 ret_allocation->mapping_length = 0;
166 while ((left > 0) && (info->first_free))
171 u32 current_mapping_size;
173 block = info->first_free;
174 info->first_free = info->first_free->next;
175 block->next = last_allocated;
176 last_allocated = block;
178 phys_addr = get_phys(info, block);
180 padding = *offset & (MALI_BLOCK_SIZE-1);
182 if (MALI_BLOCK_SIZE - padding < left)
184 current_mapping_size = MALI_BLOCK_SIZE - padding;
188 current_mapping_size = left;
191 if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, phys_addr + padding, info->cpu_usage_adjust, current_mapping_size))
193 MALI_DEBUG_PRINT(1, ("Mapping of physical memory failed\n"));
194 result = MALI_MEM_ALLOC_INTERNAL_FAILURE;
195 mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->start_offset, ret_allocation->mapping_length, (_mali_osk_mem_mapregion_flags_t)0);
197 /* release all memory back to the pool */
198 while (last_allocated)
200 /* This relinks every block we've just allocated back into the free-list */
201 block = last_allocated->next;
202 last_allocated->next = info->first_free;
203 info->first_free = last_allocated;
204 last_allocated = block;
210 *offset += current_mapping_size;
211 left -= current_mapping_size;
212 ret_allocation->mapping_length += current_mapping_size;
215 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
219 if (left) result = MALI_MEM_ALLOC_PARTIAL;
220 else result = MALI_MEM_ALLOC_FINISHED;
222 /* Record all the information about this allocation */
223 ret_allocation->last_allocated = last_allocated;
224 ret_allocation->engine = engine;
225 ret_allocation->descriptor = descriptor;
227 alloc_info->ctx = info;
228 alloc_info->handle = ret_allocation;
229 alloc_info->release = block_allocator_release;
233 /* Free the allocation information - nothing to be passed back */
234 _mali_osk_free( ret_allocation );
240 static void block_allocator_release(void * ctx, void * handle)
242 block_allocator * info;
243 block_info * block, * next;
244 block_allocator_allocation *allocation;
246 MALI_DEBUG_ASSERT_POINTER(ctx);
247 MALI_DEBUG_ASSERT_POINTER(handle);
249 info = (block_allocator*)ctx;
250 allocation = (block_allocator_allocation*)handle;
251 block = allocation->last_allocated;
253 MALI_DEBUG_ASSERT_POINTER(block);
255 if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW))
257 MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n"));
262 mali_allocation_engine_unmap_physical(allocation->engine, allocation->descriptor, allocation->start_offset, allocation->mapping_length, (_mali_osk_mem_mapregion_flags_t)0);
266 MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks))));
270 /* relink into free-list */
271 block->next = info->first_free;
272 info->first_free = block;
274 /* advance the loop */
278 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
280 _mali_osk_free( allocation );
284 static mali_physical_memory_allocation_result block_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block)
286 block_allocator * info;
287 mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_INTERNAL_FAILURE;
289 MALI_DEBUG_ASSERT_POINTER(ctx);
290 MALI_DEBUG_ASSERT_POINTER(block);
291 info = (block_allocator*)ctx;
293 if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE;
295 if (NULL != info->first_free)
301 alloc = info->first_free;
303 phys = get_phys(info, alloc); /* Does not modify info or alloc */
304 size = MALI_BLOCK_SIZE; /* Must be multiple of MALI_MMU_PAGE_SIZE */
305 virt = _mali_osk_mem_mapioregion( phys, size, "Mali block allocator page tables" );
307 /* Failure of _mali_osk_mem_mapioregion will result in MALI_MEM_ALLOC_INTERNAL_FAILURE,
308 * because it's unlikely another allocator will be able to map in. */
312 block->ctx = info; /* same as incoming ctx */
313 block->handle = alloc;
314 block->phys_base = phys;
316 block->release = block_allocator_release_page_table_block;
317 block->mapping = virt;
319 info->first_free = alloc->next;
321 alloc->next = NULL; /* Could potentially link many blocks together instead */
323 result = MALI_MEM_ALLOC_FINISHED;
326 else result = MALI_MEM_ALLOC_NONE;
328 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
334 static void block_allocator_release_page_table_block( mali_page_table_block *page_table_block )
336 block_allocator * info;
337 block_info * block, * next;
339 MALI_DEBUG_ASSERT_POINTER( page_table_block );
341 info = (block_allocator*)page_table_block->ctx;
342 block = (block_info*)page_table_block->handle;
344 MALI_DEBUG_ASSERT_POINTER(info);
345 MALI_DEBUG_ASSERT_POINTER(block);
348 if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW))
350 MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n"));
354 /* Unmap all the physical memory at once */
355 _mali_osk_mem_unmapioregion( page_table_block->phys_base, page_table_block->size, page_table_block->mapping );
357 /** @note This loop handles the case where more than one block_info was linked.
358 * Probably unnecessary for page table block releasing. */
363 MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks))));
365 block->next = info->first_free;
366 info->first_free = block;
371 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
374 static u32 block_allocator_stat(mali_physical_memory_allocator * allocator)
376 block_allocator * info;
380 MALI_DEBUG_ASSERT_POINTER(allocator);
382 info = (block_allocator*)allocator->ctx;
383 block = info->first_free;
390 return (info->num_blocks - free_blocks) * MALI_BLOCK_SIZE;