PostgreSQL Source Code git master
Loading...
Searching...
No Matches
dsa.c File Reference
#include "postgres.h"
#include "port/atomics.h"
#include "port/pg_bitutils.h"
#include "storage/dsm.h"
#include "storage/lwlock.h"
#include "utils/dsa.h"
#include "utils/freepage.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
Include dependency graph for dsa.c:

Go to the source code of this file.

Data Structures

struct  dsa_segment_header
 
struct  dsa_area_span
 
struct  dsa_area_pool
 
struct  dsa_area_control
 
struct  dsa_segment_map
 
struct  dsa_area
 

Macros

#define DSA_NUM_SEGMENTS_AT_EACH_SIZE   2
 
#define DSA_MAX_SEGMENTS    Min(1024, (1 << ((SIZEOF_DSA_POINTER * 8) - DSA_OFFSET_WIDTH)))
 
#define DSA_OFFSET_BITMASK   (((dsa_pointer) 1 << DSA_OFFSET_WIDTH) - 1)
 
#define DSA_PAGES_PER_SUPERBLOCK   16
 
#define DSA_SEGMENT_HEADER_MAGIC   0x0ce26608
 
#define DSA_MAKE_POINTER(segment_number, offset)    (((dsa_pointer) (segment_number) << DSA_OFFSET_WIDTH) | (offset))
 
#define DSA_EXTRACT_SEGMENT_NUMBER(dp)   ((dp) >> DSA_OFFSET_WIDTH)
 
#define DSA_EXTRACT_OFFSET(dp)   ((dp) & DSA_OFFSET_BITMASK)
 
#define DSA_SEGMENT_INDEX_NONE   (~(dsa_segment_index)0)
 
#define DSA_NUM_SEGMENT_BINS   16
 
#define DSA_AREA_LOCK(area)   (&area->control->lock)
 
#define DSA_SCLASS_LOCK(area, sclass)   (&area->control->pools[sclass].lock)
 
#define NextFreeObjectIndex(object)   (* (uint16 *) (object))
 
#define DSA_NUM_SIZE_CLASSES   lengthof(dsa_size_classes)
 
#define DSA_SCLASS_BLOCK_OF_SPANS   0
 
#define DSA_SCLASS_SPAN_LARGE   1
 
#define DSA_SIZE_CLASS_MAP_QUANTUM   8
 
#define DSA_FULLNESS_CLASSES   4
 
#define DsaAreaPoolToDsaPointer(area, p)    DSA_MAKE_POINTER(0, (char *) p - (char *) area->control)
 
#define DSA_SPAN_NOTHING_FREE   ((uint16) -1)
 
#define DSA_SUPERBLOCK_SIZE   (DSA_PAGES_PER_SUPERBLOCK * FPM_PAGE_SIZE)
 
#define get_segment_index(area, segment_map_ptr)    (segment_map_ptr - &area->segment_maps[0])
 

Typedefs

typedef size_t dsa_segment_index
 

Functions

static size_t contiguous_pages_to_segment_bin (size_t n)
 
static void init_span (dsa_area *area, dsa_pointer span_pointer, dsa_area_pool *pool, dsa_pointer start, size_t npages, uint16 size_class)
 
static bool transfer_first_span (dsa_area *area, dsa_area_pool *pool, int fromclass, int toclass)
 
static dsa_pointer alloc_object (dsa_area *area, int size_class)
 
static bool ensure_active_superblock (dsa_area *area, dsa_area_pool *pool, int size_class)
 
static dsa_segment_mapget_segment_by_index (dsa_area *area, dsa_segment_index index)
 
static void destroy_superblock (dsa_area *area, dsa_pointer span_pointer)
 
static void unlink_span (dsa_area *area, dsa_area_span *span)
 
static void add_span_to_fullness_class (dsa_area *area, dsa_area_span *span, dsa_pointer span_pointer, int fclass)
 
static void unlink_segment (dsa_area *area, dsa_segment_map *segment_map)
 
static dsa_segment_mapget_best_segment (dsa_area *area, size_t npages)
 
static dsa_segment_mapmake_new_segment (dsa_area *area, size_t requested_pages)
 
static dsa_areacreate_internal (void *place, size_t size, int tranche_id, dsm_handle control_handle, dsm_segment *control_segment, size_t init_segment_size, size_t max_segment_size)
 
static dsa_areaattach_internal (void *place, dsm_segment *segment, dsa_handle handle)
 
static void check_for_freed_segments (dsa_area *area)
 
static void check_for_freed_segments_locked (dsa_area *area)
 
static void rebin_segment (dsa_area *area, dsa_segment_map *segment_map)
 
dsa_areadsa_create_ext (int tranche_id, size_t init_segment_size, size_t max_segment_size)
 
dsa_areadsa_create_in_place_ext (void *place, size_t size, int tranche_id, dsm_segment *segment, size_t init_segment_size, size_t max_segment_size)
 
dsa_handle dsa_get_handle (dsa_area *area)
 
dsa_areadsa_attach (dsa_handle handle)
 
bool dsa_is_attached (dsa_handle handle)
 
dsa_areadsa_attach_in_place (void *place, dsm_segment *segment)
 
void dsa_on_dsm_detach_release_in_place (dsm_segment *segment, Datum place)
 
void dsa_on_shmem_exit_release_in_place (int code, Datum place)
 
void dsa_release_in_place (void *place)
 
void dsa_pin_mapping (dsa_area *area)
 
dsa_pointer dsa_allocate_extended (dsa_area *area, size_t size, int flags)
 
void dsa_free (dsa_area *area, dsa_pointer dp)
 
voiddsa_get_address (dsa_area *area, dsa_pointer dp)
 
void dsa_pin (dsa_area *area)
 
void dsa_unpin (dsa_area *area)
 
void dsa_set_size_limit (dsa_area *area, size_t limit)
 
size_t dsa_get_total_size (dsa_area *area)
 
size_t dsa_get_total_size_from_handle (dsa_handle handle)
 
void dsa_trim (dsa_area *area)
 
void dsa_dump (dsa_area *area)
 
size_t dsa_minimum_size (void)
 
void dsa_detach (dsa_area *area)
 

Variables

static const uint16 dsa_size_classes []
 
static const uint8 dsa_size_class_map []
 

Macro Definition Documentation

◆ DSA_AREA_LOCK

#define DSA_AREA_LOCK (   area)    (&area->control->lock)

Definition at line 132 of file dsa.c.

◆ DSA_EXTRACT_OFFSET

#define DSA_EXTRACT_OFFSET (   dp)    ((dp) & DSA_OFFSET_BITMASK)

Definition at line 99 of file dsa.c.

◆ DSA_EXTRACT_SEGMENT_NUMBER

#define DSA_EXTRACT_SEGMENT_NUMBER (   dp)    ((dp) >> DSA_OFFSET_WIDTH)

Definition at line 96 of file dsa.c.

◆ DSA_FULLNESS_CLASSES

#define DSA_FULLNESS_CLASSES   4

Definition at line 266 of file dsa.c.

◆ DSA_MAKE_POINTER

#define DSA_MAKE_POINTER (   segment_number,
  offset 
)     (((dsa_pointer) (segment_number) << DSA_OFFSET_WIDTH) | (offset))

Definition at line 92 of file dsa.c.

