bztalloc.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /*
  2. * libc/bztalloc.c
  3. * https://gitlab.com/bztsrc/bztalloc
  4. *
  5. * Copyright (C) 2016 bzt (bztsrc@gitlab)
  6. *
  7. * Permission is hereby granted, free of charge, to any person
  8. * obtaining a copy of this software and associated documentation
  9. * files (the "Software"), to deal in the Software without
  10. * restriction, including without limitation the rights to use, copy,
  11. * modify, merge, publish, distribute, sublicense, and/or sell copies
  12. * of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be
  16. * included in all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  19. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  20. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  21. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  22. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  23. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  25. * DEALINGS IN THE SOFTWARE.
  26. *
  27. * @brief Memory allocator and deallocator.
  28. */
  29. #include <sys/mman.h>
  30. #include "bztalloc.h"
  31. #define MAXARENA ((ARENASIZE/sizeof(void*))-1) //maximum number of fragments in an arena
  32. #define BITMAPSIZE (ALLOCSIZE/PAGESIZE/sizeof(void*)/8) //number of allocation blocks in a chunk
  33. #define numallocmaps(a) (a[0]&0x7ffffffff) //number of allocation maps in an arena
  34. #define chunksize(q) ((q*BITMAPSIZE*sizeof(void*)*8/PAGESIZE)*PAGESIZE) //size of a chunk for a given quantum
  35. #ifndef MAP_SPARSE
  36. # define MAP_SPARSE 0x40
  37. #endif
  38. typedef struct {
  39. uint64_t quantum; //allocation unit in this chunk
  40. void *ptr; //memory pointer (page or slot aligned)
  41. uint64_t map[BITMAPSIZE]; //free units in this chunk (512 bits), or first item size
  42. } chunkmap_t;
  43. /* this must work with a zeroed page, hence no magic and zero bit represents free */
  44. typedef struct {
  45. uint64_t numchunks; //number of chunkmap_t
  46. chunkmap_t chunk[0]; //chunks, array dynamically allocated
  47. } allocmap_t;
  48. /**
  49. * In OS/Z arena points to a memory block allocated independently to other arenas.
  50. * The argument arena is an array of *allocmap_t, first element records size and lock flag.
  51. *
  52. * free memory and give back RAM to system if possible
  53. */
  54. void bzt_free(uint64_t *arena, void *ptr)
  55. {
  56. int i,j,k,cnt=0;
  57. uint64_t l,sh;
  58. allocmap_t *am;
  59. chunkmap_t *ocm=NULL; //old chunk map
  60. if(ptr==NULL)
  61. return;
  62. lockacquire(63,arena);
  63. // iterate through allocmaps
  64. for(i=1;i<MAXARENA && cnt<numallocmaps(arena);i++) {
  65. if(arena[i]==0)
  66. continue;
  67. cnt++;
  68. // get chunk for this quantum size
  69. am=(allocmap_t*)arena[i];
  70. for(j=0;j<am->numchunks;j++) {
  71. if( am->chunk[j].ptr <= ptr &&
  72. ptr < am->chunk[j].ptr +
  73. (am->chunk[j].quantum<ALLOCSIZE? chunksize(am->chunk[j].quantum) : am->chunk[j].map[0])) {
  74. ocm=&am->chunk[j];
  75. l=0;
  76. if(ocm->quantum<ALLOCSIZE) {
  77. sh=(ptr-ocm->ptr)/ocm->quantum;
  78. ocm->map[sh>>6] &= ~(1UL<<(sh%64));
  79. //was it the last allocation in this chunk?
  80. l=0; for(k=0;k<BITMAPSIZE;k++) l+=ocm->map[k];
  81. if(ocm->quantum<PAGESIZE) {
  82. if(l==0)
  83. munmap(ocm->ptr,chunksize(ocm->quantum));
  84. } else {
  85. munmap(ptr,ocm->quantum);
  86. }
  87. } else {
  88. munmap(ocm->ptr,ocm->map[0]);
  89. }
  90. if(l==0) {
  91. //not last chunk?
  92. if(j+1<am->numchunks) {
  93. memcpy( &((allocmap_t*)arena[i])->chunk[j],
  94. &((allocmap_t*)arena[i])->chunk[j+1],
  95. (((allocmap_t*)arena[i])->numchunks-j-1)*sizeof(chunkmap_t));
  96. }
  97. ((allocmap_t*)arena[i])->numchunks--;
  98. //was it the last chunk in this allocmap?
  99. if(((allocmap_t*)arena[i])->numchunks==0) {
  100. munmap((void *)arena[i], ALLOCSIZE);
  101. arena[i]=0;
  102. arena[0]--;
  103. }
  104. }
  105. lockrelease(63,arena);
  106. return;
  107. }
  108. }
  109. }
  110. lockrelease(63,arena);
  111. seterr(EFAULT);
  112. }
  113. /**
  114. * The argument arena is an array of *allocmap_t, first element records size and lock flag.
  115. *
  116. * Allocate aligned memory in a memory arena
  117. */
  118. __attribute__((malloc)) void *bzt_alloc(uint64_t *arena,size_t a,void *ptr,size_t s,int flag)
  119. {
  120. uint64_t q,sh,sf;
  121. int i,j,k,l,fc=0,cnt=0,ocmi=0,ocmj=0;
  122. void *fp, *sp, *lp, *end;
  123. allocmap_t *am; //allocation map
  124. chunkmap_t *ncm=NULL; //new chunk map
  125. chunkmap_t *ocm=NULL; //old chunk map
  126. uint64_t maxchunks = (ALLOCSIZE-8)/sizeof(chunkmap_t);
  127. int prot = PROT_READ | PROT_WRITE;
  128. flag |= MAP_FIXED | MAP_ANONYMOUS;
  129. if(s==0) {
  130. bzt_free(arena,ptr);
  131. return NULL;
  132. }
  133. // get quantum
  134. for(q=8;q<s;q<<=1);
  135. // minimum alignment is quantum
  136. if(a<q) a=q;
  137. // shifts
  138. sh=-1; sf=a/q;
  139. lockacquire(63,arena);
  140. // if we don't have any allocmaps yet
  141. if(!numallocmaps(arena)) {
  142. if(mmap((void*)((uint64_t)arena+ARENASIZE), ALLOCSIZE, prot, flag&~MAP_SPARSE, -1, 0)==MAP_FAILED) {
  143. seterr(ENOMEM);
  144. lockrelease(63,arena);
  145. return NULL;
  146. }
  147. arena[0]++;
  148. arena[1]=(uint64_t)arena+ARENASIZE;
  149. }
  150. fp=(void*)((uint64_t)arena+ARENASIZE+ALLOCSIZE);
  151. sp=lp=NULL;
  152. // iterate through allocmaps
  153. for(i=1;i<MAXARENA && cnt<numallocmaps(arena);i++) {
  154. if(arena[i]==0)
  155. continue;
  156. cnt++;
  157. // get chunk for this quantum size
  158. am=(allocmap_t*)arena[i];
  159. if(((void*)am)+ALLOCSIZE > fp)
  160. fp = ((void*)am)+ALLOCSIZE;
  161. if(lp==NULL || ((void*)am)+ALLOCSIZE < lp)
  162. lp = ((void*)am)+ALLOCSIZE;
  163. if(fc==0 && am->numchunks<maxchunks)
  164. fc=i;
  165. for(j=0;j<am->numchunks;j++) {
  166. end=am->chunk[j].ptr +
  167. (am->chunk[j].quantum<ALLOCSIZE? chunksize(am->chunk[j].quantum) : am->chunk[j].map[0]);
  168. if(am->chunk[j].quantum==q && q<ALLOCSIZE && ((uint64_t)(am->chunk[j].ptr)&(a-1))==0) {
  169. for(k=0;k<BITMAPSIZE*64;k+=sf) {
  170. if(((am->chunk[j].map[k>>6])&(1UL<<(k%64)))==0) { ncm=&am->chunk[j]; sh=k; break; }
  171. }
  172. }
  173. if(ptr!=NULL && am->chunk[j].ptr <= ptr && ptr < end) {
  174. ocm=&am->chunk[j];
  175. ocmi=i; ocmj=j;
  176. }
  177. if(sp==NULL || am->chunk[j].ptr < sp)
  178. sp=am->chunk[j].ptr;
  179. if(end > fp)
  180. fp=end;
  181. }
  182. }
  183. // same as before, new size fits in quantum
  184. if(q<ALLOCSIZE && ptr!=NULL && ncm!=NULL && ncm==ocm) {
  185. lockrelease(63,arena);
  186. return ptr;
  187. }
  188. // if we have a big enough, aligned space after allocmap, use that area
  189. if(lp!=NULL && sp!=NULL &&
  190. lp+((s+ALLOCSIZE-1)/ALLOCSIZE)*ALLOCSIZE < sp &&
  191. ((uint64_t)lp&(a-1))==0)
  192. fp=lp;
  193. if(ncm==NULL) {
  194. // first allocmap with free chunks
  195. if(fc==0) {
  196. //do we have place for a new allocmap?
  197. if(i>=numallocmaps(arena) || mmap(fp, ALLOCSIZE, prot, flag&~MAP_SPARSE, -1, 0)==MAP_FAILED) {
  198. seterr(ENOMEM);
  199. lockrelease(63,arena);
  200. return ptr;
  201. }
  202. arena[0]++;
  203. //did we just passed a page boundary?
  204. if((arena[0]*sizeof(void*)/PAGESIZE)!=((arena[0]+1)*sizeof(void*)/PAGESIZE)) {
  205. if(mmap(&arena[arena[0]], PAGESIZE, prot, flag&~MAP_SPARSE, -1, 0)==MAP_FAILED) {
  206. seterr(ENOMEM);
  207. lockrelease(63,arena);
  208. return ptr;
  209. }
  210. }
  211. arena[i]=(uint64_t)fp;
  212. fp+=ALLOCSIZE;
  213. fc=i;
  214. }
  215. // add a new chunk
  216. am=(allocmap_t*)arena[fc];
  217. if(q>=ALLOCSIZE)
  218. fp=(void*)((((uint64_t)fp+a-1)/a)*a);
  219. i=am->numchunks++;
  220. am->chunk[i].quantum = q;
  221. am->chunk[i].ptr = fp;
  222. for(k=0;k<BITMAPSIZE;k++)
  223. am->chunk[i].map[k]=0;
  224. // allocate memory for small quantum sizes
  225. if((flag&MAP_SPARSE)==0 && q<PAGESIZE && mmap(fp, chunksize(q), prot, flag, -1, 0)==MAP_FAILED) {
  226. seterr(ENOMEM);
  227. lockrelease(63,arena);
  228. return ptr;
  229. }
  230. ncm=&am->chunk[i];
  231. sh=0;
  232. }
  233. if(q<ALLOCSIZE) {
  234. if(sh!=-1) {
  235. ncm->map[sh>>6] |= (1UL<<(sh%64));
  236. fp=ncm->ptr + sh*ncm->quantum;
  237. //allocate new memory for medium quantum sizes
  238. if((flag&MAP_SPARSE)==0 && q>=PAGESIZE && mmap(fp, ncm->quantum, prot, flag, -1, 0)==MAP_FAILED)
  239. fp=NULL;
  240. } else
  241. fp=NULL;
  242. } else {
  243. s=((s+ALLOCSIZE-1)/ALLOCSIZE)*ALLOCSIZE;
  244. //allocate new memory for large quantum sizes
  245. if((flag&MAP_SPARSE)==0 && mmap(fp, s, prot, flag, -1, 0)==MAP_FAILED)
  246. fp=NULL;
  247. else
  248. ncm->map[0]=s;
  249. }
  250. if(fp==NULL){
  251. seterr(ENOMEM);
  252. lockrelease(63,arena);
  253. return ptr;
  254. }
  255. if((flag&MAP_SPARSE)==0 && ocm==NULL)
  256. memzero(fp,ncm->quantum);
  257. //free old memory
  258. if(ocm!=NULL) {
  259. if((flag&MAP_SPARSE)==0){
  260. memcpy(fp,ptr,ocm->quantum);
  261. if(ncm->quantum>ocm->quantum)
  262. memzero(fp+ocm->quantum,ncm->quantum-ocm->quantum);
  263. }
  264. l=0;
  265. if(ocm->quantum<ALLOCSIZE) {
  266. sh=(ptr-ocm->ptr)/ocm->quantum;
  267. ocm->map[sh>>6] &= ~(1UL<<(sh%64));
  268. //was it the last allocation in this chunk?
  269. l=0; for(k=0;k<BITMAPSIZE;k++) l+=ocm->map[k];
  270. if(ocm->quantum<PAGESIZE) {
  271. if(l==0)
  272. munmap(ocm->ptr,chunksize(ocm->quantum));
  273. } else {
  274. munmap(ptr,ocm->quantum);
  275. }
  276. } else {
  277. munmap(ocm->ptr,ocm->map[0]);
  278. }
  279. if(l==0) {
  280. //not last chunk?
  281. if(ocmj+1<am->numchunks) {
  282. memcpy( &((allocmap_t*)arena[ocmi])->chunk[ocmj],
  283. &((allocmap_t*)arena[ocmi])->chunk[ocmj+1],
  284. (((allocmap_t*)arena[ocmi])->numchunks-ocmj-1)*sizeof(chunkmap_t));
  285. }
  286. ((allocmap_t*)arena[ocmi])->numchunks--;
  287. //was it the last chunk in this allocmap?
  288. if(((allocmap_t*)arena[ocmi])->numchunks==0) {
  289. munmap((void *)arena[ocmi], ALLOCSIZE);
  290. arena[ocmi]=0;
  291. arena[0]--;
  292. }
  293. }
  294. }
  295. lockrelease(63,arena);
  296. return fp;
  297. }
  298. /**
  299. * dump memory map, for debugging purposes
  300. */
  301. #if DEBUG
  302. void bzt_dumpmem(uint64_t *arena)
  303. {
  304. int i,j,k,l,n,o,cnt;
  305. int mask[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
  306. char *bmap=".123456789ABCDEF";
  307. uint16_t *m;
  308. dbg_printf("-----Arena %x, %s%s, ",
  309. arena,(uint64_t)arena==(uint64_t)BSS_ADDRESS?"lts":"shared",
  310. (arena[0] & (1UL<<63))!=0?" LOCKED":"");
  311. dbg_printf("allocmaps: %d/%d, chunks: %d, %d units\n",
  312. numallocmaps(arena), MAXARENA, (ALLOCSIZE-8)/sizeof(chunkmap_t), BITMAPSIZE*64);
  313. o=1; cnt=0;
  314. for(i=1;i<MAXARENA && cnt<numallocmaps(arena);i++) {
  315. if(arena[i]==0)
  316. continue;
  317. cnt++;
  318. dbg_printf(" --allocmap %d %x, chunks: %d--\n",i,arena[i],((allocmap_t*)arena[i])->numchunks);
  319. for(j=0;j<((allocmap_t*)arena[i])->numchunks;j++) {
  320. dbg_printf("%3d. %9d %x - %x ",o++,
  321. ((allocmap_t*)arena[i])->chunk[j].quantum,
  322. ((allocmap_t*)arena[i])->chunk[j].ptr,
  323. ((allocmap_t*)arena[i])->chunk[j].ptr+
  324. (((allocmap_t*)arena[i])->chunk[j].quantum<ALLOCSIZE?
  325. chunksize(((allocmap_t*)arena[i])->chunk[j].quantum) :
  326. ((allocmap_t*)arena[i])->chunk[j].map[0]));
  327. if(((allocmap_t*)arena[i])->chunk[j].quantum<ALLOCSIZE) {
  328. m=(uint16_t*)&((allocmap_t*)arena[i])->chunk[j].map;
  329. for(k=0;k<BITMAPSIZE*4;k++) {
  330. l=0; for(n=0;n<16;n++) { if(*m & mask[n]) l++; }
  331. dbg_printf("%c",bmap[l]);
  332. m++;
  333. }
  334. dbg_printf("%8dk\n",chunksize(((allocmap_t*)arena[i])->chunk[j].quantum)/1024);
  335. } else {
  336. dbg_printf("(no bitmap) %8dM\n",((allocmap_t*)arena[i])->chunk[j].map[0]/1024/1024);
  337. }
  338. }
  339. }
  340. dbg_printf("\n");
  341. }
  342. #endif