PostgreSQL Source Code  git master
dsm_impl.c File Reference
#include "postgres.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include "common/file_perm.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "portability/mem.h"
#include "postmaster/postmaster.h"
#include "storage/dsm_impl.h"
#include "storage/fd.h"
#include "utils/guc.h"
#include "utils/memutils.h"
Include dependency graph for dsm_impl.c:

Go to the source code of this file.

Macros

#define ZBUFFER_SIZE   8192
 
#define SEGMENT_NAME_PREFIX   "Global/PostgreSQL"
 

Functions

static bool dsm_impl_sysv (dsm_op op, dsm_handle handle, Size request_size, void **impl_private, void **mapped_address, Size *mapped_size, int elevel)
 
static bool dsm_impl_mmap (dsm_op op, dsm_handle handle, Size request_size, void **impl_private, void **mapped_address, Size *mapped_size, int elevel)
 
static int errcode_for_dynamic_shared_memory (void)
 
bool dsm_impl_op (dsm_op op, dsm_handle handle, Size request_size, void **impl_private, void **mapped_address, Size *mapped_size, int elevel)
 
void dsm_impl_pin_segment (dsm_handle handle, void *impl_private, void **impl_private_pm_handle)
 
void dsm_impl_unpin_segment (dsm_handle handle, void **impl_private)
 

Variables

const struct config_enum_entry dynamic_shared_memory_options []
 
int dynamic_shared_memory_type
 
int min_dynamic_shared_memory
 

Macro Definition Documentation

◆ SEGMENT_NAME_PREFIX

#define SEGMENT_NAME_PREFIX   "Global/PostgreSQL"

Definition at line 122 of file dsm_impl.c.

Referenced by dsm_impl_pin_segment(), dsm_impl_sysv(), and dsm_impl_unpin_segment().

◆ ZBUFFER_SIZE

#define ZBUFFER_SIZE   8192

Definition at line 120 of file dsm_impl.c.

Referenced by dsm_impl_mmap().

Function Documentation

◆ dsm_impl_mmap()

static bool dsm_impl_mmap ( dsm_op  op,
dsm_handle  handle,
Size  request_size,
void **  impl_private,
void **  mapped_address,
Size mapped_size,
int  elevel 
)
static

Definition at line 784 of file dsm_impl.c.

References CloseTransientFile(), DSM_OP_ATTACH, DSM_OP_CREATE, DSM_OP_DESTROY, DSM_OP_DETACH, ereport, errcode_for_dynamic_shared_memory(), errcode_for_file_access(), errmsg(), fd(), fstat, MAP_FAILED, MAP_HASSEMAPHORE, MAP_NOSYNC, name, OpenTransientFile(), palloc0(), PG_DYNSHMEM_DIR, PG_DYNSHMEM_MMAP_FILE_PREFIX, pgstat_report_wait_end(), pgstat_report_wait_start(), remaining, snprintf, stat::st_size, success, WAIT_EVENT_DSM_FILL_ZERO_WRITE, write, and ZBUFFER_SIZE.

Referenced by dsm_impl_op().