119{
120 size_t bin;
121
122 if (n == 0)
123 bin = 0;
124 else
125 bin = pg_leftmost_one_pos_size_t(n) + 1;
126
127 return Min(bin, DSA_NUM_SEGMENT_BINS - 1);
128}
129
130/* Macros for access to locks. */
131#define DSA_AREA_LOCK(area) (&area->control->lock)
132#define DSA_SCLASS_LOCK(area, sclass) (&area->control->pools[sclass].lock)
133
134/*
135 * The header for an individual segment. This lives at the start of each DSM
136 * segment owned by a DSA area including the first segment (where it appears
137 * as part of the dsa_area_control struct).
138 */
139typedef struct
140{
141 /* Sanity check magic value. */
142 uint32 magic;
143 /* Total number of pages in this segment (excluding metadata area). */
144 size_t usable_pages;
145 /* Total size of this segment in bytes. */
146 size_t size;
147
148 /*
149 * Index of the segment that precedes this one in the same segment bin, or
150 * DSA_SEGMENT_INDEX_NONE if this is the first one.
151 */
153
154 /*
155 * Index of the segment that follows this one in the same segment bin, or
156 * DSA_SEGMENT_INDEX_NONE if this is the last one.
157 */
159 /* The index of the bin that contains this segment. */
160 size_t bin;
161
162 /*
163 * A flag raised to indicate that this segment is being returned to the
164 * operating system and has been unpinned.
165 */
166 bool freed;
168
169/*
170 * Metadata for one superblock.
171 *
172 * For most blocks, span objects are stored out-of-line; that is, the span
173 * object is not stored within the block itself. But, as an exception, for a
174 * "span of spans", the span object is stored "inline". The allocation is
175 * always exactly one page, and the dsa_area_span object is located at
176 * the beginning of that page. The size class is DSA_SCLASS_BLOCK_OF_SPANS,
177 * and the remaining fields are used just as they would be in an ordinary
178 * block. We can't allocate spans out of ordinary superblocks because
179 * creating an ordinary superblock requires us to be able to allocate a span
180 * *first*. Doing it this way avoids that circularity.
181 */
182typedef struct
183{
184 dsa_pointer pool; /* Containing pool. */
185 dsa_pointer prevspan; /* Previous span. */
186 dsa_pointer nextspan; /* Next span. */
187 dsa_pointer start; /* Starting address. */
188 size_t npages; /* Length of span in pages. */
189 uint16 size_class; /* Size class. */
190 uint16 ninitialized; /* Maximum number of objects ever allocated. */
191 uint16 nallocatable; /* Number of objects currently allocatable. */
192 uint16 firstfree; /* First object on free list. */
193 uint16 nmax; /* Maximum number of objects ever possible. */
194 uint16 fclass; /* Current fullness class. */
196
197/*
198 * Given a pointer to an object in a span, access the index of the next free
199 * object in the same span (ie in the span's freelist) as an L-value.
200 */
201#define NextFreeObjectIndex(object) (* (uint16 *) (object))
202
203/*
204 * Small allocations are handled by dividing a single block of memory into
205 * many small objects of equal size. The possible allocation sizes are
206 * defined by the following array. Larger size classes are spaced more widely
207 * than smaller size classes. We fudge the spacing for size classes >1kB to
208 * avoid space wastage: based on the knowledge that we plan to allocate 64kB
209 * blocks, we bump the maximum object size up to the largest multiple of
210 * 8 bytes that still lets us fit the same number of objects into one block.
211 *
212 * NB: Because of this fudging, if we were ever to use differently-sized blocks
213 * for small allocations, these size classes would need to be reworked to be
214 * optimal for the new size.
215 *
216 * NB: The optimal spacing for size classes, as well as the size of the blocks
217 * out of which small objects are allocated, is not a question that has one
218 * right answer. Some allocators (such as tcmalloc) use more closely-spaced
219 * size classes than we do here, while others (like aset.c) use more
220 * widely-spaced classes. Spacing the classes more closely avoids wasting
221 * memory within individual chunks, but also means a larger number of
222 * potentially-unfilled blocks.
223 */
224static const uint16 dsa_size_classes[] = {
225 sizeof(dsa_area_span), 0, /* special size classes */
226 8, 16, 24, 32, 40, 48, 56, 64, /* 8 classes separated by 8 bytes */
227 80, 96, 112, 128, /* 4 classes separated by 16 bytes */
228 160, 192, 224, 256, /* 4 classes separated by 32 bytes */
229 320, 384, 448, 512, /* 4 classes separated by 64 bytes */
230 640, 768, 896, 1024, /* 4 classes separated by 128 bytes */
231 1280, 1560, 1816, 2048, /* 4 classes separated by ~256 bytes */
232 2616, 3120, 3640, 4096, /* 4 classes separated by ~512 bytes */
233 5456, 6552, 7280, 8192 /* 4 classes separated by ~1024 bytes */
234};
235#define DSA_NUM_SIZE_CLASSES lengthof(dsa_size_classes)
236
237/* Special size classes. */
238#define DSA_SCLASS_BLOCK_OF_SPANS 0
239#define DSA_SCLASS_SPAN_LARGE 1
240
241/*
242 * The following lookup table is used to map the size of small objects
243 * (less than 1kB) onto the corresponding size class. To use this table,
244 * round the size of the object up to the next multiple of 8 bytes, and then
245 * index into this array.
246 */
247static const uint8 dsa_size_class_map[] = {
248 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 11, 12, 12, 13, 13,
249 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17,
250 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19,
251 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21,
252 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
253 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
254 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
255 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25
256};
257#define DSA_SIZE_CLASS_MAP_QUANTUM 8
258
259/*
260 * Superblocks are binned by how full they are. Generally, each fullness
261 * class corresponds to one quartile, but the block being used for
262 * allocations is always at the head of the list for fullness class 1,
263 * regardless of how full it really is.
264 */
265#define DSA_FULLNESS_CLASSES 4
266
267/*
268 * A dsa_area_pool represents a set of objects of a given size class.
269 *
270 * Perhaps there should be multiple pools for the same size class for
271 * contention avoidance, but for now there is just one!
272 */
273typedef struct
274{
275 /* A lock protecting access to this pool. */
276 LWLock lock;
277 /* A set of linked lists of spans, arranged by fullness. */
279 /* Should we pad this out to a cacheline boundary? */
281
282/*
283 * The control block for an area. This lives in shared memory, at the start of
284 * the first DSM segment controlled by this area.
285 */
286typedef struct
287{
288 /* The segment header for the first segment. */
289 dsa_segment_header segment_header;
290 /* The handle for this area. */
291 dsa_handle handle;
292 /* The handles of the segments owned by this area. */
293 dsm_handle segment_handles[DSA_MAX_SEGMENTS];
294 /* Lists of segments, binned by maximum contiguous run of free pages. */
296 /* The object pools for each size class. */
298 /* initial allocation segment size */
299 size_t init_segment_size;
300 /* maximum allocation segment size */
301 size_t max_segment_size;
302 /* The total size of all active segments. */
303 size_t total_segment_size;
304 /* The maximum total size of backing storage we are allowed. */
305 size_t max_total_segment_size;
306 /* Highest used segment index in the history of this area. */
307 dsa_segment_index high_segment_index;
308 /* The reference count for this area. */
309 int refcnt;
310 /* A flag indicating that this area has been pinned. */
311 bool pinned;
312 /* The number of times that segments have been freed. */
313 size_t freed_segment_counter;
314 /* The LWLock tranche ID. */
315 int lwlock_tranche_id;
316 /* The general lock (protects everything except object pools). */
317 LWLock lock;
319
320/* Given a pointer to a pool, find a dsa_pointer. */
321#define DsaAreaPoolToDsaPointer(area, p) \
322 DSA_MAKE_POINTER(0, (char *) p - (char *) area->control)
323
324/*
325 * A dsa_segment_map is stored within the backend-private memory of each
326 * individual backend. It holds the base address of the segment within that
327 * backend, plus the addresses of key objects within the segment. Those
328 * could instead be derived from the base address but it's handy to have them
329 * around.
330 */
331typedef struct
332{
333 dsm_segment *segment; /* DSM segment */
334 char *mapped_address; /* Address at which segment is mapped */
335 dsa_segment_header *header; /* Header (same as mapped_address) */
336 FreePageManager *fpm; /* Free page manager within segment. */
337 dsa_pointer *pagemap; /* Page map within segment. */
339
340/*
341 * Per-backend state for a storage area. Backends obtain one of these by
342 * creating an area or attaching to an existing one using a handle. Each
343 * process that needs to use an area uses its own object to track where the
344 * segments are mapped.
345 */
346struct dsa_area
347{
348 /* Pointer to the control object in shared memory. */
350
351 /*
352 * All the mappings are owned by this. The dsa_area itself is not
353 * directly tracked by the ResourceOwner, but the effect is the same. NULL
354 * if the attachment has session lifespan, i.e if dsa_pin_mapping() has
355 * been called.
356 */
358
359 /*
360 * This backend's array of segment maps, ordered by segment index
361 * corresponding to control->segment_handles. Some of the area's segments
362 * may not be mapped in this backend yet, and some slots may have been
363 * freed and need to be detached; these operations happen on demand.
364 */
366
367 /* The highest segment index this backend has ever mapped. */
369
370 /* The last observed freed_segment_counter. */
372};
373
374#define DSA_SPAN_NOTHING_FREE ((uint16) -1)
375#define DSA_SUPERBLOCK_SIZE (DSA_PAGES_PER_SUPERBLOCK * FPM_PAGE_SIZE)
376
377/* Given a pointer to a segment_map, obtain a segment index number. */
378#define get_segment_index(area, segment_map_ptr) \
379 (segment_map_ptr - &area->segment_maps[0])
380
381static void init_span(dsa_area *area, dsa_pointer span_pointer,
382 dsa_area_pool *pool, dsa_pointer start, size_t npages,
383 uint16 size_class);
384static bool transfer_first_span(dsa_area *area, dsa_area_pool *pool,
385 int fromclass, int toclass);
386static inline dsa_pointer alloc_object(dsa_area *area, int size_class);
387static bool ensure_active_superblock(dsa_area *area, dsa_area_pool *pool,
388 int size_class);
392static void unlink_span(dsa_area *area, dsa_area_span *span);
394 dsa_pointer span_pointer, int fclass);
396static dsa_segment_map *get_best_segment(dsa_area *area, size_t npages);
398static dsa_area *create_internal(void *place, size_t size,
399 int tranche_id,
402 size_t init_segment_size,
403 size_t max_segment_size);
404static dsa_area *attach_internal(void *place, dsm_segment *segment,
405 dsa_handle handle);
406static void check_for_freed_segments(dsa_area *area);
409
410/*
411 * Create a new shared area in a new DSM segment. Further DSM segments will
412 * be allocated as required to extend the available space.
413 *
414 * We can't allocate a LWLock tranche_id within this function, because tranche
415 * IDs are a scarce resource; there are only 64k available, using low numbers
416 * when possible matters, and we have no provision for recycling them. So,
417 * we require the caller to provide one.
418 */
419dsa_area *
420dsa_create_ext(int tranche_id, size_t init_segment_size, size_t max_segment_size)
421{
422 dsm_segment *segment;
423 dsa_area *area;
424
425 /*
426 * Create the DSM segment that will hold the shared control object and the
427 * first segment of usable space.
428 */
429 segment = dsm_create(init_segment_size, 0);
430
431 /*
432 * All segments backing this area are pinned, so that DSA can explicitly
433 * control their lifetime (otherwise a newly created segment belonging to
434 * this area might be freed when the only backend that happens to have it
435 * mapped in ends, corrupting the area).
436 */
437 dsm_pin_segment(segment);
438
439 /* Create a new DSA area with the control object in this segment. */
440 area = create_internal(dsm_segment_address(segment),
441 init_segment_size,
442 tranche_id,
443 dsm_segment_handle(segment), segment,
444 init_segment_size, max_segment_size);
445
446 /* Clean up when the control segment detaches. */
449
450 return area;
451}
452
453/*
454 * Create a new shared area in an existing shared memory space, which may be
455 * either DSM or Postmaster-initialized memory. DSM segments will be
456 * allocated as required to extend the available space, though that can be
457 * prevented with dsa_set_size_limit(area, size) using the same size provided
458 * to dsa_create_in_place.
459 *
460 * Areas created in-place must eventually be released by the backend that
461 * created them and all backends that attach to them. This can be done
462 * explicitly with dsa_release_in_place, or, in the special case that 'place'
463 * happens to be in a pre-existing DSM segment, by passing in a pointer to the
464 * segment so that a detach hook can be registered with the containing DSM
465 * segment.
466 *
467 * See dsa_create() for a note about the tranche arguments.
468 */
469dsa_area *
470dsa_create_in_place_ext(void *place, size_t size,
471 int tranche_id, dsm_segment *segment,
472 size_t init_segment_size, size_t max_segment_size)
473{
474 dsa_area *area;
475
476 area = create_internal(place, size, tranche_id,
478 init_segment_size, max_segment_size);
479
480 /*
481 * Clean up when the control segment detaches, if a containing DSM segment
482 * was provided.
483 */
484 if (segment != NULL)
486 PointerGetDatum(place));
487
488 return area;
489}
490
491/*
492 * Obtain a handle that can be passed to other processes so that they can
493 * attach to the given area. Cannot be called for areas created with
494 * dsa_create_in_place.
495 */
498{
500 return area->control->handle;
501}
502
503/*
504 * Attach to an area given a handle generated (possibly in another process) by
505 * dsa_get_handle. The area must have been created with dsa_create (not
506 * dsa_create_in_place).
507 */
508dsa_area *
510{
511 dsm_segment *segment;
512 dsa_area *area;
513
514 /*
515 * An area handle is really a DSM segment handle for the first segment, so
516 * we go ahead and attach to that.
517 */
518 segment = dsm_attach(handle);
519 if (segment == NULL)
522 errmsg("could not attach to dynamic shared area")));
523
524 area = attach_internal(dsm_segment_address(segment), segment, handle);
525
526 /* Clean up when the control segment detaches. */
529
530 return area;
531}
532
533/*
534 * Returns whether the area with the given handle was already attached by the
535 * current process. The area must have been created with dsa_create (not
536 * dsa_create_in_place).
537 */
538bool
540{
541 /*
542 * An area handle is really a DSM segment handle for the first segment, so
543 * we can just search for that.
544 */
545 return dsm_find_mapping(handle) != NULL;
546}
547
548/*
549 * Attach to an area that was created with dsa_create_in_place. The caller
550 * must somehow know the location in memory that was used when the area was
551 * created, though it may be mapped at a different virtual address in this
552 * process.
553 *
554 * See dsa_create_in_place for note about releasing in-place areas, and the
555 * optional 'segment' argument which can be provided to allow automatic
556 * release if the containing memory happens to be a DSM segment.
557 */
558dsa_area *
559dsa_attach_in_place(void *place, dsm_segment *segment)
560{
561 dsa_area *area;
562
564
565 /*
566 * Clean up when the control segment detaches, if a containing DSM segment
567 * was provided.
568 */
569 if (segment != NULL)
571 PointerGetDatum(place));
572
573 return area;
574}
575
576/*
577 * Release a DSA area that was produced by dsa_create_in_place or
578 * dsa_attach_in_place. The 'segment' argument is ignored but provides an
579 * interface suitable for on_dsm_detach, for the convenience of users who want
580 * to create a DSA segment inside an existing DSM segment and have it
581 * automatically released when the containing DSM segment is detached.
582 * 'place' should be the address of the place where the area was created.
583 *
584 * This callback is automatically registered for the DSM segment containing
585 * the control object of in-place areas when a segment is provided to
586 * dsa_create_in_place or dsa_attach_in_place, and also for all areas created
587 * with dsa_create.
588 */
589void
591{
593}
594
595/*
596 * Release a DSA area that was produced by dsa_create_in_place or
597 * dsa_attach_in_place. The 'code' argument is ignored but provides an
598 * interface suitable for on_shmem_exit or before_shmem_exit, for the
599 * convenience of users who want to create a DSA segment inside shared memory
600 * other than a DSM segment and have it automatically release at backend exit.
601 * 'place' should be the address of the place where the area was created.
602 */
603void
605{
607}
608
609/*
610 * Release a DSA area that was produced by dsa_create_in_place or
611 * dsa_attach_in_place. It is preferable to use one of the 'dsa_on_XXX'
612 * callbacks so that this is managed automatically, because failure to release
613 * an area created in-place leaks its segments permanently.
614 *
615 * This is also called automatically for areas produced by dsa_create or
616 * dsa_attach as an implementation detail.
617 */
618void
619dsa_release_in_place(void *place)
620{
621 dsa_area_control *control = (dsa_area_control *) place;
622
623 LWLockAcquire(&control->lock, LW_EXCLUSIVE);
624 Assert(control->segment_header.magic ==
625 (DSA_SEGMENT_HEADER_MAGIC ^ control->handle ^ 0));
626 Assert(control->refcnt > 0);
627 if (--control->refcnt == 0)
628 {
629 for (dsa_segment_index i = 0; i <= control->high_segment_index; ++i)
630 {
631 dsm_handle handle;
632
633 handle = control->segment_handles[i];
634 if (handle != DSM_HANDLE_INVALID)
635 dsm_unpin_segment(handle);
636 }
637 }
638 LWLockRelease(&control->lock);
639}
640
641/*
642 * Keep a DSA area attached until end of session or explicit detach.
643 *
644 * By default, areas are owned by the current resource owner, which means they
645 * are detached automatically when that scope ends.
646 */
647void
649{
650 if (area->resowner != NULL)
651 {
652 area->resowner = NULL;
653
654 for (dsa_segment_index i = 0; i <= area->high_segment_index; ++i)
655 if (area->segment_maps[i].segment != NULL)
657 }
658}
659
660/*
661 * Allocate memory in this storage area. The return value is a dsa_pointer
662 * that can be passed to other processes, and converted to a local pointer
663 * with dsa_get_address. 'flags' is a bitmap which should be constructed
664 * from the following values:
665 *
666 * DSA_ALLOC_HUGE allows allocations >= 1GB. Otherwise, such allocations
667 * will result in an ERROR.
668 *
669 * DSA_ALLOC_NO_OOM causes this function to return InvalidDsaPointer when
670 * no memory is available or a size limit established by dsa_set_size_limit
671 * would be exceeded. Otherwise, such allocations will result in an ERROR.
672 *
673 * DSA_ALLOC_ZERO causes the allocated memory to be zeroed. Otherwise, the
674 * contents of newly-allocated memory are indeterminate.
675 *
676 * These flags correspond to similarly named flags used by
677 * MemoryContextAllocExtended(). See also the macros dsa_allocate and
678 * dsa_allocate0 which expand to a call to this function with commonly used
679 * flags.
680 */
682dsa_allocate_extended(dsa_area *area, size_t size, int flags)
683{
684 uint16 size_class;
688
689 Assert(size > 0);
690
691 /* Sanity check on huge individual allocation size. */
692 if (((flags & DSA_ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) ||
693 ((flags & DSA_ALLOC_HUGE) == 0 && !AllocSizeIsValid(size)))
694 elog(ERROR, "invalid DSA memory alloc request size %zu", size);
695
696 /*
697 * If bigger than the largest size class, just grab a run of pages from
698 * the free page manager, instead of allocating an object from a pool.
699 * There will still be a span, but it's a special class of span that
700 * manages this whole allocation and simply gives all pages back to the
701 * free page manager when dsa_free is called.
702 */
704 {
705 size_t npages = fpm_size_to_pages(size);
706 size_t first_page;
709
710 /* Obtain a span object. */
713 {
714 /* Raise error unless asked not to. */
715 if ((flags & DSA_ALLOC_NO_OOM) == 0)
718 errmsg("out of memory"),
719 errdetail("Failed on DSA request of size %zu.",
720 size)));
721 return InvalidDsaPointer;
722 }
723
725
726 /* Find a segment from which to allocate. */
727 segment_map = get_best_segment(area, npages);
728 if (segment_map == NULL)
729 segment_map = make_new_segment(area, npages);
730 if (segment_map == NULL)
731 {
732 /* Can't make any more segments: game over. */
734 dsa_free(area, span_pointer);
735
736 /* Raise error unless asked not to. */
737 if ((flags & DSA_ALLOC_NO_OOM) == 0)
740 errmsg("out of memory"),
741 errdetail("Failed on DSA request of size %zu.",
742 size)));
743 return InvalidDsaPointer;
744 }
745
746 /*
747 * Ask the free page manager for a run of pages. This should always
748 * succeed, since both get_best_segment and make_new_segment should
749 * only return a non-NULL pointer if it actually contains enough
750 * contiguous freespace. If it does fail, something in our backend
751 * private state is out of whack, so use FATAL to kill the process.
752 */
753 if (!FreePageManagerGet(segment_map->fpm, npages, &first_page))
754 elog(FATAL,
755 "dsa_allocate could not find %zu free pages", npages);
757
759 first_page * FPM_PAGE_SIZE);
760
761 /* Initialize span and pagemap. */
764 init_span(area, span_pointer, pool, start_pointer, npages,
766 segment_map->pagemap[first_page] = span_pointer;
768
769 /* Zero-initialize the memory if requested. */
770 if ((flags & DSA_ALLOC_ZERO) != 0)
771 memset(dsa_get_address(area, start_pointer), 0, size);
772
773 return start_pointer;
774 }
775
776 /* Map allocation to a size class. */
778 {
779 int mapidx;
780
781 /* For smaller sizes we have a lookup table... */
782 mapidx = ((size + DSA_SIZE_CLASS_MAP_QUANTUM - 1) /
784 size_class = dsa_size_class_map[mapidx];
785 }
786 else
787 {
788 uint16 min;
789 uint16 max;
790
791 /* ... and for the rest we search by binary chop. */
794
795 while (min < max)
796 {
797 uint16 mid = (min + max) / 2;
799
800 if (class_size < size)
801 min = mid + 1;
802 else
803 max = mid;
804 }
805
806 size_class = min;
807 }
808 Assert(size <= dsa_size_classes[size_class]);
809 Assert(size_class == 0 || size > dsa_size_classes[size_class - 1]);
810
811 /* Attempt to allocate an object from the appropriate pool. */
812 result = alloc_object(area, size_class);
813
814 /* Check for failure to allocate. */
816 {
817 /* Raise error unless asked not to. */
818 if ((flags & DSA_ALLOC_NO_OOM) == 0)
821 errmsg("out of memory"),
822 errdetail("Failed on DSA request of size %zu.", size)));
823 return InvalidDsaPointer;
824 }
825
826 /* Zero-initialize the memory if requested. */
827 if ((flags & DSA_ALLOC_ZERO) != 0)
828 memset(dsa_get_address(area, result), 0, size);
829
830 return result;
831}
832
833/*
834 * Free memory obtained with dsa_allocate.
835 */
836void
838{
840 int pageno;
843 char *superblock;
844 char *object;
845 size_t size;
846 int size_class;
847
848 /* Make sure we don't have a stale segment in the slot 'dp' refers to. */
850
851 /* Locate the object, span and pool. */
854 span_pointer = segment_map->pagemap[pageno];
856 superblock = dsa_get_address(area, span->start);
857 object = dsa_get_address(area, dp);
858 size_class = span->size_class;
859 size = dsa_size_classes[size_class];
860
861 /*
862 * Special case for large objects that live in a special span: we return
863 * those pages directly to the free page manager and free the span.
864 */
865 if (span->size_class == DSA_SCLASS_SPAN_LARGE)
866 {
867
868#ifdef CLOBBER_FREED_MEMORY
869 memset(object, 0x7f, span->npages * FPM_PAGE_SIZE);
870#endif
871
872 /* Give pages back to free page manager. */
876 span->npages);
877
878 /* Move segment to appropriate bin if necessary. */
881
882 /* Unlink span. */
885 unlink_span(area, span);
887 /* Free the span object so it can be reused. */
888 dsa_free(area, span_pointer);
889 return;
890 }
891
892#ifdef CLOBBER_FREED_MEMORY
893 memset(object, 0x7f, size);
894#endif
895
896 LWLockAcquire(DSA_SCLASS_LOCK(area, size_class), LW_EXCLUSIVE);
897
898 /* Put the object on the span's freelist. */
899 Assert(object >= superblock);
901 Assert((object - superblock) % size == 0);
902 NextFreeObjectIndex(object) = span->firstfree;
903 span->firstfree = (object - superblock) / size;
904 ++span->nallocatable;
905
906 /*
907 * See if the span needs to moved to a different fullness class, or be
908 * freed so its pages can be given back to the segment.
909 */
910 if (span->nallocatable == 1 && span->fclass == DSA_FULLNESS_CLASSES - 1)
911 {
912 /*
913 * The block was completely full and is located in the
914 * highest-numbered fullness class, which is never scanned for free
915 * chunks. We must move it to the next-lower fullness class.
916 */
917 unlink_span(area, span);
920
921 /*
922 * If this is the only span, and there is no active span, then we
923 * should probably move this span to fullness class 1. (Otherwise if
924 * you allocate exactly all the objects in the only span, it moves to
925 * class 3, then you free them all, it moves to 2, and then is given
926 * back, leaving no active span).
927 */
928 }
929 else if (span->nallocatable == span->nmax &&
930 (span->fclass != 1 || span->prevspan != InvalidDsaPointer))
931 {
932 /*
933 * This entire block is free, and it's not the active block for this
934 * size class. Return the memory to the free page manager. We don't
935 * do this for the active block to prevent hysteresis: if we
936 * repeatedly allocate and free the only chunk in the active block, it
937 * will be very inefficient if we deallocate and reallocate the block
938 * every time.
939 */
941 }
942
943 LWLockRelease(DSA_SCLASS_LOCK(area, size_class));
944}
945
946/*
947 * Obtain a backend-local address for a dsa_pointer. 'dp' must point to
948 * memory allocated by the given area (possibly in another process) that
949 * hasn't yet been freed. This may cause a segment to be mapped into the
950 * current process if required, and may cause freed segments to be unmapped.
951 */
952void *
954{
956 size_t offset;
957
958 /* Convert InvalidDsaPointer to NULL. */
959 if (!DsaPointerIsValid(dp))
960 return NULL;
961
962 /* Process any requests to detach from freed segments. */
964
965 /* Break the dsa_pointer into its components. */
967 offset = DSA_EXTRACT_OFFSET(dp);
969
970 /* Check if we need to cause this segment to be mapped in. */
972 {
973 /* Call for effect (we don't need the result). */
975 }
976
977 return area->segment_maps[index].mapped_address + offset;
978}
979
980/*
981 * Pin this area, so that it will continue to exist even if all backends
982 * detach from it. In that case, the area can still be reattached to if a
983 * handle has been recorded somewhere.
984 */
985void
986dsa_pin(dsa_area *area)
987{
989 if (area->control->pinned)
990 {
992 elog(ERROR, "dsa_area already pinned");
993 }
994 area->control->pinned = true;
995 ++area->control->refcnt;
997}
998
999/*
1000 * Undo the effects of dsa_pin, so that the given area can be freed when no
1001 * backends are attached to it. May be called only if dsa_pin has been
1002 * called.
1003 */
1004void
1005dsa_unpin(dsa_area *area)
1006{
1008 Assert(area->control->refcnt > 1);
1009 if (!area->control->pinned)
1010 {
1012 elog(ERROR, "dsa_area not pinned");
1013 }
1014 area->control->pinned = false;
1015 --area->control->refcnt;
1017}
1018
1019/*
1020 * Set the total size limit for this area. This limit is checked whenever new
1021 * segments need to be allocated from the operating system. If the new size
1022 * limit is already exceeded, this has no immediate effect.
1023 *
1024 * Note that the total virtual memory usage may be temporarily larger than
1025 * this limit when segments have been freed, but not yet detached by all
1026 * backends that have attached to them.
1027 */
1028void
1029dsa_set_size_limit(dsa_area *area, size_t limit)
1030{
1032 area->control->max_total_segment_size = limit;
1034}
1035
1036/* Return the total size of all active segments */
1037size_t
1039{
1040 size_t size;
1041
1043 size = area->control->total_segment_size;
1045
1046 return size;
1047}
1048
1049/*
1050 * Same as dsa_get_total_size(), but accepts a DSA handle. The area must have
1051 * been created with dsa_create (not dsa_create_in_place).
1052 */
1053size_t
1055{
1056 size_t size;
1057 bool already_attached;
1058 dsm_segment *segment;
1059 dsa_area_control *control;
1060
1062 if (already_attached)
1063 segment = dsm_find_mapping(handle);
1064 else
1065 segment = dsm_attach(handle);
1066
1067 if (segment == NULL)
1068 ereport(ERROR,
1070 errmsg("could not attach to dynamic shared area")));
1071
1072 control = (dsa_area_control *) dsm_segment_address(segment);
1073
1074 LWLockAcquire(&control->lock, LW_SHARED);
1075 size = control->total_segment_size;
1076 LWLockRelease(&control->lock);
1077
1078 if (!already_attached)
1079 dsm_detach(segment);
1080
1081 return size;
1082}
1083
1084/*
1085 * Aggressively free all spare memory in the hope of returning DSM segments to
1086 * the operating system.
1087 */
1088void
1089dsa_trim(dsa_area *area)
1090{
1091 int size_class;
1092
1093 /*
1094 * Trim in reverse pool order so we get to the spans-of-spans last, just
1095 * in case any become entirely free while processing all the other pools.
1096 */
1097 for (size_class = DSA_NUM_SIZE_CLASSES - 1; size_class >= 0; --size_class)
1098 {
1099 dsa_area_pool *pool = &area->control->pools[size_class];
1101
1102 if (size_class == DSA_SCLASS_SPAN_LARGE)
1103 {
1104 /* Large object frees give back segments aggressively already. */
1105 continue;
1106 }
1107
1108 /*
1109 * Search fullness class 1 only. That is where we expect to find an
1110 * entirely empty superblock (entirely empty superblocks in other
1111 * fullness classes are returned to the free page map by dsa_free).
1112 */
1113 LWLockAcquire(DSA_SCLASS_LOCK(area, size_class), LW_EXCLUSIVE);
1114 span_pointer = pool->spans[1];
1116 {
1118 dsa_pointer next = span->nextspan;
1119
1120 if (span->nallocatable == span->nmax)
1122
1124 }
1125 LWLockRelease(DSA_SCLASS_LOCK(area, size_class));
1126 }
1127}
1128
1129/*
1130 * Print out debugging information about the internal state of the shared
1131 * memory area.
1132 */
1133void
1134dsa_dump(dsa_area *area)
1135{
1136 size_t i,
1137 j;
1138
1139 /*
1140 * Note: This gives an inconsistent snapshot as it acquires and releases
1141 * individual locks as it goes...
1142 */
1143
1146 fprintf(stderr, "dsa_area handle %x:\n", area->control->handle);
1147 fprintf(stderr, " max_total_segment_size: %zu\n",
1149 fprintf(stderr, " total_segment_size: %zu\n",
1151 fprintf(stderr, " refcnt: %d\n", area->control->refcnt);
1152 fprintf(stderr, " pinned: %c\n", area->control->pinned ? 't' : 'f');
1153 fprintf(stderr, " segment bins:\n");
1154 for (i = 0; i < DSA_NUM_SEGMENT_BINS; ++i)
1155 {
1157 {
1159
1160 if (i == 0)
1162 " segment bin %zu (no contiguous free pages):\n", i);
1163 else
1165 " segment bin %zu (at least %d contiguous pages free):\n",
1166 i, 1 << (i - 1));
1169 {
1171
1172 segment_map =
1174
1176 " segment index %zu, usable_pages = %zu, "
1177 "contiguous_pages = %zu, mapped at %p\n",
1179 segment_map->header->usable_pages,
1181 segment_map->mapped_address);
1182 segment_index = segment_map->header->next;
1183 }
1184 }
1185 }
1187
1188 fprintf(stderr, " pools:\n");
1189 for (i = 0; i < DSA_NUM_SIZE_CLASSES; ++i)
1190 {
1191 bool found = false;
1192
1194 for (j = 0; j < DSA_FULLNESS_CLASSES; ++j)
1195 if (DsaPointerIsValid(area->control->pools[i].spans[j]))
1196 found = true;
1197 if (found)
1198 {
1200 fprintf(stderr, " pool for blocks of span objects:\n");
1201 else if (i == DSA_SCLASS_SPAN_LARGE)
1202 fprintf(stderr, " pool for large object spans:\n");
1203 else
1205 " pool for size class %zu (object size %hu bytes):\n",
1206 i, dsa_size_classes[i]);
1207 for (j = 0; j < DSA_FULLNESS_CLASSES; ++j)
1208 {
1209 if (!DsaPointerIsValid(area->control->pools[i].spans[j]))
1210 fprintf(stderr, " fullness class %zu is empty\n", j);
1211 else
1212 {
1214
1215 fprintf(stderr, " fullness class %zu:\n", j);
1217 {
1219
1222 " span descriptor at "
1223 DSA_POINTER_FORMAT ", superblock at "
1225 ", pages = %zu, objects free = %hu/%hu\n",
1226 span_pointer, span->start, span->npages,
1227 span->nallocatable, span->nmax);
1228 span_pointer = span->nextspan;
1229 }
1230 }
1231 }
1232 }
1234 }
1235}
1236
1237/*
1238 * Return the smallest size that you can successfully provide to
1239 * dsa_create_in_place.
1240 */
1241size_t
1242dsa_minimum_size(void)
1243{
1244 size_t size;
1245 size_t pages = 0;
1246
1247 size = MAXALIGN(sizeof(dsa_area_control)) +
1248 MAXALIGN(sizeof(FreePageManager));
1249
1250 /* Figure out how many pages we need, including the page map... */
1251 while (((size + FPM_PAGE_SIZE - 1) / FPM_PAGE_SIZE) > pages)
1252 {
1253 ++pages;
1254 size += sizeof(dsa_pointer);
1255 }
1256
1257 return pages * FPM_PAGE_SIZE;
1258}
1259
1260/*
1261 * Workhorse function for dsa_create and dsa_create_in_place.
1262 */
1263static dsa_area *
1264create_internal(void *place, size_t size,
1265 int tranche_id,
1268 size_t init_segment_size, size_t max_segment_size)
1269{
1270 dsa_area_control *control;
1271 dsa_area *area;
1273 size_t usable_pages;
1274 size_t total_pages;
1275 size_t metadata_bytes;
1276
1277 /* Check the initial and maximum block sizes */
1278 Assert(init_segment_size >= DSA_MIN_SEGMENT_SIZE);
1279 Assert(max_segment_size >= init_segment_size);
1280 Assert(max_segment_size <= DSA_MAX_SEGMENT_SIZE);
1281
1282 /* Sanity check on the space we have to work in. */
1283 if (size < dsa_minimum_size())
1284 elog(ERROR, "dsa_area space must be at least %zu, but %zu provided",
1285 dsa_minimum_size(), size);
1286
1287 /* Now figure out how much space is usable */
1288 total_pages = size / FPM_PAGE_SIZE;
1290 MAXALIGN(sizeof(dsa_area_control)) +
1291 MAXALIGN(sizeof(FreePageManager)) +
1292 total_pages * sizeof(dsa_pointer);
1293 /* Add padding up to next page boundary. */
1294 if (metadata_bytes % FPM_PAGE_SIZE != 0)
1296 Assert(metadata_bytes <= size);
1297 usable_pages = (size - metadata_bytes) / FPM_PAGE_SIZE;
1298
1299 /*
1300 * Initialize the dsa_area_control object located at the start of the
1301 * space.
1302 */
1303 control = (dsa_area_control *) place;
1304 memset(place, 0, sizeof(*control));
1305 control->segment_header.magic =
1309 control->segment_header.usable_pages = usable_pages;
1310 control->segment_header.freed = false;
1311 control->segment_header.size = size;
1312 control->handle = control_handle;
1313 control->init_segment_size = init_segment_size;
1314 control->max_segment_size = max_segment_size;
1315 control->max_total_segment_size = (size_t) -1;
1316 control->total_segment_size = size;
1317 control->segment_handles[0] = control_handle;
1318 for (int i = 0; i < DSA_NUM_SEGMENT_BINS; ++i)
1320 control->refcnt = 1;
1321 control->lwlock_tranche_id = tranche_id;
1322
1323 /*
1324 * Create the dsa_area object that this backend will use to access the
1325 * area. Other backends will need to obtain their own dsa_area object by
1326 * attaching.
1327 */
1328 area = palloc_object(dsa_area);
1329 area->control = control;
1332 area->high_segment_index = 0;
1333 area->freed_segment_counter = 0;
1334 LWLockInitialize(&control->lock, control->lwlock_tranche_id);
1335 for (size_t i = 0; i < DSA_NUM_SIZE_CLASSES; ++i)
1337 control->lwlock_tranche_id);
1338
1339 /* Set up the segment map for this process's mapping. */
1340 segment_map = &area->segment_maps[0];
1342 segment_map->mapped_address = place;
1343 segment_map->header = (dsa_segment_header *) place;
1344 segment_map->fpm = (FreePageManager *)
1345 (segment_map->mapped_address +
1346 MAXALIGN(sizeof(dsa_area_control)));
1347 segment_map->pagemap = (dsa_pointer *)
1348 (segment_map->mapped_address +
1349 MAXALIGN(sizeof(dsa_area_control)) +
1350 MAXALIGN(sizeof(FreePageManager)));
1351
1352 /* Set up the free page map. */
1353 FreePageManagerInitialize(segment_map->fpm, segment_map->mapped_address);
1354 /* There can be 0 usable pages if size is dsa_minimum_size(). */
1355
1356 if (usable_pages > 0)
1358 usable_pages);
1359
1360 /* Put this segment into the appropriate bin. */
1361 control->segment_bins[contiguous_pages_to_segment_bin(usable_pages)] = 0;
1362 segment_map->header->bin = contiguous_pages_to_segment_bin(usable_pages);
1363
1364 return area;
1365}
1366
1367/*
1368 * Workhorse function for dsa_attach and dsa_attach_in_place.
1369 */
1370static dsa_area *
1371attach_internal(void *place, dsm_segment *segment, dsa_handle handle)
1372{
1373 dsa_area_control *control;
1374 dsa_area *area;
1376
1377 control = (dsa_area_control *) place;
1378 Assert(control->handle == handle);
1379 Assert(control->segment_handles[0] == handle);
1380 Assert(control->segment_header.magic ==
1381 (DSA_SEGMENT_HEADER_MAGIC ^ handle ^ 0));
1382
1383 /* Build the backend-local area object. */
1384 area = palloc_object(dsa_area);
1385 area->control = control;
1387 memset(&area->segment_maps[0], 0,
1389 area->high_segment_index = 0;
1390
1391 /* Set up the segment map for this process's mapping. */
1392 segment_map = &area->segment_maps[0];
1393 segment_map->segment = segment; /* NULL for in-place */
1394 segment_map->mapped_address = place;
1395 segment_map->header = (dsa_segment_header *) segment_map->mapped_address;
1396 segment_map->fpm = (FreePageManager *)
1397 (segment_map->mapped_address + MAXALIGN(sizeof(dsa_area_control)));
1398 segment_map->pagemap = (dsa_pointer *)
1399 (segment_map->mapped_address + MAXALIGN(sizeof(dsa_area_control)) +
1400 MAXALIGN(sizeof(FreePageManager)));
1401
1402 /* Bump the reference count. */
1404 if (control->refcnt == 0)
1405 {
1406 /* We can't attach to a DSA area that has already been destroyed. */
1407 ereport(ERROR,
1409 errmsg("could not attach to dynamic shared area")));
1410 }
1411 ++control->refcnt;
1414
1415 return area;
1416}
1417
1418/*
1419 * Add a new span to fullness class 1 of the indicated pool.
1420 */
1421static void
1422init_span(dsa_area *area,
1424 dsa_area_pool *pool, dsa_pointer start, size_t npages,
1425 uint16 size_class)
1426{
1428 size_t obsize = dsa_size_classes[size_class];
1429
1430 /*
1431 * The per-pool lock must be held because we manipulate the span list for
1432 * this pool.
1433 */
1434 Assert(LWLockHeldByMe(DSA_SCLASS_LOCK(area, size_class)));
1435
1436 /* Push this span onto the front of the span list for fullness class 1. */
1437 if (DsaPointerIsValid(pool->spans[1]))
1438 {
1439 dsa_area_span *head = (dsa_area_span *)
1440 dsa_get_address(area, pool->spans[1]);
1441
1442 head->prevspan = span_pointer;
1443 }
1444 span->pool = DsaAreaPoolToDsaPointer(area, pool);
1445 span->nextspan = pool->spans[1];
1446 span->prevspan = InvalidDsaPointer;
1447 pool->spans[1] = span_pointer;
1448
1449 span->start = start;
1450 span->npages = npages;
1451 span->size_class = size_class;
1452 span->ninitialized = 0;
1453 if (size_class == DSA_SCLASS_BLOCK_OF_SPANS)
1454 {
1455 /*
1456 * A block-of-spans contains its own descriptor, so mark one object as
1457 * initialized and reduce the count of allocatable objects by one.
1458 * Doing this here has the side effect of also reducing nmax by one,
1459 * which is important to make sure we free this object at the correct
1460 * time.
1461 */
1462 span->ninitialized = 1;
1463 span->nallocatable = FPM_PAGE_SIZE / obsize - 1;
1464 }
1465 else if (size_class != DSA_SCLASS_SPAN_LARGE)
1466 span->nallocatable = DSA_SUPERBLOCK_SIZE / obsize;
1467 span->firstfree = DSA_SPAN_NOTHING_FREE;
1468 span->nmax = span->nallocatable;
1469 span->fclass = 1;
1470}
1471
1472/*
1473 * Transfer the first span in one fullness class to the head of another
1474 * fullness class.
1475 */
1476static bool
1478 dsa_area_pool *pool, int fromclass, int toclass)
1479{
1482 dsa_area_span *nextspan;
1483
1484 /* Can't do it if source list is empty. */
1485 span_pointer = pool->spans[fromclass];
1487 return false;
1488
1489 /* Remove span from head of source list. */
1491 pool->spans[fromclass] = span->nextspan;
1492 if (DsaPointerIsValid(span->nextspan))
1493 {
1494 nextspan = (dsa_area_span *)
1495 dsa_get_address(area, span->nextspan);
1496 nextspan->prevspan = InvalidDsaPointer;
1497 }
1498
1499 /* Add span to head of target list. */
1500 span->nextspan = pool->spans[toclass];
1501 pool->spans[toclass] = span_pointer;
1502 if (DsaPointerIsValid(span->nextspan))
1503 {
1504 nextspan = (dsa_area_span *)
1505 dsa_get_address(area, span->nextspan);
1506 nextspan->prevspan = span_pointer;
1507 }
1508 span->fclass = toclass;
1509
1510 return true;
1511}
1512
1513/*
1514 * Allocate one object of the requested size class from the given area.
1515 */
1516static inline dsa_pointer
1517alloc_object(dsa_area *area, int size_class)
1518{
1519 dsa_area_pool *pool = &area->control->pools[size_class];
1521 dsa_pointer block;
1523 char *object;
1524 size_t size;
1525
1526 /*
1527 * Even though ensure_active_superblock can in turn call alloc_object if
1528 * it needs to allocate a new span, that's always from a different pool,
1529 * and the order of lock acquisition is always the same, so it's OK that
1530 * we hold this lock for the duration of this function.
1531 */
1532 Assert(!LWLockHeldByMe(DSA_SCLASS_LOCK(area, size_class)));
1533 LWLockAcquire(DSA_SCLASS_LOCK(area, size_class), LW_EXCLUSIVE);
1534
1535 /*
1536 * If there's no active superblock, we must successfully obtain one or
1537 * fail the request.
1538 */
1539 if (!DsaPointerIsValid(pool->spans[1]) &&
1540 !ensure_active_superblock(area, pool, size_class))
1541 {
1543 }
1544 else
1545 {
1546 /*
1547 * There should be a block in fullness class 1 at this point, and it
1548 * should never be completely full. Thus we can either pop an object
1549 * from the free list or, failing that, initialize a new object.
1550 */
1551 Assert(DsaPointerIsValid(pool->spans[1]));
1552 span = (dsa_area_span *)
1553 dsa_get_address(area, pool->spans[1]);
1554 Assert(span->nallocatable > 0);
1555 block = span->start;
1556 Assert(size_class < DSA_NUM_SIZE_CLASSES);
1557 size = dsa_size_classes[size_class];
1558 if (span->firstfree != DSA_SPAN_NOTHING_FREE)
1559 {
1560 result = block + span->firstfree * size;
1561 object = dsa_get_address(area, result);
1562 span->firstfree = NextFreeObjectIndex(object);
1563 }
1564 else
1565 {
1566 result = block + span->ninitialized * size;
1567 ++span->ninitialized;
1568 }
1569 --span->nallocatable;
1570
1571 /* If it's now full, move it to the highest-numbered fullness class. */
1572 if (span->nallocatable == 0)
1573 transfer_first_span(area, pool, 1, DSA_FULLNESS_CLASSES - 1);
1574 }
1575
1576 Assert(LWLockHeldByMe(DSA_SCLASS_LOCK(area, size_class)));
1577 LWLockRelease(DSA_SCLASS_LOCK(area, size_class));
1578
1579 return result;
1580}
1581
1582/*
1583 * Ensure an active (i.e. fullness class 1) superblock, unless all existing
1584 * superblocks are completely full and no more can be allocated.
1585 *
1586 * Fullness classes K of 0..N are loosely intended to represent blocks whose
1587 * utilization percentage is at least K/N, but we only enforce this rigorously
1588 * for the highest-numbered fullness class, which always contains exactly
1589 * those blocks that are completely full. It's otherwise acceptable for a
1590 * block to be in a higher-numbered fullness class than the one to which it
1591 * logically belongs. In addition, the active block, which is always the
1592 * first block in fullness class 1, is permitted to have a higher allocation
1593 * percentage than would normally be allowable for that fullness class; we
1594 * don't move it until it's completely full, and then it goes to the
1595 * highest-numbered fullness class.
1596 *
1597 * It might seem odd that the active block is the head of fullness class 1
1598 * rather than fullness class 0, but experience with other allocators has
1599 * shown that it's usually better to allocate from a block that's moderately
1600 * full rather than one that's nearly empty. Insofar as is reasonably
1601 * possible, we want to avoid performing new allocations in a block that would
1602 * otherwise become empty soon.
1603 */
1604static bool
1606 int size_class)
1607{
1610 size_t obsize = dsa_size_classes[size_class];
1611 size_t nmax;
1612 int fclass;
1613 size_t npages = 1;
1614 size_t first_page;
1615 size_t i;
1617
1618 Assert(LWLockHeldByMe(DSA_SCLASS_LOCK(area, size_class)));
1619
1620 /*
1621 * Compute the number of objects that will fit in a block of this size
1622 * class. Span-of-spans blocks are just a single page, and the first
1623 * object isn't available for use because it describes the block-of-spans
1624 * itself.
1625 */
1626 if (size_class == DSA_SCLASS_BLOCK_OF_SPANS)
1627 nmax = FPM_PAGE_SIZE / obsize - 1;
1628 else
1629 nmax = DSA_SUPERBLOCK_SIZE / obsize;
1630
1631 /*
1632 * If fullness class 1 is empty, try to find a span to put in it by
1633 * scanning higher-numbered fullness classes (excluding the last one,
1634 * whose blocks are certain to all be completely full).
1635 */
1636 for (fclass = 2; fclass < DSA_FULLNESS_CLASSES - 1; ++fclass)
1637 {
1638 span_pointer = pool->spans[fclass];
1639
1641 {
1642 int tfclass;
1644 dsa_area_span *nextspan;
1645 dsa_area_span *prevspan;
1647
1648 span = (dsa_area_span *)
1651
1652 /* Figure out what fullness class should contain this span. */
1653 tfclass = (nmax - span->nallocatable)
1654 * (DSA_FULLNESS_CLASSES - 1) / nmax;
1655
1656 /* Look up next span. */
1657 if (DsaPointerIsValid(span->nextspan))
1658 nextspan = (dsa_area_span *)
1659 dsa_get_address(area, span->nextspan);
1660 else
1661 nextspan = NULL;
1662
1663 /*
1664 * If utilization has dropped enough that this now belongs in some
1665 * other fullness class, move it there.
1666 */
1667 if (tfclass < fclass)
1668 {
1669 /* Remove from the current fullness class list. */
1670 if (pool->spans[fclass] == span_pointer)
1671 {
1672 /* It was the head; remove it. */
1673 Assert(!DsaPointerIsValid(span->prevspan));
1674 pool->spans[fclass] = span->nextspan;
1675 if (nextspan != NULL)
1676 nextspan->prevspan = InvalidDsaPointer;
1677 }
1678 else
1679 {
1680 /* It was not the head. */
1681 Assert(DsaPointerIsValid(span->prevspan));
1682 prevspan = (dsa_area_span *)
1683 dsa_get_address(area, span->prevspan);
1684 prevspan->nextspan = span->nextspan;
1685 }
1686 if (nextspan != NULL)
1687 nextspan->prevspan = span->prevspan;
1688
1689 /* Push onto the head of the new fullness class list. */
1690 span->nextspan = pool->spans[tfclass];
1691 pool->spans[tfclass] = span_pointer;
1692 span->prevspan = InvalidDsaPointer;
1693 if (DsaPointerIsValid(span->nextspan))
1694 {
1695 nextspan = (dsa_area_span *)
1696 dsa_get_address(area, span->nextspan);
1697 nextspan->prevspan = span_pointer;
1698 }
1699 span->fclass = tfclass;
1700 }
1701
1702 /* Advance to next span on list. */
1704 }
1705
1706 /* Stop now if we found a suitable block. */
1707 if (DsaPointerIsValid(pool->spans[1]))
1708 return true;
1709 }
1710
1711 /*
1712 * If there are no blocks that properly belong in fullness class 1, pick
1713 * one from some other fullness class and move it there anyway, so that we
1714 * have an allocation target. Our last choice is to transfer a block
1715 * that's almost empty (and might become completely empty soon if left
1716 * alone), but even that is better than failing, which is what we must do
1717 * if there are no blocks at all with freespace.
1718 */
1719 Assert(!DsaPointerIsValid(pool->spans[1]));
1720 for (fclass = 2; fclass < DSA_FULLNESS_CLASSES - 1; ++fclass)
1721 if (transfer_first_span(area, pool, fclass, 1))
1722 return true;
1723 if (!DsaPointerIsValid(pool->spans[1]) &&
1724 transfer_first_span(area, pool, 0, 1))
1725 return true;
1726
1727 /*
1728 * We failed to find an existing span with free objects, so we need to
1729 * allocate a new superblock and construct a new span to manage it.
1730 *
1731 * First, get a dsa_area_span object to describe the new superblock block
1732 * ... unless this allocation is for a dsa_area_span object, in which case
1733 * that's surely not going to work. We handle that case by storing the
1734 * span describing a block-of-spans inline.
1735 */
1736 if (size_class != DSA_SCLASS_BLOCK_OF_SPANS)
1737 {
1740 return false;
1741 npages = DSA_PAGES_PER_SUPERBLOCK;
1742 }
1743
1744 /* Find or create a segment and allocate the superblock. */
1746 segment_map = get_best_segment(area, npages);
1747 if (segment_map == NULL)
1748 {
1749 segment_map = make_new_segment(area, npages);
1750 if (segment_map == NULL)
1751 {
1753 return false;
1754 }
1755 }
1756
1757 /*
1758 * This shouldn't happen: get_best_segment() or make_new_segment()
1759 * promised that we can successfully allocate npages.
1760 */
1761 if (!FreePageManagerGet(segment_map->fpm, npages, &first_page))
1762 elog(FATAL,
1763 "dsa_allocate could not find %zu free pages for superblock",
1764 npages);
1766
1767 /* Compute the start of the superblock. */
1770 first_page * FPM_PAGE_SIZE);
1771
1772 /*
1773 * If this is a block-of-spans, carve the descriptor right out of the
1774 * allocated space.
1775 */
1776 if (size_class == DSA_SCLASS_BLOCK_OF_SPANS)
1777 {
1778 /*
1779 * We have a pointer into the segment. We need to build a dsa_pointer
1780 * from the segment index and offset into the segment.
1781 */
1783 }
1784
1785 /* Initialize span and pagemap. */
1786 init_span(area, span_pointer, pool, start_pointer, npages, size_class);
1787 for (i = 0; i < npages; ++i)
1788 segment_map->pagemap[first_page + i] = span_pointer;
1789
1790 return true;
1791}
1792
1793/*
1794 * Return the segment map corresponding to a given segment index, mapping the
1795 * segment in if necessary. For internal segment book-keeping, this is called
1796 * with the area lock held. It is also called by dsa_free and dsa_get_address
1797 * without any locking, relying on the fact they have a known live segment
1798 * index and they always call check_for_freed_segments to ensures that any
1799 * freed segment occupying the same slot is detached first.
1800 */
1801static dsa_segment_map *
1803{
1805 {
1806 dsm_handle handle;
1807 dsm_segment *segment;
1809 ResourceOwner oldowner;
1810
1811 /*
1812 * If we are reached by dsa_free or dsa_get_address, there must be at
1813 * least one object allocated in the referenced segment. Otherwise,
1814 * their caller has a double-free or access-after-free bug, which we
1815 * have no hope of detecting. So we know it's safe to access this
1816 * array slot without holding a lock; it won't change underneath us.
1817 * Furthermore, we know that we can see the latest contents of the
1818 * slot, as explained in check_for_freed_segments, which those
1819 * functions call before arriving here.
1820 */
1821 handle = area->control->segment_handles[index];
1822
1823 /* It's an error to try to access an unused slot. */
1824 if (handle == DSM_HANDLE_INVALID)
1825 elog(ERROR,
1826 "dsa_area could not attach to a segment that has been freed");
1827
1828 oldowner = CurrentResourceOwner;
1830 segment = dsm_attach(handle);
1831 CurrentResourceOwner = oldowner;
1832 if (segment == NULL)
1833 elog(ERROR, "dsa_area could not attach to segment");
1834 segment_map = &area->segment_maps[index];
1835 segment_map->segment = segment;
1837 segment_map->header =
1838 (dsa_segment_header *) segment_map->mapped_address;
1839 segment_map->fpm = (FreePageManager *)
1840 (segment_map->mapped_address +
1841 MAXALIGN(sizeof(dsa_segment_header)));
1842 segment_map->pagemap = (dsa_pointer *)
1843 (segment_map->mapped_address +
1844 MAXALIGN(sizeof(dsa_segment_header)) +
1845 MAXALIGN(sizeof(FreePageManager)));
1846
1847 /* Remember the highest index this backend has ever mapped. */
1848 if (area->high_segment_index < index)
1849 area->high_segment_index = index;
1850
1851 Assert(segment_map->header->magic ==
1853 }
1854
1855 /*
1856 * Callers of dsa_get_address() and dsa_free() don't hold the area lock,
1857 * but it's a bug in the calling code and undefined behavior if the
1858 * address is not live (ie if the segment might possibly have been freed,
1859 * they're trying to use a dangling pointer).
1860 *
1861 * For dsa.c code that holds the area lock to manipulate segment_bins
1862 * lists, it would be a bug if we ever reach a freed segment here. After
1863 * it's marked as freed, the only thing any backend should do with it is
1864 * unmap it, and it should always have done that in
1865 * check_for_freed_segments_locked() before arriving here to resolve an
1866 * index to a segment_map.
1867 *
1868 * Either way we can assert that we aren't returning a freed segment.
1869 */
1871
1872 return &area->segment_maps[index];
1873}
1874
1875/*
1876 * Return a superblock to the free page manager. If the underlying segment
1877 * has become entirely free, then return it to the operating system.
1878 *
1879 * The appropriate pool lock must be held.
1880 */
1881static void
1883{
1885 int size_class = span->size_class;
1887
1888
1889 /* Remove it from its fullness class list. */
1890 unlink_span(area, span);
1891
1892 /*
1893 * Note: Here we acquire the area lock while we already hold a per-pool
1894 * lock. We never hold the area lock and then take a pool lock, or we
1895 * could deadlock.
1896 */
1899 segment_map =
1903 span->npages);
1904 /* Check if the segment is now entirely free. */
1905 if (fpm_largest(segment_map->fpm) == segment_map->header->usable_pages)
1906 {
1908
1909 /* If it's not the segment with extra control data, free it. */
1910 if (index != 0)
1911 {
1912 /*
1913 * Give it back to the OS, and allow other backends to detect that
1914 * they need to detach.
1915 */
1917 segment_map->header->freed = true;
1919 segment_map->header->size);
1920 area->control->total_segment_size -=
1921 segment_map->header->size;
1923 dsm_detach(segment_map->segment);
1926 segment_map->segment = NULL;
1927 segment_map->header = NULL;
1928 segment_map->mapped_address = NULL;
1929 }
1930 }
1931
1932 /* Move segment to appropriate bin if necessary. */
1933 if (segment_map->header != NULL)
1935
1937
1938 /*
1939 * Span-of-spans blocks store the span which describes them within the
1940 * block itself, so freeing the storage implicitly frees the descriptor
1941 * also. If this is a block of any other type, we need to separately free
1942 * the span object also. This recursive call to dsa_free will acquire the
1943 * span pool's lock. We can't deadlock because the acquisition order is
1944 * always some other pool and then the span pool.
1945 */
1946 if (size_class != DSA_SCLASS_BLOCK_OF_SPANS)
1947 dsa_free(area, span_pointer);
1948}
1949
1950static void
1952{
1953 if (DsaPointerIsValid(span->nextspan))
1954 {
1955 dsa_area_span *next = dsa_get_address(area, span->nextspan);
1956
1957 next->prevspan = span->prevspan;
1958 }
1959 if (DsaPointerIsValid(span->prevspan))
1960 {
1961 dsa_area_span *prev = dsa_get_address(area, span->prevspan);
1962
1963 prev->nextspan = span->nextspan;
1964 }
1965 else
1966 {
1967 dsa_area_pool *pool = dsa_get_address(area, span->pool);
1968
1969 pool->spans[span->fclass] = span->nextspan;
1970 }
1971}
1972
1973static void
1976 int fclass)
1977{
1978 dsa_area_pool *pool = dsa_get_address(area, span->pool);
1979
1980 if (DsaPointerIsValid(pool->spans[fclass]))
1981 {
1982 dsa_area_span *head = dsa_get_address(area,
1983 pool->spans[fclass]);
1984
1985 head->prevspan = span_pointer;
1986 }
1987 span->prevspan = InvalidDsaPointer;
1988 span->nextspan = pool->spans[fclass];
1989 pool->spans[fclass] = span_pointer;
1990 span->fclass = fclass;
1991}
1992
1993/*
1994 * Detach from an area that was either created or attached to by this process.
1995 */
1996void
1997dsa_detach(dsa_area *area)
1998{
1999 /* Detach from all segments. */
2000 for (dsa_segment_index i = 0; i <= area->high_segment_index; ++i)
2001 if (area->segment_maps[i].segment != NULL)
2003
2004 /*
2005 * Note that 'detaching' (= detaching from DSM segments) doesn't include
2006 * 'releasing' (= adjusting the reference count). It would be nice to
2007 * combine these operations, but client code might never get around to
2008 * calling dsa_detach because of an error path, and a detach hook on any
2009 * particular segment is too late to detach other segments in the area
2010 * without risking a 'leak' warning in the non-error path.
2011 */
2012
2013 /* Free the backend-local area object. */
2014 pfree(area);
2015}
2016
2017/*
2018 * Unlink a segment from the bin that contains it.
2019 */
2020static void
2022{
2023 if (segment_map->header->prev != DSA_SEGMENT_INDEX_NONE)
2024 {
2025 dsa_segment_map *prev;
2026
2027 prev = get_segment_by_index(area, segment_map->header->prev);
2028 prev->header->next = segment_map->header->next;
2029 }
2030 else
2031 {
2032 Assert(area->control->segment_bins[segment_map->header->bin] ==
2034 area->control->segment_bins[segment_map->header->bin] =
2035 segment_map->header->next;
2036 }
2037 if (segment_map->header->next != DSA_SEGMENT_INDEX_NONE)
2038 {
2040
2041 next = get_segment_by_index(area, segment_map->header->next);
2042 next->header->prev = segment_map->header->prev;
2043 }
2044}
2045
2046/*
2047 * Find a segment that could satisfy a request for 'npages' of contiguous
2048 * memory, or return NULL if none can be found. This may involve attaching to
2049 * segments that weren't previously attached so that we can query their free
2050 * pages map.
2051 */
2052static dsa_segment_map *
2053get_best_segment(dsa_area *area, size_t npages)
2054{
2055 size_t bin;
2056
2059
2060 /*
2061 * Start searching from the first bin that *might* have enough contiguous
2062 * pages.
2063 */
2064 for (bin = contiguous_pages_to_segment_bin(npages);
2066 ++bin)
2067 {
2068 /*
2069 * The minimum contiguous size that any segment in this bin should
2070 * have. We'll re-bin if we see segments with fewer.
2071 */
2072 size_t threshold = (size_t) 1 << (bin - 1);
2074
2075 /* Search this bin for a segment with enough contiguous space. */
2076 segment_index = area->control->segment_bins[bin];
2078 {
2081 size_t contiguous_pages;
2082
2084 next_segment_index = segment_map->header->next;
2085 contiguous_pages = fpm_largest(segment_map->fpm);
2086
2087 /* Not enough for the request, still enough for this bin. */
2088 if (contiguous_pages >= threshold && contiguous_pages < npages)
2089 {
2091 continue;
2092 }
2093
2094 /* Re-bin it if it's no longer in the appropriate bin. */
2095 if (contiguous_pages < threshold)
2096 {
2098
2099 /*
2100 * But fall through to see if it's enough to satisfy this
2101 * request anyway....
2102 */
2103 }
2104
2105 /* Check if we are done. */
2106 if (contiguous_pages >= npages)
2107 return segment_map;
2108
2109 /* Continue searching the same bin. */
2111 }
2112 }
2113
2114 /* Not found. */
2115 return NULL;
2116}
2117
2118/*
2119 * Create a new segment that can handle at least requested_pages. Returns
2120 * NULL if the requested total size limit or maximum allowed number of
2121 * segments would be exceeded.
2122 */
2123static dsa_segment_map *
2125{
2126 dsa_segment_index new_index;
2127 size_t metadata_bytes;
2128 size_t total_size;
2129 size_t total_pages;
2130 size_t usable_pages;
2132 dsm_segment *segment;
2133 ResourceOwner oldowner;
2134
2136
2137 /* Find a segment slot that is not in use (linearly for now). */
2138 for (new_index = 1; new_index < DSA_MAX_SEGMENTS; ++new_index)
2139 {
2140 if (area->control->segment_handles[new_index] == DSM_HANDLE_INVALID)
2141 break;
2142 }
2143 if (new_index == DSA_MAX_SEGMENTS)
2144 return NULL;
2145
2146 /*
2147 * If the total size limit is already exceeded, then we exit early and
2148 * avoid arithmetic wraparound in the unsigned expressions below.
2149 */
2150 if (area->control->total_segment_size >=
2152 return NULL;
2153
2154 /*
2155 * The size should be at least as big as requested, and at least big
2156 * enough to follow a geometric series that approximately doubles the
2157 * total storage each time we create a new segment. We use geometric
2158 * growth because the underlying DSM system isn't designed for large
2159 * numbers of segments (otherwise we might even consider just using one
2160 * DSM segment for each large allocation and for each superblock, and then
2161 * we wouldn't need to use FreePageManager).
2162 *
2163 * We decide on a total segment size first, so that we produce tidy
2164 * power-of-two sized segments. This is a good property to have if we
2165 * move to huge pages in the future. Then we work back to the number of
2166 * pages we can fit.
2167 */
2169 ((size_t) 1 << (new_index / DSA_NUM_SEGMENTS_AT_EACH_SIZE));
2174
2177 MAXALIGN(sizeof(dsa_segment_header)) +
2178 MAXALIGN(sizeof(FreePageManager)) +
2179 sizeof(dsa_pointer) * total_pages;
2180
2181 /* Add padding up to next page boundary. */
2182 if (metadata_bytes % FPM_PAGE_SIZE != 0)
2185 return NULL;
2186 usable_pages = (total_size - metadata_bytes) / FPM_PAGE_SIZE;
2187 Assert(metadata_bytes + usable_pages * FPM_PAGE_SIZE <= total_size);
2188
2189 /* See if that is enough... */
2190 if (requested_pages > usable_pages)
2191 {
2193
2194 /*
2195 * We'll make an odd-sized segment, working forward from the requested
2196 * number of pages.
2197 */
2198 usable_pages = requested_pages;
2200 MAXALIGN(sizeof(dsa_segment_header)) +
2201 MAXALIGN(sizeof(FreePageManager)) +
2202 usable_pages * sizeof(dsa_pointer);
2203
2204 /*
2205 * We must also account for pagemap entries needed to cover the
2206 * metadata pages themselves. The pagemap must track all pages in the
2207 * segment, including the pages occupied by metadata.
2208 *
2209 * This formula uses integer ceiling division to compute the exact
2210 * number of additional entries needed. The divisor (FPM_PAGE_SIZE -
2211 * sizeof(dsa_pointer)) accounts for the fact that each metadata page
2212 * consumes one pagemap entry of sizeof(dsa_pointer) bytes, leaving
2213 * only (FPM_PAGE_SIZE - sizeof(dsa_pointer)) net bytes per metadata
2214 * page.
2215 */
2217 ((metadata_bytes + (FPM_PAGE_SIZE - sizeof(dsa_pointer)) - 1) /
2218 (FPM_PAGE_SIZE - sizeof(dsa_pointer))) *
2219 sizeof(dsa_pointer);
2220
2221 /* Add padding up to next page boundary. */
2222 if (metadata_bytes % FPM_PAGE_SIZE != 0)
2224 total_size = metadata_bytes + usable_pages * FPM_PAGE_SIZE;
2226
2227 /*
2228 * Verify that we allocated enough pagemap entries for metadata and
2229 * usable pages. This reverse-engineers the new calculation of
2230 * "metadata_bytes" done based on the new "requested_pages" for an
2231 * odd-sized segment.
2232 */
2235
2236 /* Is that too large for dsa_pointer's addressing scheme? */
2238 return NULL;
2239
2240 /* Would that exceed the limit? */
2243 return NULL;
2244 }
2245
2246 /* Create the segment. */
2247 oldowner = CurrentResourceOwner;
2249 segment = dsm_create(total_size, 0);
2250 CurrentResourceOwner = oldowner;
2251 if (segment == NULL)
2252 return NULL;
2253 dsm_pin_segment(segment);
2254
2255 /* Store the handle in shared memory to be found by index. */
2256 area->control->segment_handles[new_index] =
2257 dsm_segment_handle(segment);
2258 /* Track the highest segment index in the history of the area. */
2259 if (area->control->high_segment_index < new_index)
2260 area->control->high_segment_index = new_index;
2261 /* Track the highest segment index this backend has ever mapped. */
2262 if (area->high_segment_index < new_index)
2263 area->high_segment_index = new_index;
2264 /* Track total size of all segments. */
2268
2269 /* Build a segment map for this segment in this backend. */
2270 segment_map = &area->segment_maps[new_index];
2271 segment_map->segment = segment;
2273 segment_map->header = (dsa_segment_header *) segment_map->mapped_address;
2274 segment_map->fpm = (FreePageManager *)
2275 (segment_map->mapped_address +
2276 MAXALIGN(sizeof(dsa_segment_header)));
2277 segment_map->pagemap = (dsa_pointer *)
2278 (segment_map->mapped_address +
2279 MAXALIGN(sizeof(dsa_segment_header)) +
2280 MAXALIGN(sizeof(FreePageManager)));
2281
2282 /* Set up the free page map. */
2283 FreePageManagerInitialize(segment_map->fpm, segment_map->mapped_address);
2285 usable_pages);
2286
2287 /* Set up the segment header and put it in the appropriate bin. */
2288 segment_map->header->magic =
2289 DSA_SEGMENT_HEADER_MAGIC ^ area->control->handle ^ new_index;
2290 segment_map->header->usable_pages = usable_pages;
2291 segment_map->header->size = total_size;
2292 segment_map->header->bin = contiguous_pages_to_segment_bin(usable_pages);
2293 segment_map->header->prev = DSA_SEGMENT_INDEX_NONE;
2294 segment_map->header->next =
2295 area->control->segment_bins[segment_map->header->bin];
2296 segment_map->header->freed = false;
2297 area->control->segment_bins[segment_map->header->bin] = new_index;
2298 if (segment_map->header->next != DSA_SEGMENT_INDEX_NONE)
2299 {
2301 get_segment_by_index(area, segment_map->header->next);
2302
2303 Assert(next->header->bin == segment_map->header->bin);
2304 next->header->prev = new_index;
2305 }
2306
2307 return segment_map;
2308}
2309
2310/*
2311 * Check if any segments have been freed by destroy_superblock, so we can
2312 * detach from them in this backend. This function is called by
2313 * dsa_get_address and dsa_free to make sure that a dsa_pointer they have
2314 * received can be resolved to the correct segment.
2315 *
2316 * The danger we want to defend against is that there could be an old segment
2317 * mapped into a given slot in this backend, and the dsa_pointer they have
2318 * might refer to some new segment in the same slot. So those functions must
2319 * be sure to process all instructions to detach from a freed segment that had
2320 * been generated by the time this process received the dsa_pointer, before
2321 * they call get_segment_by_index.
2322 */
2323static void
2325{
2326 size_t freed_segment_counter;
2327
2328 /*
2329 * Any other process that has freed a segment has incremented
2330 * freed_segment_counter while holding an LWLock, and that must precede
2331 * any backend creating a new segment in the same slot while holding an
2332 * LWLock, and that must precede the creation of any dsa_pointer pointing
2333 * into the new segment which might reach us here, and the caller must
2334 * have sent the dsa_pointer to this process using appropriate memory
2335 * synchronization (some kind of locking or atomic primitive or system
2336 * call). So all we need to do on the reading side is ask for the load of
2337 * freed_segment_counter to follow the caller's load of the dsa_pointer it
2338 * has, and we can be sure to detect any segments that had been freed as
2339 * of the time that the dsa_pointer reached this process.
2340 */
2342 freed_segment_counter = area->control->freed_segment_counter;
2343 if (unlikely(area->freed_segment_counter != freed_segment_counter))
2344 {
2345 /* Check all currently mapped segments to find what's been freed. */
2349 }
2350}
2351
2352/*
2353 * Workhorse for check_for_freed_segments(), and also used directly in path
2354 * where the area lock is already held. This should be called after acquiring
2355 * the lock but before looking up any segment by index number, to make sure we
2356 * unmap any stale segments that might have previously had the same index as a
2357 * current segment.
2358 */
2359static void
2361{
2362 size_t freed_segment_counter;
2363
2365 freed_segment_counter = area->control->freed_segment_counter;
2366 if (unlikely(area->freed_segment_counter != freed_segment_counter))
2367 {
2368 for (dsa_segment_index i = 0; i <= area->high_segment_index; ++i)
2369 {
2370 if (area->segment_maps[i].header != NULL &&
2371 area->segment_maps[i].header->freed)
2372 {
2374 area->segment_maps[i].segment = NULL;
2375 area->segment_maps[i].header = NULL;
2377 }
2378 }
2379 area->freed_segment_counter = freed_segment_counter;
2380 }
2381}
2382
2383/*
2384 * Re-bin segment if it's no longer in the appropriate bin.
2385 */
2386static void
2388{
2389 size_t new_bin;
2391
2393 if (segment_map->header->bin == new_bin)
2394 return;
2395
2396 /* Remove it from its current bin. */
2398
2399 /* Push it onto the front of its new bin. */
2401 segment_map->header->prev = DSA_SEGMENT_INDEX_NONE;
2402 segment_map->header->next = area->control->segment_bins[new_bin];
2403 segment_map->header->bin = new_bin;
2405 if (segment_map->header->next != DSA_SEGMENT_INDEX_NONE)
2406 {
2408
2409 next = get_segment_by_index(area, segment_map->header->next);
2410 Assert(next->header->bin == new_bin);
2411 next->header->prev = segment_index;
2412 }
2413}
#define pg_read_barrier()
Definition atomics.h:154
static int32 next
Definition blutils.c:225
#define Min(x, y)
Definition c.h:1150
#define MAXALIGN(LEN)
Definition c.h:955
uint8_t uint8
Definition c.h:681
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:308
#define Assert(condition)
Definition c.h:1002
uint16_t uint16
Definition c.h:682
#define unlikely(x)
Definition c.h:497
uint32_t uint32
Definition c.h:683
#define lengthof(array)
Definition c.h:932
uint32 result
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
static void unlink_segment(dsa_area *area, dsa_segment_map *segment_map)
Definition dsa.c:2022
static void check_for_freed_segments(dsa_area *area)
Definition dsa.c:2325
static const uint16 dsa_size_classes[]
Definition dsa.c:225
#define DSA_EXTRACT_SEGMENT_NUMBER(dp)
Definition dsa.c:96
#define DSA_AREA_LOCK(area)
Definition dsa.c:132
#define DSA_NUM_SEGMENTS_AT_EACH_SIZE
Definition dsa.c:69
static void add_span_to_fullness_class(dsa_area *area, dsa_area_span *span, dsa_pointer span_pointer, int fclass)
Definition dsa.c:1975
static bool ensure_active_superblock(dsa_area *area, dsa_area_pool *pool, int size_class)
Definition dsa.c:1606
#define DSA_SEGMENT_INDEX_NONE
Definition dsa.c:105
static dsa_area * create_internal(void *place, size_t size, int tranche_id, dsm_handle control_handle, dsm_segment *control_segment, size_t init_segment_size, size_t max_segment_size)
Definition dsa.c:1265
dsa_area * dsa_attach(dsa_handle handle)
Definition dsa.c:510
#define DSA_SEGMENT_HEADER_MAGIC
Definition dsa.c:89
void dsa_trim(dsa_area *area)
Definition dsa.c:1090
#define DSA_SPAN_NOTHING_FREE
Definition dsa.c:375
#define DSA_MAKE_POINTER(segment_number, offset)
Definition dsa.c:92
dsa_area * dsa_create_in_place_ext(void *place, size_t size, int tranche_id, dsm_segment *segment, size_t init_segment_size, size_t max_segment_size)
Definition dsa.c:471
#define get_segment_index(area, segment_map_ptr)
Definition dsa.c:379
dsa_area * dsa_attach_in_place(void *place, dsm_segment *segment)
Definition dsa.c:560
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition dsa.c:954
void dsa_on_shmem_exit_release_in_place(int code, Datum place)
Definition dsa.c:605
void dsa_on_dsm_detach_release_in_place(dsm_segment *segment, Datum place)
Definition dsa.c:591
static dsa_pointer alloc_object(dsa_area *area, int size_class)
Definition dsa.c:1518
#define DSA_PAGES_PER_SUPERBLOCK
Definition dsa.c:82
#define DSA_SIZE_CLASS_MAP_QUANTUM
Definition dsa.c:258
static size_t contiguous_pages_to_segment_bin(size_t n)
Definition dsa.c:119
static const uint8 dsa_size_class_map[]
Definition dsa.c:248
dsa_pointer dsa_allocate_extended(dsa_area *area, size_t size, int flags)
Definition dsa.c:683
static dsa_segment_map * make_new_segment(dsa_area *area, size_t requested_pages)
Definition dsa.c:2125
size_t dsa_get_total_size(dsa_area *area)
Definition dsa.c:1039
#define DSA_SUPERBLOCK_SIZE
Definition dsa.c:376
#define DsaAreaPoolToDsaPointer(area, p)
Definition dsa.c:322
size_t dsa_get_total_size_from_handle(dsa_handle handle)
Definition dsa.c:1055
static void check_for_freed_segments_locked(dsa_area *area)
Definition dsa.c:2361
#define DSA_EXTRACT_OFFSET(dp)
Definition dsa.c:99
dsa_area * dsa_create_ext(int tranche_id, size_t init_segment_size, size_t max_segment_size)
Definition dsa.c:421
static void destroy_superblock(dsa_area *area, dsa_pointer span_pointer)
Definition dsa.c:1883
#define DSA_MAX_SEGMENTS
Definition dsa.c:75
size_t dsa_segment_index
Definition dsa.c:102
static void rebin_segment(dsa_area *area, dsa_segment_map *segment_map)
Definition dsa.c:2388
#define DSA_SCLASS_LOCK(area, sclass)
Definition dsa.c:133
void dsa_release_in_place(void *place)
Definition dsa.c:620
static dsa_segment_map * get_segment_by_index(dsa_area *area, dsa_segment_index index)
Definition dsa.c:1803
void dsa_set_size_limit(dsa_area *area, size_t limit)
Definition dsa.c:1030
#define DSA_SCLASS_BLOCK_OF_SPANS
Definition dsa.c:239
static bool transfer_first_span(dsa_area *area, dsa_area_pool *pool, int fromclass, int toclass)
Definition dsa.c:1478
static void unlink_span(dsa_area *area, dsa_area_span *span)
Definition dsa.c:1952
#define DSA_SCLASS_SPAN_LARGE
Definition dsa.c:240
#define DSA_NUM_SIZE_CLASSES
Definition dsa.c:236
void dsa_unpin(dsa_area *area)
Definition dsa.c:1006
void dsa_pin_mapping(dsa_area *area)
Definition dsa.c:649
static dsa_area * attach_internal(void *place, dsm_segment *segment, dsa_handle handle)
Definition dsa.c:1372
#define NextFreeObjectIndex(object)
Definition dsa.c:202
void dsa_dump(dsa_area *area)
Definition dsa.c:1135
dsa_handle dsa_get_handle(dsa_area *area)
Definition dsa.c:498
static void init_span(dsa_area *area, dsa_pointer span_pointer, dsa_area_pool *pool, dsa_pointer start, size_t npages, uint16 size_class)
Definition dsa.c:1423
bool dsa_is_attached(dsa_handle handle)
Definition dsa.c:540
void dsa_detach(dsa_area *area)
Definition dsa.c:1998
static dsa_segment_map * get_best_segment(dsa_area *area, size_t npages)
Definition dsa.c:2054
#define DSA_FULLNESS_CLASSES
Definition dsa.c:266
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition dsa.c:838
#define DSA_NUM_SEGMENT_BINS
Definition dsa.c:111
size_t dsa_minimum_size(void)
Definition dsa.c:1243
void dsa_pin(dsa_area *area)
Definition dsa.c:987
uint64 dsa_pointer
Definition dsa.h:62
#define DSA_POINTER_FORMAT
Definition dsa.h:69
#define DSA_MIN_SEGMENT_SIZE
Definition dsa.h:100
dsm_handle dsa_handle
Definition dsa.h:136
#define InvalidDsaPointer
Definition dsa.h:78
#define DSA_ALLOC_NO_OOM
Definition dsa.h:74
#define DSA_HANDLE_INVALID
Definition dsa.h:139
#define DsaPointerIsValid(x)
Definition dsa.h:106
#define DSA_MAX_SEGMENT_SIZE
Definition dsa.h:103
#define DSA_ALLOC_HUGE
Definition dsa.h:73
#define DSA_ALLOC_ZERO
Definition dsa.h:75
dsm_handle dsm_segment_handle(dsm_segment *seg)
Definition dsm.c:1131
void dsm_detach(dsm_segment *seg)
Definition dsm.c:811
void on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function, Datum arg)
Definition dsm.c:1140
void dsm_pin_mapping(dsm_segment *seg)
Definition dsm.c:923
void dsm_unpin_segment(dsm_handle handle)
Definition dsm.c:996
void dsm_pin_segment(dsm_segment *seg)
Definition dsm.c:963
void * dsm_segment_address(dsm_segment *seg)
Definition dsm.c:1103
dsm_segment * dsm_create(Size size, int flags)
Definition dsm.c:524
dsm_segment * dsm_attach(dsm_handle h)
Definition dsm.c:673
dsm_segment * dsm_find_mapping(dsm_handle handle)
Definition dsm.c:1084
uint32 dsm_handle
Definition dsm_impl.h:55
#define DSM_HANDLE_INVALID
Definition dsm_impl.h:58
int errcode(int sqlerrcode)
Definition elog.c:875
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define FATAL
Definition elog.h:42
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
#define palloc_object(type)
Definition fe_memutils.h:89
bool FreePageManagerGet(FreePageManager *fpm, Size npages, Size *first_page)
Definition freepage.c:210
void FreePageManagerPut(FreePageManager *fpm, Size first_page, Size npages)
Definition freepage.c:379
void FreePageManagerInitialize(FreePageManager *fpm, char *base)
Definition freepage.c:183
#define fpm_largest(fpm)
Definition freepage.h:88
#define fpm_size_to_pages(sz)
Definition freepage.h:74
#define FPM_PAGE_SIZE
Definition freepage.h:30
return str start
int j
Definition isn.c:78
int i
Definition isn.c:77
bool LWLockHeldByMe(LWLock *lock)
Definition lwlock.c:1885
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1150
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1767
void LWLockInitialize(LWLock *lock, int tranche_id)
Definition lwlock.c:670
@ LW_SHARED
Definition lwlock.h:105
@ LW_EXCLUSIVE
Definition lwlock.h:104
void pfree(void *pointer)
Definition mcxt.c:1619
#define AllocHugeSizeIsValid(size)
Definition memutils.h:49
#define AllocSizeIsValid(size)
Definition memutils.h:42
static char * errmsg
#define pg_leftmost_one_pos_size_t
static int64 total_size
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
#define PointerGetDatum(X)
Definition postgres.h:354
static int fb(int x)
#define min(a, b)
Definition private.h:155
#define max(a, b)
Definition private.h:154
ResourceOwner CurrentResourceOwner
Definition resowner.c:173
dsa_segment_header segment_header
Definition dsa.c:290
size_t init_segment_size
Definition dsa.c:300
size_t total_segment_size
Definition dsa.c:304
int lwlock_tranche_id
Definition dsa.c:316
size_t max_segment_size
Definition dsa.c:302
dsa_segment_index high_segment_index
Definition dsa.c:308
bool pinned
Definition dsa.c:312
size_t max_total_segment_size
Definition dsa.c:306
dsa_segment_index segment_bins[DSA_NUM_SEGMENT_BINS]
Definition dsa.c:296
dsa_area_pool pools[DSA_NUM_SIZE_CLASSES]
Definition dsa.c:298
size_t freed_segment_counter
Definition dsa.c:314
LWLock lock
Definition dsa.c:318
dsa_handle handle
Definition dsa.c:292
dsm_handle segment_handles[DSA_MAX_SEGMENTS]
Definition dsa.c:294
dsa_pointer spans[DSA_FULLNESS_CLASSES]
Definition dsa.c:279
dsa_pointer nextspan
Definition dsa.c:187
dsa_pointer prevspan
Definition dsa.c:186
dsa_segment_map segment_maps[DSA_MAX_SEGMENTS]
Definition dsa.c:366
dsa_segment_index high_segment_index
Definition dsa.c:369
size_t freed_segment_counter
Definition dsa.c:372
dsa_area_control * control
Definition dsa.c:350
ResourceOwner resowner
Definition dsa.c:358
uint32 magic
Definition dsa.c:143
size_t size
Definition dsa.c:147
dsa_segment_index next
Definition dsa.c:159
dsa_segment_index prev
Definition dsa.c:153
size_t usable_pages
Definition dsa.c:145
dsa_segment_header * header
Definition dsa.c:336
dsm_segment * segment
Definition dsa.c:334
char * mapped_address
Definition dsa.c:335
void * mapped_address
Definition dsm.c:74
Definition type.h:97

