blob: d5ffe7f6d388d7c209ca012c7147439f39e87ce9 [file] [log] [blame]
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "MemoryDealer"
18
Mathias Agopianc5b2c0b2009-05-19 19:08:10 -070019#include <binder/MemoryDealer.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080020
21#include <utils/Log.h>
Mathias Agopianc5b2c0b2009-05-19 19:08:10 -070022#include <binder/IPCThreadState.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080023#include <utils/SortedVector.h>
24#include <utils/String8.h>
Mathias Agopianc5b2c0b2009-05-19 19:08:10 -070025#include <binder/MemoryBase.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080026
27#include <stdint.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <fcntl.h>
31#include <unistd.h>
32#include <errno.h>
33#include <string.h>
34
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <sys/mman.h>
38#include <sys/file.h>
39
40namespace android {
Mathias Agopian83c04462009-05-22 19:00:22 -070041// ----------------------------------------------------------------------------
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080042
Mathias Agopian83c04462009-05-22 19:00:22 -070043HeapInterface::HeapInterface() { }
44HeapInterface::~HeapInterface() { }
45
46// ----------------------------------------------------------------------------
47
48AllocatorInterface::AllocatorInterface() { }
49AllocatorInterface::~AllocatorInterface() { }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080050
51// ----------------------------------------------------------------------------
52
53class SimpleMemory : public MemoryBase {
54public:
55 SimpleMemory(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
56 virtual ~SimpleMemory();
57};
58
59
60// ----------------------------------------------------------------------------
61
62MemoryDealer::Allocation::Allocation(
63 const sp<MemoryDealer>& dealer, ssize_t offset, size_t size,
64 const sp<IMemory>& memory)
65 : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory)
66{
67}
68
69MemoryDealer::Allocation::~Allocation()
70{
71 if (mSize) {
72 /* NOTE: it's VERY important to not free allocations of size 0 because
73 * they're special as they don't have any record in the allocator
74 * and could alias some real allocation (their offset is zero). */
75 mDealer->deallocate(mOffset);
76 }
77}
78
79sp<IMemoryHeap> MemoryDealer::Allocation::getMemory(
80 ssize_t* offset, size_t* size) const
81{
82 return mMemory->getMemory(offset, size);
83}
84
85// ----------------------------------------------------------------------------
86
87MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name)
88 : mHeap(new SharedHeap(size, flags, name)),
89 mAllocator(new SimpleBestFitAllocator(size))
90{
91}
92
93MemoryDealer::MemoryDealer(const sp<HeapInterface>& heap)
94 : mHeap(heap),
95 mAllocator(new SimpleBestFitAllocator(heap->virtualSize()))
96{
97}
98
99MemoryDealer::MemoryDealer( const sp<HeapInterface>& heap,
100 const sp<AllocatorInterface>& allocator)
101 : mHeap(heap), mAllocator(allocator)
102{
103}
104
105MemoryDealer::~MemoryDealer()
106{
107}
108
109sp<IMemory> MemoryDealer::allocate(size_t size, uint32_t flags)
110{
111 sp<IMemory> memory;
112 const ssize_t offset = allocator()->allocate(size, flags);
113 if (offset >= 0) {
114 sp<IMemory> new_memory = heap()->mapMemory(offset, size);
115 if (new_memory != 0) {
116 memory = new Allocation(this, offset, size, new_memory);
117 } else {
Mathias Agopian83c04462009-05-22 19:00:22 -0700118 LOGE("couldn't map [%8lx, %u]", offset, size);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800119 if (size) {
120 /* NOTE: it's VERY important to not free allocations of size 0
121 * because they're special as they don't have any record in the
122 * allocator and could alias some real allocation
123 * (their offset is zero). */
124 allocator()->deallocate(offset);
125 }
126 }
127 }
128 return memory;
129}
130
131void MemoryDealer::deallocate(size_t offset)
132{
133 allocator()->deallocate(offset);
134}
135
136void MemoryDealer::dump(const char* what, uint32_t flags) const
137{
138 allocator()->dump(what, flags);
139}
140
141const sp<HeapInterface>& MemoryDealer::heap() const {
142 return mHeap;
143}
144
145const sp<AllocatorInterface>& MemoryDealer::allocator() const {
146 return mAllocator;
147}
148
149// ----------------------------------------------------------------------------
150
151// align all the memory blocks on a cache-line boundary
152const int SimpleBestFitAllocator::kMemoryAlign = 32;
153
154SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size)
155{
156 size_t pagesize = getpagesize();
157 mHeapSize = ((size + pagesize-1) & ~(pagesize-1));
158
159 chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign);
160 mList.insertHead(node);
161}
162
163SimpleBestFitAllocator::~SimpleBestFitAllocator()
164{
165 while(!mList.isEmpty()) {
166 delete mList.remove(mList.head());
167 }
168}
169
170size_t SimpleBestFitAllocator::size() const
171{
172 return mHeapSize;
173}
174
175size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags)
176{
177 Mutex::Autolock _l(mLock);
178 ssize_t offset = alloc(size, flags);
179 return offset;
180}
181
182status_t SimpleBestFitAllocator::deallocate(size_t offset)
183{
184 Mutex::Autolock _l(mLock);
185 chunk_t const * const freed = dealloc(offset);
186 if (freed) {
187 return NO_ERROR;
188 }
189 return NAME_NOT_FOUND;
190}
191
192ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags)
193{
194 if (size == 0) {
195 return 0;
196 }
197 size = (size + kMemoryAlign-1) / kMemoryAlign;
198 chunk_t* free_chunk = 0;
199 chunk_t* cur = mList.head();
200
201 size_t pagesize = getpagesize();
202 while (cur) {
203 int extra = 0;
204 if (flags & PAGE_ALIGNED)
205 extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ;
206
207 // best fit
208 if (cur->free && (cur->size >= (size+extra))) {
209 if ((!free_chunk) || (cur->size < free_chunk->size)) {
210 free_chunk = cur;
211 }
212 if (cur->size == size) {
213 break;
214 }
215 }
216 cur = cur->next;
217 }
218
219 if (free_chunk) {
220 const size_t free_size = free_chunk->size;
221 free_chunk->free = 0;
222 free_chunk->size = size;
223 if (free_size > size) {
224 int extra = 0;
225 if (flags & PAGE_ALIGNED)
226 extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ;
227 if (extra) {
228 chunk_t* split = new chunk_t(free_chunk->start, extra);
229 free_chunk->start += extra;
230 mList.insertBefore(free_chunk, split);
231 }
232
233 LOGE_IF((flags&PAGE_ALIGNED) &&
234 ((free_chunk->start*kMemoryAlign)&(pagesize-1)),
235 "PAGE_ALIGNED requested, but page is not aligned!!!");
236
237 const ssize_t tail_free = free_size - (size+extra);
238 if (tail_free > 0) {
239 chunk_t* split = new chunk_t(
240 free_chunk->start + free_chunk->size, tail_free);
241 mList.insertAfter(free_chunk, split);
242 }
243 }
244 return (free_chunk->start)*kMemoryAlign;
245 }
246 return NO_MEMORY;
247}
248
249SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start)
250{
251 start = start / kMemoryAlign;
252 chunk_t* cur = mList.head();
253 while (cur) {
254 if (cur->start == start) {
255 LOG_FATAL_IF(cur->free,
256 "block at offset 0x%08lX of size 0x%08lX already freed",
257 cur->start*kMemoryAlign, cur->size*kMemoryAlign);
258
259 // merge freed blocks together
260 chunk_t* freed = cur;
261 cur->free = 1;
262 do {
263 chunk_t* const p = cur->prev;
264 chunk_t* const n = cur->next;
265 if (p && (p->free || !cur->size)) {
266 freed = p;
267 p->size += cur->size;
268 mList.remove(cur);
269 delete cur;
270 }
271 cur = n;
272 } while (cur && cur->free);
273
274 #ifndef NDEBUG
275 if (!freed->free) {
276 dump_l("dealloc (!freed->free)");
277 }
278 #endif
279 LOG_FATAL_IF(!freed->free,
280 "freed block at offset 0x%08lX of size 0x%08lX is not free!",
281 freed->start * kMemoryAlign, freed->size * kMemoryAlign);
282
283 return freed;
284 }
285 cur = cur->next;
286 }
287 return 0;
288}
289
290void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const
291{
292 Mutex::Autolock _l(mLock);
293 dump_l(what, flags);
294}
295
296void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const
297{
298 String8 result;
299 dump_l(result, what, flags);
300 LOGD("%s", result.string());
301}
302
303void SimpleBestFitAllocator::dump(String8& result,
304 const char* what, uint32_t flags) const
305{
306 Mutex::Autolock _l(mLock);
307 dump_l(result, what, flags);
308}
309
310void SimpleBestFitAllocator::dump_l(String8& result,
311 const char* what, uint32_t flags) const
312{
313 size_t size = 0;
314 int32_t i = 0;
315 chunk_t const* cur = mList.head();
316
317 const size_t SIZE = 256;
318 char buffer[SIZE];
319 snprintf(buffer, SIZE, " %s (%p, size=%u)\n",
320 what, this, (unsigned int)mHeapSize);
321
322 result.append(buffer);
323
324 while (cur) {
325 const char* errs[] = {"", "| link bogus NP",
326 "| link bogus PN", "| link bogus NP+PN" };
327 int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0;
328 int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0;
329
330 snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n",
331 i, int(cur), int(cur->start*kMemoryAlign),
332 int(cur->size*kMemoryAlign),
333 int(cur->free) ? "F" : "A",
334 errs[np|pn]);
335
336 result.append(buffer);
337
338 if (!cur->free)
339 size += cur->size*kMemoryAlign;
340
341 i++;
342 cur = cur->next;
343 }
344 snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024));
345 result.append(buffer);
346}
347
348// ----------------------------------------------------------------------------
349
Mathias Agopian83c04462009-05-22 19:00:22 -0700350SharedHeap::SharedHeap()
351 : HeapInterface(), MemoryHeapBase()
352{
353}
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800354
355SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name)
356 : MemoryHeapBase(size, flags, name)
357{
358}
359
360SharedHeap::~SharedHeap()
361{
362}
363
364sp<IMemory> SharedHeap::mapMemory(size_t offset, size_t size)
365{
366 return new SimpleMemory(this, offset, size);
367}
368
369
370SimpleMemory::SimpleMemory(const sp<IMemoryHeap>& heap,
371 ssize_t offset, size_t size)
372 : MemoryBase(heap, offset, size)
373{
374#ifndef NDEBUG
375 void* const start_ptr = (void*)(intptr_t(heap->base()) + offset);
376 memset(start_ptr, 0xda, size);
377#endif
378}
379
380SimpleMemory::~SimpleMemory()
381{
382 size_t freedOffset = getOffset();
383 size_t freedSize = getSize();
384
385 // keep the size to unmap in excess
386 size_t pagesize = getpagesize();
387 size_t start = freedOffset;
388 size_t end = start + freedSize;
389 start &= ~(pagesize-1);
390 end = (end + pagesize-1) & ~(pagesize-1);
391
392 // give back to the kernel the pages we don't need
393 size_t free_start = freedOffset;
394 size_t free_end = free_start + freedSize;
395 if (start < free_start)
396 start = free_start;
397 if (end > free_end)
398 end = free_end;
399 start = (start + pagesize-1) & ~(pagesize-1);
400 end &= ~(pagesize-1);
401
402 if (start < end) {
403 void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start);
404 size_t size = end-start;
405
406#ifndef NDEBUG
407 memset(start_ptr, 0xdf, size);
408#endif
409
410 // MADV_REMOVE is not defined on Dapper based Goobuntu
411#ifdef MADV_REMOVE
412 if (size) {
413 int err = madvise(start_ptr, size, MADV_REMOVE);
414 LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s",
415 start_ptr, size, err<0 ? strerror(errno) : "Ok");
416 }
417#endif
418 }
419}
420
421}; // namespace android