Header And Logo

PostgreSQL
| The world's most advanced open source database.

shmem.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * shmem.c
00004  *    create shared memory and initialize shared memory data structures.
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/storage/ipc/shmem.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 /*
00016  * POSTGRES processes share one or more regions of shared memory.
00017  * The shared memory is created by a postmaster and is inherited
00018  * by each backend via fork() (or, in some ports, via other OS-specific
00019  * methods).  The routines in this file are used for allocating and
00020  * binding to shared memory data structures.
00021  *
00022  * NOTES:
00023  *      (a) There are three kinds of shared memory data structures
00024  *  available to POSTGRES: fixed-size structures, queues and hash
00025  *  tables.  Fixed-size structures contain things like global variables
00026  *  for a module and should never be allocated after the shared memory
00027  *  initialization phase.  Hash tables have a fixed maximum size, but
00028  *  their actual size can vary dynamically.  When entries are added
00029  *  to the table, more space is allocated.  Queues link data structures
00030  *  that have been allocated either within fixed-size structures or as hash
00031  *  buckets.  Each shared data structure has a string name to identify
00032  *  it (assigned in the module that declares it).
00033  *
00034  *      (b) During initialization, each module looks for its
00035  *  shared data structures in a hash table called the "Shmem Index".
00036  *  If the data structure is not present, the caller can allocate
00037  *  a new one and initialize it.  If the data structure is present,
00038  *  the caller "attaches" to the structure by initializing a pointer
00039  *  in the local address space.
00040  *      The shmem index has two purposes: first, it gives us
00041  *  a simple model of how the world looks when a backend process
00042  *  initializes.  If something is present in the shmem index,
00043  *  it is initialized.  If it is not, it is uninitialized.  Second,
00044  *  the shmem index allows us to allocate shared memory on demand
00045  *  instead of trying to preallocate structures and hard-wire the
00046  *  sizes and locations in header files.  If you are using a lot
00047  *  of shared memory in a lot of different places (and changing
00048  *  things during development), this is important.
00049  *
00050  *      (c) In standard Unix-ish environments, individual backends do not
00051  *  need to re-establish their local pointers into shared memory, because
00052  *  they inherit correct values of those variables via fork() from the
00053  *  postmaster.  However, this does not work in the EXEC_BACKEND case.
00054  *  In ports using EXEC_BACKEND, new backends have to set up their local
00055  *  pointers using the method described in (b) above.
00056  *
00057  *      (d) memory allocation model: shared memory can never be
00058  *  freed, once allocated.   Each hash table has its own free list,
00059  *  so hash buckets can be reused when an item is deleted.  However,
00060  *  if one hash table grows very large and then shrinks, its space
00061  *  cannot be redistributed to other tables.  We could build a simple
00062  *  hash bucket garbage collector if need be.  Right now, it seems
00063  *  unnecessary.
00064  */
00065 
00066 #include "postgres.h"
00067 
00068 #include "access/transam.h"
00069 #include "miscadmin.h"
00070 #include "storage/lwlock.h"
00071 #include "storage/pg_shmem.h"
00072 #include "storage/shmem.h"
00073 #include "storage/spin.h"
00074 
00075 
00076 /* shared memory global variables */
00077 
00078 static PGShmemHeader *ShmemSegHdr;      /* shared mem segment header */
00079 
00080 static void *ShmemBase;         /* start address of shared memory */
00081 
00082 static void *ShmemEnd;          /* end+1 address of shared memory */
00083 
00084 slock_t    *ShmemLock;          /* spinlock for shared memory and LWLock
00085                                  * allocation */
00086 
00087 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
00088 
00089 
00090 /*
00091  *  InitShmemAccess() --- set up basic pointers to shared memory.
00092  *
00093  * Note: the argument should be declared "PGShmemHeader *seghdr",
00094  * but we use void to avoid having to include ipc.h in shmem.h.
00095  */
00096 void
00097 InitShmemAccess(void *seghdr)
00098 {
00099     PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
00100 
00101     ShmemSegHdr = shmhdr;
00102     ShmemBase = (void *) shmhdr;
00103     ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
00104 }
00105 
00106 /*
00107  *  InitShmemAllocation() --- set up shared-memory space allocation.
00108  *
00109  * This should be called only in the postmaster or a standalone backend.
00110  */
00111 void
00112 InitShmemAllocation(void)
00113 {
00114     PGShmemHeader *shmhdr = ShmemSegHdr;
00115 
00116     Assert(shmhdr != NULL);
00117 
00118     /*
00119      * Initialize the spinlock used by ShmemAlloc.  We have to do the space
00120      * allocation the hard way, since obviously ShmemAlloc can't be called
00121      * yet.
00122      */
00123     ShmemLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset);
00124     shmhdr->freeoffset += MAXALIGN(sizeof(slock_t));
00125     Assert(shmhdr->freeoffset <= shmhdr->totalsize);
00126 
00127     SpinLockInit(ShmemLock);
00128 
00129     /* ShmemIndex can't be set up yet (need LWLocks first) */
00130     shmhdr->index = NULL;
00131     ShmemIndex = (HTAB *) NULL;
00132 
00133     /*
00134      * Initialize ShmemVariableCache for transaction manager. (This doesn't
00135      * really belong here, but not worth moving.)
00136      */
00137     ShmemVariableCache = (VariableCache)
00138         ShmemAlloc(sizeof(*ShmemVariableCache));
00139     memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
00140 }
00141 
00142 /*
00143  * ShmemAlloc -- allocate max-aligned chunk from shared memory
00144  *
00145  * Assumes ShmemLock and ShmemSegHdr are initialized.
00146  *
00147  * Returns: real pointer to memory or NULL if we are out
00148  *      of space.  Has to return a real pointer in order
00149  *      to be compatible with malloc().
00150  */
00151 void *
00152 ShmemAlloc(Size size)
00153 {
00154     Size        newStart;
00155     Size        newFree;
00156     void       *newSpace;
00157 
00158     /* use volatile pointer to prevent code rearrangement */
00159     volatile PGShmemHeader *shmemseghdr = ShmemSegHdr;
00160 
00161     /*
00162      * ensure all space is adequately aligned.
00163      */
00164     size = MAXALIGN(size);
00165 
00166     Assert(shmemseghdr != NULL);
00167 
00168     SpinLockAcquire(ShmemLock);
00169 
00170     newStart = shmemseghdr->freeoffset;
00171 
00172     /* extra alignment for large requests, since they are probably buffers */
00173     if (size >= BLCKSZ)
00174         newStart = BUFFERALIGN(newStart);
00175 
00176     newFree = newStart + size;
00177     if (newFree <= shmemseghdr->totalsize)
00178     {
00179         newSpace = (void *) ((char *) ShmemBase + newStart);
00180         shmemseghdr->freeoffset = newFree;
00181     }
00182     else
00183         newSpace = NULL;
00184 
00185     SpinLockRelease(ShmemLock);
00186 
00187     if (!newSpace)
00188         ereport(WARNING,
00189                 (errcode(ERRCODE_OUT_OF_MEMORY),
00190                  errmsg("out of shared memory")));
00191 
00192     return newSpace;
00193 }
00194 
00195 /*
00196  * ShmemAddrIsValid -- test if an address refers to shared memory
00197  *
00198  * Returns TRUE if the pointer points within the shared memory segment.
00199  */
00200 bool
00201 ShmemAddrIsValid(const void *addr)
00202 {
00203     return (addr >= ShmemBase) && (addr < ShmemEnd);
00204 }
00205 
00206 /*
00207  *  InitShmemIndex() --- set up or attach to shmem index table.
00208  */
00209 void
00210 InitShmemIndex(void)
00211 {
00212     HASHCTL     info;
00213     int         hash_flags;
00214 
00215     /*
00216      * Create the shared memory shmem index.
00217      *
00218      * Since ShmemInitHash calls ShmemInitStruct, which expects the ShmemIndex
00219      * hashtable to exist already, we have a bit of a circularity problem in
00220      * initializing the ShmemIndex itself.  The special "ShmemIndex" hash
00221      * table name will tell ShmemInitStruct to fake it.
00222      */
00223     info.keysize = SHMEM_INDEX_KEYSIZE;
00224     info.entrysize = sizeof(ShmemIndexEnt);
00225     hash_flags = HASH_ELEM;
00226 
00227     ShmemIndex = ShmemInitHash("ShmemIndex",
00228                                SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE,
00229                                &info, hash_flags);
00230 }
00231 
00232 /*
00233  * ShmemInitHash -- Create and initialize, or attach to, a
00234  *      shared memory hash table.
00235  *
00236  * We assume caller is doing some kind of synchronization
00237  * so that two processes don't try to create/initialize the same
00238  * table at once.  (In practice, all creations are done in the postmaster
00239  * process; child processes should always be attaching to existing tables.)
00240  *
00241  * max_size is the estimated maximum number of hashtable entries.  This is
00242  * not a hard limit, but the access efficiency will degrade if it is
00243  * exceeded substantially (since it's used to compute directory size and
00244  * the hash table buckets will get overfull).
00245  *
00246  * init_size is the number of hashtable entries to preallocate.  For a table
00247  * whose maximum size is certain, this should be equal to max_size; that
00248  * ensures that no run-time out-of-shared-memory failures can occur.
00249  *
00250  * Note: before Postgres 9.0, this function returned NULL for some failure
00251  * cases.  Now, it always throws error instead, so callers need not check
00252  * for NULL.
00253  */
00254 HTAB *
00255 ShmemInitHash(const char *name, /* table string name for shmem index */
00256               long init_size,   /* initial table size */
00257               long max_size,    /* max size of the table */
00258               HASHCTL *infoP,   /* info about key and bucket size */
00259               int hash_flags)   /* info about infoP */
00260 {
00261     bool        found;
00262     void       *location;
00263 
00264     /*
00265      * Hash tables allocated in shared memory have a fixed directory; it can't
00266      * grow or other backends wouldn't be able to find it. So, make sure we
00267      * make it big enough to start with.
00268      *
00269      * The shared memory allocator must be specified too.
00270      */
00271     infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size);
00272     infoP->alloc = ShmemAlloc;
00273     hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
00274 
00275     /* look it up in the shmem index */
00276     location = ShmemInitStruct(name,
00277                                hash_get_shared_size(infoP, hash_flags),
00278                                &found);
00279 
00280     /*
00281      * if it already exists, attach to it rather than allocate and initialize
00282      * new space
00283      */
00284     if (found)
00285         hash_flags |= HASH_ATTACH;
00286 
00287     /* Pass location of hashtable header to hash_create */
00288     infoP->hctl = (HASHHDR *) location;
00289 
00290     return hash_create(name, init_size, infoP, hash_flags);
00291 }
00292 
00293 /*
00294  * ShmemInitStruct -- Create/attach to a structure in shared memory.
00295  *
00296  *      This is called during initialization to find or allocate
00297  *      a data structure in shared memory.  If no other process
00298  *      has created the structure, this routine allocates space
00299  *      for it.  If it exists already, a pointer to the existing
00300  *      structure is returned.
00301  *
00302  *  Returns: pointer to the object.  *foundPtr is set TRUE if the object was
00303  *      already in the shmem index (hence, already initialized).
00304  *
00305  *  Note: before Postgres 9.0, this function returned NULL for some failure
00306  *  cases.  Now, it always throws error instead, so callers need not check
00307  *  for NULL.
00308  */
00309 void *
00310 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
00311 {
00312     ShmemIndexEnt *result;
00313     void       *structPtr;
00314 
00315     LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
00316 
00317     if (!ShmemIndex)
00318     {
00319         PGShmemHeader *shmemseghdr = ShmemSegHdr;
00320 
00321         /* Must be trying to create/attach to ShmemIndex itself */
00322         Assert(strcmp(name, "ShmemIndex") == 0);
00323 
00324         if (IsUnderPostmaster)
00325         {
00326             /* Must be initializing a (non-standalone) backend */
00327             Assert(shmemseghdr->index != NULL);
00328             structPtr = shmemseghdr->index;
00329             *foundPtr = TRUE;
00330         }
00331         else
00332         {
00333             /*
00334              * If the shmem index doesn't exist, we are bootstrapping: we must
00335              * be trying to init the shmem index itself.
00336              *
00337              * Notice that the ShmemIndexLock is released before the shmem
00338              * index has been initialized.  This should be OK because no other
00339              * process can be accessing shared memory yet.
00340              */
00341             Assert(shmemseghdr->index == NULL);
00342             structPtr = ShmemAlloc(size);
00343             if (structPtr == NULL)
00344                 ereport(ERROR,
00345                         (errcode(ERRCODE_OUT_OF_MEMORY),
00346                          errmsg("not enough shared memory for data structure"
00347                                 " \"%s\" (%lu bytes requested)",
00348                                 name, (unsigned long) size)));
00349             shmemseghdr->index = structPtr;
00350             *foundPtr = FALSE;
00351         }
00352         LWLockRelease(ShmemIndexLock);
00353         return structPtr;
00354     }
00355 
00356     /* look it up in the shmem index */
00357     result = (ShmemIndexEnt *)
00358         hash_search(ShmemIndex, name, HASH_ENTER_NULL, foundPtr);
00359 
00360     if (!result)
00361     {
00362         LWLockRelease(ShmemIndexLock);
00363         ereport(ERROR,
00364                 (errcode(ERRCODE_OUT_OF_MEMORY),
00365         errmsg("could not create ShmemIndex entry for data structure \"%s\"",
00366                name)));
00367     }
00368 
00369     if (*foundPtr)
00370     {
00371         /*
00372          * Structure is in the shmem index so someone else has allocated it
00373          * already.  The size better be the same as the size we are trying to
00374          * initialize to, or there is a name conflict (or worse).
00375          */
00376         if (result->size != size)
00377         {
00378             LWLockRelease(ShmemIndexLock);
00379             ereport(ERROR,
00380                   (errmsg("ShmemIndex entry size is wrong for data structure"
00381                           " \"%s\": expected %lu, actual %lu",
00382                           name,
00383                           (unsigned long) size,
00384                           (unsigned long) result->size)));
00385         }
00386         structPtr = result->location;
00387     }
00388     else
00389     {
00390         /* It isn't in the table yet. allocate and initialize it */
00391         structPtr = ShmemAlloc(size);
00392         if (structPtr == NULL)
00393         {
00394             /* out of memory; remove the failed ShmemIndex entry */
00395             hash_search(ShmemIndex, name, HASH_REMOVE, NULL);
00396             LWLockRelease(ShmemIndexLock);
00397             ereport(ERROR,
00398                     (errcode(ERRCODE_OUT_OF_MEMORY),
00399                      errmsg("not enough shared memory for data structure"
00400                             " \"%s\" (%lu bytes requested)",
00401                             name, (unsigned long) size)));
00402         }
00403         result->size = size;
00404         result->location = structPtr;
00405     }
00406 
00407     LWLockRelease(ShmemIndexLock);
00408 
00409     Assert(ShmemAddrIsValid(structPtr));
00410     return structPtr;
00411 }
00412 
00413 
00414 /*
00415  * Add two Size values, checking for overflow
00416  */
00417 Size
00418 add_size(Size s1, Size s2)
00419 {
00420     Size        result;
00421 
00422     result = s1 + s2;
00423     /* We are assuming Size is an unsigned type here... */
00424     if (result < s1 || result < s2)
00425         ereport(ERROR,
00426                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00427                  errmsg("requested shared memory size overflows size_t")));
00428     return result;
00429 }
00430 
00431 /*
00432  * Multiply two Size values, checking for overflow
00433  */
00434 Size
00435 mul_size(Size s1, Size s2)
00436 {
00437     Size        result;
00438 
00439     if (s1 == 0 || s2 == 0)
00440         return 0;
00441     result = s1 * s2;
00442     /* We are assuming Size is an unsigned type here... */
00443     if (result / s2 != s1)
00444         ereport(ERROR,
00445                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00446                  errmsg("requested shared memory size overflows size_t")));
00447     return result;
00448 }