]> granicus.if.org Git - postgresql/blobdiff - src/backend/port/sysv_shmem.c
Update copyright for 2016
[postgresql] / src / backend / port / sysv_shmem.c
index f7596bf6e0b8d5a66df2ad01559c162b550fd6af..6c442b927a2ab5d07f0e760ebf2ab5904b997bc0 100644 (file)
@@ -6,7 +6,7 @@
  * These routines represent a fairly thin layer on top of SysV shared
  * memory functionality.
  *
- * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
@@ -30,6 +30,7 @@
 
 #include "miscadmin.h"
 #include "portability/mem.h"
+#include "storage/dsm.h"
 #include "storage/ipc.h"
 #include "storage/pg_shmem.h"
 #include "utils/guc.h"
@@ -73,15 +74,17 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 
        if (shmid < 0)
        {
+               int                     shmget_errno = errno;
+
                /*
                 * Fail quietly if error indicates a collision with existing segment.
                 * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
                 * we could get a permission violation instead?  Also, EIDRM might
                 * occur if an old seg is slated for destruction but not gone yet.
                 */
-               if (errno == EEXIST || errno == EACCES
+               if (shmget_errno == EEXIST || shmget_errno == EACCES
 #ifdef EIDRM
-                       || errno == EIDRM
+                       || shmget_errno == EIDRM
 #endif
                        )
                        return NULL;
@@ -95,10 +98,8 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
                 * against SHMMIN in the preexisting-segment case, so we will not get
                 * EINVAL a second time if there is such a segment.
                 */
-               if (errno == EINVAL)
+               if (shmget_errno == EINVAL)
                {
-                       int                     save_errno = errno;
-
                        shmid = shmget(memKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection);
 
                        if (shmid < 0)
@@ -124,8 +125,6 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
                                        elog(LOG, "shmctl(%d, %d, 0) failed: %m",
                                                 (int) shmid, IPC_RMID);
                        }
-
-                       errno = save_errno;
                }
 
                /*
@@ -137,25 +136,26 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
                 * it should be.  SHMMNI violation is ENOSPC, per spec.  Just plain
                 * not-enough-RAM is ENOMEM.
                 */
+               errno = shmget_errno;
                ereport(FATAL,
                                (errmsg("could not create shared memory segment: %m"),
                  errdetail("Failed system call was shmget(key=%lu, size=%zu, 0%o).",
                                        (unsigned long) memKey, size,
                                        IPC_CREAT | IPC_EXCL | IPCProtection),
-                                (errno == EINVAL) ?
+                                (shmget_errno == EINVAL) ?
                                 errhint("This error usually means that PostgreSQL's request for a shared memory "
                 "segment exceeded your kernel's SHMMAX parameter, or possibly that "
                                                 "it is less than "
                                                 "your kernel's SHMMIN parameter.\n"
                "The PostgreSQL documentation contains more information about shared "
                                                 "memory configuration.") : 0,
-                                (errno == ENOMEM) ?
+                                (shmget_errno == ENOMEM) ?
                                 errhint("This error usually means that PostgreSQL's request for a shared "
                                                 "memory segment exceeded your kernel's SHMALL parameter.  You might need "
                                                 "to reconfigure the kernel with larger SHMALL.\n"
                "The PostgreSQL documentation contains more information about shared "
                                                 "memory configuration.") : 0,
-                                (errno == ENOSPC) ?
+                                (shmget_errno == ENOSPC) ?
                                 errhint("This error does *not* mean that you have run out of disk space.  "
                                                 "It occurs either if all available shared memory IDs have been taken, "
                                                 "in which case you need to raise the SHMMNI parameter in your kernel, "
@@ -195,7 +195,7 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
 
 /****************************************************************************/
 /*     IpcMemoryDetach(status, shmaddr)        removes a shared memory segment         */
-/*                                                                             from process' address spaceq            */
+/*                                                                             from process' address space                     */
 /*     (called as an on_shmem_exit callback, hence funny argument list)                */
 /****************************************************************************/
 static void
@@ -228,7 +228,7 @@ IpcMemoryDelete(int status, Datum shmId)
  * Is a previously-existing shmem segment still existing and in use?
  *
  * The point of this exercise is to detect the case where a prior postmaster
- * crashed, but it left child backends that are still running. Therefore
+ * crashed, but it left child backends that are still running.  Therefore
  * we only care about shmem segments that are associated with the intended
  * DataDir.  This is an important consideration since accidental matches of
  * shmem segment IDs are reasonably common.
@@ -329,16 +329,17 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
 static void *
 CreateAnonymousSegment(Size *size)
 {
-       Size            allocsize;
+       Size            allocsize = *size;
        void       *ptr = MAP_FAILED;
+       int                     mmap_errno = 0;
 
 #ifndef MAP_HUGETLB
-       if (huge_tlb_pages == HUGE_TLB_ON)
+       if (huge_pages == HUGE_PAGES_ON)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("huge TLB pages not supported on this platform")));
 #else
-       if (huge_tlb_pages == HUGE_TLB_ON || huge_tlb_pages == HUGE_TLB_TRY)
+       if (huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY)
        {
                /*
                 * Round up the request size to a suitable large value.
@@ -358,35 +359,44 @@ CreateAnonymousSegment(Size *size)
                 */
                int                     hugepagesize = 2 * 1024 * 1024;
 
-               allocsize = *size;
                if (allocsize % hugepagesize != 0)
                        allocsize += hugepagesize - (allocsize % hugepagesize);
 
-               ptr = mmap(NULL, *size, PROT_READ | PROT_WRITE,
+               ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
                                   PG_MMAP_FLAGS | MAP_HUGETLB, -1, 0);
-               if (huge_tlb_pages == HUGE_TLB_TRY && ptr == MAP_FAILED)
+               mmap_errno = errno;
+               if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
                        elog(DEBUG1, "mmap with MAP_HUGETLB failed, huge pages disabled: %m");
        }
 #endif
 
-       if (huge_tlb_pages == HUGE_TLB_OFF ||
-               (huge_tlb_pages == HUGE_TLB_TRY && ptr == MAP_FAILED))
+       if (huge_pages == HUGE_PAGES_OFF ||
+               (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED))
        {
+               /*
+                * use the original size, not the rounded up value, when falling back
+                * to non-huge pages.
+                */
                allocsize = *size;
-               ptr = mmap(NULL, *size, PROT_READ | PROT_WRITE, PG_MMAP_FLAGS, -1, 0);
+               ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
+                                  PG_MMAP_FLAGS, -1, 0);
+               mmap_errno = errno;
        }
 
        if (ptr == MAP_FAILED)
+       {
+               errno = mmap_errno;
                ereport(FATAL,
                                (errmsg("could not map anonymous shared memory: %m"),
-                                (errno == ENOMEM) ?
+                                (mmap_errno == ENOMEM) ?
                                 errhint("This error usually means that PostgreSQL's request "
                                        "for a shared memory segment exceeded available memory, "
-                                         "swap space or huge pages. To reduce the request size "
-                                                "(currently  %zu bytes), reduce PostgreSQL's shared "
+                                        "swap space, or huge pages. To reduce the request size "
+                                                "(currently %zu bytes), reduce PostgreSQL's shared "
                                           "memory usage, perhaps by reducing shared_buffers or "
                                                 "max_connections.",
                                                 *size) : 0));
+       }
 
        *size = allocsize;
        return ptr;
@@ -401,18 +411,19 @@ CreateAnonymousSegment(Size *size)
  * the storage.
  *
  * Dead Postgres segments are recycled if found, but we do not fail upon
- * collision with non-Postgres shmem segments. The idea here is to detect and
+ * collision with non-Postgres shmem segments.  The idea here is to detect and
  * re-use keys that may have been assigned by a crashed postmaster or backend.
  *
  * makePrivate means to always create a new segment, rather than attach to
  * or recycle any existing segment.
  *
  * The port number is passed for possible use as a key (for SysV, we use
- * it to generate the starting shmem key).     In a standalone backend,
+ * it to generate the starting shmem key).  In a standalone backend,
  * zero will be passed.
  */
 PGShmemHeader *
-PGSharedMemoryCreate(Size size, bool makePrivate, int port)
+PGSharedMemoryCreate(Size size, bool makePrivate, int port,
+                                        PGShmemHeader **shim)
 {
        IpcMemoryKey NextShmemSegID;
        void       *memAddress;
@@ -422,10 +433,10 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
        Size            sysvsize;
 
 #if defined(EXEC_BACKEND) || !defined(MAP_HUGETLB)
-       if (huge_tlb_pages == HUGE_TLB_ON)
+       if (huge_pages == HUGE_PAGES_ON)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("huge TLB pages not supported on this platform")));
+                                errmsg("huge pages not supported on this platform")));
 #endif
 
        /* Room for a header? */