787 {
788  char name[64];
789  int flags;
790  int fd;
791  char *address;
792 
794  handle);
795 
796  /* Handle teardown cases. */
797  if (op == DSM_OP_DETACH || op == DSM_OP_DESTROY)
798  {
799  if (*mapped_address != NULL
800  && munmap(*mapped_address, *mapped_size) != 0)
801  {
802  ereport(elevel,
804  errmsg("could not unmap shared memory segment \"%s\": %m",
805  name)));
806  return false;
807  }
808  *mapped_address = NULL;
809  *mapped_size = 0;
810  if (op == DSM_OP_DESTROY && unlink(name) != 0)
811  {
812  ereport(elevel,
814  errmsg("could not remove shared memory segment \"%s\": %m",
815  name)));
816  return false;
817  }
818  return true;
819  }
820 
821  /* Create new segment or open an existing one for attach. */
822  flags = O_RDWR | (op == DSM_OP_CREATE ? O_CREAT | O_EXCL : 0);
823  if ((fd = OpenTransientFile(name, flags)) == -1)
824  {
825  if (errno != EEXIST)
826  ereport(elevel,
828  errmsg("could not open shared memory segment \"%s\": %m",
829  name)));
830  return false;
831  }
832 
833  /*
834  * If we're attaching the segment, determine the current size; if we are
835  * creating the segment, set the size to the requested value.
836  */
837  if (op == DSM_OP_ATTACH)
838  {
839  struct stat st;
840 
841  if (fstat(fd, &st) != 0)
842  {
843  int save_errno;
844 
845  /* Back out what's already been done. */
846  save_errno = errno;
847  CloseTransientFile(fd);
848  errno = save_errno;
849 
850  ereport(elevel,
852  errmsg("could not stat shared memory segment \"%s\": %m",
853  name)));
854  return false;
855  }
856  request_size = st.st_size;
857  }
858  else
859  {
860  /*
861  * Allocate a buffer full of zeros.
862  *
863  * Note: palloc zbuffer, instead of just using a local char array, to
864  * ensure it is reasonably well-aligned; this may save a few cycles
865  * transferring data to the kernel.
866  */
867  char *zbuffer = (char *) palloc0(ZBUFFER_SIZE);
868  uint32 remaining = request_size;
869  bool success = true;
870 
871  /*
872  * Zero-fill the file. We have to do this the hard way to ensure that
873  * all the file space has really been allocated, so that we don't
874  * later seg fault when accessing the memory mapping. This is pretty
875  * pessimal.
876  */
877  while (success && remaining > 0)
878  {
879  Size goal = remaining;
880 
881  if (goal > ZBUFFER_SIZE)
882  goal = ZBUFFER_SIZE;
884  if (write(fd, zbuffer, goal) == goal)
885  remaining -= goal;
886  else
887  success = false;
889  }
890 
891  if (!success)
892  {
893  int save_errno;
894 
895  /* Back out what's already been done. */
896  save_errno = errno;
897  CloseTransientFile(fd);
898  unlink(name);
899  errno = save_errno ? save_errno : ENOSPC;
900 
901  ereport(elevel,
903  errmsg("could not resize shared memory segment \"%s\" to %zu bytes: %m",
904  name, request_size)));
905  return false;
906  }
907  }
908 
909  /* Map it. */
910  address = mmap(NULL, request_size, PROT_READ | PROT_WRITE,
911  MAP_SHARED | MAP_HASSEMAPHORE | MAP_NOSYNC, fd, 0);
912  if (address == MAP_FAILED)
913  {
914  int save_errno;
915 
916  /* Back out what's already been done. */
917  save_errno = errno;
918  CloseTransientFile(fd);
919  if (op == DSM_OP_CREATE)
920  unlink(name);
921  errno = save_errno;
922 
923  ereport(elevel,
925  errmsg("could not map shared memory segment \"%s\": %m",
926  name)));
927  return false;
928  }
929  *mapped_address = address;
930  *mapped_size = request_size;
931 
932  if (CloseTransientFile(fd) != 0)
933  {
934  ereport(elevel,
936  errmsg("could not close shared memory segment \"%s\": %m",
937  name)));
938  return false;
939  }
940 
941  return true;
942 }
int remaining
Definition: informix.c:667
#define MAP_HASSEMAPHORE
Definition: mem.h:30
#define MAP_FAILED
Definition: mem.h:45
#define PG_DYNSHMEM_DIR
Definition: dsm_impl.h:51
#define write(a, b, c)
Definition: win32.h:14
#define MAP_NOSYNC
Definition: mem.h:38
static int fd(const char *x, int i)
Definition: preproc-init.c:105
static int errcode_for_dynamic_shared_memory(void)
Definition: dsm_impl.c:1037
#define fstat
Definition: win32_port.h:274
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2372
int errcode_for_file_access(void)
Definition: elog.c:714
unsigned int uint32
Definition: c.h:429
static void pgstat_report_wait_end(void)
Definition: pgstat.h:1466
int CloseTransientFile(int fd)
Definition: fd.c:2549
static int elevel
Definition: vacuumlazy.c:333
void * palloc0(Size size)
Definition: mcxt.c:981
#define ereport(elevel,...)
Definition: elog.h:155
size_t Size
Definition: c.h:528
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: pgstat.h:1442
const char * name
Definition: encode.c:561
int errmsg(const char *fmt,...)
Definition: elog.c:902
#define PG_DYNSHMEM_MMAP_FILE_PREFIX
Definition: dsm_impl.h:52
#define ZBUFFER_SIZE
Definition: dsm_impl.c:120
static bool success
Definition: initdb.c:162
#define snprintf
Definition: port.h:215