◆ DSA_MAX_SEGMENTS

#define DSA_MAX_SEGMENTS    Min(1024, (1 << ((SIZEOF_DSA_POINTER * 8) - DSA_OFFSET_WIDTH)))

Definition at line 75 of file dsa.c.

◆ DSA_NUM_SEGMENT_BINS

#define DSA_NUM_SEGMENT_BINS   16

Definition at line 111 of file dsa.c.

◆ DSA_NUM_SEGMENTS_AT_EACH_SIZE

#define DSA_NUM_SEGMENTS_AT_EACH_SIZE   2

Definition at line 69 of file dsa.c.

◆ DSA_NUM_SIZE_CLASSES

#define DSA_NUM_SIZE_CLASSES   lengthof(dsa_size_classes)

Definition at line 236 of file dsa.c.

◆ DSA_OFFSET_BITMASK

#define DSA_OFFSET_BITMASK   (((dsa_pointer) 1 << DSA_OFFSET_WIDTH) - 1)

Definition at line 79 of file dsa.c.

◆ DSA_PAGES_PER_SUPERBLOCK

#define DSA_PAGES_PER_SUPERBLOCK   16

Definition at line 82 of file dsa.c.

◆ DSA_SCLASS_BLOCK_OF_SPANS

#define DSA_SCLASS_BLOCK_OF_SPANS   0

