/src/postgres/src/backend/postmaster/pmchild.c
Line | Count | Source (jump to first uncovered line) |
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 | | * MaxLivePostmasterChildren |
64 | | * |
65 | | * This reports the number of postmaster child processes that can be active. |
66 | | * It includes all children except for dead-end children. This allows the |
67 | | * array in shared memory (PMChildFlags) to have a fixed maximum size. |
68 | | */ |
69 | | int |
70 | | MaxLivePostmasterChildren(void) |
71 | 0 | { |
72 | 0 | if (num_pmchild_slots == 0) |
73 | 0 | elog(ERROR, "PM child array not initialized yet"); |
74 | 0 | return num_pmchild_slots; |
75 | 0 | } |
76 | | |
77 | | /* |
78 | | * Initialize at postmaster startup |
79 | | * |
80 | | * Note: This is not called on crash restart. We rely on PMChild entries to |
81 | | * remain valid through the restart process. This is important because the |
82 | | * syslogger survives through the crash restart process, so we must not |
83 | | * invalidate its PMChild slot. |
84 | | */ |
85 | | void |
86 | | InitPostmasterChildSlots(void) |
87 | 0 | { |
88 | 0 | int slotno; |
89 | 0 | PMChild *slots; |
90 | | |
91 | | /* |
92 | | * We allow more connections here than we can have backends because some |
93 | | * might still be authenticating; they might fail auth, or some existing |
94 | | * backend might exit before the auth cycle is completed. The exact |
95 | | * MaxConnections limit is enforced when a new backend tries to join the |
96 | | * PGPROC array. |
97 | | * |
98 | | * WAL senders start out as regular backends, so they share the same pool. |
99 | | */ |
100 | 0 | pmchild_pools[B_BACKEND].size = 2 * (MaxConnections + max_wal_senders); |
101 | |
|
102 | 0 | pmchild_pools[B_AUTOVAC_WORKER].size = autovacuum_worker_slots; |
103 | 0 | pmchild_pools[B_BG_WORKER].size = max_worker_processes; |
104 | 0 | pmchild_pools[B_IO_WORKER].size = MAX_IO_WORKERS; |
105 | | |
106 | | /* |
107 | | * There can be only one of each of these running at a time. They each |
108 | | * get their own pool of just one entry. |
109 | | */ |
110 | 0 | pmchild_pools[B_AUTOVAC_LAUNCHER].size = 1; |
111 | 0 | pmchild_pools[B_SLOTSYNC_WORKER].size = 1; |
112 | 0 | pmchild_pools[B_ARCHIVER].size = 1; |
113 | 0 | pmchild_pools[B_BG_WRITER].size = 1; |
114 | 0 | pmchild_pools[B_CHECKPOINTER].size = 1; |
115 | 0 | pmchild_pools[B_STARTUP].size = 1; |
116 | 0 | pmchild_pools[B_WAL_RECEIVER].size = 1; |
117 | 0 | pmchild_pools[B_WAL_SUMMARIZER].size = 1; |
118 | 0 | pmchild_pools[B_WAL_WRITER].size = 1; |
119 | 0 | pmchild_pools[B_LOGGER].size = 1; |
120 | | |
121 | | /* The rest of the pmchild_pools are left at zero size */ |
122 | | |
123 | | /* Count the total number of slots */ |
124 | 0 | num_pmchild_slots = 0; |
125 | 0 | for (int i = 0; i < BACKEND_NUM_TYPES; i++) |
126 | 0 | num_pmchild_slots += pmchild_pools[i].size; |
127 | | |
128 | | /* Initialize them */ |
129 | 0 | slots = palloc(num_pmchild_slots * sizeof(PMChild)); |
130 | 0 | slotno = 0; |
131 | 0 | for (int btype = 0; btype < BACKEND_NUM_TYPES; btype++) |
132 | 0 | { |
133 | 0 | pmchild_pools[btype].first_slotno = slotno + 1; |
134 | 0 | dlist_init(&pmchild_pools[btype].freelist); |
135 | |
|
136 | 0 | for (int j = 0; j < pmchild_pools[btype].size; j++) |
137 | 0 | { |
138 | 0 | slots[slotno].pid = 0; |
139 | 0 | slots[slotno].child_slot = slotno + 1; |
140 | 0 | slots[slotno].bkend_type = B_INVALID; |
141 | 0 | slots[slotno].rw = NULL; |
142 | 0 | slots[slotno].bgworker_notify = false; |
143 | 0 | dlist_push_tail(&pmchild_pools[btype].freelist, &slots[slotno].elem); |
144 | 0 | slotno++; |
145 | 0 | } |
146 | 0 | } |
147 | 0 | Assert(slotno == num_pmchild_slots); |
148 | | |
149 | | /* Initialize other structures */ |
150 | 0 | dlist_init(&ActiveChildList); |
151 | 0 | } |
152 | | |
153 | | /* |
154 | | * Allocate a PMChild entry for a postmaster child process of given type. |
155 | | * |
156 | | * The entry is taken from the right pool for the type. |
157 | | * |
158 | | * pmchild->child_slot in the returned struct is unique among all active child |
159 | | * processes. |
160 | | */ |
161 | | PMChild * |
162 | | AssignPostmasterChildSlot(BackendType btype) |
163 | 0 | { |
164 | 0 | dlist_head *freelist; |
165 | 0 | PMChild *pmchild; |
166 | |
|
167 | 0 | if (pmchild_pools[btype].size == 0) |
168 | 0 | elog(ERROR, "cannot allocate a PMChild slot for backend type %d", btype); |
169 | | |
170 | 0 | freelist = &pmchild_pools[btype].freelist; |
171 | 0 | if (dlist_is_empty(freelist)) |
172 | 0 | return NULL; |
173 | | |
174 | 0 | pmchild = dlist_container(PMChild, elem, dlist_pop_head_node(freelist)); |
175 | 0 | pmchild->pid = 0; |
176 | 0 | pmchild->bkend_type = btype; |
177 | 0 | pmchild->rw = NULL; |
178 | 0 | pmchild->bgworker_notify = true; |
179 | | |
180 | | /* |
181 | | * pmchild->child_slot for each entry was initialized when the array of |
182 | | * slots was allocated. Sanity check it. |
183 | | */ |
184 | 0 | if (!(pmchild->child_slot >= pmchild_pools[btype].first_slotno && |
185 | 0 | pmchild->child_slot < pmchild_pools[btype].first_slotno + pmchild_pools[btype].size)) |
186 | 0 | { |
187 | 0 | elog(ERROR, "pmchild freelist for backend type %d is corrupt", |
188 | 0 | pmchild->bkend_type); |
189 | 0 | } |
190 | | |
191 | 0 | dlist_push_head(&ActiveChildList, &pmchild->elem); |
192 | | |
193 | | /* Update the status in the shared memory array */ |
194 | 0 | MarkPostmasterChildSlotAssigned(pmchild->child_slot); |
195 | |
|
196 | 0 | elog(DEBUG2, "assigned pm child slot %d for %s", |
197 | 0 | pmchild->child_slot, PostmasterChildName(btype)); |
198 | | |
199 | 0 | return pmchild; |
200 | 0 | } |
201 | | |
202 | | /* |
203 | | * Allocate a PMChild struct for a dead-end backend. Dead-end children are |
204 | | * not assigned a child_slot number. The struct is palloc'd; returns NULL if |
205 | | * out of memory. |
206 | | */ |
207 | | PMChild * |
208 | | AllocDeadEndChild(void) |
209 | 0 | { |
210 | 0 | PMChild *pmchild; |
211 | |
|
212 | 0 | elog(DEBUG2, "allocating dead-end child"); |
213 | | |
214 | 0 | pmchild = (PMChild *) palloc_extended(sizeof(PMChild), MCXT_ALLOC_NO_OOM); |
215 | 0 | if (pmchild) |
216 | 0 | { |
217 | 0 | pmchild->pid = 0; |
218 | 0 | pmchild->child_slot = 0; |
219 | 0 | pmchild->bkend_type = B_DEAD_END_BACKEND; |
220 | 0 | pmchild->rw = NULL; |
221 | 0 | pmchild->bgworker_notify = false; |
222 | |
|
223 | 0 | dlist_push_head(&ActiveChildList, &pmchild->elem); |
224 | 0 | } |
225 | |
|
226 | 0 | return pmchild; |
227 | 0 | } |
228 | | |
229 | | /* |
230 | | * Release a PMChild slot, after the child process has exited. |
231 | | * |
232 | | * Returns true if the child detached cleanly from shared memory, false |
233 | | * otherwise (see MarkPostmasterChildSlotUnassigned). |
234 | | */ |
235 | | bool |
236 | | ReleasePostmasterChildSlot(PMChild *pmchild) |
237 | 0 | { |
238 | 0 | dlist_delete(&pmchild->elem); |
239 | 0 | if (pmchild->bkend_type == B_DEAD_END_BACKEND) |
240 | 0 | { |
241 | 0 | elog(DEBUG2, "releasing dead-end backend"); |
242 | 0 | pfree(pmchild); |
243 | 0 | return true; |
244 | 0 | } |
245 | 0 | else |
246 | 0 | { |
247 | 0 | PMChildPool *pool; |
248 | |
|
249 | 0 | elog(DEBUG2, "releasing pm child slot %d", pmchild->child_slot); |
250 | | |
251 | | /* WAL senders start out as regular backends, and share the pool */ |
252 | 0 | if (pmchild->bkend_type == B_WAL_SENDER) |
253 | 0 | pool = &pmchild_pools[B_BACKEND]; |
254 | 0 | else |
255 | 0 | pool = &pmchild_pools[pmchild->bkend_type]; |
256 | | |
257 | | /* sanity check that we return the entry to the right pool */ |
258 | 0 | if (!(pmchild->child_slot >= pool->first_slotno && |
259 | 0 | pmchild->child_slot < pool->first_slotno + pool->size)) |
260 | 0 | { |
261 | 0 | elog(ERROR, "pmchild freelist for backend type %d is corrupt", |
262 | 0 | pmchild->bkend_type); |
263 | 0 | } |
264 | | |
265 | 0 | dlist_push_head(&pool->freelist, &pmchild->elem); |
266 | 0 | return MarkPostmasterChildSlotUnassigned(pmchild->child_slot); |
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | | /* |
271 | | * Find the PMChild entry of a running child process by PID. |
272 | | */ |
273 | | PMChild * |
274 | | FindPostmasterChildByPid(int pid) |
275 | 0 | { |
276 | 0 | dlist_iter iter; |
277 | |
|
278 | 0 | dlist_foreach(iter, &ActiveChildList) |
279 | 0 | { |
280 | 0 | PMChild *bp = dlist_container(PMChild, elem, iter.cur); |
281 | |
|
282 | 0 | if (bp->pid == pid) |
283 | 0 | return bp; |
284 | 0 | } |
285 | 0 | return NULL; |
286 | 0 | } |