◆ dsm_impl_op()

bool dsm_impl_op ( dsm_op  op,
dsm_handle  handle,
Size  request_size,
void **  impl_private,
void **  mapped_address,
Size mapped_size,
int  elevel 
)

Definition at line 161 of file dsm_impl.c.

References Assert, CHECK_FOR_INTERRUPTS, close, DSM_IMPL_MMAP, dsm_impl_mmap(), DSM_IMPL_POSIX, DSM_IMPL_SYSV, dsm_impl_sysv(), DSM_IMPL_WINDOWS, DSM_OP_ATTACH, DSM_OP_CREATE, DSM_OP_DESTROY, DSM_OP_DETACH, dynamic_shared_memory_type, EINTR, elevel, elog, ereport, errcode_for_dynamic_shared_memory(), errmsg(), ERROR, fd(), fstat, ftruncate, MAP_FAILED, MAP_HASSEMAPHORE, MAP_NOSYNC, name, PG_FILE_MODE_OWNER, pgstat_report_wait_end(), pgstat_report_wait_start(), ProcDiePending, QueryCancelPending, ReleaseExternalFD(), ReserveExternalFD(), snprintf, stat::st_size, and WAIT_EVENT_DSM_FILL_ZERO_WRITE.

Referenced by dsm_attach(), dsm_backend_startup(), dsm_cleanup_using_control_segment(), dsm_create(), dsm_detach(), dsm_detach_all(), dsm_postmaster_shutdown(), dsm_postmaster_startup(), and dsm_unpin_segment().

164 {
165  Assert(op == DSM_OP_CREATE || request_size == 0);
166  Assert((op != DSM_OP_CREATE && op != DSM_OP_ATTACH) ||
167  (*mapped_address == NULL && *mapped_size == 0));
168 
170  {
171 #ifdef USE_DSM_POSIX
172  case DSM_IMPL_POSIX:
173  return dsm_impl_posix(op, handle, request_size, impl_private,
174  mapped_address, mapped_size, elevel);
175 #endif
176 #ifdef USE_DSM_SYSV
177  case DSM_IMPL_SYSV:
178  return dsm_impl_sysv(op, handle, request_size, impl_private,
179  mapped_address, mapped_size, elevel);
180 #endif
181 #ifdef USE_DSM_WINDOWS
182  case DSM_IMPL_WINDOWS:
183  return dsm_impl_windows(op, handle, request_size, impl_private,
184  mapped_address, mapped_size, elevel);
185 #endif
186 #ifdef USE_DSM_MMAP
187  case DSM_IMPL_MMAP:
188  return dsm_impl_mmap(op, handle, request_size, impl_private,
189  mapped_address, mapped_size, elevel);
190 #endif
191  default:
192  elog(ERROR, "unexpected dynamic shared memory type: %d",
194  return false;
195  }
196 }
#define DSM_IMPL_MMAP
Definition: dsm_impl.h:20
#define DSM_IMPL_SYSV
Definition: dsm_impl.h:18
#define DSM_IMPL_WINDOWS
Definition: dsm_impl.h:19
static bool dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size, void **impl_private, void **mapped_address, Size *mapped_size, int elevel)
Definition: dsm_impl.c:784
#define ERROR
Definition: elog.h:43
#define DSM_IMPL_POSIX
Definition: dsm_impl.h:17
int dynamic_shared_memory_type
Definition: dsm_impl.c:114
static int elevel
Definition: vacuumlazy.c:333
static bool dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size, void **impl_private, void **mapped_address, Size *mapped_size, int elevel)
Definition: dsm_impl.c:415
#define Assert(condition)
Definition: c.h:800
#define elog(elevel,...)
Definition: elog.h:228

◆ dsm_impl_pin_segment()

void dsm_impl_pin_segment ( dsm_handle  handle,
void *  impl_private,
void **  impl_private_pm_handle 
)

Definition at line 955 of file dsm_impl.c.

