sb_region_lookup
authorRobert Haas <rhaas@postgresql.org>
Wed, 12 Mar 2014 16:40:43 +0000 (12:40 -0400)
committerRobert Haas <rhaas@postgresql.org>
Wed, 12 Mar 2014 16:40:43 +0000 (12:40 -0400)
src/backend/utils/mmgr/sb_region.c
src/include/utils/sb_region.h

index 8b73cf6a11c3778c8a3e78f54cc56c249c0c72ce..bbaa3f6a7aa1419588c4a6fe592949eaedd1fa3f 100644 (file)
@@ -3,6 +3,27 @@
  * sb_region.c
  *       Superblock allocator memory region manager.
  *
+ * The superblock allocator operates on ranges of pages managed by a
+ * FreePageManager and reverse-mapped by an sb_map.  When it's asked to
+ * free an object, it just gets a pointer address; our job is to figure
+ * out which page range contains that object and locate the
+ * FreePageManager, sb_map, and other metadata that the superblock
+ * allocator will need to do its thing.  Moreover, when allocating an
+ * object, the caller is only required to provide the superblock allocator
+ * with a pointer to the sb_allocator object, which could be in either
+ * shared or backend-private memory; our job again is to know which it
+ * is and provide pointers to the appropriate supporting data structures.
+ * To do all this, we have to keep track of where all dynamic shared memory
+ * segments configured for memory allocation are located; and we also have
+ * to keep track of where all chunks of memory obtained from the operating
+ * system for backend-private allocations are located.
+ *
+ * On a 32-bit system, the number of chunks can never get very big, so
+ * we just store them all in a single array and use binary search for
+ * lookups.  On a 64-bit system, this might get dicey, so we maintain
+ * one such array for every 4GB of address space; chunks that span a 4GB
+ * boundary require multiple entries.
+ *
  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
 #include "postgres.h"
 
 #include "utils/sb_region.h"
+
+/*
+ * On 64-bit systems, we use a two-level radix tree to find the data for
+ * the relevant 4GB range.  The radix tree is deliberately unbalanced, with
+ * more entries at the first level than at the second level.  We expect this
+ * to save memory, because the first level has a cache, and the full array
+ * is only instantiated if the cache overflows.  Since each L2 entry
+ * covers 2^44 bytes of address space (16TB), we expect overflows of the
+ * four-entry cache to happen essentially never.
+ */
+#define SB_LOOKUP_ROOT_BITS                    20
+#define SB_LOOKUP_ROOT_ENTRIES         (1 << SB_LOOKUP_ROOT_BITS)
+#define SB_LOOKUP_ROOT_CACHE_SIZE      4
+#define SB_LOOKUP_L2_BITS                      12
+#define SB_LOOKUP_L2_ENTRIES           (1 << SB_LOOKUP_L2_BITS)
+
+/* Lookup data for a 4GB range of address space. */
+typedef struct
+{
+       int             nused;
+       int             nallocated;
+       sb_region **region;
+} sb_lookup_leaf;
+
+/* Lookup data for a 16TB range of address space, direct mapped. */
+typedef struct
+{
+       sb_lookup_leaf *leaf[SB_LOOKUP_L2_ENTRIES];
+} sb_lookup_l2;
+
+/* Lookup data for an entire 64-bit address space. */
+typedef struct
+{
+       uint32  ncached;
+       uint32  cache_key[SB_LOOKUP_ROOT_CACHE_SIZE];
+       sb_lookup_l2 *cache_value[SB_LOOKUP_L2_ENTRIES];
+       sb_lookup_l2 **l2;
+} sb_lookup_root;
+
+#if SIZEOF_SIZE_T > 4
+static sb_lookup_root lookup_root;
+#else
+static sb_lookup_leaf lookup_root_leaf;
+#endif
+
+/*
+ * Find the region to which a pointer belongs.
+ */
+sb_region *
+sb_lookup_region(void *ptr)
+{
+       Size p = (Size) ptr;
+       sb_lookup_leaf *leaf = NULL;
+       int             high, low;
+
+       /*
+        * If this is a 64-bit system, locate the lookup table that pertains
+        * to the upper 32 bits of ptr.  On a 32-bit system, there's only one
+        * lookup table.
+        */
+#if SIZEOF_SIZE_T > 4
+       {
+               uint32  highbits = p >> 32;
+               sb_lookup_l2 *l2 = NULL;
+               int     i;
+
+               /* Check for entry in cache. */
+               for (i = 0; i < lookup_root.ncached; ++i)
+                       if (lookup_root.cache_key[i] == highbits)
+                               l2 = lookup_root.cache_value[i];
+
+               /*
+                * If there's nothing in cache but the full table has been initialized,
+                * find the l2 entry there and pull it into the cache.  Since we expect
+                * this path to be taken virtually never, we don't worry about LRU but
+                * just pick a slot more or less arbitrarily.
+                */
+               if (l2 == NULL && lookup_root.l2 != NULL)
+               {
+                       uint32  rootbits = highbits >> SB_LOOKUP_L2_BITS;
+                       rootbits &= SB_LOOKUP_ROOT_ENTRIES - 1;
+                       l2 = lookup_root.l2[rootbits];
+
+                       if (l2 != NULL)
+                       {
+                               i = highbits % SB_LOOKUP_ROOT_CACHE_SIZE;
+                               lookup_root.cache_key[i] = highbits;
+                               lookup_root.cache_value[i] = l2;
+                       }
+               }
+
+               /* Now use the L2 map (if any) to find the correct leaf node. */
+               if (l2 != NULL)
+                       leaf = l2->leaf[highbits & (SB_LOOKUP_L2_ENTRIES - 1)];
+
+               /* No lookup table for this 4GB range?  OK, no matching region. */
+               if (leaf == NULL)
+                       return NULL;
+       }
+#else
+       leaf = &lookup_root_leaf;
+#endif
+
+       /* Now we use binary search on the sb_lookup_leaf. */
+       high = leaf->nused;
+       low = 0;
+       while (low < high)
+       {
+               int mid;
+               sb_region *region;
+
+               mid = (high + low) / 2;
+               region = leaf->region[mid];
+               if (region->region_start > (char *) ptr)
+                       high = mid;
+               else if (region->region_start + region->region_size < (char *) ptr)
+                       low = mid + 1;
+               else
+                       return region;
+       }
+       return NULL;
+}
index 394bc2e800b1bceef7e1f5334e3ac95c557109da..83e3245f298f53743d0c321c06ab43c757212014 100644 (file)
@@ -28,6 +28,7 @@ typedef struct sb_region
 {
        char *region_start;
        Size region_size;
+       Size usable_pages;
        dsm_segment *seg;
        FreePageManager *fpm;
        sb_map *pagemap;
@@ -56,7 +57,8 @@ extern sb_allocator *sb_attach_shared_region(dsm_segment *,
                                                sb_shared_region *);
 
 /* For use by sb_alloc/sb_free. */
-extern sb_region *sb_private_region_for_allocator(Size npages);
 extern sb_region *sb_lookup_region(void *);
+extern sb_region *sb_private_region_for_allocator(Size npages);
+extern void sb_release_private_region(sb_region *);
 
 #endif         /* SB_REGION_H */