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 }