/Users/deen/code/yugabyte-db/build/debugcov-clang-dynamic-arm64-ninja/postgres_build/src/backend/port/pg_sema.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * sysv_sema.c |
4 | | * Implement PGSemaphores using SysV semaphore facilities |
5 | | * |
6 | | * |
7 | | * Portions Copyright (c) 1996-2018, 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 */ |
37 | | } PGSemaphoreData; |
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 | 1.22M | #define SEMAS_PER_SET 16 |
58 | | |
59 | 72.4k | #define IPCProtection (0600) /* access/modify by user only */ |
60 | | |
61 | 72.4k | #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 | | |
74 | | static IpcSemaphoreId InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, |
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); |
81 | | static IpcSemaphoreId IpcSemaphoreCreate(int numSems); |
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 |
96 | | InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems) |
97 | 72.4k | { |
98 | 72.4k | int semId; |
99 | | |
100 | 72.4k | semId = semget(semKey, numSems, IPC_CREAT | IPC_EXCL | IPCProtection); |
101 | | |
102 | 72.4k | if (semId < 0) |
103 | 199 | { |
104 | 199 | 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 | 199 | if (saved_errno == EEXIST || saved_errno == EACCES |
113 | 199 | #ifdef EIDRM |
114 | 0 | || saved_errno == EIDRM |
115 | 199 | #endif |
116 | 199 | ) |
117 | 199 | return -1; |
118 | | |
119 | | /* |
120 | | * Else complain and abort |
121 | | */ |
122 | 0 | ereport(FATAL, |
123 | 0 | (errmsg("could not create semaphores: %m"), |
124 | 0 | errdetail("Failed system call was semget(%lu, %d, 0%o).", |
125 | 0 | (unsigned long) semKey, numSems, |
126 | 0 | IPC_CREAT | IPC_EXCL | IPCProtection), |
127 | 0 | (saved_errno == ENOSPC) ? |
128 | 0 | errhint("This error does *not* mean that you have run out of disk space. " |
129 | 0 | "It occurs when either the system limit for the maximum number of " |
130 | 0 | "semaphore sets (SEMMNI), or the system wide maximum number of " |
131 | 0 | "semaphores (SEMMNS), would be exceeded. You need to raise the " |
132 | 0 | "respective kernel parameter. Alternatively, reduce PostgreSQL's " |
133 | 0 | "consumption of semaphores by reducing its max_connections parameter.\n" |
134 | 0 | "The PostgreSQL documentation contains more information about " |
135 | 0 | "configuring your system for PostgreSQL.") : 0)); |
136 | 0 | } |
137 | | |
138 | 72.2k | return semId; |
139 | 72.4k | } |
140 | | |
141 | | /* |
142 | | * Initialize a semaphore to the specified value. |
143 | | */ |
144 | | static void |
145 | | IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum, int value) |
146 | 1.21M | { |
147 | 1.21M | union semun semun; |
148 | | |
149 | 1.21M | semun.val = value; |
150 | 1.21M | if (semctl(semId, semNum, SETVAL, semun) < 0) |
151 | 0 | { |
152 | 0 | int saved_errno = errno; |
153 | |
|
154 | 0 | ereport(FATAL, |
155 | 0 | (errmsg_internal("semctl(%d, %d, SETVAL, %d) failed: %m", |
156 | 0 | semId, semNum, value), |
157 | 0 | (saved_errno == ERANGE) ? |
158 | 0 | errhint("You possibly need to raise your kernel's SEMVMX value to be at least " |
159 | 0 | "%d. Look into the PostgreSQL documentation for details.", |
160 | 0 | value) : 0)); |
161 | 0 | } |
162 | 1.21M | } |
163 | | |
164 | | /* |
165 | | * IpcSemaphoreKill(semId) - removes a semaphore set |
166 | | */ |
167 | | static void |
168 | | IpcSemaphoreKill(IpcSemaphoreId semId) |
169 | 72.2k | { |
170 | 72.2k | union semun semun; |
171 | | |
172 | 72.2k | semun.val = 0; /* unused, but keep compiler quiet */ |
173 | | |
174 | 72.2k | if (semctl(semId, 0, IPC_RMID, semun) < 0) |
175 | 0 | elog(LOG, "semctl(%d, 0, IPC_RMID, ...) failed: %m", semId); |
176 | 72.2k | } |
177 | | |
178 | | /* Get the current value (semval) of the semaphore */ |
179 | | static int |
180 | | IpcSemaphoreGetValue(IpcSemaphoreId semId, int semNum) |
181 | 199 | { |
182 | 199 | union semun dummy; /* for Solaris */ |
183 | | |
184 | 199 | dummy.val = 0; /* unused */ |
185 | | |
186 | 199 | return semctl(semId, semNum, GETVAL, dummy); |
187 | 199 | } |
188 | | |
189 | | /* Get the PID of the last process to do semop() on the semaphore */ |
190 | | static pid_t |
191 | | IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int semNum) |
192 | 199 | { |
193 | 199 | union semun dummy; /* for Solaris */ |
194 | | |
195 | 199 | dummy.val = 0; /* unused */ |
196 | | |
197 | 199 | return semctl(semId, semNum, GETPID, dummy); |
198 | 199 | } |
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 |
211 | | IpcSemaphoreCreate(int numSems) |
212 | 72.2k | { |
213 | 72.2k | IpcSemaphoreId semId; |
214 | 72.2k | union semun semun; |
215 | 72.2k | PGSemaphoreData mysema; |
216 | | |
217 | | /* Loop till we find a free IPC key */ |
218 | 199 | for (nextSemaKey++;; nextSemaKey++) |
219 | 72.4k | { |
220 | 72.4k | pid_t creatorPID; |
221 | | |
222 | | /* Try to create new semaphore set */ |
223 | 72.4k | semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1); |
224 | 72.4k | if (semId >= 0) |
225 | 72.2k | break; /* successful create */ |
226 | | |
227 | | /* See if it looks to be leftover from a dead Postgres process */ |
228 | 199 | semId = semget(nextSemaKey, numSems + 1, 0); |
229 | 199 | if (semId < 0) |
230 | 0 | continue; /* failed: must be some other app's */ |
231 | 199 | if (IpcSemaphoreGetValue(semId, numSems) != PGSemaMagic) |
232 | 0 | 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 | 199 | creatorPID = IpcSemaphoreGetLastPID(semId, numSems); |
239 | 199 | if (creatorPID <= 0) |
240 | 0 | continue; /* oops, GETPID failed */ |
241 | 199 | if (creatorPID != getpid()) |
242 | 199 | { |
243 | 199 | if (kill(creatorPID, 0) == 0 || errno != ESRCH) |
244 | 199 | continue; /* sema belongs to a live process */ |
245 | 0 | } |
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 | 0 | semun.val = 0; /* unused, but keep compiler quiet */ |
254 | 0 | if (semctl(semId, 0, IPC_RMID, semun) < 0) |
255 | 0 | continue; |
256 | | |
257 | | /* |
258 | | * Now try again to create the sema set. |
259 | | */ |
260 | 0 | semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1); |
261 | 0 | if (semId >= 0) |
262 | 0 | 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 | 0 | } |
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 | 72.2k | IpcSemaphoreInitialize(semId, numSems, PGSemaMagic - 1); |
278 | 72.2k | mysema.semId = semId; |
279 | 72.2k | mysema.semNum = numSems; |
280 | 72.2k | PGSemaphoreUnlock(&mysema); |
281 | | |
282 | 72.2k | return semId; |
283 | 72.2k | } |
284 | | |
285 | | |
286 | | /* |
287 | | * Report amount of shared memory needed for semaphores |
288 | | */ |
289 | | Size |
290 | | PGSemaphoreShmemSize(int maxSemas) |
291 | 7.22k | { |
292 | 7.22k | return mul_size(maxSemas, sizeof(PGSemaphoreData)); |
293 | 7.22k | } |
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 | 3.61k | { |
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 | 3.61k | sharedSemas = (PGSemaphore) |
325 | 3.61k | ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas)); |
326 | 3.61k | numSharedSemas = 0; |
327 | 3.61k | maxSharedSemas = maxSemas; |
328 | | |
329 | 3.61k | maxSemaSets = (maxSemas + SEMAS_PER_SET - 1) / SEMAS_PER_SET; |
330 | 3.61k | mySemaSets = (IpcSemaphoreId *) |
331 | 3.61k | malloc(maxSemaSets * sizeof(IpcSemaphoreId)); |
332 | 3.61k | if (mySemaSets == NULL) |
333 | 0 | elog(PANIC, "out of memory"); |
334 | 3.61k | numSemaSets = 0; |
335 | 3.61k | nextSemaKey = port * 1000; |
336 | 3.61k | nextSemaNumber = SEMAS_PER_SET; /* force sema set alloc on 1st call */ |
337 | | |
338 | 3.61k | on_shmem_exit(ReleaseSemaphores, 0); |
339 | 3.61k | } |
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 |
347 | | ReleaseSemaphores(int status, Datum arg) |
348 | 3.61k | { |
349 | 3.61k | int i; |
350 | | |
351 | 75.8k | for (i = 0; i < numSemaSets; i++) |
352 | 72.2k | IpcSemaphoreKill(mySemaSets[i]); |
353 | 3.61k | free(mySemaSets); |
354 | 3.61k | } |
355 | | |
356 | | /* |
357 | | * PGSemaphoreCreate |
358 | | * |
359 | | * Allocate a PGSemaphore structure with initial count 1 |
360 | | */ |
361 | | PGSemaphore |
362 | | PGSemaphoreCreate(void) |
363 | 1.14M | { |
364 | 1.14M | PGSemaphore sema; |
365 | | |
366 | | /* Can't do this in a backend, because static state is postmaster's */ |
367 | 1.14M | Assert(!IsUnderPostmaster); |
368 | | |
369 | 1.14M | if (nextSemaNumber >= SEMAS_PER_SET) |
370 | 72.2k | { |
371 | | /* Time to allocate another semaphore set */ |
372 | 72.2k | if (numSemaSets >= maxSemaSets) |
373 | 0 | elog(PANIC, "too many semaphores created"); |
374 | 72.2k | mySemaSets[numSemaSets] = IpcSemaphoreCreate(SEMAS_PER_SET); |
375 | 72.2k | numSemaSets++; |
376 | 72.2k | nextSemaNumber = 0; |
377 | 72.2k | } |
378 | | /* Use the next shared PGSemaphoreData */ |
379 | 1.14M | if (numSharedSemas >= maxSharedSemas) |
380 | 0 | elog(PANIC, "too many semaphores created"); |
381 | 1.14M | sema = &sharedSemas[numSharedSemas++]; |
382 | | /* Assign the next free semaphore in the current set */ |
383 | 1.14M | sema->semId = mySemaSets[numSemaSets - 1]; |
384 | 1.14M | sema->semNum = nextSemaNumber++; |
385 | | /* Initialize it to count 1 */ |
386 | 1.14M | IpcSemaphoreInitialize(sema->semId, sema->semNum, 1); |
387 | | |
388 | 1.14M | return sema; |
389 | 1.14M | } |
390 | | |
391 | | /* |
392 | | * PGSemaphoreReset |
393 | | * |
394 | | * Reset a previously-initialized PGSemaphore to have count 0 |
395 | | */ |
396 | | void |
397 | | PGSemaphoreReset(PGSemaphore sema) |
398 | 5.26k | { |
399 | 5.26k | IpcSemaphoreInitialize(sema->semId, sema->semNum, 0); |
400 | 5.26k | } |
401 | | |
402 | | /* |
403 | | * PGSemaphoreLock |
404 | | * |
405 | | * Lock a semaphore (decrement count), blocking if count would be < 0 |
406 | | */ |
407 | | void |
408 | | PGSemaphoreLock(PGSemaphore sema) |
409 | 354 | { |
410 | 354 | int errStatus; |
411 | 354 | struct sembuf sops; |
412 | | |
413 | 354 | sops.sem_op = -1; /* decrement */ |
414 | 354 | sops.sem_flg = 0; |
415 | 354 | 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 | 354 | do |
427 | 354 | { |
428 | 354 | errStatus = semop(sema->semId, &sops, 1); |
429 | 354 | } while (errStatus < 0 && errno == EINTR); |
430 | | |
431 | 354 | if (errStatus < 0) |
432 | 1 | elog(FATAL, "semop(id=%d) failed: %m", sema->semId); |
433 | 354 | } |
434 | | |
435 | | /* |
436 | | * PGSemaphoreUnlock |
437 | | * |
438 | | * Unlock a semaphore (increment count) |
439 | | */ |
440 | | void |
441 | | PGSemaphoreUnlock(PGSemaphore sema) |
442 | 72.6k | { |
443 | 72.6k | int errStatus; |
444 | 72.6k | struct sembuf sops; |
445 | | |
446 | 72.6k | sops.sem_op = 1; /* increment */ |
447 | 72.6k | sops.sem_flg = 0; |
448 | 72.6k | 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 | 72.6k | do |
457 | 72.6k | { |
458 | 72.6k | errStatus = semop(sema->semId, &sops, 1); |
459 | 72.6k | } while (errStatus < 0 && errno == EINTR); |
460 | | |
461 | 72.6k | if (errStatus < 0) |
462 | 1 | elog(FATAL, "semop(id=%d) failed: %m", sema->semId); |
463 | 72.6k | } |
464 | | |
465 | | /* |
466 | | * PGSemaphoreTryLock |
467 | | * |
468 | | * Lock a semaphore only if able to do so without blocking |
469 | | */ |
470 | | bool |
471 | | PGSemaphoreTryLock(PGSemaphore sema) |
472 | 0 | { |
473 | 0 | int errStatus; |
474 | 0 | struct sembuf sops; |
475 | |
|
476 | 0 | sops.sem_op = -1; /* decrement */ |
477 | 0 | sops.sem_flg = IPC_NOWAIT; /* but don't block */ |
478 | 0 | 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 | 0 | do |
486 | 0 | { |
487 | 0 | errStatus = semop(sema->semId, &sops, 1); |
488 | 0 | } while (errStatus < 0 && errno == EINTR); |
489 | |
|
490 | 0 | if (errStatus < 0) |
491 | 0 | { |
492 | | /* Expect EAGAIN or EWOULDBLOCK (platform-dependent) */ |
493 | 0 | #ifdef EAGAIN |
494 | 0 | if (errno == EAGAIN) |
495 | 0 | return false; /* failed to lock it */ |
496 | 0 | #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 | 0 | elog(FATAL, "semop(id=%d) failed: %m", sema->semId); |
503 | 0 | } |
504 | |
|
505 | 0 | return true; |
506 | 0 | } |