123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- MODULE UsbVarTdAlloc; (** AUTHOR "Timothée Martiel"; PURPOSE "Variable-size data-structure allocator for EHCI."; *)
- IMPORT Machine;
- CONST
- AllocSize = 1024 * 1024;
- AllocAlign = 1024 * 1024;
- TYPE
- (**
- Buffer descriptor
- *)
- TdBuffer = OBJECT
- VAR
- buffer: POINTER TO ARRAY OF CHAR;
- used: POINTER TO ARRAY OF SET;
- ofs: LONGINT;
- next: TdBuffer;
- PROCEDURE & Init (block: LONGINT);
- VAR
- bitmaskSize: LONGINT;
- BEGIN
- NEW(buffer, AllocSize + AllocAlign);
- ofs := AllocAlign - ADDRESSOF(buffer[0]) MOD AllocAlign;
- bitmaskSize := AllocSize DIV (block * SetSize);
- NEW(used, bitmaskSize)
- END Init;
- PROCEDURE Used(block: LONGINT): BOOLEAN;
- BEGIN
- RETURN (block MOD SetSize) IN used[block DIV SetSize];
- END Used;
- PROCEDURE SetUsed(block: LONGINT);
- BEGIN
- INCL(used[block DIV SetSize], block MOD SetSize);
- END SetUsed;
-
- (* faster version of SetUsed for blocks
- PROCEDURE SetUsedR(from, to: LONGINT);
- VAR startSet, stopSet, startBit, stopBit: LONGINT;
- BEGIN
- IF to < from THEN RETURN END;
- startBit := from MOD SetSize;
- stopBit := to MOD SetSize;
- startSet := from DIV SetSize;
- stopSet := to DIV SetSize;
- IF startSet < stopSet THEN
- used[startSet] := used[startSet] + {startBit .. MAX(SET)};
- INC(startSet);
- WHILE startSet < stopSet DO
- used[startSet] := {MIN(SET)..MAX(SET)};
- INC(startSet);
- END;
- used[stopSet] := used[stopSet] + {MIN(SET) .. stopBit};
- ELSE
- used[stopSet] := used[stopSet] + {startBit .. stopBit};
- END;
- END SetUsedR;
- *)
-
- PROCEDURE SetFree(block: LONGINT);
- BEGIN
- EXCL(used[block DIV SetSize], block MOD SetSize);
- END SetFree;
- (* faster version of SetFree for blocks
- PROCEDURE SetFreeR(from, to: LONGINT);
- VAR startSet, stopSet, startBit, stopBit: LONGINT;
- BEGIN
- IF to < from THEN RETURN END;
- startBit := from MOD SetSize;
- stopBit := to MOD SetSize;
- startSet := from DIV SetSize;
- stopSet := to DIV SetSize;
- IF startSet < stopSet THEN
- used[startSet] := used[startSet] - {startBit .. MAX(SET)};
- INC(startSet);
- WHILE startSet < stopSet DO
- used[startSet] := {};
- INC(startSet);
- END;
- used[stopSet] := used[stopSet] - {MIN(SET) .. stopBit};
- ELSE
- used[stopSet] := used[stopSet] - {startBit .. stopBit};
- END;
- END SetFreeR;
- *)
-
- END TdBuffer;
- (**
- Allocator.
-
- The allocator is created with a page size and a block size. It can then allocate memory blocks with a granularity of the block size.
- Each allocated block is guaranteed not to cross a page boundary.
-
- Allocated blocks must be freed manually.
- *)
- CONST SetSize = SIZEOF(SET) * 8;
- TYPE Allocator * = OBJECT
- VAR
- tdBuffers: TdBuffer;
- pageSize, blockSize, bitmaskSize: LONGINT;
- PROCEDURE & Setup * (pageSize, blockSize: LONGINT);
- BEGIN
- ASSERT(SetSize = 32);
- ASSERT((AllocSize MOD blockSize) MOD SetSize = 0);
- SELF.pageSize := pageSize;
- SELF.blockSize := blockSize;
- bitmaskSize := AllocSize DIV (blockSize * SetSize)
- END Setup;
- (** Allocate memory for a TD or a QH of the given size. The size must be a multiple of 32. *)
- PROCEDURE Allocate * (size: SIZE): ADDRESS;
- VAR
- buf: TdBuffer;
- start, pos, count: LONGINT;
- adr: ADDRESS;
-
- (** Allocate a new TD buffer and mark as used the last 32-byte block before a 4kB page boundary. *)
- PROCEDURE AllocateBuffer;
- VAR
- buf: TdBuffer;
- count, mod: LONGINT;
- BEGIN
- (* No buffer found: allocate a new one *)
- NEW(buf, blockSize);
- (*NEW(buf.buffer, AllocSize + AllocAlign);
- NEW(buf.used, bitmaskSize);*)
- buf.next := tdBuffers;
- tdBuffers := buf;
- count := 0;
- mod := ADDRESSOF(buf.buffer[0]) MOD AllocAlign;
- IF mod # 0 THEN
- buf.ofs := AllocAlign - mod
- END;
- Machine.DisableDCacheRange(ADDRESSOF(buf.buffer[buf.ofs]), AllocSize);
- (* Remove last 32-byte block before a 4kB page boundary from free blocks *)
- LOOP
- IF count >= AllocSize DIV blockSize THEN EXIT END;
- IF (ADDRESSOF(buf.buffer[buf.ofs + count * blockSize]) MOD pageSize) = pageSize - blockSize THEN
- buf.SetUsed(count);
- END;
- INC(count)
- END;
- END AllocateBuffer;
-
- BEGIN {EXCLUSIVE}
- ASSERT(size MOD blockSize = 0);
- size := size DIV blockSize;
-
- buf := tdBuffers;
- LOOP
- IF buf = NIL THEN
- AllocateBuffer;
- buf := tdBuffers;
- ASSERT(buf # NIL)
- END;
- count := 0;
- pos := 0;
- start := pos;
- WHILE (count < size) & (pos < bitmaskSize) DO
- IF buf.Used(pos) THEN
- count := 0;
- start := pos + 1;
- ELSE
- INC(count);
- END;
- INC(pos);
- END;
- IF count = size THEN EXIT END;
- buf := buf.next
- END;
-
- ASSERT(buf # NIL);
- adr := ADDRESSOF(buf.buffer[buf.ofs + start*blockSize]);
-
- (* faster version:
- buf.SetUsedR(start, start+count-1);
- *)
- WHILE(count > 0) DO
- ASSERT(~buf.Used(start));
- buf.SetUsed(start);
- INC(start); DEC(count);
- END;
- Machine.Fill32(adr, size * blockSize, 0);
- RETURN adr;
- END Allocate;
- (** Marks a TD as free, so that its memory can be used again *)
- PROCEDURE Free * (td: ADDRESS; size: SIZE);
- VAR
- buf: TdBuffer;
- adr: ADDRESS;
- slot: LONGINT;
- BEGIN {EXCLUSIVE}
- ASSERT(size MOD blockSize = 0);
- size := size DIV blockSize;
- buf := tdBuffers;
- LOOP
- IF buf = NIL THEN EXIT END;
- adr := ADDRESSOF(buf.buffer[buf.ofs]);
- IF (adr <= td) & (td < adr + AllocSize) THEN EXIT END;
- buf := buf.next
- END;
- ASSERT(buf # NIL); (* Not a TD *)
- slot := (td - adr) DIV blockSize;
- (* faster version:
- buf.SetFreeR(slot, slot+size-1);
- *)
- WHILE (size > 0) DO
- ASSERT(buf.Used(slot));
- buf.SetFree(slot);
- INC(slot); DEC(size);
- END;
- END Free;
- END Allocator;
- StaticAllocator * = OBJECT
- VAR
- buffers: TdBuffer;
- size: LONGINT;
- PROCEDURE & Setup * (allocSize: LONGINT);
- BEGIN
- size := allocSize
- END Setup;
- PROCEDURE Allocate * (): ADDRESS;
- BEGIN {EXCLUSIVE}
- END Allocate;
- PROCEDURE Free * (td: ADDRESS);
- BEGIN {EXCLUSIVE}
- END Free;
- END StaticAllocator;
- (*VAR
- padding: POINTER TO ARRAY OF CHAR;
- BEGIN
- NEW(padding, 1024 * 1024)*)
- END UsbVarTdAlloc.
- (* test module, uncomment for running a randomized test
- MODULE TestUsbVarTdAlloc; (** AUTHOR ""; PURPOSE ""; *)
- IMPORT UsbVarTdAlloc, Random;
- PROCEDURE Test*;
- VAR allocator: UsbVarTdAlloc.Allocator; adr: POINTER TO ARRAY OF ADDRESS;
- gen: Random.Generator; i,j, k: LONGINT; size: POINTER TO ARRAY OF SIZE;
- BEGIN
- NEW(allocator, 4096, 32);
- NEW(gen);
- NEW(adr, 1024); NEW(size, 1024);
- FOR j := 0 TO 100 DO
- FOR i := 0 TO LEN(adr)-1 DO
- size[i] := 32+gen.Dice(120)*32;
- adr[i] := allocator.Allocate(size[i]);
- ASSERT(adr[i] MOD 32 = 0);
- ASSERT(adr[i] DIV 4096 = (adr[i] + size[i]) DIV 4096);
- FOR k := 0 TO i-1 DO
- ASSERT(adr[k] # adr[i]);
- END;
- END;
- FOR i := 0 TO LEN(adr)-1 DO
- allocator.Free(adr[i], size[i]);
- END;
- TRACE(j);
- END;
- TRACE("done");
- END Test;
-
- END TestUsbVarTdAlloc.
- SystemTools.Free TestUsbVarTdAlloc UsbVarTdAlloc ~
- TestUsbVarTdAlloc.Test ~
- *)
|