References _dosmaperr(), DSM_IMPL_WINDOWS, dynamic_shared_memory_type, ereport, errcode_for_dynamic_shared_memory(), errmsg(), ERROR, name, SEGMENT_NAME_PREFIX, and snprintf.

Referenced by dsm_pin_segment().

957 {
959  {
960 #ifdef USE_DSM_WINDOWS
961  case DSM_IMPL_WINDOWS:
962  {
963  HANDLE hmap;
964 
965  if (!DuplicateHandle(GetCurrentProcess(), impl_private,
966  PostmasterHandle, &hmap, 0, FALSE,
967  DUPLICATE_SAME_ACCESS))
968  {
969  char name[64];
970 
971  snprintf(name, 64, "%s.%u", SEGMENT_NAME_PREFIX, handle);
972  _dosmaperr(GetLastError());
973  ereport(ERROR,
975  errmsg("could not duplicate handle for \"%s\": %m",
976  name)));
977  }
978 
979  /*
980  * Here, we remember the handle that we created in the
981  * postmaster process. This handle isn't actually usable in
982  * any process other than the postmaster, but that doesn't
983  * matter. We're just holding onto it so that, if the segment
984  * is unpinned, dsm_impl_unpin_segment can close it.
985  */
986  *impl_private_pm_handle = hmap;
987  break;
988  }
989 #endif
990  default:
991  break;
992  }
993 }
void _dosmaperr(unsigned long)
Definition: win32error.c:171
#define DSM_IMPL_WINDOWS
Definition: dsm_impl.h:19
static int errcode_for_dynamic_shared_memory(void)
Definition: dsm_impl.c:1037
#define ERROR
Definition: elog.h:43
#define SEGMENT_NAME_PREFIX
Definition: dsm_impl.c:122
int dynamic_shared_memory_type
Definition: dsm_impl.c:114
#define ereport(elevel,...)
Definition: elog.h:155
const char * name
Definition: encode.c:561
int errmsg(const char *fmt,...)
Definition: elog.c:902
#define snprintf
Definition: port.h:215

◆ dsm_impl_sysv()

static bool dsm_impl_sysv ( dsm_op  op,
dsm_handle  handle,
Size  request_size,
void **  impl_private,
void **  mapped_address,
Size mapped_size,
int  elevel 
)
static

Definition at line 415 of file dsm_impl.c.

References _dosmaperr(), DEBUG4, DSM_OP_ATTACH, DSM_OP_CREATE, DSM_OP_DESTROY, DSM_OP_DETACH, elevel, elog, ereport, errcode(), errcode_for_dynamic_shared_memory(), errmsg(), IPC_CREAT, IPC_EXCL, IPC_PRIVATE, IPC_RMID, IPC_STAT, IPCProtection, sort-test::key, MemoryContextAlloc(), name, pfree(), PG_SHMAT_FLAGS, SEGMENT_NAME_PREFIX, snprintf, and TopMemoryContext.

Referenced by dsm_impl_op().

