Hack, hack. memory
authorRobert Haas <[email protected]>
Thu, 6 Feb 2014 16:39:09 +0000 (11:39 -0500)
committerRobert Haas <[email protected]>
Thu, 6 Feb 2014 16:39:09 +0000 (11:39 -0500)
src/backend/utils/mmgr/mspan.c
src/include/utils/mspan.h

index c84de1b92c03121bec3938a263dae452f1ccf78d..8f040b948378ae7f644882f975e978a3ff9d8586 100644 (file)
 /* 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
@@ -125,6 +132,8 @@ struct mspan
        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)
@@ -443,8 +452,8 @@ mspan_allocate_context_descriptor(char *base, mspan_manager *mgr)
        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.
@@ -534,56 +543,6 @@ mspan_allocate_from_superblock(char *base, mspan *superblock)
        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.
  *
@@ -603,6 +562,8 @@ mspan_allocate_span(char *base, mspan_manager *mgr, mspan_context *cxt,
                                        uint16 span_type, Size pages)
 {
        mspan  *span;
+       char   *syschunk = NULL;
+       Size    syspages = 0;
        Size    first_page;
 
        /*
@@ -665,6 +626,10 @@ mspan_allocate_span(char *base, mspan_manager *mgr, mspan_context *cxt,
                 * 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)
@@ -688,6 +653,10 @@ mspan_allocate_span(char *base, mspan_manager *mgr, mspan_context *cxt,
         * 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)
        {
@@ -703,34 +672,93 @@ mspan_allocate_span(char *base, mspan_manager *mgr, mspan_context *cxt,
                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)
        {
@@ -742,11 +770,70 @@ mspan_allocate_span(char *base, mspan_manager *mgr, mspan_context *cxt,
        /* 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.
  */
index 67a8e5191115df4ec54205f49d76c3b5f113eb60..6c33dcf3877bc2b3a96b434a60c68daa6861421b 100644 (file)
@@ -76,8 +76,7 @@ struct mspan_manager
        Size            base;           /* offset of page 0 within dsm; 0 for private */
        Size            boundary;       /* first unallocated page in dsm; 0 for private */
        Size            ncontexts;      /* # of outstanding contexts */
-       Size            nspans;         /* # of outstanding spans */
-       Size            nspansuperblocks;       /* # of superblocks for span objects */
+       Size            nsyschunks;     /* # of chunks allocated from OS */
        relptr(mspan) span_of_spans;    /* superblock for span descriptors */
        relptr(mspan_context) freecontext;      /* allocatable context object */
        aspace_map      page_map;       /* map pages to mspans */