alloc.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. #include <unistd.h>
  2. #include <stdint.h>
  3. #include <string.h>
  4. #include <limits.h>
  5. #include <assert.h>
  6. #include "alloc.h"
  7. #include "build_assert/build_assert.h"
  8. #include "config.h"
  9. #if HAVE_ALIGNOF
  10. #define ALIGNOF(t) __alignof__(t)
  11. #else
  12. /* Alignment by measuring structure padding. */
  13. #define ALIGNOF(t) (sizeof(struct { char c; t _h; }) - 1 - sizeof(t))
  14. #endif
  15. /* FIXME: Doesn't handle non-page-aligned poolsize. */
  16. /* FIXME: Reduce. */
  17. #define MIN_SIZE (getpagesize() * 2)
  18. /* What's the granularity of sub-page allocs? */
  19. #define BITMAP_GRANULARITY 4
  20. /* File layout:
  21. *
  22. * file := pagestates pad metadata
  23. * pagestates := pages * 2-bits-per-page
  24. * pad := pad to next ALIGNOF(metadata)
  25. *
  26. * metadata := metalen next-ptr metabits
  27. * metabits := freeblock | bitblock
  28. * freeblock := 0+
  29. * bitblock := 2-bits-per-bit-in-page 1
  30. */
  31. struct metaheader
  32. {
  33. /* Length (after this header). (FIXME: Could be in pages). */
  34. unsigned long metalen;
  35. /* Next meta header, or 0 */
  36. unsigned long next;
  37. /* Bits start here. */
  38. };
  39. #define BITS_PER_PAGE 2
  40. /* FIXME: Don't use page states for bitblock. It's tacky and confusing. */
  41. enum page_state
  42. {
  43. FREE,
  44. TAKEN,
  45. TAKEN_START,
  46. SUBPAGE,
  47. };
  48. /* Assumes a is a power of two. */
  49. static unsigned long align_up(unsigned long x, unsigned long a)
  50. {
  51. return (x + a - 1) & ~(a - 1);
  52. }
  53. static unsigned long div_up(unsigned long x, unsigned long a)
  54. {
  55. return (x + a - 1) / a;
  56. }
  57. /* The offset of metadata for a subpage allocation is found at the end
  58. * of the subpage */
  59. #define SUBPAGE_METAOFF (getpagesize() - sizeof(unsigned long))
  60. /* This is the length of metadata in bits. It consists of a TAKEN header,
  61. * then two bits for every BITMAP_GRANULARITY of usable bytes in the. */
  62. #define BITMAP_METABITLEN \
  63. ((1 + div_up(SUBPAGE_METAOFF, BITMAP_GRANULARITY)) * BITS_PER_PAGE)
  64. /* This is the length in bytes. */
  65. #define BITMAP_METALEN (div_up(BITMAP_METABITLEN, CHAR_BIT))
  66. static enum page_state get_page_state(const uint8_t *bits, unsigned long page)
  67. {
  68. return bits[page * 2 / CHAR_BIT] >> (page * 2 % CHAR_BIT) & 3;
  69. }
  70. static void set_page_state(uint8_t *bits, unsigned long page, enum page_state s)
  71. {
  72. bits[page * 2 / CHAR_BIT] &= ~(3 << (page * 2 % CHAR_BIT));
  73. bits[page * 2 / CHAR_BIT] |= ((uint8_t)s << (page * 2 % CHAR_BIT));
  74. }
  75. static struct metaheader *first_mheader(void *pool, unsigned long poolsize)
  76. {
  77. unsigned int pagestatelen;
  78. pagestatelen = align_up(div_up(poolsize/getpagesize() * BITS_PER_PAGE,
  79. CHAR_BIT),
  80. ALIGNOF(struct metaheader));
  81. return (struct metaheader *)((char *)pool + pagestatelen);
  82. }
  83. static struct metaheader *next_mheader(void *pool, struct metaheader *mh)
  84. {
  85. if (!mh->next)
  86. return NULL;
  87. return (struct metaheader *)((char *)pool + mh->next);
  88. }
  89. static unsigned long pool_offset(void *pool, void *p)
  90. {
  91. return (char *)p - (char *)pool;
  92. }
  93. void alloc_init(void *pool, unsigned long poolsize)
  94. {
  95. /* FIXME: Alignment assumptions about pool. */
  96. unsigned long len, i;
  97. struct metaheader *mh;
  98. if (poolsize < MIN_SIZE)
  99. return;
  100. mh = first_mheader(pool, poolsize);
  101. /* len covers all page states, plus the metaheader. */
  102. len = (char *)(mh + 1) - (char *)pool;
  103. /* Mark all page states FREE */
  104. BUILD_ASSERT(FREE == 0);
  105. memset(pool, 0, len);
  106. /* metaheader len takes us up to next page boundary. */
  107. mh->metalen = align_up(len, getpagesize()) - len;
  108. /* Mark the pagestate and metadata page(s) allocated. */
  109. set_page_state(pool, 0, TAKEN_START);
  110. for (i = 1; i < div_up(len, getpagesize()); i++)
  111. set_page_state(pool, i, TAKEN);
  112. }
  113. /* Two bits per element, representing page states. Returns 0 on fail. */
  114. static unsigned long alloc_from_bitmap(uint8_t *bits, unsigned long elems,
  115. unsigned long want, unsigned long align)
  116. {
  117. long i;
  118. unsigned long free;
  119. free = 0;
  120. /* We allocate from far end, to increase ability to expand metadata. */
  121. for (i = elems - 1; i >= 0; i--) {
  122. switch (get_page_state(bits, i)) {
  123. case FREE:
  124. if (++free >= want) {
  125. unsigned long j;
  126. /* They might ask for large alignment. */
  127. if (align && i % align)
  128. continue;
  129. for (j = i+1; j < i + want; j++)
  130. set_page_state(bits, j, TAKEN);
  131. set_page_state(bits, i, TAKEN_START);
  132. return i;
  133. }
  134. break;
  135. case SUBPAGE:
  136. case TAKEN_START:
  137. case TAKEN:
  138. free = 0;
  139. break;
  140. }
  141. }
  142. return 0;
  143. }
  144. static unsigned long alloc_get_pages(void *pool, unsigned long poolsize,
  145. unsigned long pages, unsigned long align)
  146. {
  147. long i;
  148. unsigned long free;
  149. free = 0;
  150. /* We allocate from far end, to increase ability to expand metadata. */
  151. for (i = poolsize / getpagesize() - 1; i >= 0; i--) {
  152. switch (get_page_state(pool, i)) {
  153. case FREE:
  154. if (++free >= pages) {
  155. unsigned long j, addr;
  156. addr = (unsigned long)pool + i * getpagesize();
  157. /* They might ask for multi-page alignment. */
  158. if (addr % align)
  159. continue;
  160. for (j = i+1; j < i + pages; j++)
  161. set_page_state(pool, j, TAKEN);
  162. set_page_state(pool, i, TAKEN_START);
  163. return i;
  164. }
  165. break;
  166. case SUBPAGE:
  167. case TAKEN_START:
  168. case TAKEN:
  169. free = 0;
  170. break;
  171. }
  172. }
  173. return 0;
  174. }
  175. /* Offset to metadata is at end of page. */
  176. static unsigned long *metadata_off(void *pool, unsigned long page)
  177. {
  178. return (unsigned long *)
  179. ((char *)pool + (page+1)*getpagesize() - sizeof(unsigned long));
  180. }
  181. static uint8_t *get_page_metadata(void *pool, unsigned long page)
  182. {
  183. return (uint8_t *)pool + *metadata_off(pool, page);
  184. }
  185. static void set_page_metadata(void *pool, unsigned long page, uint8_t *meta)
  186. {
  187. *metadata_off(pool, page) = meta - (uint8_t *)pool;
  188. }
  189. static void *sub_page_alloc(void *pool, unsigned long page,
  190. unsigned long size, unsigned long align)
  191. {
  192. uint8_t *bits = get_page_metadata(pool, page);
  193. unsigned long i;
  194. /* TAKEN at end means a bitwise alloc. */
  195. assert(get_page_state(bits, getpagesize()/BITMAP_GRANULARITY - 1)
  196. == TAKEN);
  197. /* Our bits are the same as the page bits. */
  198. i = alloc_from_bitmap(bits, getpagesize()/BITMAP_GRANULARITY,
  199. div_up(size, BITMAP_GRANULARITY),
  200. align / BITMAP_GRANULARITY);
  201. /* Can't allocate? */
  202. if (i == 0)
  203. return NULL;
  204. return (char *)pool + page*getpagesize() + i*BITMAP_GRANULARITY;
  205. }
  206. static uint8_t *alloc_metaspace(struct metaheader *mh, unsigned long bytes)
  207. {
  208. uint8_t *meta = (uint8_t *)(mh + 1);
  209. unsigned long free = 0, len;
  210. long i;
  211. /* TAKEN tags end a subpage alloc. */
  212. for (i = mh->metalen * CHAR_BIT / BITS_PER_PAGE - 1; i >= 0; i -= len) {
  213. switch (get_page_state(meta, i)) {
  214. case FREE:
  215. len = 1;
  216. free++;
  217. if (free == bytes * CHAR_BIT / BITS_PER_PAGE) {
  218. /* TAKEN marks end of metablock. */
  219. set_page_state(meta, i + free - 1, TAKEN);
  220. return meta + i / (CHAR_BIT / BITS_PER_PAGE);
  221. }
  222. break;
  223. case TAKEN:
  224. /* Skip over this allocated part. */
  225. len = BITMAP_METALEN * CHAR_BIT;
  226. free = 0;
  227. break;
  228. default:
  229. assert(0);
  230. return NULL;
  231. }
  232. }
  233. return NULL;
  234. }
  235. /* We need this many bytes of metadata. */
  236. static uint8_t *new_metadata(void *pool, unsigned long poolsize,
  237. unsigned long bytes)
  238. {
  239. struct metaheader *mh, *newmh;
  240. unsigned long page;
  241. for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){
  242. uint8_t *meta = alloc_metaspace(mh, bytes);
  243. if (meta)
  244. return meta;
  245. }
  246. /* No room for metadata? Can we expand an existing one? */
  247. for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){
  248. /* It should end on a page boundary. */
  249. unsigned long nextpage;
  250. nextpage = pool_offset(pool, (char *)(mh + 1) + mh->metalen);
  251. assert(nextpage % getpagesize() == 0);
  252. /* Now, can we grab that page? */
  253. if (get_page_state(pool, nextpage / getpagesize()) != FREE)
  254. continue;
  255. /* OK, expand metadata, do it again. */
  256. set_page_state(pool, nextpage / getpagesize(), TAKEN);
  257. memset((char *)pool + nextpage, 0, getpagesize());
  258. mh->metalen += getpagesize();
  259. return alloc_metaspace(mh, bytes);
  260. }
  261. /* No metadata left at all? */
  262. page = alloc_get_pages(pool, poolsize, div_up(bytes, getpagesize()), 1);
  263. if (!page)
  264. return NULL;
  265. newmh = (struct metaheader *)((char *)pool + page * getpagesize());
  266. newmh->metalen = getpagesize() - sizeof(*mh);
  267. memset(newmh + 1, 0, newmh->metalen);
  268. /* Sew it into linked list */
  269. mh = first_mheader(pool,poolsize);
  270. newmh->next = mh->next;
  271. mh->next = (char *)newmh - (char *)pool;
  272. return alloc_metaspace(newmh, bytes);
  273. }
  274. static void alloc_free_pages(void *pool, unsigned long pagenum)
  275. {
  276. assert(get_page_state(pool, pagenum) == TAKEN_START);
  277. set_page_state(pool, pagenum, FREE);
  278. while (get_page_state(pool, ++pagenum) == TAKEN)
  279. set_page_state(pool, pagenum, FREE);
  280. }
  281. static void *alloc_sub_page(void *pool, unsigned long poolsize,
  282. unsigned long size, unsigned long align)
  283. {
  284. unsigned long i;
  285. uint8_t *metadata;
  286. /* Look for partial page. */
  287. for (i = 0; i < poolsize / getpagesize(); i++) {
  288. void *ret;
  289. if (get_page_state(pool, i) != SUBPAGE)
  290. continue;
  291. ret = sub_page_alloc(pool, i, size, align);
  292. if (ret)
  293. return ret;
  294. }
  295. /* Create new SUBPAGE page. */
  296. i = alloc_get_pages(pool, poolsize, 1, 1);
  297. if (i == 0)
  298. return NULL;
  299. /* Get metadata for page. */
  300. metadata = new_metadata(pool, poolsize, BITMAP_METALEN);
  301. if (!metadata) {
  302. alloc_free_pages(pool, i);
  303. return NULL;
  304. }
  305. /* Actually, this is a SUBPAGE page now. */
  306. set_page_state(pool, i, SUBPAGE);
  307. /* Set metadata pointer for page. */
  308. set_page_metadata(pool, i, metadata);
  309. /* Do allocation like normal */
  310. return sub_page_alloc(pool, i, size, align);
  311. }
  312. void *alloc_get(void *pool, unsigned long poolsize,
  313. unsigned long size, unsigned long align)
  314. {
  315. if (poolsize < MIN_SIZE)
  316. return NULL;
  317. /* Sub-page allocations have an overhead of 25%. */
  318. if (size + size/4 >= getpagesize() || align >= getpagesize()) {
  319. unsigned long ret, pages = div_up(size, getpagesize());
  320. ret = alloc_get_pages(pool, poolsize, pages, align);
  321. if (ret == 0)
  322. return NULL;
  323. return (char *)pool + ret * getpagesize();
  324. }
  325. return alloc_sub_page(pool, poolsize, size, align);
  326. }
  327. static void subpage_free(void *pool, unsigned long pagenum, void *free)
  328. {
  329. unsigned long off = (unsigned long)free % getpagesize();
  330. uint8_t *metadata;
  331. assert(off < SUBPAGE_METAOFF);
  332. assert(off % BITMAP_GRANULARITY == 0);
  333. metadata = get_page_metadata(pool, pagenum);
  334. off /= BITMAP_GRANULARITY;
  335. set_page_state(metadata, off++, FREE);
  336. while (off < SUBPAGE_METAOFF / BITMAP_GRANULARITY
  337. && get_page_state(metadata, off) == TAKEN)
  338. set_page_state(metadata, off++, FREE);
  339. /* FIXME: If whole page free, free page and metadata. */
  340. }
  341. void alloc_free(void *pool, unsigned long poolsize, void *free)
  342. {
  343. unsigned long pagenum;
  344. struct metaheader *mh;
  345. if (!free)
  346. return;
  347. assert(poolsize >= MIN_SIZE);
  348. mh = first_mheader(pool, poolsize);
  349. assert((char *)free >= (char *)(mh + 1) + mh->metalen);
  350. assert((char *)pool + poolsize > (char *)free);
  351. pagenum = pool_offset(pool, free) / getpagesize();
  352. if (get_page_state(pool, pagenum) == SUBPAGE)
  353. subpage_free(pool, pagenum, free);
  354. else {
  355. assert((unsigned long)free % getpagesize() == 0);
  356. alloc_free_pages(pool, pagenum);
  357. }
  358. }
  359. static bool is_metadata_page(void *pool, unsigned long poolsize,
  360. unsigned long page)
  361. {
  362. struct metaheader *mh;
  363. for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){
  364. unsigned long start, end;
  365. start = pool_offset(pool, mh);
  366. end = pool_offset(pool, (char *)(mh+1) + mh->metalen);
  367. if (page >= start/getpagesize() && page < end/getpagesize())
  368. return true;
  369. }
  370. return false;
  371. }
  372. static bool check_subpage(void *pool, unsigned long poolsize,
  373. unsigned long page)
  374. {
  375. unsigned long *mhoff = metadata_off(pool, page);
  376. unsigned int i;
  377. enum page_state last_state = FREE;
  378. if (*mhoff + sizeof(struct metaheader) > poolsize)
  379. return false;
  380. if (*mhoff % ALIGNOF(struct metaheader) != 0)
  381. return false;
  382. /* It must point to a metadata page. */
  383. if (!is_metadata_page(pool, poolsize, *mhoff / getpagesize()))
  384. return false;
  385. /* Marker at end of subpage allocation is "taken" */
  386. if (get_page_state((uint8_t *)pool + *mhoff,
  387. getpagesize()/BITMAP_GRANULARITY - 1) != TAKEN)
  388. return false;
  389. for (i = 0; i < SUBPAGE_METAOFF / BITMAP_GRANULARITY; i++) {
  390. enum page_state state;
  391. state = get_page_state((uint8_t *)pool + *mhoff, i);
  392. switch (state) {
  393. case SUBPAGE:
  394. return false;
  395. case TAKEN:
  396. if (last_state == FREE)
  397. return false;
  398. break;
  399. default:
  400. break;
  401. }
  402. last_state = state;
  403. }
  404. return true;
  405. }
  406. bool alloc_check(void *pool, unsigned long poolsize)
  407. {
  408. unsigned long i;
  409. struct metaheader *mh;
  410. enum page_state last_state = FREE;
  411. bool was_metadata = false;
  412. if (poolsize < MIN_SIZE)
  413. return true;
  414. if (get_page_state(pool, 0) != TAKEN_START)
  415. return false;
  416. /* First check metadata pages. */
  417. /* Metadata pages will be marked TAKEN. */
  418. for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){
  419. unsigned long start, end;
  420. start = pool_offset(pool, mh);
  421. if (start + sizeof(*mh) > poolsize)
  422. return false;
  423. end = pool_offset(pool, (char *)(mh+1) + mh->metalen);
  424. if (end > poolsize)
  425. return false;
  426. /* Non-first pages should start on a page boundary. */
  427. if (mh != first_mheader(pool, poolsize)
  428. && start % getpagesize() != 0)
  429. return false;
  430. /* It should end on a page boundary. */
  431. if (end % getpagesize() != 0)
  432. return false;
  433. }
  434. for (i = 0; i < poolsize / getpagesize(); i++) {
  435. enum page_state state = get_page_state(pool, i);
  436. bool is_metadata = is_metadata_page(pool, poolsize,i);
  437. switch (state) {
  438. case FREE:
  439. /* metadata pages are never free. */
  440. if (is_metadata)
  441. return false;
  442. case TAKEN_START:
  443. break;
  444. case TAKEN:
  445. /* This should continue a previous block. */
  446. if (last_state == FREE)
  447. return false;
  448. if (is_metadata != was_metadata)
  449. return false;
  450. break;
  451. case SUBPAGE:
  452. /* Check metadata pointer etc. */
  453. if (!check_subpage(pool, poolsize, i))
  454. return false;
  455. }
  456. last_state = state;
  457. was_metadata = is_metadata;
  458. }
  459. return true;
  460. }