418 {
419  key_t key;
420  int ident;
421  char *address;
422  char name[64];
423  int *ident_cache;
424 
425  /*
426  * POSIX shared memory and mmap-based shared memory identify segments with
427  * names. To avoid needless error message variation, we use the handle as
428  * the name.
429  */
430  snprintf(name, 64, "%u", handle);
431 
432  /*
433  * The System V shared memory namespace is very restricted; names are of
434  * type key_t, which is expected to be some sort of integer data type, but
435  * not necessarily the same one as dsm_handle. Since we use dsm_handle to
436  * identify shared memory segments across processes, this might seem like
437  * a problem, but it's really not. If dsm_handle is bigger than key_t,
438  * the cast below might truncate away some bits from the handle the
439  * user-provided, but it'll truncate exactly the same bits away in exactly
440  * the same fashion every time we use that handle, which is all that
441  * really matters. Conversely, if dsm_handle is smaller than key_t, we
442  * won't use the full range of available key space, but that's no big deal
443  * either.
444  *
445  * We do make sure that the key isn't negative, because that might not be
446  * portable.
447  */
448  key = (key_t) handle;
449  if (key < 1) /* avoid compiler warning if type is unsigned */
450  key = -key;
451 
452  /*
453  * There's one special key, IPC_PRIVATE, which can't be used. If we end
454  * up with that value by chance during a create operation, just pretend it
455  * already exists, so that caller will retry. If we run into it anywhere
456  * else, the caller has passed a handle that doesn't correspond to
457  * anything we ever created, which should not happen.
458  */
459  if (key == IPC_PRIVATE)
460  {
461  if (op != DSM_OP_CREATE)
462  elog(DEBUG4, "System V shared memory key may not be IPC_PRIVATE");
463  errno = EEXIST;
464  return false;
465  }
466 
467  /*
468  * Before we can do anything with a shared memory segment, we have to map
469  * the shared memory key to a shared memory identifier using shmget(). To
470  * avoid repeated lookups, we store the key using impl_private.
471  */
472  if (*impl_private != NULL)
473  {
474  ident_cache = *impl_private;
475  ident = *ident_cache;
476  }
477  else
478  {
479  int flags = IPCProtection;
480  size_t segsize;
481 
482  /*
483  * Allocate the memory BEFORE acquiring the resource, so that we don't
484  * leak the resource if memory allocation fails.
485  */
486  ident_cache = MemoryContextAlloc(TopMemoryContext, sizeof(int));
487 
488  /*
489  * When using shmget to find an existing segment, we must pass the
490  * size as 0. Passing a non-zero size which is greater than the
491  * actual size will result in EINVAL.
492  */
493  segsize = 0;
494 
495  if (op == DSM_OP_CREATE)
496  {
497  flags |= IPC_CREAT | IPC_EXCL;
498  segsize = request_size;
499  }
500 
501  if ((ident = shmget(key, segsize, flags)) == -1)
502  {
503  if (errno != EEXIST)
504  {
505  int save_errno = errno;
506 
507  pfree(ident_cache);
508  errno = save_errno;
509  ereport(elevel,
511  errmsg("could not get shared memory segment: %m")));
512  }
513  return false;
514  }
515 
516  *ident_cache = ident;
517  *impl_private = ident_cache;
518  }
519 
520  /* Handle teardown cases. */
521  if (op == DSM_OP_DETACH || op == DSM_OP_DESTROY)
522  {
523  pfree(ident_cache);
524  *impl_private = NULL;
525  if (*mapped_address != NULL && shmdt(*mapped_address) != 0)
526  {
527  ereport(elevel,
529  errmsg("could not unmap shared memory segment \"%s\": %m",
530  name)));
531  return false;
532  }
533  *mapped_address = NULL;
534  *mapped_size = 0;
535  if (op == DSM_OP_DESTROY && shmctl(ident, IPC_RMID, NULL) < 0)
536  {
537  ereport(elevel,
539  errmsg("could not remove shared memory segment \"%s\": %m",
540  name)));
541  return false;
542  }
543  return true;
544  }
545 
546  /* If we're attaching it, we must use IPC_STAT to determine the size. */
547  if (op == DSM_OP_ATTACH)
548  {
549  struct shmid_ds shm;
550 
551  if (shmctl(ident, IPC_STAT, &shm) != 0)
552  {
553  ereport(elevel,
555  errmsg("could not stat shared memory segment \"%s\": %m",
556  name)));
557  return false;
558  }
559  request_size = shm.shm_segsz;
560  }
561 
562  /* Map it. */
563  address = shmat(ident, NULL, PG_SHMAT_FLAGS);
564  if (address == (void *) -1)
565  {
566  int save_errno;
567 
568  /* Back out what's already been done. */
569  save_errno = errno;
570  if (op == DSM_OP_CREATE)
571  shmctl(ident, IPC_RMID, NULL);
572  errno = save_errno;
573 
574  ereport(elevel,
576  errmsg("could not map shared memory segment \"%s\": %m",
577  name)));
578  return false;
579  }
580  *mapped_address = address;
581  *mapped_size = request_size;
582 
583  return true;
584 }
#define IPC_CREAT
Definition: win32_port.h:87
#define IPCProtection
Definition: posix_sema.c:59
#define PG_SHMAT_FLAGS
Definition: mem.h:20
#define DEBUG4
Definition: elog.h:22
static int errcode_for_dynamic_shared_memory(void)
Definition: dsm_impl.c:1037
void pfree(void *pointer)
Definition: mcxt.c:1057
#define IPC_PRIVATE
Definition: win32_port.h:89
MemoryContext TopMemoryContext
Definition: mcxt.c:44
static int elevel
Definition: vacuumlazy.c:333
#define IPC_RMID
Definition: win32_port.h:86
#define ereport(elevel,...)
Definition: elog.h:155
long key_t
Definition: win32_port.h:239
#define IPC_EXCL
Definition: win32_port.h:88
const char * name
Definition: encode.c:561
int errmsg(const char *fmt,...)
Definition: elog.c:902
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
#define elog(elevel,...)
Definition: elog.h:228
#define snprintf
Definition: port.h:215
#define IPC_STAT
Definition: win32_port.h:91