Definition at line 239 of file dsa.c.

◆ DSA_SCLASS_LOCK

#define DSA_SCLASS_LOCK (   area,
  sclass 
)    (&area->control->pools[sclass].lock)

Definition at line 133 of file dsa.c.

◆ DSA_SCLASS_SPAN_LARGE

#define DSA_SCLASS_SPAN_LARGE   1

Definition at line 240 of file dsa.c.

◆ DSA_SEGMENT_HEADER_MAGIC

#define DSA_SEGMENT_HEADER_MAGIC   0x0ce26608

Definition at line 89 of file dsa.c.

◆ DSA_SEGMENT_INDEX_NONE

#define DSA_SEGMENT_INDEX_NONE   (~(dsa_segment_index)0)

Definition at line 105 of file dsa.c.

◆ DSA_SIZE_CLASS_MAP_QUANTUM

#define DSA_SIZE_CLASS_MAP_QUANTUM   8

Definition at line 258 of file dsa.c.

◆ DSA_SPAN_NOTHING_FREE

#define DSA_SPAN_NOTHING_FREE   ((uint16) -1)

Definition at line 375 of file dsa.c.

◆ DSA_SUPERBLOCK_SIZE

#define DSA_SUPERBLOCK_SIZE   (DSA_PAGES_PER_SUPERBLOCK * FPM_PAGE_SIZE)

Definition at line 376 of file dsa.c.

◆ DsaAreaPoolToDsaPointer

#define DsaAreaPoolToDsaPointer (   area,
 
)     DSA_MAKE_POINTER(0, (char *) p - (char *) area->control)

Definition at line 322 of file dsa.c.

◆ get_segment_index

#define get_segment_index (   area,
  segment_map_ptr 
)     (segment_map_ptr - &area->segment_maps[0])

Definition at line 379 of file dsa.c.

◆ NextFreeObjectIndex

#define NextFreeObjectIndex (   object)    (* (uint16 *) (object))

Definition at line 202 of file dsa.c.

Typedef Documentation

◆ dsa_segment_index

Definition at line 102 of file dsa.c.

Function Documentation

◆ add_span_to_fullness_class()

static void add_span_to_fullness_class ( dsa_area area,
dsa_area_span span,
dsa_pointer  span_pointer,
int  fclass 
)
static

Definition at line 1975 of file dsa.c.

1978{
1979 dsa_area_pool *pool = dsa_get_address(area, span->pool);
1980
1981 if (DsaPointerIsValid(pool->spans[fclass]))
1982 {
1983 dsa_area_span *head = dsa_get_address(area,
1984 pool->spans[fclass]);
1985
1986 head->prevspan = span_pointer;
1987 }
1988 span->prevspan = InvalidDsaPointer;
1989 span->nextspan = pool->spans[fclass];
1990 pool->spans[fclass] = span_pointer;
1991 span->fclass = fclass;
1992}

References dsa_get_address(), DsaPointerIsValid, fb(), InvalidDsaPointer, dsa_area_span::prevspan, and dsa_area_pool::spans.

Referenced by dsa_free().

◆ alloc_object()

static dsa_pointer alloc_object ( dsa_area area,
int  size_class 
)
inlinestatic

Definition at line 1518 of file dsa.c.

1519{
1520 dsa_area_pool *pool = &area->control->pools[size_class];
1522 dsa_pointer block;
1524 char *object;
1525 size_t size;
1526
1527 /*
1528 * Even though ensure_active_superblock can in turn call alloc_object if
1529 * it needs to allocate a new span, that's always from a different pool,
1530 * and the order of lock acquisition is always the same, so it's OK that
1531 * we hold this lock for the duration of this function.
1532 */
1533 Assert(!LWLockHeldByMe(DSA_SCLASS_LOCK(area, size_class)));
1534 LWLockAcquire(DSA_SCLASS_LOCK(area, size_class), LW_EXCLUSIVE);
1535
1536 /*
1537 * If there's no active superblock, we must successfully obtain one or
1538 * fail the request.
1539 */
1540 if (!DsaPointerIsValid(pool->spans[1]) &&
1541 !ensure_active_superblock(area, pool, size_class))
1542 {
1544 }
1545 else
1546 {
1547 /*
1548 * There should be a block in fullness class 1 at this point, and it
1549 * should never be completely full. Thus we can either pop an object
1550 * from the free list or, failing that, initialize a new object.
1551 */
1552 Assert(DsaPointerIsValid(pool->spans[1]));
1553 span = (dsa_area_span *)
1554 dsa_get_address(area, pool->spans[1]);
1555 Assert(span->nallocatable > 0);
1556 block = span->start;
1557 Assert(size_class < DSA_NUM_SIZE_CLASSES);
1558 size = dsa_size_classes[size_class];
1559 if (span->firstfree != DSA_SPAN_NOTHING_FREE)
1560 {
1561 result = block + span->firstfree * size;
1562 object = dsa_get_address(area, result);
1563 span->firstfree = NextFreeObjectIndex(object);
1564 }
1565 else
1566 {
1567 result = block + span->ninitialized * size;
1568 ++span->ninitialized;
1569 }
1570 --span->nallocatable;
1571
1572 /* If it's now full, move it to the highest-numbered fullness class. */
1573 if (span->nallocatable == 0)
1574 transfer_first_span(area, pool, 1, DSA_FULLNESS_CLASSES - 1);
1575 }
1576
1577 Assert(LWLockHeldByMe(DSA_SCLASS_LOCK(area, size_class)));
1578 LWLockRelease(DSA_SCLASS_LOCK(area, size_class));
1579
1580 return result;
1581}

References Assert, dsa_area::control, DSA_FULLNESS_CLASSES, dsa_get_address(), DSA_NUM_SIZE_CLASSES, DSA_SCLASS_LOCK, dsa_size_classes, DSA_SPAN_NOTHING_FREE, DsaPointerIsValid, ensure_active_superblock(), fb(), InvalidDsaPointer, LW_EXCLUSIVE, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), NextFreeObjectIndex, dsa_area_control::pools, result, dsa_area_pool::spans, and transfer_first_span().

Referenced by dsa_allocate_extended(), and ensure_active_superblock().

◆ attach_internal()

static dsa_area * attach_internal ( void place,
dsm_segment segment,
dsa_handle  handle 
)
static

Definition at line 1372 of file dsa.c.

1373{
1374 dsa_area_control *control;
1375 dsa_area *area;
1377
1378 control = (dsa_area_control *) place;
1379 Assert(control->handle == handle);
1380 Assert(control->segment_handles[0] == handle);
1381 Assert(control->segment_header.magic ==
1382 (DSA_SEGMENT_HEADER_MAGIC ^ handle ^ 0));
1383
1384 /* Build the backend-local area object. */
1385 area = palloc_object(dsa_area);
1386 area->control = control;
1388 memset(&area->segment_maps[0], 0,
1390 area->high_segment_index = 0;
1391
1392 /* Set up the segment map for this process's mapping. */
1393 segment_map = &area->segment_maps[0];
1394 segment_map->segment = segment; /* NULL for in-place */
1395 segment_map->mapped_address = place;
1396 segment_map->header = (dsa_segment_header *) segment_map->mapped_address;
1397 segment_map->fpm = (FreePageManager *)
1398 (segment_map->mapped_address + MAXALIGN(sizeof(dsa_area_control)));
1399 segment_map->pagemap = (dsa_pointer *)
1400 (segment_map->mapped_address + MAXALIGN(sizeof(dsa_area_control)) +
1401 MAXALIGN(sizeof(FreePageManager)));
1402
1403 /* Bump the reference count. */
1405 if (control->refcnt == 0)
1406 {
1407 /* We can't attach to a DSA area that has already been destroyed. */
1408 ereport(ERROR,
1410 errmsg("could not attach to dynamic shared area")));
1411 }
1412 ++control->refcnt;
1413 area->freed_segment_counter = area->control->freed_segment_counter;
1415
1416 return area;
1417}

References Assert, dsa_area::control, CurrentResourceOwner, DSA_AREA_LOCK, DSA_MAX_SEGMENTS, DSA_SEGMENT_HEADER_MAGIC, ereport, errcode(), errmsg, ERROR, fb(), dsa_area_control::freed_segment_counter, dsa_area::freed_segment_counter, dsa_area_control::handle, dsa_area::high_segment_index, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), dsa_segment_header::magic, dsm_segment::mapped_address, MAXALIGN, palloc_object, dsa_area_control::refcnt, dsa_area::resowner, dsa_segment_map::segment, dsa_area_control::segment_handles, dsa_area_control::segment_header, and dsa_area::segment_maps.

Referenced by dsa_attach(), and dsa_attach_in_place().

◆ check_for_freed_segments()

static void check_for_freed_segments ( dsa_area area)
static

Definition at line 2325 of file dsa.c.

2326{
2327 size_t freed_segment_counter;
2328
2329 /*
2330 * Any other process that has freed a segment has incremented
2331 * freed_segment_counter while holding an LWLock, and that must precede
2332 * any backend creating a new segment in the same slot while holding an
2333 * LWLock, and that must precede the creation of any dsa_pointer pointing
2334 * into the new segment which might reach us here, and the caller must
2335 * have sent the dsa_pointer to this process using appropriate memory
2336 * synchronization (some kind of locking or atomic primitive or system
2337 * call). So all we need to do on the reading side is ask for the load of
2338 * freed_segment_counter to follow the caller's load of the dsa_pointer it
2339 * has, and we can be sure to detect any segments that had been freed as
2340 * of the time that the dsa_pointer reached this process.
2341 */
2343 freed_segment_counter = area->control->freed_segment_counter;
2344 if (unlikely(area->freed_segment_counter != freed_segment_counter))
2345 {
2346 /* Check all currently mapped segments to find what's been freed. */
2350 }
2351}

References check_for_freed_segments_locked(), dsa_area::control, DSA_AREA_LOCK, dsa_area_control::freed_segment_counter, dsa_area::freed_segment_counter, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), pg_read_barrier, and unlikely.

Referenced by dsa_free(), and dsa_get_address().

◆ check_for_freed_segments_locked()

static void check_for_freed_segments_locked ( dsa_area area)
static

Definition at line 2361 of file dsa.c.

2362{
2363 size_t freed_segment_counter;
2364
2366 freed_segment_counter = area->control->freed_segment_counter;
2367 if (unlikely(area->freed_segment_counter != freed_segment_counter))
2368 {
2369 for (dsa_segment_index i = 0; i <= area->high_segment_index; ++i)
2370 {
2371 if (area->segment_maps[i].header != NULL &&
2372 area->segment_maps[i].header->freed)
2373 {
2375 area->segment_maps[i].segment = NULL;
2376 area->segment_maps[i].header = NULL;
2378 }
2379 }
2380 area->freed_segment_counter = freed_segment_counter;
2381 }
2382}

References Assert, dsa_area::control, DSA_AREA_LOCK, dsm_detach(), fb(), dsa_segment_header::freed, dsa_area_control::freed_segment_counter, dsa_area::freed_segment_counter, dsa_segment_map::header, dsa_area::high_segment_index, i, LWLockHeldByMe(), dsa_segment_map::mapped_address, dsa_segment_map::segment, dsa_area::segment_maps, and unlikely.

Referenced by check_for_freed_segments(), destroy_superblock(), dsa_dump(), and get_best_segment().

◆ contiguous_pages_to_segment_bin()

static size_t contiguous_pages_to_segment_bin ( size_t  n)
inlinestatic

Definition at line 119 of file dsa.c.

120{
121 size_t bin;
122
123 if (n == 0)
124 bin = 0;
125 else
126 bin = pg_leftmost_one_pos_size_t(n) + 1;
127
128 return Min(bin, DSA_NUM_SEGMENT_BINS - 1);
129}

References DSA_NUM_SEGMENT_BINS, Min, and pg_leftmost_one_pos_size_t.

Referenced by create_internal(), get_best_segment(), make_new_segment(), and rebin_segment().

◆ create_internal()

static dsa_area * create_internal ( void place,
size_t  size,
int  tranche_id,
dsm_handle  control_handle,
dsm_segment control_segment,
size_t  init_segment_size,
size_t  max_segment_size 
)
static

Definition at line 1265 of file dsa.c.

