Forgotten commit. Added automount.
[AROS.git] / rom / exec / memory.c
blob0759448bac42eda4487363ce62e2df08b301ddb2
1 #include <aros/debug.h>
2 #include <exec/rawfmt.h>
3 #include <proto/kernel.h>
5 #include "exec_intern.h"
6 #include "exec_util.h"
7 #include "etask.h"
8 #include "memory.h"
9 #include "mungwall.h"
11 #define DMH(x)
13 /* Find MemHeader to which address belongs */
14 struct MemHeader *FindMem(APTR address, struct ExecBase *SysBase)
16 struct MemHeader *mh;
18 /* Nobody should change the memory list now. */
19 MEM_LOCK_SHARED;
21 /* Follow the list of MemHeaders */
22 mh = (struct MemHeader *)SysBase->MemList.lh_Head;
24 while(mh->mh_Node.ln_Succ != NULL)
26 /* Check if this MemHeader fits */
27 if(address >= mh->mh_Lower && address < mh->mh_Upper)
29 /* Yes. Return it. */
30 MEM_UNLOCK;
31 return mh;
34 /* Go to next MemHeader */
35 mh = (struct MemHeader *)mh->mh_Node.ln_Succ;
38 MEM_UNLOCK;
39 return NULL;
42 char *FormatMMContext(char *buffer, struct MMContext *ctx, struct ExecBase *SysBase)
44 if (ctx->addr)
45 buffer = NewRawDoFmt("In %s, block at 0x%p, size %lu", (VOID_FUNC)RAWFMTFUNC_STRING, buffer, ctx->func, ctx->addr, ctx->size) - 1;
46 else
47 buffer = NewRawDoFmt("In %s, size %lu", (VOID_FUNC)RAWFMTFUNC_STRING, buffer, ctx->func, ctx->size) - 1;
49 if (ctx->mc)
51 buffer = NewRawDoFmt("\nCorrupted MemChunk 0x%p (next 0x%p, size %lu)", (VOID_FUNC)RAWFMTFUNC_STRING, buffer, ctx->mc, ctx->mc->mc_Next, ctx->mc->mc_Bytes) - 1;
53 if (ctx->mcPrev)
54 buffer = NewRawDoFmt("\nPrevious MemChunk 0x%p (next 0x%p, size %lu)", (VOID_FUNC)RAWFMTFUNC_STRING, buffer, ctx->mcPrev, ctx->mcPrev->mc_Next, ctx->mcPrev->mc_Bytes) - 1;
57 /* Print MemHeader details */
58 buffer = NewRawDoFmt("\nMemHeader 0x%p (0x%p - 0x%p)", (VOID_FUNC)RAWFMTFUNC_STRING, buffer, ctx->mh, ctx->mh->mh_Lower, ctx->mh->mh_Upper) - 1;
59 if ((IPTR)ctx->mh->mh_First & (MEMCHUNK_TOTAL - 1))
60 buffer = NewRawDoFmt("\n- Unaligned first chunk address (0x%p)", (VOID_FUNC)RAWFMTFUNC_STRING, buffer, ctx->mh->mh_First) - 1;
62 if (ctx->mh->mh_Free & (MEMCHUNK_TOTAL - 1))
63 buffer = NewRawDoFmt("\n- Unaligned free space count (0x%p)", (VOID_FUNC)RAWFMTFUNC_STRING, buffer, ctx->mh->mh_Free) - 1;
65 if (ctx->mh->mh_First)
67 if ((APTR)ctx->mh->mh_First < ctx->mh->mh_Lower)
68 buffer = NewRawDoFmt("\n- First chunk (0x%p) below lower address", (VOID_FUNC)RAWFMTFUNC_STRING, buffer, ctx->mh->mh_First) - 1;
70 if (((APTR)ctx->mh->mh_First + ctx->mh->mh_Free > ctx->mh->mh_Upper))
71 buffer = NewRawDoFmt("\n- Free space count too large (%lu, first chunk 0x%xp)", (VOID_FUNC)RAWFMTFUNC_STRING, buffer, ctx->mh->mh_Free, ctx->mh->mh_First) - 1;
74 return buffer;
77 #ifdef NO_CONSISTENCY_CHECKS
79 #define validateHeader(mh, op, addr, size, SysBase) TRUE
80 #define validateChunk(mc, prev, mh, op, addr, size, SysBase) TRUE
82 #else
84 static ULONG memAlerts[] =
86 AT_DeadEnd|AN_MemoryInsane, /* MM_ALLOC */
87 AT_DeadEnd|AN_MemCorrupt, /* MM_FREE */
88 AN_FreeTwice /* MM_OVERLAP */
92 * MemHeader validation routine. Rules are:
94 * 1. Both mh_First and mh_Free must be MEMCHUNK_TOTAL-aligned.
95 * 2. Free space (if present) must completely fit in between mh_Lower and mh_Upper.
96 * We intentionally don't check header's own location. We assume that in future we'll
97 * be able to put MEMF_CHIP headers inside MEMF_FAST memory, for speed up.
99 static BOOL validateHeader(struct MemHeader *mh, UBYTE op, APTR addr, IPTR size, struct TraceLocation *tp, struct ExecBase *SysBase)
101 if (((IPTR)mh->mh_First & (MEMCHUNK_TOTAL - 1)) || (mh->mh_Free & (MEMCHUNK_TOTAL - 1)) || /* 1 */
102 (mh->mh_First &&
103 (((APTR)mh->mh_First < mh->mh_Lower) || ((APTR)mh->mh_First + mh->mh_Free > mh->mh_Upper)))) /* 2 */
105 if (tp)
107 /* TraceLocation is not supplied by PrepareExecBase(). Fail silently. */
108 struct MMContext alertData;
110 alertData.mh = mh;
111 alertData.mc = NULL;
112 alertData.mcPrev = NULL;
113 alertData.func = tp->function;
114 alertData.addr = addr;
115 alertData.size = size;
116 alertData.op = op;
118 Exec_ExtAlert(memAlerts[op], tp->caller, tp->stack, AT_MEMORY, &alertData, SysBase);
122 * Theoretically during very early boot we can fail to post an alert (no KernelBase yet).
123 * In this case we return with fault indication.
125 return FALSE;
127 return TRUE;
131 * MemChunk consistency check. Rules are:
133 * 1. Both mc_Next and mc_Bytes must me MEMCHUNK_TOTAL-aligned, and mc_Bytes can not be zero.
134 * 2. End of this chunk must not be greater than mh->mh_Upper
135 * 3. mc_Next (if present) must point in between end of this chunk and mh->mh_Upper - MEMCHUNK_TOTAL.
136 * There must be at least MEMHCUNK_TOTAL allocated bytes between free chunks.
138 * This function is inlined for speed improvements.
140 static inline BOOL validateChunk(struct MemChunk *p2, struct MemChunk *p1, struct MemHeader *mh,
141 UBYTE op, APTR addr, IPTR size,
142 struct TraceLocation *tp, struct ExecBase *SysBase)
144 if (((IPTR)p2->mc_Next & (MEMCHUNK_TOTAL-1)) || (p2->mc_Bytes == 0) || (p2->mc_Bytes & (MEMCHUNK_TOTAL-1)) || /* 1 */
145 ((APTR)p2 + p2->mc_Bytes > mh->mh_Upper) || /* 2 */
146 (p2->mc_Next && (((APTR)p2->mc_Next < (APTR)p2 + p2->mc_Bytes + MEMCHUNK_TOTAL) || /* 3 */
147 ((APTR)p2->mc_Next > mh->mh_Upper - MEMCHUNK_TOTAL))))
149 if (tp)
151 struct MMContext alertData;
153 alertData.mh = mh;
154 alertData.mc = p2;
155 alertData.mcPrev = (p1 == (struct MemChunk *)&mh->mh_First) ? NULL : p1;
156 alertData.func = tp->function;
157 alertData.addr = addr;
158 alertData.size = size;
159 alertData.op = op;
161 Exec_ExtAlert(memAlerts[op], tp->caller, tp->stack, AT_MEMORY, &alertData, SysBase);
163 return FALSE;
166 return TRUE;
169 #endif
172 * Allocate block from the given MemHeader in a specific way.
173 * This routine can be called with SysBase = NULL.
175 APTR stdAlloc(struct MemHeader *mh, IPTR size, ULONG requirements, struct TraceLocation *tp, struct ExecBase *SysBase)
177 /* First round byteSize up to a multiple of MEMCHUNK_TOTAL */
178 IPTR byteSize = AROS_ROUNDUP2(size, MEMCHUNK_TOTAL);
179 struct MemChunk *mc=NULL, *p1, *p2;
181 /* Validate MemHeader before doing anything. */
182 if (!validateHeader(mh, MM_ALLOC, NULL, size, tp, SysBase))
183 return NULL;
186 * The free memory list is only single linked, i.e. to remove
187 * elements from the list I need node's predessor. For the
188 * first element I can use mh->mh_First instead of a real predessor.
190 p1 = (struct MemChunk *)&mh->mh_First;
191 p2 = p1->mc_Next;
194 * Follow the memory list. p1 is the previous MemChunk, p2 is the current one.
195 * On 1st pass p1 points to mh->mh_First, so that chaning p1->mc_Next actually
196 * changes mh->mh_First.
198 while (p2 != NULL)
200 /* Validate the current chunk */
201 if (!validateChunk(p2, p1, mh, MM_ALLOC, NULL, size, tp, SysBase))
202 return NULL;
204 /* Check if the current block is large enough */
205 if (p2->mc_Bytes>=byteSize)
207 /* It is. */
208 mc = p1;
210 /* Use this one if MEMF_REVERSE is not set.*/
211 if (!(requirements & MEMF_REVERSE))
212 break;
213 /* Else continue - there may be more to come. */
216 /* Go to next block */
217 p1 = p2;
218 p2 = p1->mc_Next;
221 /* Something found? */
222 if (mc != NULL)
224 /* Remember: if MEMF_REVERSE is set p1 and p2 are now invalid. */
225 p1 = mc;
226 p2 = p1->mc_Next;
228 /* Remove the block from the list and return it. */
229 if (p2->mc_Bytes == byteSize)
231 /* Fits exactly. Just relink the list. */
232 p1->mc_Next = p2->mc_Next;
233 mc = p2;
235 else
237 if (requirements & MEMF_REVERSE)
239 /* Return the last bytes. */
240 p1->mc_Next=p2;
241 mc = (struct MemChunk *)((UBYTE *)p2+p2->mc_Bytes-byteSize);
243 else
245 /* Return the first bytes. */
246 p1->mc_Next=(struct MemChunk *)((UBYTE *)p2+byteSize);
247 mc=p2;
250 p1 = p1->mc_Next;
251 p1->mc_Next = p2->mc_Next;
252 p1->mc_Bytes = p2->mc_Bytes-byteSize;
255 mh->mh_Free -= byteSize;
257 /* Clear the block if requested */
258 if (requirements & MEMF_CLEAR)
259 memset(mc, 0, byteSize);
262 return mc;
265 /* Free 'byteSize' bytes starting at 'memoryBlock' belonging to MemHeader 'freeList' */
266 void stdDealloc(struct MemHeader *freeList, APTR addr, IPTR size, struct TraceLocation *tp, struct ExecBase *SysBase)
268 APTR memoryBlock;
269 IPTR byteSize;
270 struct MemChunk *p1, *p2, *p3;
271 UBYTE *p4;
273 /* Make sure the MemHeader is OK */
274 if (!validateHeader(freeList, MM_FREE, addr, size, tp, SysBase))
275 return;
277 /* Align size to the requirements */
278 byteSize = size + ((IPTR)addr & (MEMCHUNK_TOTAL-1));
279 byteSize = (byteSize + MEMCHUNK_TOTAL-1) & ~(MEMCHUNK_TOTAL-1);
281 /* Align the block as well */
282 memoryBlock = (APTR)((IPTR)addr & ~(MEMCHUNK_TOTAL-1));
285 The free memory list is only single linked, i.e. to insert
286 elements into the list I need the node as well as it's
287 predessor. For the first element I can use freeList->mh_First
288 instead of a real predessor.
290 p1=(struct MemChunk *)&freeList->mh_First;
291 p2=freeList->mh_First;
293 /* Start and end(+1) of the block */
294 p3=(struct MemChunk *)memoryBlock;
295 p4=(UBYTE *)p3+byteSize;
297 /* No chunk in list? Just insert the current one and return. */
298 if(p2==NULL)
300 p3->mc_Bytes=byteSize;
301 p3->mc_Next=NULL;
302 p1->mc_Next=p3;
303 freeList->mh_Free+=byteSize;
304 return;
307 /* Follow the list to find a place where to insert our memory. */
310 if (!validateChunk(p2, p1, freeList, MM_FREE, addr, size, tp, SysBase))
311 return;
313 /* Found a block with a higher address? */
314 if (p2 >= p3)
316 #if !defined(NO_CONSISTENCY_CHECKS)
318 If the memory to be freed overlaps with the current
319 block something must be wrong.
321 if (p4>(UBYTE *)p2)
323 bug("[MM] Chunk allocator error\n");
324 bug("[MM] Attempt to free %u bytes at 0x%p from MemHeader 0x%p\n", byteSize, memoryBlock, freeList);
325 bug("[MM] Block overlaps with chunk 0x%p (%u bytes)\n", p2, p2->mc_Bytes);
327 Alert(AN_FreeTwice);
328 return;
330 #endif
331 /* End the loop with p2 non-zero */
332 break;
334 /* goto next block */
335 p1=p2;
336 p2=p2->mc_Next;
338 /* If the loop ends with p2 zero add it at the end. */
339 }while(p2!=NULL);
341 /* If there was a previous block merge with it. */
342 if(p1!=(struct MemChunk *)&freeList->mh_First)
344 #if !defined(NO_CONSISTENCY_CHECKS)
345 /* Check if they overlap. */
346 if ((UBYTE *)p1+p1->mc_Bytes>(UBYTE *)p3)
348 bug("[MM] Chunk allocator error\n");
349 bug("[MM] Attempt to free %u bytes at 0x%p from MemHeader 0x%p\n", byteSize, memoryBlock, freeList);
350 bug("[MM] Block overlaps with chunk 0x%p (%u bytes)\n", p1, p1->mc_Bytes);
352 Alert(AN_FreeTwice);
353 return;
355 #endif
356 /* Merge if possible */
357 if((UBYTE *)p1+p1->mc_Bytes==(UBYTE *)p3)
358 p3=p1;
359 else
360 /* Not possible to merge */
361 p1->mc_Next=p3;
362 }else
364 There was no previous block. Just insert the memory at
365 the start of the list.
367 p1->mc_Next=p3;
369 /* Try to merge with next block (if there is one ;-) ). */
370 if(p4==(UBYTE *)p2&&p2!=NULL)
373 Overlap checking already done. Doing it here after
374 the list potentially changed would be a bad idea.
376 p4+=p2->mc_Bytes;
377 p2=p2->mc_Next;
379 /* relink the list and return. */
380 p3->mc_Next=p2;
381 p3->mc_Bytes=p4-(UBYTE *)p3;
382 freeList->mh_Free+=byteSize;
386 * TODO:
387 * During transition period four routines below use nommu allocator.
388 * When transition is complete they should use them only if MMU
389 * is inactive. Otherwise they should use KrnAllocPages()/KrnFreePages().
392 /* Non-mungwalled AllocAbs(). Does not destroy sideways regions. */
393 APTR InternalAllocAbs(APTR location, IPTR byteSize, struct ExecBase *SysBase)
395 return nommu_AllocAbs(location, byteSize, SysBase);
399 * Use this if you want to free region allocated by InternalAllocAbs().
400 * Otherwise you hit mungwall problem (FreeMem() expects header).
402 void InternalFreeMem(APTR location, IPTR byteSize, struct TraceLocation *loc, struct ExecBase *SysBase)
404 nommu_FreeMem(location, byteSize, loc, SysBase);
407 /* Allocate a region managed by own header */
408 APTR AllocMemHeader(IPTR size, ULONG flags, struct TraceLocation *loc, struct ExecBase *SysBase)
410 struct MemHeader *mh;
412 mh = nommu_AllocMem(size, flags, loc, SysBase);
413 DMH(bug("[AllocMemHeader] Allocated %u bytes at 0x%p\n", size, mh));
415 if (mh)
417 struct MemHeader *orig = FindMem(mh, SysBase);
419 size -= MEMHEADER_TOTAL;
422 * Initialize new MemHeader.
423 * Inherit attributes from system MemHeader from which
424 * our chunk was allocated.
426 mh->mh_Node.ln_Type = NT_MEMORY;
427 mh->mh_Node.ln_Pri = orig->mh_Node.ln_Pri;
428 mh->mh_Attributes = orig->mh_Attributes;
429 mh->mh_Lower = (APTR)mh + MEMHEADER_TOTAL;
430 mh->mh_Upper = mh->mh_Lower + size;
431 mh->mh_First = mh->mh_Lower;
432 mh->mh_Free = size;
434 /* Create the first (and the only) MemChunk */
435 mh->mh_First->mc_Next = NULL;
436 mh->mh_First->mc_Bytes = size;
438 return mh;
441 /* Free a region allocated by AllocMemHeader() */
442 void FreeMemHeader(APTR addr, struct TraceLocation *loc, struct ExecBase *SysBase)
444 ULONG size = ((struct MemHeader *)addr)->mh_Upper - addr;
446 DMH(bug("[FreeMemHeader] Freeing %u bytes at 0x%p\n", size, addr));
447 nommu_FreeMem(addr, size, loc, SysBase);
451 * This is our own Enqueue() version. Currently the only differece is that
452 * we insert our node before the first node with LOWER OR EQUAL priority,
453 * so that for nodes with equal priority it will be LIFO, not FIFO queue.
454 * This speeds up the allocator.
455 * TODO: implement secondary sorting by mh_Free. This will allow to
456 * implement best-match algorithm (so that puddles with smaller free space
457 * will be picked up first). This way the smallest allocations will reuse
458 * smallest chunks instead of fragmenting large ones.
460 static void EnqueueMemHeader(struct MinList *list, struct MemHeader *mh)
462 struct MemHeader *next;
464 /* Look through the list */
465 ForeachNode (list, next)
468 Look for the first MemHeader with a lower or equal pri as the node
469 we have to insert into the list.
471 if (mh->mh_Node.ln_Pri >= next->mh_Node.ln_Pri)
472 break;
475 /* Insert the node before next */
476 mh->mh_Node.ln_Pred = next->mh_Node.ln_Pred;
477 mh->mh_Node.ln_Succ = &next->mh_Node;
478 next->mh_Node.ln_Pred->ln_Succ = &mh->mh_Node;
479 next->mh_Node.ln_Pred = &mh->mh_Node;
483 * Allocate memory with given physical properties from the given pool.
484 * Our pools can be mixed. This means that different puddles from the
485 * pool can have different physical flags. For example the same pool
486 * can contain puddles from both CHIP and FAST memory. This is done in
487 * order to provide a single system default pool for all types of memory.
489 APTR InternalAllocPooled(APTR poolHeader, IPTR memSize, ULONG flags, struct TraceLocation *loc, struct ExecBase *SysBase)
491 struct ProtectedPool *pool = poolHeader + MEMHEADER_TOTAL;
492 APTR ret = NULL;
493 IPTR origSize;
494 struct MemHeader *mh;
496 D(bug("[exec] InternalAllocPooled(0x%p, %u, 0x%08X), header 0x%p\n", poolHeader, memSize, flags, pool));
499 * Memory blocks allocated from the pool store pointers to the MemHeader they were
500 * allocated from. This is done in order to avoid slow lookups in InternalFreePooled().
501 * This is done in AllocVec()-alike manner, the pointer is placed right before the block.
503 memSize += sizeof(struct MemHeader *);
504 origSize = memSize;
506 /* If mungwall is enabled, count also size of walls */
507 if (PrivExecBase(SysBase)->IntFlags & EXECF_MungWall)
508 memSize += MUNGWALL_TOTAL_SIZE;
510 if (pool->pool.Requirements & MEMF_SEM_PROTECTED)
512 ObtainSemaphore(&pool->sem);
515 /* Follow the list of MemHeaders */
516 mh = (struct MemHeader *)pool->pool.PuddleList.mlh_Head;
517 for(;;)
519 ULONG physFlags = flags & MEMF_PHYSICAL_MASK;
521 /* Are there no more MemHeaders? */
522 if (mh->mh_Node.ln_Succ==NULL)
525 * Get a new one.
526 * Usually we allocate puddles of default size, specified during
527 * pool creation. However we can be asked to allocate block whose
528 * size will be larger than default puddle size.
529 * Previously this was handled by threshSize parameter. In our new
530 * implementation we just allocate enlarged puddle. This is done
531 * in order not to waste page tails beyond the allocated large block.
532 * These tails will be used for our pool too. Their size is smaller
533 * than page size but they still perfectly fit for small allocations
534 * (the primary use for pools).
535 * Since our large block is also a puddle, it will be reused for our
536 * pool when the block is freed. It can also be reused for another
537 * large allocation, if it fits in.
538 * Our final puddle size still includes MEMHEADER_TOTAL in any case.
540 IPTR puddleSize = pool->pool.PuddleSize;
542 if (memSize > puddleSize - MEMHEADER_TOTAL)
544 IPTR align = PrivExecBase(SysBase)->PageSize - 1;
546 puddleSize = memSize + MEMHEADER_TOTAL;
547 /* Align the size up to page boundary */
548 puddleSize = (puddleSize + align) & ~align;
551 mh = AllocMemHeader(puddleSize, flags, loc, SysBase);
552 D(bug("[InternalAllocPooled] Allocated new puddle 0x%p, size %u\n", mh, puddleSize));
554 /* No memory left? */
555 if(mh == NULL)
556 break;
558 /* Add the new puddle to our pool */
559 mh->mh_Node.ln_Name = (STRPTR)pool;
560 Enqueue((struct List *)&pool->pool.PuddleList, &mh->mh_Node);
562 /* Fall through to get the memory */
564 else
566 /* Ignore existing MemHeaders with memory type that differ from the requested ones */
567 if (physFlags & ~mh->mh_Attributes)
569 D(bug("[InternalAllocPooled] Wrong flags for puddle 0x%p (wanted 0x%08X, have 0x%08X\n", flags, mh->mh_Attributes));
571 mh = (struct MemHeader *)mh->mh_Node.ln_Succ;
572 continue;
576 /* Try to get the memory */
577 ret = stdAlloc(mh, memSize, flags, loc, SysBase);
578 D(bug("[InternalAllocPooled] Allocated memory at 0x%p from puddle 0x%p\n", ret, mh));
580 /* Got it? */
581 if (ret != NULL)
584 * If this is not the first MemHeader and it has some free space,
585 * move it forward (so that the next allocation will attempt to use it first).
586 * IMPORTANT: We use modification of Enqueue() because we still sort MemHeaders
587 * according to their priority (which they inherit from system MemHeaders).
588 * This allows us to have mixed pools (e. g. with both CHIP and FAST regions). This
589 * will be needed in future for memory protection.
591 if (mh->mh_Node.ln_Pred != NULL && mh->mh_Free > 32)
593 D(bug("[InternalAllocPooled] Re-sorting puddle list\n"));
594 Remove(&mh->mh_Node);
595 EnqueueMemHeader(&pool->pool.PuddleList, mh);
598 break;
601 /* No. Try next MemHeader */
602 mh = (struct MemHeader *)mh->mh_Node.ln_Succ;
605 if (pool->pool.Requirements & MEMF_SEM_PROTECTED)
607 ReleaseSemaphore(&pool->sem);
610 if (ret)
612 /* Build munge walls if requested */
613 ret = MungWall_Build(ret, pool, origSize, flags, loc, SysBase);
615 /* Remember where we were allocated from */
616 *((struct MemHeader **)ret) = mh;
617 ret += sizeof(struct MemHeader *);
620 /* Everything fine */
621 return ret;
625 * This is a pair to InternalAllocPooled()
626 * This code separated from FreePooled() in order to provide compatibility with various
627 * memory tracking patches. If some exec code calls InternalAllocPooled() directly
628 * (AllocMem() will do it), it has to call also InternalFreePooled() directly.
629 * Our chunks remember from which pool they came, so we don't need a pointer to pool
630 * header here. This will save us from headaches in future FreeMem() implementation.
632 void InternalFreePooled(APTR memory, IPTR memSize, struct TraceLocation *loc, struct ExecBase *SysBase)
634 struct MemHeader *mh;
635 APTR freeStart;
636 IPTR freeSize;
638 D(bug("[exec] InternalFreePooled(0x%p, %u)\n", memory, memSize));
640 if (!memory || !memSize) return;
642 /* Get MemHeader pointer. It is stored right before our block. */
643 freeStart = memory - sizeof(struct MemHeader *);
644 freeSize = memSize + sizeof(struct MemHeader *);
645 mh = *((struct MemHeader **)freeStart);
647 /* Check walls first */
648 freeStart = MungWall_Check(freeStart, freeSize, loc, SysBase);
649 if (PrivExecBase(SysBase)->IntFlags & EXECF_MungWall)
650 freeSize += MUNGWALL_TOTAL_SIZE;
652 /* Verify that MemHeader pointer is correct */
653 if ((mh->mh_Node.ln_Type != NT_MEMORY) ||
654 (freeStart < mh->mh_Lower) || (freeStart + freeSize > mh->mh_Upper))
657 * Something is wrong.
658 * TODO: the following should actually be printed as part of the alert.
659 * In future there should be some kind of "alert context". CPU alerts
660 * (like illegal access) should remember CPU context there. Memory manager
661 * alerts (like this one) should remember some own information.
663 bug("[MM] Pool manager error\n");
664 bug("[MM] Attempt to free %u bytes at 0x%p\n", memSize, memory);
665 bug("[MM] The chunk does not belong to a pool\n");
667 Alert(AN_BadFreeAddr);
669 else
671 struct ProtectedPool *pool = (struct ProtectedPool *)mh->mh_Node.ln_Name;
672 IPTR size;
674 if (pool->pool.Requirements & MEMF_SEM_PROTECTED)
676 ObtainSemaphore(&pool->sem);
679 size = mh->mh_Upper - mh->mh_Lower;
680 D(bug("[FreePooled] Allocated from puddle 0x%p, size %u\n", mh, size));
682 /* Free the memory. */
683 stdDealloc(mh, freeStart, freeSize, loc, SysBase);
684 D(bug("[FreePooled] Deallocated chunk, %u free bytes in the puddle\n", mh->mh_Free));
686 /* Is this MemHeader completely free now? */
687 if (mh->mh_Free == size)
689 D(bug("[FreePooled] Puddle is empty, giving back to the system\n"));
691 /* Yes. Remove it from the list. */
692 Remove(&mh->mh_Node);
693 /* And free it. */
694 FreeMemHeader(mh, loc, SysBase);
696 /* All done. */
698 if (pool->pool.Requirements & MEMF_SEM_PROTECTED)
700 ReleaseSemaphore(&pool->sem);
705 ULONG checkMemHandlers(struct checkMemHandlersState *cmhs, struct ExecBase *SysBase)
707 struct Node *tmp;
708 struct Interrupt *lmh;
710 if (cmhs->cmhs_Data.memh_RequestFlags & MEMF_NO_EXPUNGE)
711 return MEM_DID_NOTHING;
713 /* In order to keep things clean, we must run in a single thread */
714 ObtainSemaphore(&PrivExecBase(SysBase)->LowMemSem);
717 * Loop over low memory handlers. Handlers can remove
718 * themselves from the list while being invoked, thus
719 * we need to be careful!
721 for (lmh = (struct Interrupt *)cmhs->cmhs_CurNode;
722 (tmp = lmh->is_Node.ln_Succ);
723 lmh = (struct Interrupt *)(cmhs->cmhs_CurNode = tmp))
725 ULONG ret;
727 ret = AROS_UFC3 (LONG, lmh->is_Code,
728 AROS_UFCA(struct MemHandlerData *, &cmhs->cmhs_Data, A0),
729 AROS_UFCA(APTR, lmh->is_Data, A1),
730 AROS_UFCA(struct ExecBase *, SysBase, A6)
733 if (ret == MEM_TRY_AGAIN)
735 /* MemHandler said he did something. Try again. */
736 /* Is there any program that depends on this flag??? */
737 cmhs->cmhs_Data.memh_Flags |= MEMHF_RECYCLE;
739 ReleaseSemaphore(&PrivExecBase(SysBase)->LowMemSem);
740 return MEM_TRY_AGAIN;
742 else
743 /* Nothing more to expect from this handler. */
744 cmhs->cmhs_Data.memh_Flags &= ~MEMHF_RECYCLE;
747 ReleaseSemaphore(&PrivExecBase(SysBase)->LowMemSem);
748 return MEM_DID_NOTHING;