◆ dsm_impl_unpin_segment()

void dsm_impl_unpin_segment ( dsm_handle  handle,
void **  impl_private 
)

Definition at line 1005 of file dsm_impl.c.

References _dosmaperr(), DSM_IMPL_WINDOWS, dynamic_shared_memory_type, ereport, errcode_for_dynamic_shared_memory(), errmsg(), ERROR, name, SEGMENT_NAME_PREFIX, and snprintf.

Referenced by dsm_unpin_segment().

1006 {
1008  {
1009 #ifdef USE_DSM_WINDOWS
1010  case DSM_IMPL_WINDOWS:
1011  {
1012  if (*impl_private &&
1013  !DuplicateHandle(PostmasterHandle, *impl_private,
1014  NULL, NULL, 0, FALSE,
1015  DUPLICATE_CLOSE_SOURCE))
1016  {
1017  char name[64];
1018 
1019  snprintf(name, 64, "%s.%u", SEGMENT_NAME_PREFIX, handle);
1020  _dosmaperr(GetLastError());
1021  ereport(ERROR,
1023  errmsg("could not duplicate handle for \"%s\": %m",
1024  name)));
1025  }
1026 
1027  *impl_private = NULL;
1028  break;
1029  }
1030 #endif
1031  default:
1032  break;
1033  }
1034 }
void _dosmaperr(unsigned long)
Definition: win32error.c:171
#define DSM_IMPL_WINDOWS
Definition: dsm_impl.h:19
static int errcode_for_dynamic_shared_memory(void)
Definition: dsm_impl.c:1037
#define ERROR
Definition: elog.h:43
#define SEGMENT_NAME_PREFIX
Definition: dsm_impl.c:122
int dynamic_shared_memory_type
Definition: dsm_impl.c:114
#define ereport(elevel,...)
Definition: elog.h:155
const char * name
Definition: encode.c:561
int errmsg(const char *fmt,...)
Definition: elog.c:902
#define snprintf
Definition: port.h:215

◆ errcode_for_dynamic_shared_memory()

static int errcode_for_dynamic_shared_memory ( void  )
static

Definition at line 1037 of file dsm_impl.c.

References errcode(), and errcode_for_file_access().

Referenced by dsm_impl_mmap(), dsm_impl_op(), dsm_impl_pin_segment(), dsm_impl_sysv(), and dsm_impl_unpin_segment().

1038 {
1039  if (errno == EFBIG || errno == ENOMEM)
1040  return errcode(ERRCODE_OUT_OF_MEMORY);
1041  else
1042  return errcode_for_file_access();
1043 }
int errcode(int sqlerrcode)
Definition: elog.c:691
int errcode_for_file_access(void)
Definition: elog.c:714

Variable Documentation

◆ dynamic_shared_memory_options

const struct config_enum_entry dynamic_shared_memory_options[]
Initial value:
= {
{"sysv", DSM_IMPL_SYSV, false},
{"mmap", DSM_IMPL_MMAP, false},
{NULL, 0, false}
}
#define DSM_IMPL_MMAP
Definition: dsm_impl.h:20
#define DSM_IMPL_SYSV
Definition: dsm_impl.h:18

Definition at line 97 of file dsm_impl.c.

◆ dynamic_shared_memory_type

int dynamic_shared_memory_type

◆ min_dynamic_shared_memory

int min_dynamic_shared_memory

Definition at line 117 of file dsm_impl.c.

Referenced by dsm_estimate_size().