Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * procsignal.c
4 : * Routines for interprocess signalling
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/storage/ipc/procsignal.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include <signal.h>
18 : #include <unistd.h>
19 :
20 : #include "access/parallel.h"
21 : #include "commands/async.h"
22 : #include "miscadmin.h"
23 : #include "replication/walsender.h"
24 : #include "storage/latch.h"
25 : #include "storage/ipc.h"
26 : #include "storage/proc.h"
27 : #include "storage/shmem.h"
28 : #include "storage/sinval.h"
29 : #include "tcop/tcopprot.h"
30 :
31 :
32 : /*
33 : * The SIGUSR1 signal is multiplexed to support signalling multiple event
34 : * types. The specific reason is communicated via flags in shared memory.
35 : * We keep a boolean flag for each possible "reason", so that different
36 : * reasons can be signaled to a process concurrently. (However, if the same
37 : * reason is signaled more than once nearly simultaneously, the process may
38 : * observe it only once.)
39 : *
40 : * Each process that wants to receive signals registers its process ID
41 : * in the ProcSignalSlots array. The array is indexed by backend ID to make
42 : * slot allocation simple, and to avoid having to search the array when you
43 : * know the backend ID of the process you're signalling. (We do support
44 : * signalling without backend ID, but it's a bit less efficient.)
45 : *
46 : * The flags are actually declared as "volatile sig_atomic_t" for maximum
47 : * portability. This should ensure that loads and stores of the flag
48 : * values are atomic, allowing us to dispense with any explicit locking.
49 : */
50 : typedef struct
51 : {
52 : pid_t pss_pid;
53 : sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS];
54 : } ProcSignalSlot;
55 :
56 : /*
57 : * We reserve a slot for each possible BackendId, plus one for each
58 : * possible auxiliary process type. (This scheme assumes there is not
59 : * more than one of any auxiliary process type at a time.)
60 : */
61 : #define NumProcSignalSlots (MaxBackends + NUM_AUXPROCTYPES)
62 :
63 : static ProcSignalSlot *ProcSignalSlots = NULL;
64 : static volatile ProcSignalSlot *MyProcSignalSlot = NULL;
65 :
66 : static bool CheckProcSignal(ProcSignalReason reason);
67 : static void CleanupProcSignalState(int status, Datum arg);
68 :
69 : /*
70 : * ProcSignalShmemSize
71 : * Compute space needed for procsignal's shared memory
72 : */
73 : Size
74 10 : ProcSignalShmemSize(void)
75 : {
76 10 : return NumProcSignalSlots * sizeof(ProcSignalSlot);
77 : }
78 :
79 : /*
80 : * ProcSignalShmemInit
81 : * Allocate and initialize procsignal's shared memory
82 : */
83 : void
84 5 : ProcSignalShmemInit(void)
85 : {
86 5 : Size size = ProcSignalShmemSize();
87 : bool found;
88 :
89 5 : ProcSignalSlots = (ProcSignalSlot *)
90 5 : ShmemInitStruct("ProcSignalSlots", size, &found);
91 :
92 : /* If we're first, set everything to zeroes */
93 5 : if (!found)
94 5 : MemSet(ProcSignalSlots, 0, size);
95 5 : }
96 :
97 : /*
98 : * ProcSignalInit
99 : * Register the current process in the procsignal array
100 : *
101 : * The passed index should be my BackendId if the process has one,
102 : * or MaxBackends + aux process type if not.
103 : */
104 : void
105 342 : ProcSignalInit(int pss_idx)
106 : {
107 : volatile ProcSignalSlot *slot;
108 :
109 342 : Assert(pss_idx >= 1 && pss_idx <= NumProcSignalSlots);
110 :
111 342 : slot = &ProcSignalSlots[pss_idx - 1];
112 :
113 : /* sanity check */
114 342 : if (slot->pss_pid != 0)
115 0 : elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty",
116 : MyProcPid, pss_idx);
117 :
118 : /* Clear out any leftover signal reasons */
119 342 : MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
120 :
121 : /* Mark slot with my PID */
122 342 : slot->pss_pid = MyProcPid;
123 :
124 : /* Remember slot location for CheckProcSignal */
125 342 : MyProcSignalSlot = slot;
126 :
127 : /* Set up to release the slot on process exit */
128 342 : on_shmem_exit(CleanupProcSignalState, Int32GetDatum(pss_idx));
129 342 : }
130 :
131 : /*
132 : * CleanupProcSignalState
133 : * Remove current process from ProcSignalSlots
134 : *
135 : * This function is called via on_shmem_exit() during backend shutdown.
136 : */
137 : static void
138 342 : CleanupProcSignalState(int status, Datum arg)
139 : {
140 342 : int pss_idx = DatumGetInt32(arg);
141 : volatile ProcSignalSlot *slot;
142 :
143 342 : slot = &ProcSignalSlots[pss_idx - 1];
144 342 : Assert(slot == MyProcSignalSlot);
145 :
146 : /*
147 : * Clear MyProcSignalSlot, so that a SIGUSR1 received after this point
148 : * won't try to access it after it's no longer ours (and perhaps even
149 : * after we've unmapped the shared memory segment).
150 : */
151 342 : MyProcSignalSlot = NULL;
152 :
153 : /* sanity check */
154 342 : if (slot->pss_pid != MyProcPid)
155 : {
156 : /*
157 : * don't ERROR here. We're exiting anyway, and don't want to get into
158 : * infinite loop trying to exit
159 : */
160 0 : elog(LOG, "process %d releasing ProcSignal slot %d, but it contains %d",
161 : MyProcPid, pss_idx, (int) slot->pss_pid);
162 342 : return; /* XXX better to zero the slot anyway? */
163 : }
164 :
165 342 : slot->pss_pid = 0;
166 : }
167 :
168 : /*
169 : * SendProcSignal
170 : * Send a signal to a Postgres process
171 : *
172 : * Providing backendId is optional, but it will speed up the operation.
173 : *
174 : * On success (a signal was sent), zero is returned.
175 : * On error, -1 is returned, and errno is set (typically to ESRCH or EPERM).
176 : *
177 : * Not to be confused with ProcSendSignal
178 : */
179 : int
180 448 : SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId)
181 : {
182 : volatile ProcSignalSlot *slot;
183 :
184 448 : if (backendId != InvalidBackendId)
185 : {
186 448 : slot = &ProcSignalSlots[backendId - 1];
187 :
188 : /*
189 : * Note: Since there's no locking, it's possible that the target
190 : * process detaches from shared memory and exits right after this
191 : * test, before we set the flag and send signal. And the signal slot
192 : * might even be recycled by a new process, so it's remotely possible
193 : * that we set a flag for a wrong process. That's OK, all the signals
194 : * are such that no harm is done if they're mistakenly fired.
195 : */
196 448 : if (slot->pss_pid == pid)
197 : {
198 : /* Atomically set the proper flag */
199 448 : slot->pss_signalFlags[reason] = true;
200 : /* Send signal */
201 448 : return kill(pid, SIGUSR1);
202 : }
203 : }
204 : else
205 : {
206 : /*
207 : * BackendId not provided, so search the array using pid. We search
208 : * the array back to front so as to reduce search overhead. Passing
209 : * InvalidBackendId means that the target is most likely an auxiliary
210 : * process, which will have a slot near the end of the array.
211 : */
212 : int i;
213 :
214 0 : for (i = NumProcSignalSlots - 1; i >= 0; i--)
215 : {
216 0 : slot = &ProcSignalSlots[i];
217 :
218 0 : if (slot->pss_pid == pid)
219 : {
220 : /* the above note about race conditions applies here too */
221 :
222 : /* Atomically set the proper flag */
223 0 : slot->pss_signalFlags[reason] = true;
224 : /* Send signal */
225 0 : return kill(pid, SIGUSR1);
226 : }
227 : }
228 : }
229 :
230 0 : errno = ESRCH;
231 0 : return -1;
232 : }
233 :
234 : /*
235 : * CheckProcSignal - check to see if a particular reason has been
236 : * signaled, and clear the signal flag. Should be called after receiving
237 : * SIGUSR1.
238 : */
239 : static bool
240 12680 : CheckProcSignal(ProcSignalReason reason)
241 : {
242 12680 : volatile ProcSignalSlot *slot = MyProcSignalSlot;
243 :
244 12680 : if (slot != NULL)
245 : {
246 : /* Careful here --- don't clear flag if we haven't seen it set */
247 12680 : if (slot->pss_signalFlags[reason])
248 : {
249 448 : slot->pss_signalFlags[reason] = false;
250 448 : return true;
251 : }
252 : }
253 :
254 12232 : return false;
255 : }
256 :
257 : /*
258 : * procsignal_sigusr1_handler - handle SIGUSR1 signal.
259 : */
260 : void
261 1268 : procsignal_sigusr1_handler(SIGNAL_ARGS)
262 : {
263 1268 : int save_errno = errno;
264 :
265 1268 : if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))
266 218 : HandleCatchupInterrupt();
267 :
268 1268 : if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
269 0 : HandleNotifyInterrupt();
270 :
271 1268 : if (CheckProcSignal(PROCSIG_PARALLEL_MESSAGE))
272 230 : HandleParallelMessageInterrupt();
273 :
274 1268 : if (CheckProcSignal(PROCSIG_WALSND_INIT_STOPPING))
275 0 : HandleWalSndInitStopping();
276 :
277 1268 : if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
278 0 : RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
279 :
280 1268 : if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
281 0 : RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
282 :
283 1268 : if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))
284 0 : RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
285 :
286 1268 : if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
287 0 : RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
288 :
289 1268 : if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
290 0 : RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
291 :
292 1268 : if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
293 0 : RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
294 :
295 1268 : SetLatch(MyLatch);
296 :
297 1268 : latch_sigusr1_handler();
298 :
299 1268 : errno = save_errno;
300 1268 : }
|