+/* We need this many bytes of metadata. */
+static uint8_t *new_metadata(void *pool, unsigned long poolsize,
+ unsigned long bytes)
+{
+ struct metaheader *mh, *newmh;
+ unsigned long page;
+
+ for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){
+ uint8_t *meta = alloc_metaspace(mh, bytes);
+
+ if (meta)
+ return meta;
+ }
+
+ /* No room for metadata? Can we expand an existing one? */
+ for (mh = first_mheader(pool,poolsize); mh; mh = next_mheader(pool,mh)){
+ /* It should end on a page boundary. */
+ unsigned long nextpage;
+
+ nextpage = pool_offset(pool, (char *)(mh + 1) + mh->metalen);
+ assert(nextpage % getpagesize() == 0);
+
+ /* Now, can we grab that page? */
+ if (get_page_state(pool, nextpage / getpagesize()) != FREE)
+ continue;
+
+ /* OK, expand metadata, do it again. */
+ set_page_state(pool, nextpage / getpagesize(), TAKEN);
+ memset((char *)pool + nextpage, 0, getpagesize());
+ mh->metalen += getpagesize();
+ return alloc_metaspace(mh, bytes);
+ }
+
+ /* No metadata left at all? */
+ page = alloc_get_pages(pool, poolsize, div_up(bytes, getpagesize()), 1);
+ if (!page)
+ return NULL;
+
+ newmh = (struct metaheader *)((char *)pool + page * getpagesize());
+ newmh->metalen = getpagesize() - sizeof(*mh);
+ memset(newmh + 1, 0, newmh->metalen);
+
+ /* Sew it into linked list */
+ mh = first_mheader(pool,poolsize);
+ newmh->next = mh->next;
+ mh->next = (char *)newmh - (char *)pool;
+
+ return alloc_metaspace(newmh, bytes);
+}
+
+static void alloc_free_pages(void *pool, unsigned long pagenum)
+{
+ assert(get_page_state(pool, pagenum) == TAKEN_START);
+ set_page_state(pool, pagenum, FREE);
+ while (get_page_state(pool, ++pagenum) == TAKEN)
+ set_page_state(pool, pagenum, FREE);
+}
+
+static void *alloc_sub_page(void *pool, unsigned long poolsize,
+ unsigned long size, unsigned long align)
+{
+ unsigned long i;
+ uint8_t *metadata;
+
+ /* Look for partial page. */
+ for (i = 0; i < poolsize / getpagesize(); i++) {
+ void *ret;
+ if (get_page_state(pool, i) != SUBPAGE)
+ continue;
+
+ ret = sub_page_alloc(pool, i, size, align);
+ if (ret)
+ return ret;
+ }
+
+ /* Create new SUBPAGE page. */
+ i = alloc_get_pages(pool, poolsize, 1, 1);
+ if (i == 0)
+ return NULL;
+
+ /* Get metadata for page. */
+ metadata = new_metadata(pool, poolsize, BITMAP_METALEN);
+ if (!metadata) {
+ alloc_free_pages(pool, i);
+ return NULL;
+ }
+
+ /* Actually, this is a SUBPAGE page now. */
+ set_page_state(pool, i, SUBPAGE);
+
+ /* Set metadata pointer for page. */
+ set_page_metadata(pool, i, metadata);
+
+ /* Do allocation like normal */
+ return sub_page_alloc(pool, i, size, align);
+}
+