PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
sysv_sema.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * sysv_sema.c
4  * Implement PGSemaphores using SysV semaphore facilities
5  *
6  *
7  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * IDENTIFICATION
11  * src/backend/port/sysv_sema.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include <signal.h>
18 #include <unistd.h>
19 #include <sys/file.h>
20 #ifdef HAVE_SYS_IPC_H
21 #include <sys/ipc.h>
22 #endif
23 #ifdef HAVE_SYS_SEM_H
24 #include <sys/sem.h>
25 #endif
26 
27 #include "miscadmin.h"
28 #include "storage/ipc.h"
29 #include "storage/pg_sema.h"
30 #include "storage/shmem.h"
31 
32 
33 typedef struct PGSemaphoreData
34 {
35  int semId; /* semaphore set identifier */
36  int semNum; /* semaphore number within set */
38 
39 #ifndef HAVE_UNION_SEMUN
40 union semun
41 {
42  int val;
43  struct semid_ds *buf;
44  unsigned short *array;
45 };
46 #endif
47 
48 typedef key_t IpcSemaphoreKey; /* semaphore key passed to semget(2) */
49 typedef int IpcSemaphoreId; /* semaphore ID returned by semget(2) */
50 
51 /*
52  * SEMAS_PER_SET is the number of useful semaphores in each semaphore set
53  * we allocate. It must be *less than* your kernel's SEMMSL (max semaphores
54  * per set) parameter, which is often around 25. (Less than, because we
55  * allocate one extra sema in each set for identification purposes.)
56  */
57 #define SEMAS_PER_SET 16
58 
59 #define IPCProtection (0600) /* access/modify by user only */
60 
61 #define PGSemaMagic 537 /* must be less than SEMVMX */
62 
63 
64 static PGSemaphore sharedSemas; /* array of PGSemaphoreData in shared memory */
65 static int numSharedSemas; /* number of PGSemaphoreDatas used so far */
66 static int maxSharedSemas; /* allocated size of PGSemaphoreData array */
67 static IpcSemaphoreId *mySemaSets; /* IDs of sema sets acquired so far */
68 static int numSemaSets; /* number of sema sets acquired so far */
69 static int maxSemaSets; /* allocated size of mySemaSets array */
70 static IpcSemaphoreKey nextSemaKey; /* next key to try using */
71 static int nextSemaNumber; /* next free sem num in last sema set */
72 
73 
75  int numSems);
76 static void IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum,
77  int value);
78 static void IpcSemaphoreKill(IpcSemaphoreId semId);
79 static int IpcSemaphoreGetValue(IpcSemaphoreId semId, int semNum);
80 static pid_t IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int semNum);
82 static void ReleaseSemaphores(int status, Datum arg);
83 
84 
85 /*
86  * InternalIpcSemaphoreCreate
87  *
88  * Attempt to create a new semaphore set with the specified key.
89  * Will fail (return -1) if such a set already exists.
90  *
91  * If we fail with a failure code other than collision-with-existing-set,
92  * print out an error and abort. Other types of errors suggest nonrecoverable
93  * problems.
94  */
95 static IpcSemaphoreId
97 {
98  int semId;
99 
100  semId = semget(semKey, numSems, IPC_CREAT | IPC_EXCL | IPCProtection);
101 
102  if (semId < 0)
103  {
104  int saved_errno = errno;
105 
106  /*
107  * Fail quietly if error indicates a collision with existing set. One
108  * would expect EEXIST, given that we said IPC_EXCL, but perhaps we
109  * could get a permission violation instead? Also, EIDRM might occur
110  * if an old set is slated for destruction but not gone yet.
111  */
112  if (saved_errno == EEXIST || saved_errno == EACCES
113 #ifdef EIDRM
114  || saved_errno == EIDRM
115 #endif
116  )
117  return -1;
118 
119  /*
120  * Else complain and abort
121  */
122  ereport(FATAL,
123  (errmsg("could not create semaphores: %m"),
124  errdetail("Failed system call was semget(%lu, %d, 0%o).",
125  (unsigned long) semKey, numSems,
127  (saved_errno == ENOSPC) ?
128  errhint("This error does *not* mean that you have run out of disk space. "
129  "It occurs when either the system limit for the maximum number of "
130  "semaphore sets (SEMMNI), or the system wide maximum number of "
131  "semaphores (SEMMNS), would be exceeded. You need to raise the "
132  "respective kernel parameter. Alternatively, reduce PostgreSQL's "
133  "consumption of semaphores by reducing its max_connections parameter.\n"
134  "The PostgreSQL documentation contains more information about "
135  "configuring your system for PostgreSQL.") : 0));
136  }
137 
138  return semId;
139 }
140 
141 /*
142  * Initialize a semaphore to the specified value.
143  */
144 static void
146 {
147  union semun semun;
148 
149  semun.val = value;
150  if (semctl(semId, semNum, SETVAL, semun) < 0)
151  {
152  int saved_errno = errno;
153 
154  ereport(FATAL,
155  (errmsg_internal("semctl(%d, %d, SETVAL, %d) failed: %m",
156  semId, semNum, value),
157  (saved_errno == ERANGE) ?
158  errhint("You possibly need to raise your kernel's SEMVMX value to be at least "
159  "%d. Look into the PostgreSQL documentation for details.",
160  value) : 0));
161  }
162 }
163 
164 /*
165  * IpcSemaphoreKill(semId) - removes a semaphore set
166  */
167 static void
169 {
170  union semun semun;
171 
172  semun.val = 0; /* unused, but keep compiler quiet */
173 
174  if (semctl(semId, 0, IPC_RMID, semun) < 0)
175  elog(LOG, "semctl(%d, 0, IPC_RMID, ...) failed: %m", semId);
176 }
177 
178 /* Get the current value (semval) of the semaphore */
179 static int
181 {
182  union semun dummy; /* for Solaris */
183 
184  dummy.val = 0; /* unused */
185 
186  return semctl(semId, semNum, GETVAL, dummy);
187 }
188 
189 /* Get the PID of the last process to do semop() on the semaphore */
190 static pid_t
192 {
193  union semun dummy; /* for Solaris */
194 
195  dummy.val = 0; /* unused */
196 
197  return semctl(semId, semNum, GETPID, dummy);
198 }
199 
200 
201 /*
202  * Create a semaphore set with the given number of useful semaphores
203  * (an additional sema is actually allocated to serve as identifier).
204  * Dead Postgres sema sets are recycled if found, but we do not fail
205  * upon collision with non-Postgres sema sets.
206  *
207  * The idea here is to detect and re-use keys that may have been assigned
208  * by a crashed postmaster or backend.
209  */
210 static IpcSemaphoreId
212 {
213  IpcSemaphoreId semId;
214  union semun semun;
215  PGSemaphoreData mysema;
216 
217  /* Loop till we find a free IPC key */
218  for (nextSemaKey++;; nextSemaKey++)
219  {
220  pid_t creatorPID;
221 
222  /* Try to create new semaphore set */
223  semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
224  if (semId >= 0)
225  break; /* successful create */
226 
227  /* See if it looks to be leftover from a dead Postgres process */
228  semId = semget(nextSemaKey, numSems + 1, 0);
229  if (semId < 0)
230  continue; /* failed: must be some other app's */
231  if (IpcSemaphoreGetValue(semId, numSems) != PGSemaMagic)
232  continue; /* sema belongs to a non-Postgres app */
233 
234  /*
235  * If the creator PID is my own PID or does not belong to any extant
236  * process, it's safe to zap it.
237  */
238  creatorPID = IpcSemaphoreGetLastPID(semId, numSems);
239  if (creatorPID <= 0)
240  continue; /* oops, GETPID failed */
241  if (creatorPID != getpid())
242  {
243  if (kill(creatorPID, 0) == 0 || errno != ESRCH)
244  continue; /* sema belongs to a live process */
245  }
246 
247  /*
248  * The sema set appears to be from a dead Postgres process, or from a
249  * previous cycle of life in this same process. Zap it, if possible.
250  * This probably shouldn't fail, but if it does, assume the sema set
251  * belongs to someone else after all, and continue quietly.
252  */
253  semun.val = 0; /* unused, but keep compiler quiet */
254  if (semctl(semId, 0, IPC_RMID, semun) < 0)
255  continue;
256 
257  /*
258  * Now try again to create the sema set.
259  */
260  semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
261  if (semId >= 0)
262  break; /* successful create */
263 
264  /*
265  * Can only get here if some other process managed to create the same
266  * sema key before we did. Let him have that one, loop around to try
267  * next key.
268  */
269  }
270 
271  /*
272  * OK, we created a new sema set. Mark it as created by this process. We
273  * do this by setting the spare semaphore to PGSemaMagic-1 and then
274  * incrementing it with semop(). That leaves it with value PGSemaMagic
275  * and sempid referencing this process.
276  */
277  IpcSemaphoreInitialize(semId, numSems, PGSemaMagic - 1);
278  mysema.semId = semId;
279  mysema.semNum = numSems;
280  PGSemaphoreUnlock(&mysema);
281 
282  return semId;
283 }
284 
285 
286 /*
287  * Report amount of shared memory needed for semaphores
288  */
289 Size
290 PGSemaphoreShmemSize(int maxSemas)
291 {
292  return mul_size(maxSemas, sizeof(PGSemaphoreData));
293 }
294 
295 /*
296  * PGReserveSemaphores --- initialize semaphore support
297  *
298  * This is called during postmaster start or shared memory reinitialization.
299  * It should do whatever is needed to be able to support up to maxSemas
300  * subsequent PGSemaphoreCreate calls. Also, if any system resources
301  * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit
302  * callback to release them.
303  *
304  * The port number is passed for possible use as a key (for SysV, we use
305  * it to generate the starting semaphore key). In a standalone backend,
306  * zero will be passed.
307  *
308  * In the SysV implementation, we acquire semaphore sets on-demand; the
309  * maxSemas parameter is just used to size the arrays. There is an array
310  * of PGSemaphoreData structs in shared memory, and a postmaster-local array
311  * with one entry per SysV semaphore set, which we use for releasing the
312  * semaphore sets when done. (This design ensures that postmaster shutdown
313  * doesn't rely on the contents of shared memory, which a failed backend might
314  * have clobbered.)
315  */
316 void
317 PGReserveSemaphores(int maxSemas, int port)
318 {
319  /*
320  * We must use ShmemAllocUnlocked(), since the spinlock protecting
321  * ShmemAlloc() won't be ready yet. (This ordering is necessary when we
322  * are emulating spinlocks with semaphores.)
323  */
324  sharedSemas = (PGSemaphore)
326  numSharedSemas = 0;
327  maxSharedSemas = maxSemas;
328 
329  maxSemaSets = (maxSemas + SEMAS_PER_SET - 1) / SEMAS_PER_SET;
331  malloc(maxSemaSets * sizeof(IpcSemaphoreId));
332  if (mySemaSets == NULL)
333  elog(PANIC, "out of memory");
334  numSemaSets = 0;
335  nextSemaKey = port * 1000;
336  nextSemaNumber = SEMAS_PER_SET; /* force sema set alloc on 1st call */
337 
339 }
340 
341 /*
342  * Release semaphores at shutdown or shmem reinitialization
343  *
344  * (called as an on_shmem_exit callback, hence funny argument list)
345  */
346 static void
348 {
349  int i;
350 
351  for (i = 0; i < numSemaSets; i++)
353  free(mySemaSets);
354 }
355 
356 /*
357  * PGSemaphoreCreate
358  *
359  * Allocate a PGSemaphore structure with initial count 1
360  */
363 {
364  PGSemaphore sema;
365 
366  /* Can't do this in a backend, because static state is postmaster's */
368 
370  {
371  /* Time to allocate another semaphore set */
372  if (numSemaSets >= maxSemaSets)
373  elog(PANIC, "too many semaphores created");
375  numSemaSets++;
376  nextSemaNumber = 0;
377  }
378  /* Use the next shared PGSemaphoreData */
380  elog(PANIC, "too many semaphores created");
381  sema = &sharedSemas[numSharedSemas++];
382  /* Assign the next free semaphore in the current set */
383  sema->semId = mySemaSets[numSemaSets - 1];
384  sema->semNum = nextSemaNumber++;
385  /* Initialize it to count 1 */
386  IpcSemaphoreInitialize(sema->semId, sema->semNum, 1);
387 
388  return sema;
389 }
390 
391 /*
392  * PGSemaphoreReset
393  *
394  * Reset a previously-initialized PGSemaphore to have count 0
395  */
396 void
398 {
399  IpcSemaphoreInitialize(sema->semId, sema->semNum, 0);
400 }
401 
402 /*
403  * PGSemaphoreLock
404  *
405  * Lock a semaphore (decrement count), blocking if count would be < 0
406  */
407 void
409 {
410  int errStatus;
411  struct sembuf sops;
412 
413  sops.sem_op = -1; /* decrement */
414  sops.sem_flg = 0;
415  sops.sem_num = sema->semNum;
416 
417  /*
418  * Note: if errStatus is -1 and errno == EINTR then it means we returned
419  * from the operation prematurely because we were sent a signal. So we
420  * try and lock the semaphore again.
421  *
422  * We used to check interrupts here, but that required servicing
423  * interrupts directly from signal handlers. Which is hard to do safely
424  * and portably.
425  */
426  do
427  {
428  errStatus = semop(sema->semId, &sops, 1);
429  } while (errStatus < 0 && errno == EINTR);
430 
431  if (errStatus < 0)
432  elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
433 }
434 
435 /*
436  * PGSemaphoreUnlock
437  *
438  * Unlock a semaphore (increment count)
439  */
440 void
442 {
443  int errStatus;
444  struct sembuf sops;
445 
446  sops.sem_op = 1; /* increment */
447  sops.sem_flg = 0;
448  sops.sem_num = sema->semNum;
449 
450  /*
451  * Note: if errStatus is -1 and errno == EINTR then it means we returned
452  * from the operation prematurely because we were sent a signal. So we
453  * try and unlock the semaphore again. Not clear this can really happen,
454  * but might as well cope.
455  */
456  do
457  {
458  errStatus = semop(sema->semId, &sops, 1);
459  } while (errStatus < 0 && errno == EINTR);
460 
461  if (errStatus < 0)
462  elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
463 }
464 
465 /*
466  * PGSemaphoreTryLock
467  *
468  * Lock a semaphore only if able to do so without blocking
469  */
470 bool
472 {
473  int errStatus;
474  struct sembuf sops;
475 
476  sops.sem_op = -1; /* decrement */
477  sops.sem_flg = IPC_NOWAIT; /* but don't block */
478  sops.sem_num = sema->semNum;
479 
480  /*
481  * Note: if errStatus is -1 and errno == EINTR then it means we returned
482  * from the operation prematurely because we were sent a signal. So we
483  * try and lock the semaphore again.
484  */
485  do
486  {
487  errStatus = semop(sema->semId, &sops, 1);
488  } while (errStatus < 0 && errno == EINTR);
489 
490  if (errStatus < 0)
491  {
492  /* Expect EAGAIN or EWOULDBLOCK (platform-dependent) */
493 #ifdef EAGAIN
494  if (errno == EAGAIN)
495  return false; /* failed to lock it */
496 #endif
497 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
498  if (errno == EWOULDBLOCK)
499  return false; /* failed to lock it */
500 #endif
501  /* Otherwise we got trouble */
502  elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
503  }
504 
505  return true;
506 }
#define EWOULDBLOCK
Definition: win32.h:291
key_t IpcSemaphoreKey
Definition: sysv_sema.c:48
#define IPC_CREAT
Definition: win32.h:107
#define IPC_EXCL
Definition: win32.h:108
void PGSemaphoreUnlock(PGSemaphore sema)
Definition: sysv_sema.c:441
static void IpcSemaphoreKill(IpcSemaphoreId semId)
Definition: sysv_sema.c:168
int errhint(const char *fmt,...)
Definition: elog.c:987
#define SETVAL
Definition: win32.h:121
static int maxSemaSets
Definition: sysv_sema.c:69
static void ReleaseSemaphores(int status, Datum arg)
Definition: sysv_sema.c:347
#define SEMAS_PER_SET
Definition: sysv_sema.c:57
bool PGSemaphoreTryLock(PGSemaphore sema)
Definition: sysv_sema.c:471
void PGSemaphoreReset(PGSemaphore sema)
Definition: sysv_sema.c:397
static int numSems
Definition: posix_sema.c:59
#define LOG
Definition: elog.h:26
static int maxSharedSemas
Definition: sysv_sema.c:66
static PGSemaphore sharedSemas
Definition: sysv_sema.c:64
#define PANIC
Definition: elog.h:53
int IpcSemaphoreId
Definition: sysv_sema.c:49
#define malloc(a)
Definition: header.h:50
#define EAGAIN
Definition: win32.h:283
static int nextSemaNumber
Definition: sysv_sema.c:71
long key_t
Definition: win32.h:253
static void IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum, int value)
Definition: sysv_sema.c:145
#define FATAL
Definition: elog.h:52
static struct @121 value
void on_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition: ipc.c:348
bool IsUnderPostmaster
Definition: globals.c:101
void PGReserveSemaphores(int maxSemas, int port)
Definition: sysv_sema.c:317
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define PGSemaMagic
Definition: sysv_sema.c:61
#define IPC_NOWAIT
Definition: win32.h:110
int val
Definition: sysv_sema.c:42
#define ereport(elevel, rest)
Definition: elog.h:122
static int port
Definition: pg_regress.c:89
#define GETVAL
Definition: win32.h:120
Size mul_size(Size s1, Size s2)
Definition: shmem.c:492
void PGSemaphoreLock(PGSemaphore sema)
Definition: sysv_sema.c:408
static int numSharedSemas
Definition: sysv_sema.c:65
uintptr_t Datum
Definition: postgres.h:372
void * ShmemAllocUnlocked(Size size)
Definition: shmem.c:227
#define EINTR
Definition: win32.h:285
static IpcSemaphoreId * mySemaSets
Definition: sysv_sema.c:67
#define IPC_RMID
Definition: win32.h:106
static int IpcSemaphoreGetValue(IpcSemaphoreId semId, int semNum)
Definition: sysv_sema.c:180
#define free(a)
Definition: header.h:65
int errmsg_internal(const char *fmt,...)
Definition: elog.c:827
struct PGSemaphoreData PGSemaphoreData
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:676
size_t Size
Definition: c.h:356
struct semid_ds * buf
Definition: sysv_sema.c:43
#define IPCProtection
Definition: sysv_sema.c:59
int errmsg(const char *fmt,...)
Definition: elog.c:797
Size PGSemaphoreShmemSize(int maxSemas)
Definition: sysv_sema.c:290
struct PGSemaphoreData * PGSemaphore
Definition: pg_sema.h:34
int i
static IpcSemaphoreId InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems)
Definition: sysv_sema.c:96
void * arg
static IpcSemaphoreKey nextSemaKey
Definition: sysv_sema.c:70
#define EIDRM
Definition: win32.h:115
static IpcSemaphoreId IpcSemaphoreCreate(int numSems)
Definition: sysv_sema.c:211
#define elog
Definition: elog.h:219
PGSemaphore PGSemaphoreCreate(void)
Definition: sysv_sema.c:362
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:224
unsigned short * array
Definition: sysv_sema.c:44
#define GETPID
Definition: win32.h:122
static int numSemaSets
Definition: sysv_sema.c:68
static pid_t IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int semNum)
Definition: sysv_sema.c:191