@@ -500,10 +511,13 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
 
                /*
                 * The segment appears to be from a dead Postgres process, or from a
-                * previous cycle of life in this same process.  Zap it, if possible.
-                * This probably shouldn't fail, but if it does, assume the segment
-                * belongs to someone else after all, and continue quietly.
+                * previous cycle of life in this same process.  Zap it, if possible,
+                * and any associated dynamic shared memory segments, as well. This
+                * probably shouldn't fail, but if it does, assume the segment belongs
+                * to someone else after all, and continue quietly.
                 */
+               if (hdr->dsm_control != 0)
+                       dsm_cleanup_using_control_segment(hdr->dsm_control);
                shmdt(memAddress);
                if (shmctl(shmid, IPC_RMID, NULL) < 0)
                        continue;
@@ -530,6 +544,7 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
        hdr = (PGShmemHeader *) memAddress;
        hdr->creatorPID = getpid();
        hdr->magic = PGShmemMagic;
+       hdr->dsm_control = 0;
 
        /* Fill in the data directory ID info, too */
        if (stat(DataDir, &statbuf) < 0)
@@ -545,6 +560,7 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
         */
        hdr->totalsize = size;
        hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
+       *shim = hdr;
 
        /* Save info for possible future use */
        UsedShmemSegAddr = memAddress;