1270{
1271 dsa_area_control *control;
1272 dsa_area *area;
1274 size_t usable_pages;
1275 size_t total_pages;
1276 size_t metadata_bytes;
1277
1278 /* Check the initial and maximum block sizes */
1279 Assert(init_segment_size >= DSA_MIN_SEGMENT_SIZE);
1280 Assert(max_segment_size >= init_segment_size);
1281 Assert(max_segment_size <= DSA_MAX_SEGMENT_SIZE);
1282
1283 /* Sanity check on the space we have to work in. */
1284 if (size < dsa_minimum_size())
1285 elog(ERROR, "dsa_area space must be at least %zu, but %zu provided",
1286 dsa_minimum_size(), size);
1287
1288 /* Now figure out how much space is usable */
1289 total_pages = size / FPM_PAGE_SIZE;
1291 MAXALIGN(sizeof(dsa_area_control)) +
1292 MAXALIGN(sizeof(FreePageManager)) +
1293 total_pages * sizeof(dsa_pointer);
1294 /* Add padding up to next page boundary. */
1295 if (metadata_bytes % FPM_PAGE_SIZE != 0)
1297 Assert(metadata_bytes <= size);
1298 usable_pages = (size - metadata_bytes) / FPM_PAGE_SIZE;
1299
1300 /*
1301 * Initialize the dsa_area_control object located at the start of the
1302 * space.
1303 */
1304 control = (dsa_area_control *) place;
1305 memset(place, 0, sizeof(*control));
1306 control->segment_header.magic =
1310 control->segment_header.usable_pages = usable_pages;
1311 control->segment_header.freed = false;
1312 control->segment_header.size = size;
1313 control->handle = control_handle;
1314 control->init_segment_size = init_segment_size;
1315 control->max_segment_size = max_segment_size;
1316 control->max_total_segment_size = (size_t) -1;
1317 control->total_segment_size = size;
1318 control->segment_handles[0] = control_handle;
1319 for (int i = 0; i < DSA_NUM_SEGMENT_BINS; ++i)
1321 control->refcnt = 1;
1322 control->lwlock_tranche_id = tranche_id;
1323
1324 /*
1325 * Create the dsa_area object that this backend will use to access the
1326 * area. Other backends will need to obtain their own dsa_area object by
1327 * attaching.
1328 */
1329 area = palloc_object(dsa_area);
1330 area->control = control;
1333 area->high_segment_index = 0;
1334 area->freed_segment_counter = 0;
1335 LWLockInitialize(&control->lock, control->lwlock_tranche_id);
1336 for (size_t i = 0; i < DSA_NUM_SIZE_CLASSES; ++i)
1338 control->lwlock_tranche_id);
1339
1340 /* Set up the segment map for this process's mapping. */
1341 segment_map = &area->segment_maps[0];
1343 segment_map->mapped_address = place;
1344 segment_map->header = (dsa_segment_header *) place;
1345 segment_map->fpm = (FreePageManager *)
1346 (segment_map->mapped_address +
1347 MAXALIGN(sizeof(dsa_area_control)));
1348 segment_map->pagemap = (dsa_pointer *)
1349 (segment_map->mapped_address +
1350 MAXALIGN(sizeof(dsa_area_control)) +
1351 MAXALIGN(sizeof(FreePageManager)));
1352
1353 /* Set up the free page map. */
1354 FreePageManagerInitialize(segment_map->fpm, segment_map->mapped_address);
1355 /* There can be 0 usable pages if size is dsa_minimum_size(). */
1356
1357 if (usable_pages > 0)
1359 usable_pages);
1360
1361 /* Put this segment into the appropriate bin. */
1362 control->segment_bins[contiguous_pages_to_segment_bin(usable_pages)] = 0;
1363 segment_map->header->bin = contiguous_pages_to_segment_bin(usable_pages);
1364
1365 return area;
1366}

References Assert, contiguous_pages_to_segment_bin(), dsa_area::control, CurrentResourceOwner, DSA_MAX_SEGMENT_SIZE, DSA_MAX_SEGMENTS, DSA_MIN_SEGMENT_SIZE, dsa_minimum_size(), DSA_NUM_SEGMENT_BINS, DSA_NUM_SIZE_CLASSES, DSA_SCLASS_LOCK, DSA_SEGMENT_HEADER_MAGIC, DSA_SEGMENT_INDEX_NONE, elog, ERROR, fb(), FPM_PAGE_SIZE, dsa_segment_header::freed, dsa_area::freed_segment_counter, FreePageManagerInitialize(), FreePageManagerPut(), dsa_area_control::handle, dsa_area::high_segment_index, i, dsa_area_control::init_segment_size, dsa_area_control::lock, dsa_area_control::lwlock_tranche_id, LWLockInitialize(), dsa_segment_header::magic, dsm_segment::mapped_address, dsa_area_control::max_segment_size, dsa_area_control::max_total_segment_size, MAXALIGN, dsa_segment_header::next, palloc_object, dsa_segment_header::prev, dsa_area_control::refcnt, dsa_area::resowner, dsa_segment_map::segment, dsa_area_control::segment_bins, dsa_area_control::segment_handles, dsa_area_control::segment_header, dsa_area::segment_maps, dsa_segment_header::size, dsa_area_control::total_segment_size, and dsa_segment_header::usable_pages.

Referenced by dsa_create_ext(), and dsa_create_in_place_ext().

◆ destroy_superblock()

static void destroy_superblock ( dsa_area area,
dsa_pointer  span_pointer 
)
static

Definition at line 1883 of file dsa.c.

1884{
1886 int size_class = span->size_class;
1888
1889
1890 /* Remove it from its fullness class list. */
1891 unlink_span(area, span);
1892
1893 /*
1894 * Note: Here we acquire the area lock while we already hold a per-pool
1895 * lock. We never hold the area lock and then take a pool lock, or we
1896 * could deadlock.
1897 */
1900 segment_map =
1904 span->npages);
1905 /* Check if the segment is now entirely free. */
1906 if (fpm_largest(segment_map->fpm) == segment_map->header->usable_pages)
1907 {
1909
1910 /* If it's not the segment with extra control data, free it. */
1911 if (index != 0)
1912 {
1913 /*
1914 * Give it back to the OS, and allow other backends to detect that
1915 * they need to detach.
1916 */
1918 segment_map->header->freed = true;
1920 segment_map->header->size);
1921 area->control->total_segment_size -=
1922 segment_map->header->size;
1924 dsm_detach(segment_map->segment);
1927 segment_map->segment = NULL;
1928 segment_map->header = NULL;
1929 segment_map->mapped_address = NULL;
1930 }
1931 }
1932
1933 /* Move segment to appropriate bin if necessary. */
1934 if (segment_map->header != NULL)
1936
1938
1939 /*
1940 * Span-of-spans blocks store the span which describes them within the
1941 * block itself, so freeing the storage implicitly frees the descriptor
1942 * also. If this is a block of any other type, we need to separately free
1943 * the span object also. This recursive call to dsa_free will acquire the
1944 * span pool's lock. We can't deadlock because the acquisition order is
1945 * always some other pool and then the span pool.
1946 */
1947 if (size_class != DSA_SCLASS_BLOCK_OF_SPANS)
1948 dsa_free(area, span_pointer);
1949}

References Assert, check_for_freed_segments_locked(), dsa_area::control, DSA_AREA_LOCK, DSA_EXTRACT_OFFSET, DSA_EXTRACT_SEGMENT_NUMBER, dsa_free(), dsa_get_address(), DSA_SCLASS_BLOCK_OF_SPANS, dsm_detach(), DSM_HANDLE_INVALID, dsm_segment_handle(), dsm_unpin_segment(), fb(), fpm_largest, FPM_PAGE_SIZE, dsa_area_control::freed_segment_counter, FreePageManagerPut(), get_segment_by_index(), get_segment_index, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), rebin_segment(), dsa_area_control::segment_handles, dsa_area_control::total_segment_size, unlink_segment(), and unlink_span().

Referenced by dsa_free(), and dsa_trim().

◆ dsa_allocate_extended()

dsa_pointer dsa_allocate_extended ( dsa_area area,
size_t  size,
int  flags 
)

Definition at line 683 of file dsa.c.

684{
685 uint16 size_class;
689
690 Assert(size > 0);
691
692 /* Sanity check on huge individual allocation size. */
693 if (((flags & DSA_ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) ||
694 ((flags & DSA_ALLOC_HUGE) == 0 && !AllocSizeIsValid(size)))
695 elog(ERROR, "invalid DSA memory alloc request size %zu", size);
696
697 /*
698 * If bigger than the largest size class, just grab a run of pages from
699 * the free page manager, instead of allocating an object from a pool.
700 * There will still be a span, but it's a special class of span that
701 * manages this whole allocation and simply gives all pages back to the
702 * free page manager when dsa_free is called.
703 */
705 {
706 size_t npages = fpm_size_to_pages(size);
707 size_t first_page;
710
711 /* Obtain a span object. */
714 {
715 /* Raise error unless asked not to. */
716 if ((flags & DSA_ALLOC_NO_OOM) == 0)
719 errmsg("out of memory"),
720 errdetail("Failed on DSA request of size %zu.",
721 size)));
722 return InvalidDsaPointer;
723 }
724
726
727 /* Find a segment from which to allocate. */
728 segment_map = get_best_segment(area, npages);
729 if (segment_map == NULL)
730 segment_map = make_new_segment(area, npages);
731 if (segment_map == NULL)
732 {
733 /* Can't make any more segments: game over. */
735 dsa_free(area, span_pointer);
736
737 /* Raise error unless asked not to. */
738 if ((flags & DSA_ALLOC_NO_OOM) == 0)
741 errmsg("out of memory"),
742 errdetail("Failed on DSA request of size %zu.",
743 size)));
744 return InvalidDsaPointer;
745 }
746
747 /*
748 * Ask the free page manager for a run of pages. This should always
749 * succeed, since both get_best_segment and make_new_segment should
750 * only return a non-NULL pointer if it actually contains enough
751 * contiguous freespace. If it does fail, something in our backend
752 * private state is out of whack, so use FATAL to kill the process.
753 */
754 if (!FreePageManagerGet(segment_map->fpm, npages, &first_page))
755 elog(FATAL,
756 "dsa_allocate could not find %zu free pages", npages);
758
760 first_page * FPM_PAGE_SIZE);
761
762 /* Initialize span and pagemap. */
765 init_span(area, span_pointer, pool, start_pointer, npages,
767 segment_map->pagemap[first_page] = span_pointer;
769
770 /* Zero-initialize the memory if requested. */
771 if ((flags & DSA_ALLOC_ZERO) != 0)
772 memset(dsa_get_address(area, start_pointer), 0, size);
773
774 return start_pointer;
775 }
776
777 /* Map allocation to a size class. */
779 {
780 int mapidx;
781
782 /* For smaller sizes we have a lookup table... */
783 mapidx = ((size + DSA_SIZE_CLASS_MAP_QUANTUM - 1) /
785 size_class = dsa_size_class_map[mapidx];
786 }
787 else
788 {
789 uint16 min;
790 uint16 max;
791
792 /* ... and for the rest we search by binary chop. */
795
796 while (min < max)
797 {
798 uint16 mid = (min + max) / 2;
800
801 if (class_size < size)
802 min = mid + 1;
803 else
804 max = mid;
805 }
806
807 size_class = min;
808 }
809 Assert(size <= dsa_size_classes[size_class]);
810 Assert(size_class == 0 || size > dsa_size_classes[size_class - 1]);
811
812 /* Attempt to allocate an object from the appropriate pool. */
813 result = alloc_object(area, size_class);
814
815 /* Check for failure to allocate. */
817 {
818 /* Raise error unless asked not to. */
819 if ((flags & DSA_ALLOC_NO_OOM) == 0)
822 errmsg("out of memory"),
823 errdetail("Failed on DSA request of size %zu.", size)));
824 return InvalidDsaPointer;
825 }
826
827 /* Zero-initialize the memory if requested. */
828 if ((flags & DSA_ALLOC_ZERO) != 0)
829 memset(dsa_get_address(area, result), 0, size);
830
831 return result;
832}

References alloc_object(), AllocHugeSizeIsValid, AllocSizeIsValid, Assert, dsa_area::control, DSA_ALLOC_HUGE, DSA_ALLOC_NO_OOM, DSA_ALLOC_ZERO, DSA_AREA_LOCK, dsa_free(), dsa_get_address(), DSA_MAKE_POINTER, DSA_SCLASS_BLOCK_OF_SPANS, DSA_SCLASS_LOCK, DSA_SCLASS_SPAN_LARGE, dsa_size_class_map, DSA_SIZE_CLASS_MAP_QUANTUM, dsa_size_classes, DsaPointerIsValid, elog, ereport, errcode(), errdetail(), errmsg, ERROR, FATAL, fb(), FPM_PAGE_SIZE, fpm_size_to_pages, FreePageManagerGet(), get_best_segment(), get_segment_index, init_span(), InvalidDsaPointer, lengthof, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), make_new_segment(), max, min, dsa_area_control::pools, and result.

Referenced by dshash_create(), insert_into_bucket(), pagetable_allocate(), pgstat_init_entry(), and resize().

◆ dsa_attach()

dsa_area * dsa_attach ( dsa_handle  handle)

Definition at line 510 of file dsa.c.

511{
512 dsm_segment *segment;
513 dsa_area *area;
514
515 /*
516 * An area handle is really a DSM segment handle for the first segment, so
517 * we go ahead and attach to that.
518 */
519 segment = dsm_attach(handle);
520 if (segment == NULL)
523 errmsg("could not attach to dynamic shared area")));
524
525 area = attach_internal(dsm_segment_address(segment), segment, handle);
526
527 /* Clean up when the control segment detaches. */
530
531 return area;
532}

References attach_internal(), dsa_on_dsm_detach_release_in_place(), dsm_attach(), dsm_segment_address(), ereport, errcode(), errmsg, ERROR, fb(), on_dsm_detach(), and PointerGetDatum.

Referenced by GetNamedDSA(), GetNamedDSHash(), init_dsm_registry(), initGlobalChannelTable(), logicalrep_launcher_attach_dshmem(), pgsa_attach(), and TidStoreAttach().

◆ dsa_attach_in_place()

dsa_area * dsa_attach_in_place ( void place,
dsm_segment segment 
)

Definition at line 560 of file dsa.c.

561{
562 dsa_area *area;
563
565
566 /*
567 * Clean up when the control segment detaches, if a containing DSM segment
568 * was provided.
569 */
570 if (segment != NULL)
572 PointerGetDatum(place));
573
574 return area;
575}

References attach_internal(), DSA_HANDLE_INVALID, dsa_on_dsm_detach_release_in_place(), fb(), on_dsm_detach(), and PointerGetDatum.

Referenced by AttachSession(), ParallelQueryMain(), and pgstat_attach_shmem().

◆ dsa_create_ext()

dsa_area * dsa_create_ext ( int  tranche_id,
size_t  init_segment_size,
size_t  max_segment_size 
)

Definition at line 421 of file dsa.c.

422{
423 dsm_segment *segment;
424 dsa_area *area;
425
426 /*
427 * Create the DSM segment that will hold the shared control object and the
428 * first segment of usable space.
429 */
430 segment = dsm_create(init_segment_size, 0);
431
432 /*
433 * All segments backing this area are pinned, so that DSA can explicitly
434 * control their lifetime (otherwise a newly created segment belonging to
435 * this area might be freed when the only backend that happens to have it
436 * mapped in ends, corrupting the area).
437 */
438 dsm_pin_segment(segment);
439
440 /* Create a new DSA area with the control object in this segment. */
441 area = create_internal(dsm_segment_address(segment),
442 init_segment_size,
443 tranche_id,
444 dsm_segment_handle(segment), segment,
445 init_segment_size, max_segment_size);
446
447 /* Clean up when the control segment detaches. */
450
451 return area;
452}

References create_internal(), dsa_on_dsm_detach_release_in_place(), dsm_create(), dsm_pin_segment(), dsm_segment_address(), dsm_segment_handle(), on_dsm_detach(), and PointerGetDatum.

Referenced by TidStoreCreateShared().

◆ dsa_create_in_place_ext()

dsa_area * dsa_create_in_place_ext ( void place,
size_t  size,
int  tranche_id,
dsm_segment segment,
size_t  init_segment_size,
size_t  max_segment_size 
)

Definition at line 471 of file dsa.c.

474{
475 dsa_area *area;
476
477 area = create_internal(place, size, tranche_id,
479 init_segment_size, max_segment_size);
480
481 /*
482 * Clean up when the control segment detaches, if a containing DSM segment
483 * was provided.
484 */
485 if (segment != NULL)
487 PointerGetDatum(place));
488
489 return area;
490}

References create_internal(), dsa_on_dsm_detach_release_in_place(), DSM_HANDLE_INVALID, fb(), on_dsm_detach(), and PointerGetDatum.

◆ dsa_detach()

void dsa_detach ( dsa_area area)

Definition at line 1998 of file dsa.c.

1999{
2000 /* Detach from all segments. */
2001 for (dsa_segment_index i = 0; i <= area->high_segment_index; ++i)
2002 if (area->segment_maps[i].segment != NULL)
2004
2005 /*
2006 * Note that 'detaching' (= detaching from DSM segments) doesn't include
2007 * 'releasing' (= adjusting the reference count). It would be nice to
2008 * combine these operations, but client code might never get around to
2009 * calling dsa_detach because of an error path, and a detach hook on any
2010 * particular segment is too late to detach other segments in the area
2011 * without risking a 'leak' warning in the non-error path.
2012 */
2013
2014 /* Free the backend-local area object. */
2015 pfree(area);
2016}

References dsm_detach(), fb(), dsa_area::high_segment_index, i, pfree(), dsa_segment_map::segment, and dsa_area::segment_maps.

Referenced by DetachSession(), ExecParallelCleanup(), ParallelQueryMain(), pgstat_detach_shmem(), StatsShmemInit(), test_basic(), test_dsa_allocate(), test_dsa_basic(), test_dsa_resowners(), test_empty(), test_random(), TidStoreDestroy(), and TidStoreDetach().

◆ dsa_dump()

void dsa_dump ( dsa_area area)

Definition at line 1135 of file dsa.c.

1136{
1137 size_t i,
1138 j;
1139
1140 /*
1141 * Note: This gives an inconsistent snapshot as it acquires and releases
1142 * individual locks as it goes...
1143 */
1144
1147 fprintf(stderr, "dsa_area handle %x:\n", area->control->handle);
1148 fprintf(stderr, " max_total_segment_size: %zu\n",
1150 fprintf(stderr, " total_segment_size: %zu\n",
1152 fprintf(stderr, " refcnt: %d\n", area->control->refcnt);
1153 fprintf(stderr, " pinned: %c\n", area->control->pinned ? 't' : 'f');
1154 fprintf(stderr, " segment bins:\n");
1155 for (i = 0; i < DSA_NUM_SEGMENT_BINS; ++i)
1156 {
1158 {
1160
1161 if (i == 0)
1163 " segment bin %zu (no contiguous free pages):\n", i);
1164 else
1166 " segment bin %zu (at least %d contiguous pages free):\n",
1167 i, 1 << (i - 1));
1170 {
1172
1173 segment_map =
1175
1177 " segment index %zu, usable_pages = %zu, "
1178 "contiguous_pages = %zu, mapped at %p\n",
1180 segment_map->header->usable_pages,
1182 segment_map->mapped_address);
1183 segment_index = segment_map->header->next;
1184 }
1185 }
1186 }
1188
1189 fprintf(stderr, " pools:\n");
1190 for (i = 0; i < DSA_NUM_SIZE_CLASSES; ++i)
1191 {
1192 bool found = false;
1193
1195 for (j = 0; j < DSA_FULLNESS_CLASSES; ++j)
1196 if (DsaPointerIsValid(area->control->pools[i].spans[j]))
1197 found = true;
1198 if (found)
1199 {
1201 fprintf(stderr, " pool for blocks of span objects:\n");
1202 else if (i == DSA_SCLASS_SPAN_LARGE)
1203 fprintf(stderr, " pool for large object spans:\n");
1204 else
1206 " pool for size class %zu (object size %hu bytes):\n",
1207 i, dsa_size_classes[i]);
1208 for (j = 0; j < DSA_FULLNESS_CLASSES; ++j)
1209 {
1210 if (!DsaPointerIsValid(area->control->pools[i].spans[j]))
1211 fprintf(stderr, " fullness class %zu is empty\n", j);
1212 else
1213 {
1215
1216 fprintf(stderr, " fullness class %zu:\n", j);
1218 {
1220
1223 " span descriptor at "
1224 DSA_POINTER_FORMAT ", superblock at "
1226 ", pages = %zu, objects free = %hu/%hu\n",
1227 span_pointer, span->start, span->npages,
1228 span->nallocatable, span->nmax);
1229 span_pointer = span->nextspan;
1230 }
1231 }
1232 }
1233 }
1235 }
1236}

References check_for_freed_segments_locked(), dsa_area::control, DSA_AREA_LOCK, DSA_FULLNESS_CLASSES, dsa_get_address(), DSA_NUM_SEGMENT_BINS, DSA_NUM_SIZE_CLASSES, DSA_POINTER_FORMAT, DSA_SCLASS_BLOCK_OF_SPANS, DSA_SCLASS_LOCK, DSA_SCLASS_SPAN_LARGE, DSA_SEGMENT_INDEX_NONE, dsa_size_classes, DsaPointerIsValid, fb(), fpm_largest, fprintf, get_segment_by_index(), dsa_area_control::handle, i, j, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), dsa_area_control::max_total_segment_size, dsa_area_control::pinned, dsa_area_control::pools, dsa_area_control::refcnt, dsa_area_control::segment_bins, dsa_area_pool::spans, and dsa_area_control::total_segment_size.

◆ dsa_free()

void dsa_free ( dsa_area area,
dsa_pointer  dp 
)

Definition at line 838 of file dsa.c.

