00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include <signal.h>
00018 #include <unistd.h>
00019 #include <sys/file.h>
00020 #ifdef HAVE_SYS_IPC_H
00021 #include <sys/ipc.h>
00022 #endif
00023 #ifdef HAVE_SYS_SEM_H
00024 #include <sys/sem.h>
00025 #endif
00026
00027 #include "miscadmin.h"
00028 #include "storage/ipc.h"
00029 #include "storage/pg_sema.h"
00030
00031
00032 #ifndef HAVE_UNION_SEMUN
00033 union semun
00034 {
00035 int val;
00036 struct semid_ds *buf;
00037 unsigned short *array;
00038 };
00039 #endif
00040
00041 typedef key_t IpcSemaphoreKey;
00042 typedef int IpcSemaphoreId;
00043
00044
00045
00046
00047
00048
00049
00050 #define SEMAS_PER_SET 16
00051
00052 #define IPCProtection (0600)
00053
00054 #define PGSemaMagic 537
00055
00056
00057 static IpcSemaphoreId *mySemaSets;
00058 static int numSemaSets;
00059 static int maxSemaSets;
00060 static IpcSemaphoreKey nextSemaKey;
00061 static int nextSemaNumber;
00062
00063
00064 static IpcSemaphoreId InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey,
00065 int numSems);
00066 static void IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum,
00067 int value);
00068 static void IpcSemaphoreKill(IpcSemaphoreId semId);
00069 static int IpcSemaphoreGetValue(IpcSemaphoreId semId, int semNum);
00070 static pid_t IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int semNum);
00071 static IpcSemaphoreId IpcSemaphoreCreate(int numSems);
00072 static void ReleaseSemaphores(int status, Datum arg);
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 static IpcSemaphoreId
00086 InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems)
00087 {
00088 int semId;
00089
00090 semId = semget(semKey, numSems, IPC_CREAT | IPC_EXCL | IPCProtection);
00091
00092 if (semId < 0)
00093 {
00094
00095
00096
00097
00098
00099
00100 if (errno == EEXIST || errno == EACCES
00101 #ifdef EIDRM
00102 || errno == EIDRM
00103 #endif
00104 )
00105 return -1;
00106
00107
00108
00109
00110 ereport(FATAL,
00111 (errmsg("could not create semaphores: %m"),
00112 errdetail("Failed system call was semget(%lu, %d, 0%o).",
00113 (unsigned long) semKey, numSems,
00114 IPC_CREAT | IPC_EXCL | IPCProtection),
00115 (errno == ENOSPC) ?
00116 errhint("This error does *not* mean that you have run out of disk space. "
00117 "It occurs when either the system limit for the maximum number of "
00118 "semaphore sets (SEMMNI), or the system wide maximum number of "
00119 "semaphores (SEMMNS), would be exceeded. You need to raise the "
00120 "respective kernel parameter. Alternatively, reduce PostgreSQL's "
00121 "consumption of semaphores by reducing its max_connections parameter.\n"
00122 "The PostgreSQL documentation contains more information about "
00123 "configuring your system for PostgreSQL.") : 0));
00124 }
00125
00126 return semId;
00127 }
00128
00129
00130
00131
00132 static void
00133 IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum, int value)
00134 {
00135 union semun semun;
00136
00137 semun.val = value;
00138 if (semctl(semId, semNum, SETVAL, semun) < 0)
00139 ereport(FATAL,
00140 (errmsg_internal("semctl(%d, %d, SETVAL, %d) failed: %m",
00141 semId, semNum, value),
00142 (errno == ERANGE) ?
00143 errhint("You possibly need to raise your kernel's SEMVMX value to be at least "
00144 "%d. Look into the PostgreSQL documentation for details.",
00145 value) : 0));
00146 }
00147
00148
00149
00150
00151 static void
00152 IpcSemaphoreKill(IpcSemaphoreId semId)
00153 {
00154 union semun semun;
00155
00156 semun.val = 0;
00157
00158 if (semctl(semId, 0, IPC_RMID, semun) < 0)
00159 elog(LOG, "semctl(%d, 0, IPC_RMID, ...) failed: %m", semId);
00160 }
00161
00162
00163 static int
00164 IpcSemaphoreGetValue(IpcSemaphoreId semId, int semNum)
00165 {
00166 union semun dummy;
00167
00168 dummy.val = 0;
00169
00170 return semctl(semId, semNum, GETVAL, dummy);
00171 }
00172
00173
00174 static pid_t
00175 IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int semNum)
00176 {
00177 union semun dummy;
00178
00179 dummy.val = 0;
00180
00181 return semctl(semId, semNum, GETPID, dummy);
00182 }
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194 static IpcSemaphoreId
00195 IpcSemaphoreCreate(int numSems)
00196 {
00197 IpcSemaphoreId semId;
00198 union semun semun;
00199 PGSemaphoreData mysema;
00200
00201
00202 for (nextSemaKey++;; nextSemaKey++)
00203 {
00204 pid_t creatorPID;
00205
00206
00207 semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
00208 if (semId >= 0)
00209 break;
00210
00211
00212 semId = semget(nextSemaKey, numSems + 1, 0);
00213 if (semId < 0)
00214 continue;
00215 if (IpcSemaphoreGetValue(semId, numSems) != PGSemaMagic)
00216 continue;
00217
00218
00219
00220
00221
00222 creatorPID = IpcSemaphoreGetLastPID(semId, numSems);
00223 if (creatorPID <= 0)
00224 continue;
00225 if (creatorPID != getpid())
00226 {
00227 if (kill(creatorPID, 0) == 0 || errno != ESRCH)
00228 continue;
00229 }
00230
00231
00232
00233
00234
00235
00236
00237 semun.val = 0;
00238 if (semctl(semId, 0, IPC_RMID, semun) < 0)
00239 continue;
00240
00241
00242
00243
00244 semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
00245 if (semId >= 0)
00246 break;
00247
00248
00249
00250
00251
00252
00253 }
00254
00255
00256
00257
00258
00259
00260
00261 IpcSemaphoreInitialize(semId, numSems, PGSemaMagic - 1);
00262 mysema.semId = semId;
00263 mysema.semNum = numSems;
00264 PGSemaphoreUnlock(&mysema);
00265
00266 return semId;
00267 }
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287 void
00288 PGReserveSemaphores(int maxSemas, int port)
00289 {
00290 maxSemaSets = (maxSemas + SEMAS_PER_SET - 1) / SEMAS_PER_SET;
00291 mySemaSets = (IpcSemaphoreId *)
00292 malloc(maxSemaSets * sizeof(IpcSemaphoreId));
00293 if (mySemaSets == NULL)
00294 elog(PANIC, "out of memory");
00295 numSemaSets = 0;
00296 nextSemaKey = port * 1000;
00297 nextSemaNumber = SEMAS_PER_SET;
00298
00299 on_shmem_exit(ReleaseSemaphores, 0);
00300 }
00301
00302
00303
00304
00305
00306
00307 static void
00308 ReleaseSemaphores(int status, Datum arg)
00309 {
00310 int i;
00311
00312 for (i = 0; i < numSemaSets; i++)
00313 IpcSemaphoreKill(mySemaSets[i]);
00314 free(mySemaSets);
00315 }
00316
00317
00318
00319
00320
00321
00322 void
00323 PGSemaphoreCreate(PGSemaphore sema)
00324 {
00325
00326 Assert(!IsUnderPostmaster);
00327
00328 if (nextSemaNumber >= SEMAS_PER_SET)
00329 {
00330
00331 if (numSemaSets >= maxSemaSets)
00332 elog(PANIC, "too many semaphores created");
00333 mySemaSets[numSemaSets] = IpcSemaphoreCreate(SEMAS_PER_SET);
00334 numSemaSets++;
00335 nextSemaNumber = 0;
00336 }
00337
00338 sema->semId = mySemaSets[numSemaSets - 1];
00339 sema->semNum = nextSemaNumber++;
00340
00341 IpcSemaphoreInitialize(sema->semId, sema->semNum, 1);
00342 }
00343
00344
00345
00346
00347
00348
00349 void
00350 PGSemaphoreReset(PGSemaphore sema)
00351 {
00352 IpcSemaphoreInitialize(sema->semId, sema->semNum, 0);
00353 }
00354
00355
00356
00357
00358
00359
00360 void
00361 PGSemaphoreLock(PGSemaphore sema, bool interruptOK)
00362 {
00363 int errStatus;
00364 struct sembuf sops;
00365
00366 sops.sem_op = -1;
00367 sops.sem_flg = 0;
00368 sops.sem_num = sema->semNum;
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411 do
00412 {
00413 ImmediateInterruptOK = interruptOK;
00414 CHECK_FOR_INTERRUPTS();
00415 errStatus = semop(sema->semId, &sops, 1);
00416 ImmediateInterruptOK = false;
00417 } while (errStatus < 0 && errno == EINTR);
00418
00419 if (errStatus < 0)
00420 elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
00421 }
00422
00423
00424
00425
00426
00427
00428 void
00429 PGSemaphoreUnlock(PGSemaphore sema)
00430 {
00431 int errStatus;
00432 struct sembuf sops;
00433
00434 sops.sem_op = 1;
00435 sops.sem_flg = 0;
00436 sops.sem_num = sema->semNum;
00437
00438
00439
00440
00441
00442
00443
00444 do
00445 {
00446 errStatus = semop(sema->semId, &sops, 1);
00447 } while (errStatus < 0 && errno == EINTR);
00448
00449 if (errStatus < 0)
00450 elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
00451 }
00452
00453
00454
00455
00456
00457
00458 bool
00459 PGSemaphoreTryLock(PGSemaphore sema)
00460 {
00461 int errStatus;
00462 struct sembuf sops;
00463
00464 sops.sem_op = -1;
00465 sops.sem_flg = IPC_NOWAIT;
00466 sops.sem_num = sema->semNum;
00467
00468
00469
00470
00471
00472
00473 do
00474 {
00475 errStatus = semop(sema->semId, &sops, 1);
00476 } while (errStatus < 0 && errno == EINTR);
00477
00478 if (errStatus < 0)
00479 {
00480
00481 #ifdef EAGAIN
00482 if (errno == EAGAIN)
00483 return false;
00484 #endif
00485 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
00486 if (errno == EWOULDBLOCK)
00487 return false;
00488 #endif
00489
00490 elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
00491 }
00492
00493 return true;
00494 }