Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pmchild.c
4 : : * Functions for keeping track of postmaster child processes.
5 : : *
6 : : * Postmaster keeps track of all child processes so that when a process exits,
7 : : * it knows what kind of a process it was and can clean up accordingly. Every
8 : : * child process is allocated a PMChild struct from a fixed pool of structs.
9 : : * The size of the pool is determined by various settings that configure how
10 : : * many worker processes and backend connections are allowed, i.e.
11 : : * autovacuum_worker_slots, max_worker_processes, max_wal_senders, and
12 : : * max_connections.
13 : : *
14 : : * Dead-end backends are handled slightly differently. There is no limit
15 : : * on the number of dead-end backends, and they do not need unique IDs, so
16 : : * their PMChild structs are allocated dynamically, not from a pool.
17 : : *
18 : : * The structures and functions in this file are private to the postmaster
19 : : * process. But note that there is an array in shared memory, managed by
20 : : * pmsignal.c, that mirrors this.
21 : : *
22 : : *
23 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
24 : : * Portions Copyright (c) 1994, Regents of the University of California
25 : : *
26 : : * IDENTIFICATION
27 : : * src/backend/postmaster/pmchild.c
28 : : *
29 : : *-------------------------------------------------------------------------
30 : : */
31 : :
32 : : #include "postgres.h"
33 : :
34 : : #include "miscadmin.h"
35 : : #include "postmaster/autovacuum.h"
36 : : #include "postmaster/postmaster.h"
37 : : #include "replication/walsender.h"
38 : : #include "storage/pmsignal.h"
39 : : #include "storage/proc.h"
40 : :
41 : : /*
42 : : * Freelists for different kinds of child processes. We maintain separate
43 : : * pools for each, so that for example launching a lot of regular backends
44 : : * cannot prevent autovacuum or an aux process from launching.
45 : : */
46 : : typedef struct PMChildPool
47 : : {
48 : : int size; /* number of PMChild slots reserved for this
49 : : * kind of processes */
50 : : int first_slotno; /* first slot belonging to this pool */
51 : : dlist_head freelist; /* currently unused PMChild entries */
52 : : } PMChildPool;
53 : :
54 : : static PMChildPool pmchild_pools[BACKEND_NUM_TYPES];
55 : : NON_EXEC_STATIC int num_pmchild_slots = 0;
56 : :
57 : : /*
58 : : * List of active child processes. This includes dead-end children.
59 : : */
60 : : dlist_head ActiveChildList;
61 : :
62 : : /*
63 : : * Dummy pointer to persuade Valgrind that we've not leaked the array of
64 : : * PMChild structs. Make it global to ensure the compiler doesn't
65 : : * optimize it away.
66 : : */
67 : : #ifdef USE_VALGRIND
68 : : extern PMChild *pmchild_array;
69 : : PMChild *pmchild_array;
70 : : #endif
71 : :
72 : :
73 : : /*
74 : : * MaxLivePostmasterChildren
75 : : *
76 : : * This reports the number of postmaster child processes that can be active.
77 : : * It includes all children except for dead-end children. This allows the
78 : : * array in shared memory (PMChildFlags) to have a fixed maximum size.
79 : : */
80 : : int
296 heikki.linnakangas@i 81 :CBC 4996 : MaxLivePostmasterChildren(void)
82 : : {
83 [ - + ]: 4996 : if (num_pmchild_slots == 0)
296 heikki.linnakangas@i 84 [ # # ]:UBC 0 : elog(ERROR, "PM child array not initialized yet");
296 heikki.linnakangas@i 85 :CBC 4996 : return num_pmchild_slots;
86 : : }
87 : :
88 : : /*
89 : : * Initialize at postmaster startup
90 : : *
91 : : * Note: This is not called on crash restart. We rely on PMChild entries to
92 : : * remain valid through the restart process. This is important because the
93 : : * syslogger survives through the crash restart process, so we must not
94 : : * invalidate its PMChild slot.
95 : : */
96 : : void
97 : 1028 : InitPostmasterChildSlots(void)
98 : : {
99 : : int slotno;
100 : : PMChild *slots;
101 : :
102 : : /*
103 : : * We allow more connections here than we can have backends because some
104 : : * might still be authenticating; they might fail auth, or some existing
105 : : * backend might exit before the auth cycle is completed. The exact
106 : : * MaxConnections limit is enforced when a new backend tries to join the
107 : : * PGPROC array.
108 : : *
109 : : * WAL senders start out as regular backends, so they share the same pool.
110 : : */
111 : 1028 : pmchild_pools[B_BACKEND].size = 2 * (MaxConnections + max_wal_senders);
112 : :
243 nathan@postgresql.or 113 : 1028 : pmchild_pools[B_AUTOVAC_WORKER].size = autovacuum_worker_slots;
296 heikki.linnakangas@i 114 : 1028 : pmchild_pools[B_BG_WORKER].size = max_worker_processes;
172 andres@anarazel.de 115 : 1028 : pmchild_pools[B_IO_WORKER].size = MAX_IO_WORKERS;
116 : :
117 : : /*
118 : : * There can be only one of each of these running at a time. They each
119 : : * get their own pool of just one entry.
120 : : */
296 heikki.linnakangas@i 121 : 1028 : pmchild_pools[B_AUTOVAC_LAUNCHER].size = 1;
122 : 1028 : pmchild_pools[B_SLOTSYNC_WORKER].size = 1;
123 : 1028 : pmchild_pools[B_ARCHIVER].size = 1;
124 : 1028 : pmchild_pools[B_BG_WRITER].size = 1;
125 : 1028 : pmchild_pools[B_CHECKPOINTER].size = 1;
126 : 1028 : pmchild_pools[B_STARTUP].size = 1;
127 : 1028 : pmchild_pools[B_WAL_RECEIVER].size = 1;
128 : 1028 : pmchild_pools[B_WAL_SUMMARIZER].size = 1;
129 : 1028 : pmchild_pools[B_WAL_WRITER].size = 1;
130 : 1028 : pmchild_pools[B_LOGGER].size = 1;
131 : :
132 : : /* The rest of the pmchild_pools are left at zero size */
133 : :
134 : : /* Count the total number of slots */
135 : 1028 : num_pmchild_slots = 0;
136 [ + + ]: 19532 : for (int i = 0; i < BACKEND_NUM_TYPES; i++)
137 : 18504 : num_pmchild_slots += pmchild_pools[i].size;
138 : :
139 : : /* Allocate enough slots, and make sure Valgrind doesn't complain */
140 : 1028 : slots = palloc(num_pmchild_slots * sizeof(PMChild));
141 : : #ifdef USE_VALGRIND
142 : : pmchild_array = slots;
143 : : #endif
144 : :
145 : : /* Initialize them */
146 : 1028 : slotno = 0;
147 [ + + ]: 19532 : for (int btype = 0; btype < BACKEND_NUM_TYPES; btype++)
148 : : {
149 : 18504 : pmchild_pools[btype].first_slotno = slotno + 1;
150 : 18504 : dlist_init(&pmchild_pools[btype].freelist);
151 : :
152 [ + + ]: 226825 : for (int j = 0; j < pmchild_pools[btype].size; j++)
153 : : {
154 : 208321 : slots[slotno].pid = 0;
155 : 208321 : slots[slotno].child_slot = slotno + 1;
156 : 208321 : slots[slotno].bkend_type = B_INVALID;
157 : 208321 : slots[slotno].rw = NULL;
158 : 208321 : slots[slotno].bgworker_notify = false;
159 : 208321 : dlist_push_tail(&pmchild_pools[btype].freelist, &slots[slotno].elem);
160 : 208321 : slotno++;
161 : : }
162 : : }
163 [ - + ]: 1028 : Assert(slotno == num_pmchild_slots);
164 : :
165 : : /* Initialize other structures */
166 : 1028 : dlist_init(&ActiveChildList);
167 : 1028 : }
168 : :
169 : : /*
170 : : * Allocate a PMChild entry for a postmaster child process of given type.
171 : : *
172 : : * The entry is taken from the right pool for the type.
173 : : *
174 : : * pmchild->child_slot in the returned struct is unique among all active child
175 : : * processes.
176 : : */
177 : : PMChild *
178 : 21465 : AssignPostmasterChildSlot(BackendType btype)
179 : : {
180 : : dlist_head *freelist;
181 : : PMChild *pmchild;
182 : :
183 [ - + ]: 21465 : if (pmchild_pools[btype].size == 0)
296 heikki.linnakangas@i 184 [ # # ]:UBC 0 : elog(ERROR, "cannot allocate a PMChild slot for backend type %d", btype);
185 : :
296 heikki.linnakangas@i 186 :CBC 21465 : freelist = &pmchild_pools[btype].freelist;
187 [ + + ]: 21465 : if (dlist_is_empty(freelist))
188 : 29 : return NULL;
189 : :
190 : 21436 : pmchild = dlist_container(PMChild, elem, dlist_pop_head_node(freelist));
191 : 21436 : pmchild->pid = 0;
192 : 21436 : pmchild->bkend_type = btype;
193 : 21436 : pmchild->rw = NULL;
194 : 21436 : pmchild->bgworker_notify = true;
195 : :
196 : : /*
197 : : * pmchild->child_slot for each entry was initialized when the array of
198 : : * slots was allocated. Sanity check it.
199 : : */
200 [ + - ]: 21436 : if (!(pmchild->child_slot >= pmchild_pools[btype].first_slotno &&
201 [ - + ]: 21436 : pmchild->child_slot < pmchild_pools[btype].first_slotno + pmchild_pools[btype].size))
202 : : {
296 heikki.linnakangas@i 203 [ # # ]:UBC 0 : elog(ERROR, "pmchild freelist for backend type %d is corrupt",
204 : : pmchild->bkend_type);
205 : : }
206 : :
296 heikki.linnakangas@i 207 :CBC 21436 : dlist_push_head(&ActiveChildList, &pmchild->elem);
208 : :
209 : : /* Update the status in the shared memory array */
210 : 21436 : MarkPostmasterChildSlotAssigned(pmchild->child_slot);
211 : :
212 [ + + ]: 21436 : elog(DEBUG2, "assigned pm child slot %d for %s",
213 : : pmchild->child_slot, PostmasterChildName(btype));
214 : :
215 : 21436 : return pmchild;
216 : : }
217 : :
218 : : /*
219 : : * Allocate a PMChild struct for a dead-end backend. Dead-end children are
220 : : * not assigned a child_slot number. The struct is palloc'd; returns NULL if
221 : : * out of memory.
222 : : */
223 : : PMChild *
224 : 290 : AllocDeadEndChild(void)
225 : : {
226 : : PMChild *pmchild;
227 : :
228 [ + + ]: 290 : elog(DEBUG2, "allocating dead-end child");
229 : :
230 : 290 : pmchild = (PMChild *) palloc_extended(sizeof(PMChild), MCXT_ALLOC_NO_OOM);
231 [ + - ]: 290 : if (pmchild)
232 : : {
233 : 290 : pmchild->pid = 0;
234 : 290 : pmchild->child_slot = 0;
235 : 290 : pmchild->bkend_type = B_DEAD_END_BACKEND;
236 : 290 : pmchild->rw = NULL;
237 : 290 : pmchild->bgworker_notify = false;
238 : :
239 : 290 : dlist_push_head(&ActiveChildList, &pmchild->elem);
240 : : }
241 : :
242 : 290 : return pmchild;
243 : : }
244 : :
245 : : /*
246 : : * Release a PMChild slot, after the child process has exited.
247 : : *
248 : : * Returns true if the child detached cleanly from shared memory, false
249 : : * otherwise (see MarkPostmasterChildSlotUnassigned).
250 : : */
251 : : bool
252 : 21725 : ReleasePostmasterChildSlot(PMChild *pmchild)
253 : : {
254 : 21725 : dlist_delete(&pmchild->elem);
255 [ + + ]: 21725 : if (pmchild->bkend_type == B_DEAD_END_BACKEND)
256 : : {
257 [ + + ]: 290 : elog(DEBUG2, "releasing dead-end backend");
258 : 290 : pfree(pmchild);
259 : 290 : return true;
260 : : }
261 : : else
262 : : {
263 : : PMChildPool *pool;
264 : :
265 [ + + ]: 21435 : elog(DEBUG2, "releasing pm child slot %d", pmchild->child_slot);
266 : :
267 : : /* WAL senders start out as regular backends, and share the pool */
268 [ + + ]: 21435 : if (pmchild->bkend_type == B_WAL_SENDER)
269 : 32 : pool = &pmchild_pools[B_BACKEND];
270 : : else
271 : 21403 : pool = &pmchild_pools[pmchild->bkend_type];
272 : :
273 : : /* sanity check that we return the entry to the right pool */
274 [ + - ]: 21435 : if (!(pmchild->child_slot >= pool->first_slotno &&
275 [ - + ]: 21435 : pmchild->child_slot < pool->first_slotno + pool->size))
276 : : {
296 heikki.linnakangas@i 277 [ # # ]:UBC 0 : elog(ERROR, "pmchild freelist for backend type %d is corrupt",
278 : : pmchild->bkend_type);
279 : : }
280 : :
296 heikki.linnakangas@i 281 :CBC 21435 : dlist_push_head(&pool->freelist, &pmchild->elem);
282 : 21435 : return MarkPostmasterChildSlotUnassigned(pmchild->child_slot);
283 : : }
284 : : }
285 : :
286 : : /*
287 : : * Find the PMChild entry of a running child process by PID.
288 : : */
289 : : PMChild *
290 : 15245 : FindPostmasterChildByPid(int pid)
291 : : {
292 : : dlist_iter iter;
293 : :
294 [ + - + - ]: 42478 : dlist_foreach(iter, &ActiveChildList)
295 : : {
296 : 42478 : PMChild *bp = dlist_container(PMChild, elem, iter.cur);
297 : :
298 [ + + ]: 42478 : if (bp->pid == pid)
299 : 15245 : return bp;
300 : : }
296 heikki.linnakangas@i 301 :UBC 0 : return NULL;
302 : : }
|