839{
841 int pageno;
844 char *superblock;
845 char *object;
846 size_t size;
847 int size_class;
848
849 /* Make sure we don't have a stale segment in the slot 'dp' refers to. */
851
852 /* Locate the object, span and pool. */
855 span_pointer = segment_map->pagemap[pageno];
857 superblock = dsa_get_address(area, span->start);
858 object = dsa_get_address(area, dp);
859 size_class = span->size_class;
860 size = dsa_size_classes[size_class];
861
862 /*
863 * Special case for large objects that live in a special span: we return
864 * those pages directly to the free page manager and free the span.
865 */
866 if (span->size_class == DSA_SCLASS_SPAN_LARGE)
867 {
868
869#ifdef CLOBBER_FREED_MEMORY
870 memset(object, 0x7f, span->npages * FPM_PAGE_SIZE);
871#endif
872
873 /* Give pages back to free page manager. */
877 span->npages);
878
879 /* Move segment to appropriate bin if necessary. */
882
883 /* Unlink span. */
886 unlink_span(area, span);
888 /* Free the span object so it can be reused. */
889 dsa_free(area, span_pointer);
890 return;
891 }
892
893#ifdef CLOBBER_FREED_MEMORY
894 memset(object, 0x7f, size);
895#endif
896
897 LWLockAcquire(DSA_SCLASS_LOCK(area, size_class), LW_EXCLUSIVE);
898
899 /* Put the object on the span's freelist. */
900 Assert(object >= superblock);
902 Assert((object - superblock) % size == 0);
903 NextFreeObjectIndex(object) = span->firstfree;
904 span->firstfree = (object - superblock) / size;
905 ++span->nallocatable;
906
907 /*
908 * See if the span needs to moved to a different fullness class, or be
909 * freed so its pages can be given back to the segment.
910 */
911 if (span->nallocatable == 1 && span->fclass == DSA_FULLNESS_CLASSES - 1)
912 {
913 /*
914 * The block was completely full and is located in the
915 * highest-numbered fullness class, which is never scanned for free
916 * chunks. We must move it to the next-lower fullness class.
917 */
918 unlink_span(area, span);
921
922 /*
923 * If this is the only span, and there is no active span, then we
924 * should probably move this span to fullness class 1. (Otherwise if
925 * you allocate exactly all the objects in the only span, it moves to
926 * class 3, then you free them all, it moves to 2, and then is given
927 * back, leaving no active span).
928 */
929 }
930 else if (span->nallocatable == span->nmax &&
931 (span->fclass != 1 || span->prevspan != InvalidDsaPointer))
932 {
933 /*
934 * This entire block is free, and it's not the active block for this
935 * size class. Return the memory to the free page manager. We don't
936 * do this for the active block to prevent hysteresis: if we
937 * repeatedly allocate and free the only chunk in the active block, it
938 * will be very inefficient if we deallocate and reallocate the block
939 * every time.
940 */
942 }
943
944 LWLockRelease(DSA_SCLASS_LOCK(area, size_class));
945}

References add_span_to_fullness_class(), Assert, check_for_freed_segments(), destroy_superblock(), DSA_AREA_LOCK, DSA_EXTRACT_OFFSET, DSA_EXTRACT_SEGMENT_NUMBER, dsa_free(), DSA_FULLNESS_CLASSES, dsa_get_address(), DSA_SCLASS_LOCK, DSA_SCLASS_SPAN_LARGE, dsa_size_classes, DSA_SUPERBLOCK_SIZE, fb(), FPM_PAGE_SIZE, FreePageManagerPut(), get_segment_by_index(), InvalidDsaPointer, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), NextFreeObjectIndex, rebin_segment(), and unlink_span().

Referenced by CleanupListenersOnExit(), delete_item_from_bucket(), delete_key_from_bucket(), destroy_superblock(), dsa_allocate_extended(), dsa_free(), dshash_create(), dshash_destroy(), ExecHashTableDetach(), ExecHashTableDetachBatch(), ExecParallelCleanup(), ExecParallelHashIncreaseNumBatches(), ExecParallelHashIncreaseNumBuckets(), ExecParallelHashRepartitionFirst(), ExecParallelReinitialize(), find_or_make_matching_shared_tupledesc(), pagetable_free(), pgsa_clear_advice_string(), pgsa_drop_stash(), pgsa_reset_all_stashes(), pgsa_set_advice_string(), pgstat_free_entry(), PrepareTableEntriesForListen(), RemoveListenerFromChannel(), resize(), RT_FREE(), RT_FREE_LEAF(), RT_FREE_NODE(), set_val_in_hash(), tbm_free_shared_area(), test_dsa_allocate(), test_dsa_basic(), and test_dsa_resowners().

◆ dsa_get_address()

void * dsa_get_address ( dsa_area area,
dsa_pointer  dp 
)

Definition at line 954 of file dsa.c.

955{
957 size_t offset;
958
959 /* Convert InvalidDsaPointer to NULL. */
960 if (!DsaPointerIsValid(dp))
961 return NULL;
962
963 /* Process any requests to detach from freed segments. */
965
966 /* Break the dsa_pointer into its components. */
968 offset = DSA_EXTRACT_OFFSET(dp);
970
971 /* Check if we need to cause this segment to be mapped in. */
973 {
974 /* Call for effect (we don't need the result). */
976 }
977
978 return area->segment_maps[index].mapped_address + offset;
979}

References Assert, check_for_freed_segments(), DSA_EXTRACT_OFFSET, DSA_EXTRACT_SEGMENT_NUMBER, DSA_MAX_SEGMENTS, DsaPointerIsValid, fb(), get_segment_by_index(), dsa_segment_map::mapped_address, dsa_area::segment_maps, and unlikely.

Referenced by add_span_to_fullness_class(), alloc_object(), ApplyPendingListenActions(), CleanupListenersOnExit(), delete_item_from_bucket(), delete_key_from_bucket(), destroy_superblock(), dsa_allocate_extended(), dsa_dump(), dsa_free(), dsa_trim(), dshash_attach(), dshash_create(), dshash_destroy(), dshash_dump(), dshash_seq_next(), ensure_active_superblock(), ensure_valid_bucket_pointers(), ExecHashTableDetachBatch(), ExecParallelHashEnsureBatchAccessors(), ExecParallelHashFirstTuple(), ExecParallelHashIncreaseNumBatches(), ExecParallelHashIncreaseNumBuckets(), ExecParallelHashJoinSetUpBatches(), ExecParallelHashNextTuple(), ExecParallelHashPopChunkQueue(), ExecParallelHashRepartitionRest(), ExecParallelHashTableAlloc(), ExecParallelHashTableSetCurrentBatch(), ExecParallelHashTupleAlloc(), find_in_bucket(), find_or_make_matching_shared_tupledesc(), get_val_in_hash(), init_span(), insert_into_bucket(), insert_item_into_bucket(), lookup_rowtype_tupdesc_internal(), pagetable_allocate(), ParallelQueryMain(), pg_get_advice_stash_contents(), pgsa_advisor(), pgsa_set_advice_string(), pgsa_write_entries(), pgstat_build_snapshot(), pgstat_get_entry_ref(), pgstat_init_entry(), pgstat_reinit_entry(), pgstat_reset_matching_entries(), pgstat_write_statsfile(), PrepareTableEntriesForListen(), resize(), RT_PTR_SET_LOCAL(), SerializeParamExecParams(), set_val_in_hash(), share_tupledesc(), shared_record_table_compare(), shared_record_table_hash(), SignalBackends(), tbm_attach_shared_iterate(), tbm_free_shared_area(), tbm_prepare_shared_iterate(), test_custom_stats_var_create(), test_custom_stats_var_from_serialized_data(), test_custom_stats_var_report(), test_custom_stats_var_to_serialized_data(), test_dsa_basic(), test_dsa_resowners(), transfer_first_span(), and unlink_span().

◆ dsa_get_handle()

◆ dsa_get_total_size()

size_t dsa_get_total_size ( dsa_area area)

Definition at line 1039 of file dsa.c.

1040{
1041 size_t size;
1042
1044 size = area->control->total_segment_size;
1046
1047 return size;
1048}

References dsa_area::control, DSA_AREA_LOCK, LW_SHARED, LWLockAcquire(), LWLockRelease(), and dsa_area_control::total_segment_size.

Referenced by RT_MEMORY_USAGE().

◆ dsa_get_total_size_from_handle()

size_t dsa_get_total_size_from_handle ( dsa_handle  handle)

Definition at line 1055 of file dsa.c.

1056{
1057 size_t size;
1058 bool already_attached;
1059 dsm_segment *segment;
1060 dsa_area_control *control;
1061
1063 if (already_attached)
1064 segment = dsm_find_mapping(handle);
1065 else
1066 segment = dsm_attach(handle);
1067
1068 if (segment == NULL)
1069 ereport(ERROR,
1071 errmsg("could not attach to dynamic shared area")));
1072
1073 control = (dsa_area_control *) dsm_segment_address(segment);
1074
1075 LWLockAcquire(&control->lock, LW_SHARED);
1076 size = control->total_segment_size;
1077 LWLockRelease(&control->lock);
1078
1079 if (!already_attached)
1080 dsm_detach(segment);
1081
1082 return size;
1083}

References dsa_is_attached(), dsm_attach(), dsm_detach(), dsm_find_mapping(), dsm_segment_address(), ereport, errcode(), errmsg, ERROR, fb(), dsa_area_control::lock, LW_SHARED, LWLockAcquire(), LWLockRelease(), and dsa_area_control::total_segment_size.

Referenced by pg_get_dsm_registry_allocations().

◆ dsa_is_attached()

bool dsa_is_attached ( dsa_handle  handle)

Definition at line 540 of file dsa.c.

541{
542 /*
543 * An area handle is really a DSM segment handle for the first segment, so
544 * we can just search for that.
545 */
546 return dsm_find_mapping(handle) != NULL;
547}

References dsm_find_mapping(), and fb().

Referenced by dsa_get_total_size_from_handle(), GetNamedDSA(), and GetNamedDSHash().

◆ dsa_minimum_size()

size_t dsa_minimum_size ( void  )

Definition at line 1243 of file dsa.c.

1244{
1245 size_t size;
1246 size_t pages = 0;
1247
1248 size = MAXALIGN(sizeof(dsa_area_control)) +
1249 MAXALIGN(sizeof(FreePageManager));
1250
1251 /* Figure out how many pages we need, including the page map... */
1252 while (((size + FPM_PAGE_SIZE - 1) / FPM_PAGE_SIZE) > pages)
1253 {
1254 ++pages;
1255 size += sizeof(dsa_pointer);
1256 }
1257
1258 return pages * FPM_PAGE_SIZE;
1259}

References FPM_PAGE_SIZE, and MAXALIGN.

Referenced by create_internal(), ExecInitParallelPlan(), and pgstat_dsa_init_size().

◆ dsa_on_dsm_detach_release_in_place()

void dsa_on_dsm_detach_release_in_place ( dsm_segment segment,
Datum  place 
)

◆ dsa_on_shmem_exit_release_in_place()

void dsa_on_shmem_exit_release_in_place ( int  code,
Datum  place 
)

Definition at line 605 of file dsa.c.

606{
608}

References DatumGetPointer(), and dsa_release_in_place().

◆ dsa_pin()

◆ dsa_pin_mapping()

◆ dsa_release_in_place()

◆ dsa_set_size_limit()

void dsa_set_size_limit ( dsa_area area,
size_t  limit 
)

◆ dsa_trim()

void dsa_trim ( dsa_area area)

Definition at line 1090 of file dsa.c.

1091{
1092 int size_class;
1093
1094 /*
1095 * Trim in reverse pool order so we get to the spans-of-spans last, just
1096 * in case any become entirely free while processing all the other pools.
1097 */
1098 for (size_class = DSA_NUM_SIZE_CLASSES - 1; size_class >= 0; --size_class)
1099 {
1100 dsa_area_pool *pool = &area->control->pools[size_class];
1102
1103 if (size_class == DSA_SCLASS_SPAN_LARGE)
1104 {
1105 /* Large object frees give back segments aggressively already. */
1106 continue;
1107 }
1108
1109 /*
1110 * Search fullness class 1 only. That is where we expect to find an
1111 * entirely empty superblock (entirely empty superblocks in other
1112 * fullness classes are returned to the free page map by dsa_free).
1113 */
1114 LWLockAcquire(DSA_SCLASS_LOCK(area, size_class), LW_EXCLUSIVE);
1115 span_pointer = pool->spans[1];
1117 {
1119 dsa_pointer next = span->nextspan;
1120
1121 if (span->nallocatable == span->nmax)
1123
1125 }
1126 LWLockRelease(DSA_SCLASS_LOCK(area, size_class));
1127 }
1128}

References dsa_area::control, destroy_superblock(), dsa_get_address(), DSA_NUM_SIZE_CLASSES, DSA_SCLASS_LOCK, DSA_SCLASS_SPAN_LARGE, DsaPointerIsValid, fb(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), next, dsa_area_control::pools, and dsa_area_pool::spans.

◆ dsa_unpin()

void dsa_unpin ( dsa_area area)

Definition at line 1006 of file dsa.c.

1007{
1009 Assert(area->control->refcnt > 1);
1010 if (!area->control->pinned)
1011 {
1013 elog(ERROR, "dsa_area not pinned");
1014 }
1015 area->control->pinned = false;
1016 --area->control->refcnt;
1018}

References Assert, dsa_area::control, DSA_AREA_LOCK, elog, ERROR, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), dsa_area_control::pinned, and dsa_area_control::refcnt.

◆ ensure_active_superblock()

static bool ensure_active_superblock ( dsa_area area,
dsa_area_pool pool,
int  size_class 
)
static

Definition at line 1606 of file dsa.c.

1608{
1611 size_t obsize = dsa_size_classes[size_class];
1612 size_t nmax;
1613 int fclass;
1614 size_t npages = 1;
1615 size_t first_page;
1616 size_t i;
1618
1619 Assert(LWLockHeldByMe(DSA_SCLASS_LOCK(area, size_class)));
1620
1621 /*
1622 * Compute the number of objects that will fit in a block of this size
1623 * class. Span-of-spans blocks are just a single page, and the first
1624 * object isn't available for use because it describes the block-of-spans
1625 * itself.
1626 */
1627 if (size_class == DSA_SCLASS_BLOCK_OF_SPANS)
1628 nmax = FPM_PAGE_SIZE / obsize - 1;
1629 else
1630 nmax = DSA_SUPERBLOCK_SIZE / obsize;
1631
1632 /*
1633 * If fullness class 1 is empty, try to find a span to put in it by
1634 * scanning higher-numbered fullness classes (excluding the last one,
1635 * whose blocks are certain to all be completely full).
1636 */
1637 for (fclass = 2; fclass < DSA_FULLNESS_CLASSES - 1; ++fclass)
1638 {
1639 span_pointer = pool->spans[fclass];
1640
1642 {
1643 int tfclass;
1645 dsa_area_span *nextspan;
1646 dsa_area_span *prevspan;
1648
1649 span = (dsa_area_span *)
1652
1653 /* Figure out what fullness class should contain this span. */
1654 tfclass = (nmax - span->nallocatable)
1655 * (DSA_FULLNESS_CLASSES - 1) / nmax;
1656
1657 /* Look up next span. */
1658 if (DsaPointerIsValid(span->nextspan))
1659 nextspan = (dsa_area_span *)
1660 dsa_get_address(area, span->nextspan);
1661 else
1662 nextspan = NULL;
1663
1664 /*
1665 * If utilization has dropped enough that this now belongs in some
1666 * other fullness class, move it there.
1667 */
1668 if (tfclass < fclass)
1669 {
1670 /* Remove from the current fullness class list. */
1671 if (pool->spans[fclass] == span_pointer)
1672 {
1673 /* It was the head; remove it. */
1674 Assert(!DsaPointerIsValid(span->prevspan));
1675 pool->spans[fclass] = span->nextspan;
1676 if (nextspan != NULL)
1677 nextspan->prevspan = InvalidDsaPointer;
1678 }
1679 else
1680 {
1681 /* It was not the head. */
1682 Assert(DsaPointerIsValid(span->prevspan));
1683 prevspan = (dsa_area_span *)
1684 dsa_get_address(area, span->prevspan);
1685 prevspan->nextspan = span->nextspan;
1686 }
1687 if (nextspan != NULL)
1688 nextspan->prevspan = span->prevspan;
1689
1690 /* Push onto the head of the new fullness class list. */
1691 span->nextspan = pool->spans[tfclass];
1692 pool->spans[tfclass] = span_pointer;
1693 span->prevspan = InvalidDsaPointer;
1694 if (DsaPointerIsValid(span->nextspan))
1695 {
1696 nextspan = (dsa_area_span *)
1697 dsa_get_address(area, span->nextspan);
1698 nextspan->prevspan = span_pointer;
1699 }
1700 span->fclass = tfclass;
1701 }
1702
1703 /* Advance to next span on list. */
1705 }
1706
1707 /* Stop now if we found a suitable block. */
1708 if (DsaPointerIsValid(pool->spans[1]))
1709 return true;
1710 }
1711
1712 /*
1713 * If there are no blocks that properly belong in fullness class 1, pick
1714 * one from some other fullness class and move it there anyway, so that we
1715 * have an allocation target. Our last choice is to transfer a block
1716 * that's almost empty (and might become completely empty soon if left
1717 * alone), but even that is better than failing, which is what we must do
1718 * if there are no blocks at all with freespace.
1719 */
1720 Assert(!DsaPointerIsValid(pool->spans[1]));
1721 for (fclass = 2; fclass < DSA_FULLNESS_CLASSES - 1; ++fclass)
1722 if (transfer_first_span(area, pool, fclass, 1))
1723 return true;
1724 if (!DsaPointerIsValid(pool->spans[1]) &&
1725 transfer_first_span(area, pool, 0, 1))
1726 return true;
1727
1728 /*
1729 * We failed to find an existing span with free objects, so we need to
1730 * allocate a new superblock and construct a new span to manage it.
1731 *
1732 * First, get a dsa_area_span object to describe the new superblock block
1733 * ... unless this allocation is for a dsa_area_span object, in which case
1734 * that's surely not going to work. We handle that case by storing the
1735 * span describing a block-of-spans inline.
1736 */
1737 if (size_class != DSA_SCLASS_BLOCK_OF_SPANS)
1738 {
1741 return false;
1742 npages = DSA_PAGES_PER_SUPERBLOCK;
1743 }
1744
1745 /* Find or create a segment and allocate the superblock. */
1747 segment_map = get_best_segment(area, npages);
1748 if (segment_map == NULL)
1749 {
1750 segment_map = make_new_segment(area, npages);
1751 if (segment_map == NULL)
1752 {
1754 return false;
1755 }
1756 }
1757
1758 /*
1759 * This shouldn't happen: get_best_segment() or make_new_segment()
1760 * promised that we can successfully allocate npages.
1761 */
1762 if (!FreePageManagerGet(segment_map->fpm, npages, &first_page))
1763 elog(FATAL,
1764 "dsa_allocate could not find %zu free pages for superblock",
1765 npages);
1767
1768 /* Compute the start of the superblock. */
1771 first_page * FPM_PAGE_SIZE);
1772
1773 /*
1774 * If this is a block-of-spans, carve the descriptor right out of the
1775 * allocated space.
1776 */
1777 if (size_class == DSA_SCLASS_BLOCK_OF_SPANS)
1778 {
1779 /*
1780 * We have a pointer into the segment. We need to build a dsa_pointer
1781 * from the segment index and offset into the segment.
1782 */
1784 }
1785
1786 /* Initialize span and pagemap. */
1787 init_span(area, span_pointer, pool, start_pointer, npages, size_class);
1788 for (i = 0; i < npages; ++i)
1789 segment_map->pagemap[first_page + i] = span_pointer;
1790
1791 return true;
1792}

References alloc_object(), Assert, DSA_AREA_LOCK, DSA_FULLNESS_CLASSES, dsa_get_address(), DSA_MAKE_POINTER, DSA_PAGES_PER_SUPERBLOCK, DSA_SCLASS_BLOCK_OF_SPANS, DSA_SCLASS_LOCK, dsa_size_classes, DSA_SUPERBLOCK_SIZE, DsaPointerIsValid, elog, FATAL, fb(), FPM_PAGE_SIZE, FreePageManagerGet(), get_best_segment(), get_segment_index, i, init_span(), InvalidDsaPointer, LW_EXCLUSIVE, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), make_new_segment(), dsa_area_span::nextspan, dsa_area_span::prevspan, dsa_area_pool::spans, and transfer_first_span().

Referenced by alloc_object().

◆ get_best_segment()

static dsa_segment_map * get_best_segment ( dsa_area area,
size_t  npages 
)
static

Definition at line 2054 of file dsa.c.

2055{
2056 size_t bin;
2057
2060
2061 /*
2062 * Start searching from the first bin that *might* have enough contiguous
2063 * pages.
2064 */
2065 for (bin = contiguous_pages_to_segment_bin(npages);
2067 ++bin)
2068 {
2069 /*
2070 * The minimum contiguous size that any segment in this bin should
2071 * have. We'll re-bin if we see segments with fewer.
2072 */
2073 size_t threshold = (size_t) 1 << (bin - 1);
2075
2076 /* Search this bin for a segment with enough contiguous space. */
2077 segment_index = area->control->segment_bins[bin];
2079 {
2082 size_t contiguous_pages;
2083
2085 next_segment_index = segment_map->header->next;
2086 contiguous_pages = fpm_largest(segment_map->fpm);
2087
2088 /* Not enough for the request, still enough for this bin. */
2089 if (contiguous_pages >= threshold && contiguous_pages < npages)
2090 {
2092 continue;
2093 }
2094
2095 /* Re-bin it if it's no longer in the appropriate bin. */
2096 if (contiguous_pages < threshold)
2097 {
2099
2100 /*
2101 * But fall through to see if it's enough to satisfy this
2102 * request anyway....
2103 */
2104 }
2105
2106 /* Check if we are done. */
2107 if (contiguous_pages >= npages)
2108 return segment_map;
2109
2110 /* Continue searching the same bin. */
2112 }
2113 }
2114
2115 /* Not found. */
2116 return NULL;
2117}

