00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "postgres.h"
00018
00019 #include <signal.h>
00020 #include <unistd.h>
00021 #include <sys/file.h>
00022 #include <sys/mman.h>
00023 #include <sys/stat.h>
00024 #ifdef HAVE_SYS_IPC_H
00025 #include <sys/ipc.h>
00026 #endif
00027 #ifdef HAVE_SYS_SHM_H
00028 #include <sys/shm.h>
00029 #endif
00030
00031 #include "miscadmin.h"
00032 #include "storage/ipc.h"
00033 #include "storage/pg_shmem.h"
00034
00035
00036 typedef key_t IpcMemoryKey;
00037 typedef int IpcMemoryId;
00038
00039 #define IPCProtection (0600)
00040
00041 #ifdef SHM_SHARE_MMU
00042 #define PG_SHMAT_FLAGS SHM_SHARE_MMU
00043 #else
00044 #define PG_SHMAT_FLAGS 0
00045 #endif
00046
00047
00048 #ifndef MAP_ANONYMOUS
00049 #define MAP_ANONYMOUS MAP_ANON
00050 #endif
00051
00052
00053 #ifndef MAP_HASSEMAPHORE
00054 #define MAP_HASSEMAPHORE 0
00055 #endif
00056
00057 #define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE)
00058
00059
00060 #ifndef MAP_FAILED
00061 #define MAP_FAILED ((void *) -1)
00062 #endif
00063
00064
00065 unsigned long UsedShmemSegID = 0;
00066 void *UsedShmemSegAddr = NULL;
00067 static Size AnonymousShmemSize;
00068 static void *AnonymousShmem;
00069
00070 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
00071 static void IpcMemoryDetach(int status, Datum shmaddr);
00072 static void IpcMemoryDelete(int status, Datum shmId);
00073 static PGShmemHeader *PGSharedMemoryAttach(IpcMemoryKey key,
00074 IpcMemoryId *shmid);
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 static void *
00090 InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
00091 {
00092 IpcMemoryId shmid;
00093 void *memAddress;
00094
00095 shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
00096
00097 if (shmid < 0)
00098 {
00099
00100
00101
00102
00103
00104
00105 if (errno == EEXIST || errno == EACCES
00106 #ifdef EIDRM
00107 || errno == EIDRM
00108 #endif
00109 )
00110 return NULL;
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121 if (errno == EINVAL)
00122 {
00123 int save_errno = errno;
00124
00125 shmid = shmget(memKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection);
00126
00127 if (shmid < 0)
00128 {
00129
00130 if (errno == EEXIST || errno == EACCES
00131 #ifdef EIDRM
00132 || errno == EIDRM
00133 #endif
00134 )
00135 return NULL;
00136
00137 }
00138 else
00139 {
00140
00141
00142
00143
00144
00145
00146 if (shmctl(shmid, IPC_RMID, NULL) < 0)
00147 elog(LOG, "shmctl(%d, %d, 0) failed: %m",
00148 (int) shmid, IPC_RMID);
00149 }
00150
00151 errno = save_errno;
00152 }
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163 ereport(FATAL,
00164 (errmsg("could not create shared memory segment: %m"),
00165 errdetail("Failed system call was shmget(key=%lu, size=%lu, 0%o).",
00166 (unsigned long) memKey, (unsigned long) size,
00167 IPC_CREAT | IPC_EXCL | IPCProtection),
00168 (errno == EINVAL) ?
00169 errhint("This error usually means that PostgreSQL's request for a shared memory "
00170 "segment exceeded your kernel's SHMMAX parameter, or possibly that "
00171 "it is less than "
00172 "your kernel's SHMMIN parameter.\n"
00173 "The PostgreSQL documentation contains more information about shared "
00174 "memory configuration.") : 0,
00175 (errno == ENOMEM) ?
00176 errhint("This error usually means that PostgreSQL's request for a shared "
00177 "memory segment exceeded your kernel's SHMALL parameter. You may need "
00178 "to reconfigure the kernel with larger SHMALL.\n"
00179 "The PostgreSQL documentation contains more information about shared "
00180 "memory configuration.") : 0,
00181 (errno == ENOSPC) ?
00182 errhint("This error does *not* mean that you have run out of disk space. "
00183 "It occurs either if all available shared memory IDs have been taken, "
00184 "in which case you need to raise the SHMMNI parameter in your kernel, "
00185 "or because the system's overall limit for shared memory has been "
00186 "reached.\n"
00187 "The PostgreSQL documentation contains more information about shared "
00188 "memory configuration.") : 0));
00189 }
00190
00191
00192 on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));
00193
00194
00195 memAddress = shmat(shmid, NULL, PG_SHMAT_FLAGS);
00196
00197 if (memAddress == (void *) -1)
00198 elog(FATAL, "shmat(id=%d) failed: %m", shmid);
00199
00200
00201 on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
00202
00203
00204
00205
00206
00207
00208 {
00209 char line[64];
00210
00211 sprintf(line, "%9lu %9lu",
00212 (unsigned long) memKey, (unsigned long) shmid);
00213 AddToDataDirLockFile(LOCK_FILE_LINE_SHMEM_KEY, line);
00214 }
00215
00216 return memAddress;
00217 }
00218
00219
00220
00221
00222
00223
00224 static void
00225 IpcMemoryDetach(int status, Datum shmaddr)
00226 {
00227
00228 if (shmdt(DatumGetPointer(shmaddr)) < 0)
00229 elog(LOG, "shmdt(%p) failed: %m", DatumGetPointer(shmaddr));
00230
00231 if (AnonymousShmem != NULL
00232 && munmap(AnonymousShmem, AnonymousShmemSize) < 0)
00233 elog(LOG, "munmap(%p) failed: %m", AnonymousShmem);
00234 }
00235
00236
00237
00238
00239
00240 static void
00241 IpcMemoryDelete(int status, Datum shmId)
00242 {
00243 if (shmctl(DatumGetInt32(shmId), IPC_RMID, NULL) < 0)
00244 elog(LOG, "shmctl(%d, %d, 0) failed: %m",
00245 DatumGetInt32(shmId), IPC_RMID);
00246 }
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259 bool
00260 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
00261 {
00262 IpcMemoryId shmId = (IpcMemoryId) id2;
00263 struct shmid_ds shmStat;
00264 struct stat statbuf;
00265 PGShmemHeader *hdr;
00266
00267
00268
00269
00270
00271 if (shmctl(shmId, IPC_STAT, &shmStat) < 0)
00272 {
00273
00274
00275
00276
00277
00278 if (errno == EINVAL)
00279 return false;
00280
00281
00282
00283
00284
00285
00286 if (errno == EACCES)
00287 return false;
00288
00289
00290
00291
00292
00293
00294
00295
00296 #ifdef HAVE_LINUX_EIDRM_BUG
00297 if (errno == EIDRM)
00298 return false;
00299 #endif
00300
00301
00302
00303
00304
00305
00306 return true;
00307 }
00308
00309
00310 if (shmStat.shm_nattch == 0)
00311 return false;
00312
00313
00314
00315
00316
00317
00318 if (stat(DataDir, &statbuf) < 0)
00319 return true;
00320
00321 hdr = (PGShmemHeader *) shmat(shmId, NULL, PG_SHMAT_FLAGS);
00322
00323 if (hdr == (PGShmemHeader *) -1)
00324 return true;
00325
00326 if (hdr->magic != PGShmemMagic ||
00327 hdr->device != statbuf.st_dev ||
00328 hdr->inode != statbuf.st_ino)
00329 {
00330
00331
00332
00333
00334 shmdt((void *) hdr);
00335 return false;
00336 }
00337
00338
00339 shmdt((void *) hdr);
00340
00341 return true;
00342 }
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363 PGShmemHeader *
00364 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
00365 {
00366 IpcMemoryKey NextShmemSegID;
00367 void *memAddress;
00368 PGShmemHeader *hdr;
00369 IpcMemoryId shmid;
00370 struct stat statbuf;
00371 Size sysvsize = size;
00372
00373
00374 Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392 #ifndef EXEC_BACKEND
00393 {
00394 long pagesize = sysconf(_SC_PAGE_SIZE);
00395
00396
00397
00398
00399
00400
00401
00402
00403 if (pagesize > 0 && size % pagesize != 0)
00404 size += pagesize - (size % pagesize);
00405
00406
00407
00408
00409
00410
00411
00412
00413 AnonymousShmem = mmap(NULL, size, PROT_READ|PROT_WRITE, PG_MMAP_FLAGS,
00414 -1, 0);
00415 if (AnonymousShmem == MAP_FAILED)
00416 ereport(FATAL,
00417 (errmsg("could not map anonymous shared memory: %m"),
00418 (errno == ENOMEM) ?
00419 errhint("This error usually means that PostgreSQL's request "
00420 "for a shared memory segment exceeded available memory "
00421 "or swap space. To reduce the request size (currently "
00422 "%lu bytes), reduce PostgreSQL's shared memory usage, "
00423 "perhaps by reducing shared_buffers or "
00424 "max_connections.",
00425 (unsigned long) size) : 0));
00426 AnonymousShmemSize = size;
00427
00428
00429 sysvsize = sizeof(PGShmemHeader);
00430 }
00431 #endif
00432
00433
00434 UsedShmemSegAddr = NULL;
00435
00436
00437 NextShmemSegID = port * 1000;
00438
00439 for (NextShmemSegID++;; NextShmemSegID++)
00440 {
00441
00442 memAddress = InternalIpcMemoryCreate(NextShmemSegID, sysvsize);
00443 if (memAddress)
00444 break;
00445
00446
00447
00448 if (makePrivate)
00449 continue;
00450
00451 if ((memAddress = PGSharedMemoryAttach(NextShmemSegID, &shmid)) == NULL)
00452 continue;
00453
00454
00455
00456
00457
00458 hdr = (PGShmemHeader *) memAddress;
00459 if (hdr->creatorPID != getpid())
00460 {
00461 if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH)
00462 {
00463 shmdt(memAddress);
00464 continue;
00465 }
00466 }
00467
00468
00469
00470
00471
00472
00473
00474 shmdt(memAddress);
00475 if (shmctl(shmid, IPC_RMID, NULL) < 0)
00476 continue;
00477
00478
00479
00480
00481 memAddress = InternalIpcMemoryCreate(NextShmemSegID, sysvsize);
00482 if (memAddress)
00483 break;
00484
00485
00486
00487
00488
00489
00490 }
00491
00492
00493
00494
00495
00496
00497 hdr = (PGShmemHeader *) memAddress;
00498 hdr->creatorPID = getpid();
00499 hdr->magic = PGShmemMagic;
00500
00501
00502 if (stat(DataDir, &statbuf) < 0)
00503 ereport(FATAL,
00504 (errcode_for_file_access(),
00505 errmsg("could not stat data directory \"%s\": %m",
00506 DataDir)));
00507 hdr->device = statbuf.st_dev;
00508 hdr->inode = statbuf.st_ino;
00509
00510
00511
00512
00513 hdr->totalsize = size;
00514 hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
00515
00516
00517 UsedShmemSegAddr = memAddress;
00518 UsedShmemSegID = (unsigned long) NextShmemSegID;
00519
00520
00521
00522
00523
00524
00525
00526 if (AnonymousShmem == NULL)
00527 return hdr;
00528 memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
00529 return (PGShmemHeader *) AnonymousShmem;
00530 }
00531
00532 #ifdef EXEC_BACKEND
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545 void
00546 PGSharedMemoryReAttach(void)
00547 {
00548 IpcMemoryId shmid;
00549 void *hdr;
00550 void *origUsedShmemSegAddr = UsedShmemSegAddr;
00551
00552 Assert(UsedShmemSegAddr != NULL);
00553 Assert(IsUnderPostmaster);
00554
00555 #ifdef __CYGWIN__
00556
00557 PGSharedMemoryDetach();
00558 UsedShmemSegAddr = origUsedShmemSegAddr;
00559 #endif
00560
00561 elog(DEBUG3, "attaching to %p", UsedShmemSegAddr);
00562 hdr = (void *) PGSharedMemoryAttach((IpcMemoryKey) UsedShmemSegID, &shmid);
00563 if (hdr == NULL)
00564 elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %m",
00565 (int) UsedShmemSegID, UsedShmemSegAddr);
00566 if (hdr != origUsedShmemSegAddr)
00567 elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
00568 hdr, origUsedShmemSegAddr);
00569
00570 UsedShmemSegAddr = hdr;
00571 }
00572 #endif
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583 void
00584 PGSharedMemoryDetach(void)
00585 {
00586 if (UsedShmemSegAddr != NULL)
00587 {
00588 if ((shmdt(UsedShmemSegAddr) < 0)
00589 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
00590
00591 && shmdt(NULL) < 0
00592 #endif
00593 )
00594 elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
00595 UsedShmemSegAddr = NULL;
00596 }
00597
00598
00599 if (AnonymousShmem != NULL
00600 && munmap(AnonymousShmem, AnonymousShmemSize) < 0)
00601 elog(LOG, "munmap(%p) failed: %m", AnonymousShmem);
00602 }
00603
00604
00605
00606
00607
00608
00609
00610 static PGShmemHeader *
00611 PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
00612 {
00613 PGShmemHeader *hdr;
00614
00615 if ((*shmid = shmget(key, sizeof(PGShmemHeader), 0)) < 0)
00616 return NULL;
00617
00618 hdr = (PGShmemHeader *) shmat(*shmid, UsedShmemSegAddr, PG_SHMAT_FLAGS);
00619
00620 if (hdr == (PGShmemHeader *) -1)
00621 return NULL;
00622
00623 if (hdr->magic != PGShmemMagic)
00624 {
00625 shmdt((void *) hdr);
00626 return NULL;
00627 }
00628
00629 return hdr;
00630 }