3535
3636#include "lib/ilist.h"
3737#include "miscadmin.h"
38+ #include "port/pg_bitutils.h"
3839#include "storage/dsm.h"
3940#include "storage/ipc.h"
4041#include "storage/lwlock.h"
4142#include "storage/pg_shmem.h"
43+ #include "utils/freepage.h"
4244#include "utils/guc.h"
4345#include "utils/memutils.h"
4446#include "utils/resowner_private.h"
@@ -76,6 +78,8 @@ typedef struct dsm_control_item
7678{
7779dsm_handle handle ;
7880uint32 refcnt ;/* 2+ = active, 1 = moribund, 0 = gone */
81+ size_t first_page ;
82+ size_t npages ;
7983void * impl_private_pm_handle ;/* only needed on Windows */
8084bool pinned ;
8185}dsm_control_item ;
@@ -95,10 +99,15 @@ static dsm_segment *dsm_create_descriptor(void);
9599static bool dsm_control_segment_sane (dsm_control_header * control ,
96100Size mapped_size );
97101static uint64 dsm_control_bytes_needed (uint32 nitems );
102+ static inline dsm_handle make_main_region_dsm_handle (int slot );
103+ static inline bool is_main_region_dsm_handle (dsm_handle handle );
98104
99105/* Has this backend initialized the dynamic shared memory system yet? */
100106static bool dsm_init_done = false;
101107
108+ /* Preallocated DSM space in the main shared memory region. */
109+ static void * dsm_main_space_begin = NULL ;
110+
102111/*
103112 * List of dynamic shared memory segments used by this backend.
104113 *
@@ -171,7 +180,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
171180{
172181Assert (dsm_control_address == NULL );
173182Assert (dsm_control_mapped_size == 0 );
174- dsm_control_handle = random ();
183+ dsm_control_handle = random () << 1 ; /* Even numbers only */
175184if (dsm_control_handle == DSM_HANDLE_INVALID )
176185continue ;
177186if (dsm_impl_op (DSM_OP_CREATE ,dsm_control_handle ,segsize ,
@@ -247,8 +256,12 @@ dsm_cleanup_using_control_segment(dsm_handle old_control_handle)
247256if (refcnt == 0 )
248257continue ;
249258
250- /*Log debugging information . */
259+ /*If it was using the main shmem area, there is nothing to do . */
251260handle = old_control -> item [i ].handle ;
261+ if (is_main_region_dsm_handle (handle ))
262+ continue ;
263+
264+ /* Log debugging information. */
252265elog (DEBUG2 ,"cleaning up orphaned dynamic shared memory with ID %u (reference count %u)" ,
253266handle ,refcnt );
254267
@@ -348,8 +361,11 @@ dsm_postmaster_shutdown(int code, Datum arg)
348361if (dsm_control -> item [i ].refcnt == 0 )
349362continue ;
350363
351- /* Log debugging information. */
352364handle = dsm_control -> item [i ].handle ;
365+ if (is_main_region_dsm_handle (handle ))
366+ continue ;
367+
368+ /* Log debugging information. */
353369elog (DEBUG2 ,"cleaning up orphaned dynamic shared memory with ID %u" ,
354370handle );
355371
@@ -418,6 +434,45 @@ dsm_set_control_handle(dsm_handle h)
418434}
419435#endif
420436
437+ /*
438+ * Reserve some space in the main shared memory segment for DSM segments.
439+ */
440+ size_t
441+ dsm_estimate_size (void )
442+ {
443+ return 1024 * 1024 * (size_t )min_dynamic_shared_memory ;
444+ }
445+
446+ /*
447+ * Initialize space in the main shared memory segment for DSM segments.
448+ */
449+ void
450+ dsm_shmem_init (void )
451+ {
452+ size_t size = dsm_estimate_size ();
453+ bool found ;
454+
455+ if (size == 0 )
456+ return ;
457+
458+ dsm_main_space_begin = ShmemInitStruct ("Preallocated DSM" ,size ,& found );
459+ if (!found )
460+ {
461+ FreePageManager * fpm = (FreePageManager * )dsm_main_space_begin ;
462+ size_t first_page = 0 ;
463+ size_t pages ;
464+
465+ /* Reserve space for the FreePageManager. */
466+ while (first_page * FPM_PAGE_SIZE < sizeof (FreePageManager ))
467+ ++ first_page ;
468+
469+ /* Initialize it and give it all the rest of the space. */
470+ FreePageManagerInitialize (fpm ,dsm_main_space_begin );
471+ pages = (size /FPM_PAGE_SIZE )- first_page ;
472+ FreePageManagerPut (fpm ,first_page ,pages );
473+ }
474+ }
475+
421476/*
422477 * Create a new dynamic shared memory segment.
423478 *
@@ -434,6 +489,10 @@ dsm_create(Size size, int flags)
434489dsm_segment * seg ;
435490uint32 i ;
436491uint32 nitems ;
492+ size_t npages = 0 ;
493+ size_t first_page = 0 ;
494+ FreePageManager * dsm_main_space_fpm = dsm_main_space_begin ;
495+ bool using_main_dsm_region = false;
437496
438497/* Unsafe in postmaster (and pointless in a stand-alone backend). */
439498Assert (IsUnderPostmaster );
@@ -444,27 +503,63 @@ dsm_create(Size size, int flags)
444503/* Create a new segment descriptor. */
445504seg = dsm_create_descriptor ();
446505
447- /* Loop until we find an unused segment identifier. */
448- for (;;)
506+ /*
507+ * Lock the control segment while we try to allocate from the main shared
508+ * memory area, if configured.
509+ */
510+ if (dsm_main_space_fpm )
449511{
450- Assert (seg -> mapped_address == NULL && seg -> mapped_size == 0 );
451- seg -> handle = random ();
452- if (seg -> handle == DSM_HANDLE_INVALID )/* Reserve sentinel */
453- continue ;
454- if (dsm_impl_op (DSM_OP_CREATE ,seg -> handle ,size ,& seg -> impl_private ,
455- & seg -> mapped_address ,& seg -> mapped_size ,ERROR ))
456- break ;
512+ npages = size /FPM_PAGE_SIZE ;
513+ if (size %FPM_PAGE_SIZE > 0 )
514+ ++ npages ;
515+
516+ LWLockAcquire (DynamicSharedMemoryControlLock ,LW_EXCLUSIVE );
517+ if (FreePageManagerGet (dsm_main_space_fpm ,npages ,& first_page ))
518+ {
519+ /* We can carve out a piece of the main shared memory segment. */
520+ seg -> mapped_address = (char * )dsm_main_space_begin +
521+ first_page * FPM_PAGE_SIZE ;
522+ seg -> mapped_size = npages * FPM_PAGE_SIZE ;
523+ using_main_dsm_region = true;
524+ /* We'll choose a handle below. */
525+ }
457526}
458527
459- /* Lock the control segment so we can register the new segment. */
460- LWLockAcquire (DynamicSharedMemoryControlLock ,LW_EXCLUSIVE );
528+ if (!using_main_dsm_region )
529+ {
530+ /*
531+ * We need to create a new memory segment. Loop until we find an
532+ * unused segment identifier.
533+ */
534+ if (dsm_main_space_fpm )
535+ LWLockRelease (DynamicSharedMemoryControlLock );
536+ for (;;)
537+ {
538+ Assert (seg -> mapped_address == NULL && seg -> mapped_size == 0 );
539+ seg -> handle = random () <<1 ;/* Even numbers only */
540+ if (seg -> handle == DSM_HANDLE_INVALID )/* Reserve sentinel */
541+ continue ;
542+ if (dsm_impl_op (DSM_OP_CREATE ,seg -> handle ,size ,& seg -> impl_private ,
543+ & seg -> mapped_address ,& seg -> mapped_size ,ERROR ))
544+ break ;
545+ }
546+ LWLockAcquire (DynamicSharedMemoryControlLock ,LW_EXCLUSIVE );
547+ }
461548
462549/* Search the control segment for an unused slot. */
463550nitems = dsm_control -> nitems ;
464551for (i = 0 ;i < nitems ;++ i )
465552{
466553if (dsm_control -> item [i ].refcnt == 0 )
467554{
555+ if (using_main_dsm_region )
556+ {
557+ seg -> handle = make_main_region_dsm_handle (i );
558+ dsm_control -> item [i ].first_page = first_page ;
559+ dsm_control -> item [i ].npages = npages ;
560+ }
561+ else
562+ Assert (!is_main_region_dsm_handle (seg -> handle ));
468563dsm_control -> item [i ].handle = seg -> handle ;
469564/* refcnt of 1 triggers destruction, so start at 2 */
470565dsm_control -> item [i ].refcnt = 2 ;
@@ -479,9 +574,12 @@ dsm_create(Size size, int flags)
479574/* Verify that we can support an additional mapping. */
480575if (nitems >=dsm_control -> maxitems )
481576{
577+ if (using_main_dsm_region )
578+ FreePageManagerPut (dsm_main_space_fpm ,first_page ,npages );
482579LWLockRelease (DynamicSharedMemoryControlLock );
483- dsm_impl_op (DSM_OP_DESTROY ,seg -> handle ,0 ,& seg -> impl_private ,
484- & seg -> mapped_address ,& seg -> mapped_size ,WARNING );
580+ if (!using_main_dsm_region )
581+ dsm_impl_op (DSM_OP_DESTROY ,seg -> handle ,0 ,& seg -> impl_private ,
582+ & seg -> mapped_address ,& seg -> mapped_size ,WARNING );
485583if (seg -> resowner != NULL )
486584ResourceOwnerForgetDSM (seg -> resowner ,seg );
487585dlist_delete (& seg -> node );
@@ -495,6 +593,12 @@ dsm_create(Size size, int flags)
495593}
496594
497595/* Enter the handle into a new array slot. */
596+ if (using_main_dsm_region )
597+ {
598+ seg -> handle = make_main_region_dsm_handle (nitems );
599+ dsm_control -> item [i ].first_page = first_page ;
600+ dsm_control -> item [i ].npages = npages ;
601+ }
498602dsm_control -> item [nitems ].handle = seg -> handle ;
499603/* refcnt of 1 triggers destruction, so start at 2 */
500604dsm_control -> item [nitems ].refcnt = 2 ;
@@ -580,6 +684,12 @@ dsm_attach(dsm_handle h)
580684/* Otherwise we've found a match. */
581685dsm_control -> item [i ].refcnt ++ ;
582686seg -> control_slot = i ;
687+ if (is_main_region_dsm_handle (seg -> handle ))
688+ {
689+ seg -> mapped_address = (char * )dsm_main_space_begin +
690+ dsm_control -> item [i ].first_page * FPM_PAGE_SIZE ;
691+ seg -> mapped_size = dsm_control -> item [i ].npages * FPM_PAGE_SIZE ;
692+ }
583693break ;
584694}
585695LWLockRelease (DynamicSharedMemoryControlLock );
@@ -597,8 +707,9 @@ dsm_attach(dsm_handle h)
597707}
598708
599709/* Here's where we actually try to map the segment. */
600- dsm_impl_op (DSM_OP_ATTACH ,seg -> handle ,0 ,& seg -> impl_private ,
601- & seg -> mapped_address ,& seg -> mapped_size ,ERROR );
710+ if (!is_main_region_dsm_handle (seg -> handle ))
711+ dsm_impl_op (DSM_OP_ATTACH ,seg -> handle ,0 ,& seg -> impl_private ,
712+ & seg -> mapped_address ,& seg -> mapped_size ,ERROR );
602713
603714return seg ;
604715}
@@ -688,8 +799,9 @@ dsm_detach(dsm_segment *seg)
688799 */
689800if (seg -> mapped_address != NULL )
690801{
691- dsm_impl_op (DSM_OP_DETACH ,seg -> handle ,0 ,& seg -> impl_private ,
692- & seg -> mapped_address ,& seg -> mapped_size ,WARNING );
802+ if (!is_main_region_dsm_handle (seg -> handle ))
803+ dsm_impl_op (DSM_OP_DETACH ,seg -> handle ,0 ,& seg -> impl_private ,
804+ & seg -> mapped_address ,& seg -> mapped_size ,WARNING );
693805seg -> impl_private = NULL ;
694806seg -> mapped_address = NULL ;
695807seg -> mapped_size = 0 ;
@@ -729,10 +841,15 @@ dsm_detach(dsm_segment *seg)
729841 * other reason, the postmaster may not have any better luck than
730842 * we did. There's not much we can do about that, though.
731843 */
732- if (dsm_impl_op (DSM_OP_DESTROY ,seg -> handle ,0 ,& seg -> impl_private ,
844+ if (is_main_region_dsm_handle (seg -> handle )||
845+ dsm_impl_op (DSM_OP_DESTROY ,seg -> handle ,0 ,& seg -> impl_private ,
733846& seg -> mapped_address ,& seg -> mapped_size ,WARNING ))
734847{
735848LWLockAcquire (DynamicSharedMemoryControlLock ,LW_EXCLUSIVE );
849+ if (is_main_region_dsm_handle (seg -> handle ))
850+ FreePageManagerPut ((FreePageManager * )dsm_main_space_begin ,
851+ dsm_control -> item [control_slot ].first_page ,
852+ dsm_control -> item [control_slot ].npages );
736853Assert (dsm_control -> item [control_slot ].handle == seg -> handle );
737854Assert (dsm_control -> item [control_slot ].refcnt == 1 );
738855dsm_control -> item [control_slot ].refcnt = 0 ;
@@ -894,10 +1011,15 @@ dsm_unpin_segment(dsm_handle handle)
8941011 * pass the mapped size, mapped address, and private data as NULL
8951012 * here.
8961013 */
897- if (dsm_impl_op (DSM_OP_DESTROY ,handle ,0 ,& junk_impl_private ,
1014+ if (is_main_region_dsm_handle (handle )||
1015+ dsm_impl_op (DSM_OP_DESTROY ,handle ,0 ,& junk_impl_private ,
8981016& junk_mapped_address ,& junk_mapped_size ,WARNING ))
8991017{
9001018LWLockAcquire (DynamicSharedMemoryControlLock ,LW_EXCLUSIVE );
1019+ if (is_main_region_dsm_handle (handle ))
1020+ FreePageManagerPut ((FreePageManager * )dsm_main_space_begin ,
1021+ dsm_control -> item [control_slot ].first_page ,
1022+ dsm_control -> item [control_slot ].npages );
9011023Assert (dsm_control -> item [control_slot ].handle == handle );
9021024Assert (dsm_control -> item [control_slot ].refcnt == 1 );
9031025dsm_control -> item [control_slot ].refcnt = 0 ;
@@ -1094,3 +1216,28 @@ dsm_control_bytes_needed(uint32 nitems)
10941216return offsetof(dsm_control_header ,item )
10951217+ sizeof (dsm_control_item )* (uint64 )nitems ;
10961218}
1219+
1220+ static inline dsm_handle
1221+ make_main_region_dsm_handle (int slot )
1222+ {
1223+ dsm_handle handle ;
1224+
1225+ /*
1226+ * We need to create a handle that doesn't collide with any existing extra
1227+ * segment created by dsm_impl_op(), so we'll make it odd. It also
1228+ * mustn't collide with any other main area pseudo-segment, so we'll
1229+ * include the slot number in some of the bits. We also want to make an
1230+ * effort to avoid newly created and recently destroyed handles from being
1231+ * confused, so we'll make the rest of the bits random.
1232+ */
1233+ handle = 1 ;
1234+ handle |=slot <<1 ;
1235+ handle |=random () << (pg_leftmost_one_pos32 (dsm_control -> maxitems )+ 1 );
1236+ return handle ;
1237+ }
1238+
1239+ static inline bool
1240+ is_main_region_dsm_handle (dsm_handle handle )
1241+ {
1242+ return handle & 1 ;
1243+ }