References Assert, check_for_freed_segments_locked(), contiguous_pages_to_segment_bin(), dsa_area::control, DSA_AREA_LOCK, DSA_NUM_SEGMENT_BINS, DSA_SEGMENT_INDEX_NONE, fb(), fpm_largest, get_segment_by_index(), LWLockHeldByMe(), rebin_segment(), and dsa_area_control::segment_bins.

Referenced by dsa_allocate_extended(), and ensure_active_superblock().

◆ get_segment_by_index()

static dsa_segment_map * get_segment_by_index ( dsa_area area,
dsa_segment_index  index 
)
static

Definition at line 1803 of file dsa.c.

1804{
1806 {
1807 dsm_handle handle;
1808 dsm_segment *segment;
1810 ResourceOwner oldowner;
1811
1812 /*
1813 * If we are reached by dsa_free or dsa_get_address, there must be at
1814 * least one object allocated in the referenced segment. Otherwise,
1815 * their caller has a double-free or access-after-free bug, which we
1816 * have no hope of detecting. So we know it's safe to access this
1817 * array slot without holding a lock; it won't change underneath us.
1818 * Furthermore, we know that we can see the latest contents of the
1819 * slot, as explained in check_for_freed_segments, which those
1820 * functions call before arriving here.
1821 */
1822 handle = area->control->segment_handles[index];
1823
1824 /* It's an error to try to access an unused slot. */
1825 if (handle == DSM_HANDLE_INVALID)
1826 elog(ERROR,
1827 "dsa_area could not attach to a segment that has been freed");
1828
1829 oldowner = CurrentResourceOwner;
1831 segment = dsm_attach(handle);
1832 CurrentResourceOwner = oldowner;
1833 if (segment == NULL)
1834 elog(ERROR, "dsa_area could not attach to segment");
1835 segment_map = &area->segment_maps[index];
1836 segment_map->segment = segment;
1838 segment_map->header =
1839 (dsa_segment_header *) segment_map->mapped_address;
1840 segment_map->fpm = (FreePageManager *)
1841 (segment_map->mapped_address +
1842 MAXALIGN(sizeof(dsa_segment_header)));
1843 segment_map->pagemap = (dsa_pointer *)
1844 (segment_map->mapped_address +
1845 MAXALIGN(sizeof(dsa_segment_header)) +
1846 MAXALIGN(sizeof(FreePageManager)));
1847
1848 /* Remember the highest index this backend has ever mapped. */
1849 if (area->high_segment_index < index)
1850 area->high_segment_index = index;
1851
1852 Assert(segment_map->header->magic ==
1854 }
1855
1856 /*
1857 * Callers of dsa_get_address() and dsa_free() don't hold the area lock,
1858 * but it's a bug in the calling code and undefined behavior if the
1859 * address is not live (ie if the segment might possibly have been freed,
1860 * they're trying to use a dangling pointer).
1861 *
1862 * For dsa.c code that holds the area lock to manipulate segment_bins
1863 * lists, it would be a bug if we ever reach a freed segment here. After
1864 * it's marked as freed, the only thing any backend should do with it is
1865 * unmap it, and it should always have done that in
1866 * check_for_freed_segments_locked() before arriving here to resolve an
1867 * index to a segment_map.
1868 *
1869 * Either way we can assert that we aren't returning a freed segment.
1870 */
1872
1873 return &area->segment_maps[index];
1874}

References Assert, dsa_area::control, CurrentResourceOwner, DSA_SEGMENT_HEADER_MAGIC, dsm_attach(), DSM_HANDLE_INVALID, dsm_segment_address(), elog, ERROR, fb(), dsa_segment_header::freed, dsa_area_control::handle, dsa_segment_map::header, dsa_area::high_segment_index, dsm_segment::mapped_address, dsa_segment_map::mapped_address, MAXALIGN, dsa_area::resowner, dsa_segment_map::segment, dsa_area_control::segment_handles, dsa_area::segment_maps, and unlikely.

Referenced by destroy_superblock(), dsa_dump(), dsa_free(), dsa_get_address(), get_best_segment(), make_new_segment(), rebin_segment(), and unlink_segment().

◆ init_span()

static void init_span ( dsa_area area,
dsa_pointer  span_pointer,
dsa_area_pool pool,
dsa_pointer  start,
size_t  npages,
uint16  size_class 
)
static

Definition at line 1423 of file dsa.c.

1427{
1429 size_t obsize = dsa_size_classes[size_class];
1430
1431 /*
1432 * The per-pool lock must be held because we manipulate the span list for
1433 * this pool.
1434 */
1435 Assert(LWLockHeldByMe(DSA_SCLASS_LOCK(area, size_class)));
1436
1437 /* Push this span onto the front of the span list for fullness class 1. */
1438 if (DsaPointerIsValid(pool->spans[1]))
1439 {
1440 dsa_area_span *head = (dsa_area_span *)
1441 dsa_get_address(area, pool->spans[1]);
1442
1443 head->prevspan = span_pointer;
1444 }
1445 span->pool = DsaAreaPoolToDsaPointer(area, pool);
1446 span->nextspan = pool->spans[1];
1447 span->prevspan = InvalidDsaPointer;
1448 pool->spans[1] = span_pointer;
1449
1450 span->start = start;
1451 span->npages = npages;
1452 span->size_class = size_class;
1453 span->ninitialized = 0;
1454 if (size_class == DSA_SCLASS_BLOCK_OF_SPANS)
1455 {
1456 /*
1457 * A block-of-spans contains its own descriptor, so mark one object as
1458 * initialized and reduce the count of allocatable objects by one.
1459 * Doing this here has the side effect of also reducing nmax by one,
1460 * which is important to make sure we free this object at the correct
1461 * time.
1462 */
1463 span->ninitialized = 1;
1464 span->nallocatable = FPM_PAGE_SIZE / obsize - 1;
1465 }
1466 else if (size_class != DSA_SCLASS_SPAN_LARGE)
1467 span->nallocatable = DSA_SUPERBLOCK_SIZE / obsize;
1468 span->firstfree = DSA_SPAN_NOTHING_FREE;
1469 span->nmax = span->nallocatable;
1470 span->fclass = 1;
1471}

References Assert, dsa_get_address(), DSA_SCLASS_BLOCK_OF_SPANS, DSA_SCLASS_LOCK, DSA_SCLASS_SPAN_LARGE, dsa_size_classes, DSA_SPAN_NOTHING_FREE, DSA_SUPERBLOCK_SIZE, DsaAreaPoolToDsaPointer, DsaPointerIsValid, fb(), FPM_PAGE_SIZE, InvalidDsaPointer, LWLockHeldByMe(), dsa_area_span::prevspan, dsa_area_pool::spans, and start.

Referenced by dsa_allocate_extended(), and ensure_active_superblock().

◆ make_new_segment()

static dsa_segment_map * make_new_segment ( dsa_area area,
size_t  requested_pages 
)
static

Definition at line 2125 of file dsa.c.

2126{
2127 dsa_segment_index new_index;
2128 size_t metadata_bytes;
2129 size_t total_size;
2130 size_t total_pages;
2131 size_t usable_pages;
2133 dsm_segment *segment;
2134 ResourceOwner oldowner;
2135
2137
2138 /* Find a segment slot that is not in use (linearly for now). */
2139 for (new_index = 1; new_index < DSA_MAX_SEGMENTS; ++new_index)
2140 {
2141 if (area->control->segment_handles[new_index] == DSM_HANDLE_INVALID)
2142 break;
2143 }
2144 if (new_index == DSA_MAX_SEGMENTS)
2145 return NULL;
2146
2147 /*
2148 * If the total size limit is already exceeded, then we exit early and
2149 * avoid arithmetic wraparound in the unsigned expressions below.
2150 */
2151 if (area->control->total_segment_size >=
2153 return NULL;
2154
2155 /*
2156 * The size should be at least as big as requested, and at least big
2157 * enough to follow a geometric series that approximately doubles the
2158 * total storage each time we create a new segment. We use geometric
2159 * growth because the underlying DSM system isn't designed for large
2160 * numbers of segments (otherwise we might even consider just using one
2161 * DSM segment for each large allocation and for each superblock, and then
2162 * we wouldn't need to use FreePageManager).
2163 *
2164 * We decide on a total segment size first, so that we produce tidy
2165 * power-of-two sized segments. This is a good property to have if we
2166 * move to huge pages in the future. Then we work back to the number of
2167 * pages we can fit.
2168 */
2170 ((size_t) 1 << (new_index / DSA_NUM_SEGMENTS_AT_EACH_SIZE));
2175
2178 MAXALIGN(sizeof(dsa_segment_header)) +
2179 MAXALIGN(sizeof(FreePageManager)) +
2180 sizeof(dsa_pointer) * total_pages;
2181
2182 /* Add padding up to next page boundary. */
2183 if (metadata_bytes % FPM_PAGE_SIZE != 0)
2186 return NULL;
2187 usable_pages = (total_size - metadata_bytes) / FPM_PAGE_SIZE;
2188 Assert(metadata_bytes + usable_pages * FPM_PAGE_SIZE <= total_size);
2189
2190 /* See if that is enough... */
2191 if (requested_pages > usable_pages)
2192 {
2194
2195 /*
2196 * We'll make an odd-sized segment, working forward from the requested
2197 * number of pages.
2198 */
2199 usable_pages = requested_pages;
2201 MAXALIGN(sizeof(dsa_segment_header)) +
2202 MAXALIGN(sizeof(FreePageManager)) +
2203 usable_pages * sizeof(dsa_pointer);
2204
2205 /*
2206 * We must also account for pagemap entries needed to cover the
2207 * metadata pages themselves. The pagemap must track all pages in the
2208 * segment, including the pages occupied by metadata.
2209 *
2210 * This formula uses integer ceiling division to compute the exact
2211 * number of additional entries needed. The divisor (FPM_PAGE_SIZE -
2212 * sizeof(dsa_pointer)) accounts for the fact that each metadata page
2213 * consumes one pagemap entry of sizeof(dsa_pointer) bytes, leaving
2214 * only (FPM_PAGE_SIZE - sizeof(dsa_pointer)) net bytes per metadata
2215 * page.
2216 */
2218 ((metadata_bytes + (FPM_PAGE_SIZE - sizeof(dsa_pointer)) - 1) /
2219 (FPM_PAGE_SIZE - sizeof(dsa_pointer))) *
2220 sizeof(dsa_pointer);
2221
2222 /* Add padding up to next page boundary. */
2223 if (metadata_bytes % FPM_PAGE_SIZE != 0)
2225 total_size = metadata_bytes + usable_pages * FPM_PAGE_SIZE;
2227
2228 /*
2229 * Verify that we allocated enough pagemap entries for metadata and
2230 * usable pages. This reverse-engineers the new calculation of
2231 * "metadata_bytes" done based on the new "requested_pages" for an
2232 * odd-sized segment.
2233 */
2236
2237 /* Is that too large for dsa_pointer's addressing scheme? */
2239 return NULL;
2240
2241 /* Would that exceed the limit? */
2244 return NULL;
2245 }
2246
2247 /* Create the segment. */
2248 oldowner = CurrentResourceOwner;
2250 segment = dsm_create(total_size, 0);
2251 CurrentResourceOwner = oldowner;
2252 if (segment == NULL)
2253 return NULL;
2254 dsm_pin_segment(segment);
2255
2256 /* Store the handle in shared memory to be found by index. */
2257 area->control->segment_handles[new_index] =
2258 dsm_segment_handle(segment);
2259 /* Track the highest segment index in the history of the area. */
2260 if (area->control->high_segment_index < new_index)
2261 area->control->high_segment_index = new_index;
2262 /* Track the highest segment index this backend has ever mapped. */
2263 if (area->high_segment_index < new_index)
2264 area->high_segment_index = new_index;
2265 /* Track total size of all segments. */
2269
2270 /* Build a segment map for this segment in this backend. */
2271 segment_map = &area->segment_maps[new_index];
2272 segment_map->segment = segment;
2274 segment_map->header = (dsa_segment_header *) segment_map->mapped_address;
2275 segment_map->fpm = (FreePageManager *)
2276 (segment_map->mapped_address +
2277 MAXALIGN(sizeof(dsa_segment_header)));
2278 segment_map->pagemap = (dsa_pointer *)
2279 (segment_map->mapped_address +
2280 MAXALIGN(sizeof(dsa_segment_header)) +
2281 MAXALIGN(sizeof(FreePageManager)));
2282
2283 /* Set up the free page map. */
2284 FreePageManagerInitialize(segment_map->fpm, segment_map->mapped_address);
2286 usable_pages);
2287
2288 /* Set up the segment header and put it in the appropriate bin. */
2289 segment_map->header->magic =
2290 DSA_SEGMENT_HEADER_MAGIC ^ area->control->handle ^ new_index;
2291 segment_map->header->usable_pages = usable_pages;
2292 segment_map->header->size = total_size;
2293 segment_map->header->bin = contiguous_pages_to_segment_bin(usable_pages);
2294 segment_map->header->prev = DSA_SEGMENT_INDEX_NONE;
2295 segment_map->header->next =
2296 area->control->segment_bins[segment_map->header->bin];
2297 segment_map->header->freed = false;
2298 area->control->segment_bins[segment_map->header->bin] = new_index;
2299 if (segment_map->header->next != DSA_SEGMENT_INDEX_NONE)
2300 {
2302 get_segment_by_index(area, segment_map->header->next);
2303
2304 Assert(next->header->bin == segment_map->header->bin);
2305 next->header->prev = new_index;
2306 }
2307
2308 return segment_map;
2309}

References Assert, contiguous_pages_to_segment_bin(), dsa_area::control, CurrentResourceOwner, DSA_AREA_LOCK, DSA_MAX_SEGMENT_SIZE, DSA_MAX_SEGMENTS, DSA_NUM_SEGMENTS_AT_EACH_SIZE, DSA_SEGMENT_HEADER_MAGIC, DSA_SEGMENT_INDEX_NONE, dsm_create(), DSM_HANDLE_INVALID, dsm_pin_segment(), dsm_segment_address(), dsm_segment_handle(), fb(), FPM_PAGE_SIZE, FreePageManagerInitialize(), FreePageManagerPut(), get_segment_by_index(), dsa_area_control::handle, dsa_area_control::high_segment_index, dsa_area::high_segment_index, dsa_area_control::init_segment_size, LWLockHeldByMe(), dsm_segment::mapped_address, dsa_area_control::max_segment_size, dsa_area_control::max_total_segment_size, MAXALIGN, Min, next, PG_USED_FOR_ASSERTS_ONLY, dsa_area::resowner, dsa_segment_map::segment, dsa_area_control::segment_bins, dsa_area_control::segment_handles, dsa_area::segment_maps, dsa_area_control::total_segment_size, and total_size.

Referenced by dsa_allocate_extended(), and ensure_active_superblock().

◆ rebin_segment()

static void rebin_segment ( dsa_area area,
dsa_segment_map segment_map 
)
static

Definition at line 2388 of file dsa.c.

2389{
2390 size_t new_bin;
2392
2394 if (segment_map->header->bin == new_bin)
2395 return;
2396
2397 /* Remove it from its current bin. */
2399
2400 /* Push it onto the front of its new bin. */
2402 segment_map->header->prev = DSA_SEGMENT_INDEX_NONE;
2403 segment_map->header->next = area->control->segment_bins[new_bin];
2404 segment_map->header->bin = new_bin;
2406 if (segment_map->header->next != DSA_SEGMENT_INDEX_NONE)
2407 {
2409
2410 next = get_segment_by_index(area, segment_map->header->next);
2411 Assert(next->header->bin == new_bin);
2412 next->header->prev = segment_index;
2413 }
2414}

References Assert, contiguous_pages_to_segment_bin(), dsa_area::control, DSA_SEGMENT_INDEX_NONE, fb(), fpm_largest, get_segment_by_index(), get_segment_index, next, dsa_area_control::segment_bins, and unlink_segment().

Referenced by destroy_superblock(), dsa_free(), and get_best_segment().

◆ transfer_first_span()

static bool transfer_first_span ( dsa_area area,
dsa_area_pool pool,
int  fromclass,
int  toclass 
)
static

Definition at line 1478 of file dsa.c.

1480{
1483 dsa_area_span *nextspan;
1484
1485 /* Can't do it if source list is empty. */
1486 span_pointer = pool->spans[fromclass];
1488 return false;
1489
1490 /* Remove span from head of source list. */
1492 pool->spans[fromclass] = span->nextspan;
1493 if (DsaPointerIsValid(span->nextspan))
1494 {
1495 nextspan = (dsa_area_span *)
1496 dsa_get_address(area, span->nextspan);
1497 nextspan->prevspan = InvalidDsaPointer;
1498 }
1499
1500 /* Add span to head of target list. */
1501 span->nextspan = pool->spans[toclass];
1502 pool->spans[toclass] = span_pointer;
1503 if (DsaPointerIsValid(span->nextspan))
1504 {
1505 nextspan = (dsa_area_span *)
1506 dsa_get_address(area, span->nextspan);
1507 nextspan->prevspan = span_pointer;
1508 }
1509 span->fclass = toclass;
1510
1511 return true;
1512}

References dsa_get_address(), DsaPointerIsValid, fb(), InvalidDsaPointer, dsa_area_span::prevspan, and dsa_area_pool::spans.

Referenced by alloc_object(), and ensure_active_superblock().

◆ unlink_segment()

static void unlink_segment ( dsa_area area,
dsa_segment_map segment_map 
)
static

Definition at line 2022 of file dsa.c.

2023{
2024 if (segment_map->header->prev != DSA_SEGMENT_INDEX_NONE)
2025 {
2026 dsa_segment_map *prev;
2027
2028 prev = get_segment_by_index(area, segment_map->header->prev);
2029 prev->header->next = segment_map->header->next;
2030 }
2031 else
2032 {
2033 Assert(area->control->segment_bins[segment_map->header->bin] ==
2035 area->control->segment_bins[segment_map->header->bin] =
2036 segment_map->header->next;
2037 }
2038 if (segment_map->header->next != DSA_SEGMENT_INDEX_NONE)
2039 {
2041
2042 next = get_segment_by_index(area, segment_map->header->next);
2043 next->header->prev = segment_map->header->prev;
2044 }
2045}

References Assert, dsa_area::control, DSA_SEGMENT_INDEX_NONE, fb(), get_segment_by_index(), get_segment_index, dsa_segment_map::header, next, dsa_segment_header::next, and dsa_area_control::segment_bins.

Referenced by destroy_superblock(), and rebin_segment().

◆ unlink_span()

static void unlink_span ( dsa_area area,
dsa_area_span span 
)
static

Definition at line 1952 of file dsa.c.

1953{
1954 if (DsaPointerIsValid(span->nextspan))
1955 {
1956 dsa_area_span *next = dsa_get_address(area, span->nextspan);
1957
1958 next->prevspan = span->prevspan;
1959 }
1960 if (DsaPointerIsValid(span->prevspan))
1961 {
1962 dsa_area_span *prev = dsa_get_address(area, span->prevspan);
1963
1964 prev->nextspan = span->nextspan;
1965 }
1966 else
1967 {
1968 dsa_area_pool *pool = dsa_get_address(area, span->pool);
1969
1970 pool->spans[span->fclass] = span->nextspan;
1971 }
1972}

References dsa_get_address(), DsaPointerIsValid, fb(), next, dsa_area_span::nextspan, and dsa_area_pool::spans.

Referenced by destroy_superblock(), and dsa_free().

Variable Documentation

◆ dsa_size_class_map

const uint8 dsa_size_class_map[]
static
Initial value:
= {
2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 11, 12, 12, 13, 13,
14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17,
18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19,
20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25
}

Definition at line 248 of file dsa.c.

248 {
249 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 11, 12, 12, 13, 13,
250 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17,
251 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19,
252 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21,
253 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
254 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
255 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
256 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25
257};

Referenced by dsa_allocate_extended().

◆ dsa_size_classes

const uint16 dsa_size_classes[]
static
Initial value:
= {
sizeof(dsa_area_span), 0,
8, 16, 24, 32, 40, 48, 56, 64,
80, 96, 112, 128,
160, 192, 224, 256,
320, 384, 448, 512,
640, 768, 896, 1024,
1280, 1560, 1816, 2048,
2616, 3120, 3640, 4096,
5456, 6552, 7280, 8192
}

Definition at line 225 of file dsa.c.

225 {
226 sizeof(dsa_area_span), 0, /* special size classes */
227 8, 16, 24, 32, 40, 48, 56, 64, /* 8 classes separated by 8 bytes */
228 80, 96, 112, 128, /* 4 classes separated by 16 bytes */
229 160, 192, 224, 256, /* 4 classes separated by 32 bytes */
230 320, 384, 448, 512, /* 4 classes separated by 64 bytes */
231 640, 768, 896, 1024, /* 4 classes separated by 128 bytes */
232 1280, 1560, 1816, 2048, /* 4 classes separated by ~256 bytes */
233 2616, 3120, 3640, 4096, /* 4 classes separated by ~512 bytes */
234 5456, 6552, 7280, 8192 /* 4 classes separated by ~1024 bytes */
235};

Referenced by alloc_object(), dsa_allocate_extended(), dsa_dump(), dsa_free(), ensure_active_superblock(), and init_span().