2 * Copyright (C) 2011-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.
11 #include "mali_kernel_common.h"
12 #include "mali_kernel_memory_engine.h"
15 typedef struct os_allocation
19 mali_allocation_engine * engine;
20 mali_memory_allocation * descriptor;
23 typedef struct os_allocator
25 _mali_osk_lock_t *mutex;
28 * Maximum number of pages to allocate from the OS
33 * Number of pages allocated from the OS
35 u32 num_pages_allocated;
37 /** CPU Usage adjustment (add to mali physical address to get cpu physical address) */
41 static mali_physical_memory_allocation_result os_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info);
42 static mali_physical_memory_allocation_result os_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block);
43 static void os_allocator_release(void * ctx, void * handle);
44 static void os_allocator_page_table_block_release( mali_page_table_block *page_table_block );
45 static void os_allocator_destroy(mali_physical_memory_allocator * allocator);
46 static u32 os_allocator_stat(mali_physical_memory_allocator * allocator);
48 mali_physical_memory_allocator * mali_os_allocator_create(u32 max_allocation, u32 cpu_usage_adjust, const char *name)
50 mali_physical_memory_allocator * allocator;
53 max_allocation = (max_allocation + _MALI_OSK_CPU_PAGE_SIZE-1) & ~(_MALI_OSK_CPU_PAGE_SIZE-1);
55 MALI_DEBUG_PRINT(2, ("Mali OS memory allocator created with max allocation size of 0x%X bytes, cpu_usage_adjust 0x%08X\n", max_allocation, cpu_usage_adjust));
57 allocator = _mali_osk_malloc(sizeof(mali_physical_memory_allocator));
58 if (NULL != allocator)
60 info = _mali_osk_malloc(sizeof(os_allocator));
63 info->num_pages_max = max_allocation / _MALI_OSK_CPU_PAGE_SIZE;
64 info->num_pages_allocated = 0;
65 info->cpu_usage_adjust = cpu_usage_adjust;
67 info->mutex = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | _MALI_OSK_LOCKFLAG_ORDERED, 0, _MALI_OSK_LOCK_ORDER_MEM_INFO);
68 if (NULL != info->mutex)
70 allocator->allocate = os_allocator_allocate;
71 allocator->allocate_page_table_block = os_allocator_allocate_page_table_block;
72 allocator->destroy = os_allocator_destroy;
73 allocator->stat = os_allocator_stat;
74 allocator->ctx = info;
75 allocator->name = name;
81 _mali_osk_free(allocator);
87 static u32 os_allocator_stat(mali_physical_memory_allocator * allocator)
90 info = (os_allocator*)allocator->ctx;
91 return info->num_pages_allocated * _MALI_OSK_MALI_PAGE_SIZE;
94 static void os_allocator_destroy(mali_physical_memory_allocator * allocator)
97 MALI_DEBUG_ASSERT_POINTER(allocator);
98 MALI_DEBUG_ASSERT_POINTER(allocator->ctx);
99 info = (os_allocator*)allocator->ctx;
100 _mali_osk_lock_term(info->mutex);
101 _mali_osk_free(info);
102 _mali_osk_free(allocator);
105 static mali_physical_memory_allocation_result os_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info)
107 mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_NONE;
110 os_allocation * allocation;
111 int pages_allocated = 0;
112 _mali_osk_errcode_t err = _MALI_OSK_ERR_OK;
114 MALI_DEBUG_ASSERT_POINTER(ctx);
115 MALI_DEBUG_ASSERT_POINTER(engine);
116 MALI_DEBUG_ASSERT_POINTER(descriptor);
117 MALI_DEBUG_ASSERT_POINTER(offset);
118 MALI_DEBUG_ASSERT_POINTER(alloc_info);
120 info = (os_allocator*)ctx;
121 left = descriptor->size - *offset;
123 if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE;
125 /** @note this code may not work on Linux, or may require a more complex Linux implementation */
126 allocation = _mali_osk_malloc(sizeof(os_allocation));
127 if (NULL != allocation)
129 u32 os_mem_max_usage = info->num_pages_max * _MALI_OSK_CPU_PAGE_SIZE;
130 allocation->offset_start = *offset;
131 allocation->num_pages = ((left + _MALI_OSK_CPU_PAGE_SIZE - 1) & ~(_MALI_OSK_CPU_PAGE_SIZE - 1)) >> _MALI_OSK_CPU_PAGE_ORDER;
132 MALI_DEBUG_PRINT(6, ("Allocating page array of size %d bytes\n", allocation->num_pages * sizeof(struct page*)));
135 while (left > 0 && ((info->num_pages_allocated + pages_allocated) < info->num_pages_max) && _mali_osk_mem_check_allocated(os_mem_max_usage))
139 err = mali_allocation_engine_map_physical(engine, descriptor, *offset, MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC, info->cpu_usage_adjust, _MALI_OSK_CPU_PAGE_SIZE);
140 if ( _MALI_OSK_ERR_OK != err)
142 if ( _MALI_OSK_ERR_NOMEM == err)
144 /* 'Partial' allocation (or, out-of-memory on first page) */
148 MALI_DEBUG_PRINT(1, ("Mapping of physical memory failed\n"));
150 /* Fatal error, cleanup any previous pages allocated. */
151 if ( pages_allocated > 0 )
153 mali_allocation_engine_unmap_physical( engine, descriptor, allocation->offset_start, _MALI_OSK_CPU_PAGE_SIZE*pages_allocated, _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR );
154 /* (*offset) doesn't need to be restored; it will not be used by the caller on failure */
159 result = MALI_MEM_ALLOC_INTERNAL_FAILURE;
164 if (left < _MALI_OSK_CPU_PAGE_SIZE) left = 0;
165 else left -= _MALI_OSK_CPU_PAGE_SIZE;
169 *offset += _MALI_OSK_CPU_PAGE_SIZE;
172 if (left) MALI_PRINT(("Out of memory. Mali memory allocated: %d kB Configured maximum OS memory usage: %d kB\n",
173 (info->num_pages_allocated * _MALI_OSK_CPU_PAGE_SIZE)/1024, (info->num_pages_max* _MALI_OSK_CPU_PAGE_SIZE)/1024));
175 /* Loop termination; decide on result */
178 MALI_DEBUG_PRINT(6, ("Allocated %d pages\n", pages_allocated));
179 if (left) result = MALI_MEM_ALLOC_PARTIAL;
180 else result = MALI_MEM_ALLOC_FINISHED;
182 /* Some OS do not perform a full cache flush (including all outer caches) for uncached mapped memory.
183 * They zero the memory through a cached mapping, then flush the inner caches but not the outer caches.
184 * This is required for MALI to have the correct view of the memory.
186 _mali_osk_cache_ensure_uncached_range_flushed( (void *)descriptor, allocation->offset_start, pages_allocated *_MALI_OSK_CPU_PAGE_SIZE );
187 allocation->num_pages = pages_allocated;
188 allocation->engine = engine; /* Necessary to make the engine's unmap call */
189 allocation->descriptor = descriptor; /* Necessary to make the engine's unmap call */
190 info->num_pages_allocated += pages_allocated;
192 MALI_DEBUG_PRINT(6, ("%d out of %d pages now allocated\n", info->num_pages_allocated, info->num_pages_max));
194 alloc_info->ctx = info;
195 alloc_info->handle = allocation;
196 alloc_info->release = os_allocator_release;
200 MALI_DEBUG_PRINT(6, ("Releasing pages array due to no pages allocated\n"));
201 _mali_osk_free( allocation );
205 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
210 static void os_allocator_release(void * ctx, void * handle)
213 os_allocation * allocation;
214 mali_allocation_engine * engine;
215 mali_memory_allocation * descriptor;
217 MALI_DEBUG_ASSERT_POINTER(ctx);
218 MALI_DEBUG_ASSERT_POINTER(handle);
220 info = (os_allocator*)ctx;
221 allocation = (os_allocation*)handle;
222 engine = allocation->engine;
223 descriptor = allocation->descriptor;
225 MALI_DEBUG_ASSERT_POINTER( engine );
226 MALI_DEBUG_ASSERT_POINTER( descriptor );
228 if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW))
230 MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n"));
234 MALI_DEBUG_PRINT(6, ("Releasing %d os pages\n", allocation->num_pages));
236 MALI_DEBUG_ASSERT( allocation->num_pages <= info->num_pages_allocated);
237 info->num_pages_allocated -= allocation->num_pages;
239 mali_allocation_engine_unmap_physical( engine, descriptor, allocation->offset_start, _MALI_OSK_CPU_PAGE_SIZE*allocation->num_pages, _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR );
241 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
243 _mali_osk_free(allocation);
246 static mali_physical_memory_allocation_result os_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block)
249 #ifndef CONFIG_FORCE_MAX_ZONEORDER
250 int allocation_order = 10;
252 int allocation_order = CONFIG_FORCE_MAX_ZONEORDER - 1;
255 u32 size = _MALI_OSK_CPU_PAGE_SIZE << allocation_order;
260 MALI_DEBUG_ASSERT_POINTER(ctx);
261 info = (os_allocator*)ctx;
263 /* Ensure we don't allocate more than we're supposed to from the ctx */
264 if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE;
266 /* if the number of pages to be requested lead to exceeding the memory
267 * limit in info->num_pages_max, reduce the size that is to be requested. */
268 while ( (info->num_pages_allocated + (1 << allocation_order) > info->num_pages_max)
269 && _mali_osk_mem_check_allocated(info->num_pages_max * _MALI_OSK_CPU_PAGE_SIZE) )
271 if ( allocation_order > 0 ) {
275 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
276 return MALI_MEM_ALLOC_NONE;
280 /* try to allocate 2^(allocation_order) pages, if that fails, try
281 * allocation_order-1 to allocation_order 0 (inclusive) */
282 while ( allocation_order >= 0 )
284 size = _MALI_OSK_CPU_PAGE_SIZE << allocation_order;
285 virt = _mali_osk_mem_allocioregion( &cpu_phys_base, size );
287 if (NULL != virt) break;
294 MALI_DEBUG_PRINT(1, ("Failed to allocate consistent memory. Is CONSISTENT_DMA_SIZE set too low?\n"));
296 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
297 return MALI_MEM_ALLOC_NONE;
300 MALI_DEBUG_PRINT(5, ("os_allocator_allocate_page_table_block: Allocation of order %i succeeded\n",
303 /* we now know the size of the allocation since we know for what
304 * allocation_order the allocation succeeded */
305 size = _MALI_OSK_CPU_PAGE_SIZE << allocation_order;
308 block->release = os_allocator_page_table_block_release;
310 block->handle = (void*)allocation_order;
312 block->phys_base = cpu_phys_base - info->cpu_usage_adjust;
313 block->mapping = virt;
315 info->num_pages_allocated += (1 << allocation_order);
317 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);
319 return MALI_MEM_ALLOC_FINISHED;
322 static void os_allocator_page_table_block_release( mali_page_table_block *page_table_block )
325 u32 allocation_order;
328 MALI_DEBUG_ASSERT_POINTER( page_table_block );
330 info = (os_allocator*)page_table_block->ctx;
332 MALI_DEBUG_ASSERT_POINTER( info );
334 allocation_order = (u32)page_table_block->handle;
336 pages_allocated = 1 << allocation_order;
338 MALI_DEBUG_ASSERT( pages_allocated * _MALI_OSK_CPU_PAGE_SIZE == page_table_block->size );
340 if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW))
342 MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n"));
346 MALI_DEBUG_ASSERT( pages_allocated <= info->num_pages_allocated);
347 info->num_pages_allocated -= pages_allocated;
349 /* Adjust phys_base from mali physical address to CPU physical address */
350 _mali_osk_mem_freeioregion( page_table_block->phys_base + info->cpu_usage_adjust, page_table_block->size, page_table_block->mapping );
352 _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW);