/* Maximum number of pages for a 32-bit address space. */
#define MSPAN_MAX_32BIT_PAGES (1 << (32 - MSPAN_PAGE_BITS))
+/*
+ * Amount of space to allocate from the operating system at one time, as
+ * a multiple of our page size. The first chunk will be of the first size
+ * in the array, and then we work up as we allocate more chunks.
+ */
+static Size mspan_sysalloc_pages[] = { 256, 512, 1024, 2048, 4096, 8192 };
+
/*
* Small allocations are handled by dividing a relatively large chunk of
* memory called a superblock into many small objects of equal size. The
uint16 ninitialized; /* Maximum number of objects ever allocated. */
uint16 nused; /* Number of objects currently allocated. */
uint16 firstfree; /* First object on free list. */
+ void *syschunk; /* Pointer returned by OS malloc. */
+ Size syspages; /* Original size of span, before splitting. */
};
#define MSPAN_FIRSTFREE_NONE ((uint16) -1)
if (span != NULL)
{
/*
- * If the span is just one page, deallocate it completely (see
- * function header comments for why this is OK). Otherwise, remove
+ * If the span is just one page, deallocate it completely (context
+ * objects are never freed, so this is OK). Otherwise, remove
* the first page from the span and put the rest back on the
* appropriate free list. Also adjust the page map entries as
* appropriate.
return result;
}
-/*
- * Allocate new space for a new span descriptor.
- */
-static mspan *
-mspan_allocate_span_descriptor(char *base, mspan_manager *mgr)
-{
- mspan *span_of_spans;
-
- if (!relptr_is_null(mgr->span_of_spans))
- {
- char *result;
-
- /* Try to allocate from the first span-of-spans. */
- span_of_spans = relptr_access(base, mgr->span_of_spans);
- Assert(span_of_spans->span_type == MSPAN_TYPE_SPAN_OF_SPANS);
- result = mspan_allocate_from_superblock(base, span_of_spans);
- if (result != NULL)
- return (mspan *) result;
-
- /* Walk the list looking for a span-of-spans that isn't full. */
- for (;;)
- {
- span_of_spans = relptr_access(base, span_of_spans->nextspan);
- if (span_of_spans == NULL)
- break;
- Assert(span_of_spans->span_type == MSPAN_TYPE_SPAN_OF_SPANS);
- result = mspan_allocate_from_superblock(base, span_of_spans);
- if (result != NULL)
- {
- /*
- * Move the span from which we allocate to head of list in
- * the hope of speeding up future searches.
- */
- mspan_unlink_span(base, span_of_spans);
- mspan_link_span_to_manager(base, mgr, span_of_spans);
-
- /* Return a pointer to the space we allocated. */
- return (mspan *) result;
- }
- }
- }
-
- /* Create a new span descriptor. */
- span_of_spans =
- mspan_allocate_span(base, mgr, NULL, MSPAN_TYPE_SPAN_OF_SPANS, 0);
- if (span_of_spans == NULL)
- return NULL;
- return (mspan *) mspan_allocate_from_superblock(base, span_of_spans);
-}
-
/*
* Allocate a span.
*
uint16 span_type, Size pages)
{
mspan *span;
+ char *syschunk = NULL;
+ Size syspages = 0;
Size first_page;
/*
* such a span, and the only other lookups we expect are for the pages
* end pages to check whether this is a free span that can be
* consolidated.
+ *
+ * XXX. So what happens if we go to do these page map updates and
+ * run out of memory? It's not safe to just bail out here; we'd
+ * leak the allocated span.
*/
Assert(span->npages == pages);
if (span_type != MSPAN_TYPE_LARGE)
* spans. Otherwise, we prefer to allocate the span descriptor here
* rather than after finding storage, because it's easier to back this
* out if storage allocation fails than the other way around.
+ *
+ * XXX. This is completely bogus in the non-DSM case, because we might
+ * recuse back into this routine, allocate more space from the OS, and yet
+ * not know it, and thus allocate more again.
*/
if (span_type != MSPAN_TYPE_SPAN_OF_SPANS)
{
if (mgr->boundary + pages >= mgr->npages)
{
/* Not enough pages remaining. */
- mspan_destroy_span(base, span);
+ if (span != NULL)
+ mspan_destroy_span(base, span);
return NULL;
}
first_page = mgr->boundary;
mgr->boundary += pages;
+
+ /*
+ * If this is a span-of-spans, allocate a descriptor for the new span
+ * out of the span itself. If it's not, span should already be
+ * non-NULL; see above.
+ */
+ if (span_type == MSPAN_TYPE_SPAN_OF_SPANS)
+ {
+ Assert(span == NULL);
+ span = (mspan *) (base + first_page * MSPAN_PAGE_SIZE);
+ }
+ Assert(span != NULL);
+
+ /* Initialize the new span. */
+ span->first_page = first_page;
+ span->npages = pages;
+ mspan_initialize_span(base, mgr, cxt, span, span_type);
}
else
{
+ /* Allocate from the operating system. */
+ if (pages > mspan_sysalloc_pages[0])
+ {
+ /*
+ * This is a large allocation, so ask the operating system for
+ * exactly the amount of space we need.
+ */
+ syspages = pages;
+ syschunk = malloc(syspages * MSPAN_PAGE_SIZE);
+ if (syschunk == NULL)
+ {
+ if (span != NULL)
+ mspan_destroy_span(base, span);
+ return NULL;
+ }
+ }
+ else
+ {
+ Size i;
+
+ /*
+ * Try to allocate a chunk of the size appropriate to the number
+ * of system chunks already allocated. If that fails, ratchet the
+ * request back, but not below the minimum chunk size.
+ */
+ i = Max(mgr->nsyschunks, lengthof(mspan_sysalloc_pages) - 1);
+ for (;;)
+ {
+ syspages = mspan_sysalloc_pages[i];
+ syschunk = malloc(syspages * MSPAN_PAGE_SIZE);
+ if (syschunk != NULL)
+ break;
+ if (i == 0)
+ {
+ if (span != NULL)
+ mspan_destroy_span(base, span);
+ return NULL;
+ }
+ --i;
+ }
+ }
+
/*
- * XXX. Allocate more core via malloc. We need a system here for
- * this. Obviously we shouldn't just allocate the smallest amount
- * needed for this span unless that's already pretty big. Instead,
- * we should allocate enough for this span and then throw the remainder
- * in a bucket for later use. But the mechanism for that is not
- * designed yet.
- *
- * XXX. How exactly are we going to give the segments we malloc
- * back to the OS? How are we even going to know where they are?
- * We can add them to the freelists as a big old span, but that's
- * not going to help much in terms of identifying them later.
+ * Work out the number of usable pages in the span, and the location
+ * of the first one. If the operating system returned a page-aligned
+ * address, as we hope, then the number of usable pages is exactly
+ * equal to the number of pages we allocated. If not, then both the
+ * first and last pages are partial and therefore unusable.
*/
- first_page = 0; /* XXX. Bogus. */
+ first_page = ((Size) syschunk) << MSPAN_PAGE_BITS;
+ if (((Size) syschunk) % MSPAN_PAGE_BITS != 0)
+ {
+ ++first_page;
+ syspages -= 2;
+ }
}
/*
* If this is a span-of-spans, allocate a descriptor for the new span
- * out of the span itself. If it's not, span shuold already be non-NULL;
- * see above.
+ * out of the span itself. If it's not, span should already be
+ * non-NULL; see above.
*/
if (span_type == MSPAN_TYPE_SPAN_OF_SPANS)
{
/* Initialize the new span. */
span->first_page = first_page;
span->npages = pages;
+ span->syschunk = syschunk;
+ span->syspages = syspages;
mspan_initialize_span(base, mgr, cxt, span, span_type);
+ /*
+ * XXX. If we just allocated a new system chunk, it's probably larger
+ * than the number of pages we actually used for the span. We need to
+ * turn the rest into another span, put it on the free list, and make
+ * page map entries for it.
+ */
+
return span;
}
+/*
+ * Allocate new space for a new span descriptor.
+ */
+static mspan *
+mspan_allocate_span_descriptor(char *base, mspan_manager *mgr)
+{
+ mspan *span_of_spans;
+
+ if (!relptr_is_null(mgr->span_of_spans))
+ {
+ char *result;
+
+ /* Try to allocate from the first span-of-spans. */
+ span_of_spans = relptr_access(base, mgr->span_of_spans);
+ Assert(span_of_spans->span_type == MSPAN_TYPE_SPAN_OF_SPANS);
+ result = mspan_allocate_from_superblock(base, span_of_spans);
+ if (result != NULL)
+ return (mspan *) result;
+
+ /* Walk the list looking for a span-of-spans that isn't full. */
+ for (;;)
+ {
+ span_of_spans = relptr_access(base, span_of_spans->nextspan);
+ if (span_of_spans == NULL)
+ break;
+ Assert(span_of_spans->span_type == MSPAN_TYPE_SPAN_OF_SPANS);
+ result = mspan_allocate_from_superblock(base, span_of_spans);
+ if (result != NULL)
+ {
+ /*
+ * Move the span from which we allocate to head of list in
+ * the hope of speeding up future searches.
+ */
+ mspan_unlink_span(base, span_of_spans);
+ mspan_link_span_to_manager(base, mgr, span_of_spans);
+
+ /* Return a pointer to the space we allocated. */
+ return (mspan *) result;
+ }
+ }
+ }
+
+ /* Create a new span descriptor. */
+ span_of_spans =
+ mspan_allocate_span(base, mgr, NULL, MSPAN_TYPE_SPAN_OF_SPANS, 0);
+ if (span_of_spans == NULL)
+ return NULL;
+ return (mspan *) mspan_allocate_from_superblock(base, span_of_spans);
+}
+
/*
* Deallocate a span descriptor.
*/