@@ -567,9 +583,10 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
 /*
  * PGSharedMemoryReAttach
  *
- * Re-attach to an already existing shared memory segment.     In the non
- * EXEC_BACKEND case this is not used, because postmaster children inherit
- * the shared memory segment attachment via fork().
+ * This is called during startup of a postmaster child process to re-attach to
+ * an already existing shared memory segment.  This is needed only in the
+ * EXEC_BACKEND case; otherwise postmaster children inherit the shared memory
+ * segment attachment via fork().
  *
  * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
  * routine.  The caller must have already restored them to the postmaster's
@@ -599,19 +616,56 @@ PGSharedMemoryReAttach(void)
        if (hdr != origUsedShmemSegAddr)
                elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
                         hdr, origUsedShmemSegAddr);
+       dsm_set_control_handle(((PGShmemHeader *) hdr)->dsm_control);
 
        UsedShmemSegAddr = hdr;         /* probably redundant */
 }
+
+/*
+ * PGSharedMemoryNoReAttach
+ *
+ * This is called during startup of a postmaster child process when we choose
+ * *not* to re-attach to the existing shared memory segment.  We must clean up
+ * to leave things in the appropriate state.  This is not used in the non
+ * EXEC_BACKEND case, either.
+ *
+ * The child process startup logic might or might not call PGSharedMemoryDetach
+ * after this; make sure that it will be a no-op if called.
+ *
+ * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
+ * routine.  The caller must have already restored them to the postmaster's
+ * values.
+ */
+void
+PGSharedMemoryNoReAttach(void)
+{
+       Assert(UsedShmemSegAddr != NULL);
+       Assert(IsUnderPostmaster);
+
+#ifdef __CYGWIN__
+       /* cygipc (currently) appears to not detach on exec. */
+       PGSharedMemoryDetach();
+#endif
+
+       /* For cleanliness, reset UsedShmemSegAddr to show we're not attached. */
+       UsedShmemSegAddr = NULL;
+       /* And the same for UsedShmemSegID. */
+       UsedShmemSegID = 0;
+}
+
 #endif   /* EXEC_BACKEND */
 
 /*
  * PGSharedMemoryDetach
  *
  * Detach from the shared memory segment, if still attached.  This is not
- * intended for use by the process that originally created the segment
- * (it will have an on_shmem_exit callback registered to do that).     Rather,
- * this is for subprocesses that have inherited an attachment and want to
- * get rid of it.
+ * intended to be called explicitly by the process that originally created the
+ * segment (it will have an on_shmem_exit callback registered to do that).
+ * Rather, this is for subprocesses that have inherited an attachment and want
+ * to get rid of it.
+ *
+ * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
+ * routine.
  */
 void
 PGSharedMemoryDetach(void)