/src/postgres/src/backend/storage/ipc/dsm.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * dsm.c |
4 | | * manage dynamic shared memory segments |
5 | | * |
6 | | * This file provides a set of services to make programming with dynamic |
7 | | * shared memory segments more convenient. Unlike the low-level |
8 | | * facilities provided by dsm_impl.h and dsm_impl.c, mappings and segments |
9 | | * created using this module will be cleaned up automatically. Mappings |
10 | | * will be removed when the resource owner under which they were created |
11 | | * is cleaned up, unless dsm_pin_mapping() is used, in which case they |
12 | | * have session lifespan. Segments will be removed when there are no |
13 | | * remaining mappings, or at postmaster shutdown in any case. After a |
14 | | * hard postmaster crash, remaining segments will be removed, if they |
15 | | * still exist, at the next postmaster startup. |
16 | | * |
17 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
18 | | * Portions Copyright (c) 1994, Regents of the University of California |
19 | | * |
20 | | * |
21 | | * IDENTIFICATION |
22 | | * src/backend/storage/ipc/dsm.c |
23 | | * |
24 | | *------------------------------------------------------------------------- |
25 | | */ |
26 | | |
27 | | #include "postgres.h" |
28 | | |
29 | | #include <fcntl.h> |
30 | | #include <unistd.h> |
31 | | #ifndef WIN32 |
32 | | #include <sys/mman.h> |
33 | | #endif |
34 | | #include <sys/stat.h> |
35 | | |
36 | | #include "common/pg_prng.h" |
37 | | #include "lib/ilist.h" |
38 | | #include "miscadmin.h" |
39 | | #include "port/pg_bitutils.h" |
40 | | #include "storage/dsm.h" |
41 | | #include "storage/fd.h" |
42 | | #include "storage/ipc.h" |
43 | | #include "storage/lwlock.h" |
44 | | #include "storage/pg_shmem.h" |
45 | | #include "storage/shmem.h" |
46 | | #include "storage/subsystems.h" |
47 | | #include "utils/freepage.h" |
48 | | #include "utils/memutils.h" |
49 | | #include "utils/resowner.h" |
50 | | |
51 | 0 | #define PG_DYNSHMEM_CONTROL_MAGIC 0x9a503d32 |
52 | | |
53 | | #define PG_DYNSHMEM_FIXED_SLOTS 64 |
54 | | #define PG_DYNSHMEM_SLOTS_PER_BACKEND 5 |
55 | | |
56 | 0 | #define INVALID_CONTROL_SLOT ((uint32) -1) |
57 | | |
58 | | /* Backend-local tracking for on-detach callbacks. */ |
59 | | typedef struct dsm_segment_detach_callback |
60 | | { |
61 | | on_dsm_detach_callback function; |
62 | | Datum arg; |
63 | | slist_node node; |
64 | | } dsm_segment_detach_callback; |
65 | | |
66 | | /* Backend-local state for a dynamic shared memory segment. */ |
67 | | struct dsm_segment |
68 | | { |
69 | | dlist_node node; /* List link in dsm_segment_list. */ |
70 | | ResourceOwner resowner; /* Resource owner. */ |
71 | | dsm_handle handle; /* Segment name. */ |
72 | | uint32 control_slot; /* Slot in control segment. */ |
73 | | void *impl_private; /* Implementation-specific private data. */ |
74 | | void *mapped_address; /* Mapping address, or NULL if unmapped. */ |
75 | | Size mapped_size; /* Size of our mapping. */ |
76 | | slist_head on_detach; /* On-detach callbacks. */ |
77 | | }; |
78 | | |
79 | | /* Shared-memory state for a dynamic shared memory segment. */ |
80 | | typedef struct dsm_control_item |
81 | | { |
82 | | dsm_handle handle; |
83 | | uint32 refcnt; /* 2+ = active, 1 = moribund, 0 = gone */ |
84 | | size_t first_page; |
85 | | size_t npages; |
86 | | void *impl_private_pm_handle; /* only needed on Windows */ |
87 | | bool pinned; |
88 | | } dsm_control_item; |
89 | | |
90 | | /* Layout of the dynamic shared memory control segment. */ |
91 | | typedef struct dsm_control_header |
92 | | { |
93 | | uint32 magic; |
94 | | uint32 nitems; |
95 | | uint32 maxitems; |
96 | | dsm_control_item item[FLEXIBLE_ARRAY_MEMBER]; |
97 | | } dsm_control_header; |
98 | | |
99 | | static void dsm_cleanup_for_mmap(void); |
100 | | static void dsm_postmaster_shutdown(int code, Datum arg); |
101 | | static dsm_segment *dsm_create_descriptor(void); |
102 | | static bool dsm_control_segment_sane(dsm_control_header *control, |
103 | | Size mapped_size); |
104 | | static uint64 dsm_control_bytes_needed(uint32 nitems); |
105 | | static inline dsm_handle make_main_region_dsm_handle(int slot); |
106 | | static inline bool is_main_region_dsm_handle(dsm_handle handle); |
107 | | |
108 | | /* Has this backend initialized the dynamic shared memory system yet? */ |
109 | | static bool dsm_init_done = false; |
110 | | |
111 | | /* Preallocated DSM space in the main shared memory region. */ |
112 | | static void *dsm_main_space_begin = NULL; |
113 | | static size_t dsm_main_space_size; |
114 | | |
115 | | static void dsm_main_space_request(void *arg); |
116 | | static void dsm_main_space_init(void *arg); |
117 | | |
118 | | const ShmemCallbacks dsm_shmem_callbacks = { |
119 | | .request_fn = dsm_main_space_request, |
120 | | .init_fn = dsm_main_space_init, |
121 | | }; |
122 | | |
123 | | /* |
124 | | * List of dynamic shared memory segments used by this backend. |
125 | | * |
126 | | * At process exit time, we must decrement the reference count of each |
127 | | * segment we have attached; this list makes it possible to find all such |
128 | | * segments. |
129 | | * |
130 | | * This list should always be empty in the postmaster. We could probably |
131 | | * allow the postmaster to map dynamic shared memory segments before it |
132 | | * begins to start child processes, provided that each process adjusted |
133 | | * the reference counts for those segments in the control segment at |
134 | | * startup time, but there's no obvious need for such a facility, which |
135 | | * would also be complex to handle in the EXEC_BACKEND case. Once the |
136 | | * postmaster has begun spawning children, there's an additional problem: |
137 | | * each new mapping would require an update to the control segment, |
138 | | * which requires locking, in which the postmaster must not be involved. |
139 | | */ |
140 | | static dlist_head dsm_segment_list = DLIST_STATIC_INIT(dsm_segment_list); |
141 | | |
142 | | /* |
143 | | * Control segment information. |
144 | | * |
145 | | * Unlike ordinary shared memory segments, the control segment is not |
146 | | * reference counted; instead, it lasts for the postmaster's entire |
147 | | * life cycle. For simplicity, it doesn't have a dsm_segment object either. |
148 | | */ |
149 | | static dsm_handle dsm_control_handle; |
150 | | static dsm_control_header *dsm_control; |
151 | | static Size dsm_control_mapped_size = 0; |
152 | | static void *dsm_control_impl_private = NULL; |
153 | | |
154 | | |
155 | | /* ResourceOwner callbacks to hold DSM segments */ |
156 | | static void ResOwnerReleaseDSM(Datum res); |
157 | | static char *ResOwnerPrintDSM(Datum res); |
158 | | |
159 | | static const ResourceOwnerDesc dsm_resowner_desc = |
160 | | { |
161 | | .name = "dynamic shared memory segment", |
162 | | .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS, |
163 | | .release_priority = RELEASE_PRIO_DSMS, |
164 | | .ReleaseResource = ResOwnerReleaseDSM, |
165 | | .DebugPrint = ResOwnerPrintDSM |
166 | | }; |
167 | | |
168 | | /* Convenience wrappers over ResourceOwnerRemember/Forget */ |
169 | | static inline void |
170 | | ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg) |
171 | 0 | { |
172 | 0 | ResourceOwnerRemember(owner, PointerGetDatum(seg), &dsm_resowner_desc); |
173 | 0 | } |
174 | | static inline void |
175 | | ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg) |
176 | 0 | { |
177 | 0 | ResourceOwnerForget(owner, PointerGetDatum(seg), &dsm_resowner_desc); |
178 | 0 | } |
179 | | |
180 | | /* |
181 | | * Start up the dynamic shared memory system. |
182 | | * |
183 | | * This is called just once during each cluster lifetime, at postmaster |
184 | | * startup time. |
185 | | */ |
186 | | void |
187 | | dsm_postmaster_startup(PGShmemHeader *shim) |
188 | | { |
189 | | void *dsm_control_address = NULL; |
190 | | uint32 maxitems; |
191 | | Size segsize; |
192 | | |
193 | | Assert(!IsUnderPostmaster); |
194 | | |
195 | | /* |
196 | | * If we're using the mmap implementations, clean up any leftovers. |
197 | | * Cleanup isn't needed on Windows, and happens earlier in startup for |
198 | | * POSIX and System V shared memory, via a direct call to |
199 | | * dsm_cleanup_using_control_segment. |
200 | | */ |
201 | | if (dynamic_shared_memory_type == DSM_IMPL_MMAP) |
202 | | dsm_cleanup_for_mmap(); |
203 | | |
204 | | /* Determine size for new control segment. */ |
205 | | maxitems = PG_DYNSHMEM_FIXED_SLOTS |
206 | | + PG_DYNSHMEM_SLOTS_PER_BACKEND * MaxBackends; |
207 | | elog(DEBUG2, "dynamic shared memory system will support %u segments", |
208 | | maxitems); |
209 | | segsize = dsm_control_bytes_needed(maxitems); |
210 | | |
211 | | /* |
212 | | * Loop until we find an unused identifier for the new control segment. We |
213 | | * sometimes use DSM_HANDLE_INVALID as a sentinel value indicating "no |
214 | | * control segment", so avoid generating that value for a real handle. |
215 | | */ |
216 | | for (;;) |
217 | | { |
218 | | Assert(dsm_control_address == NULL); |
219 | | Assert(dsm_control_mapped_size == 0); |
220 | | /* Use even numbers only */ |
221 | | dsm_control_handle = pg_prng_uint32(&pg_global_prng_state) << 1; |
222 | | if (dsm_control_handle == DSM_HANDLE_INVALID) |
223 | | continue; |
224 | | if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize, |
225 | | &dsm_control_impl_private, &dsm_control_address, |
226 | | &dsm_control_mapped_size, ERROR)) |
227 | | break; |
228 | | } |
229 | | dsm_control = dsm_control_address; |
230 | | on_shmem_exit(dsm_postmaster_shutdown, PointerGetDatum(shim)); |
231 | | elog(DEBUG2, |
232 | | "created dynamic shared memory control segment %u (%zu bytes)", |
233 | | dsm_control_handle, segsize); |
234 | | shim->dsm_control = dsm_control_handle; |
235 | | |
236 | | /* Initialize control segment. */ |
237 | | dsm_control->magic = PG_DYNSHMEM_CONTROL_MAGIC; |
238 | | dsm_control->nitems = 0; |
239 | | dsm_control->maxitems = maxitems; |
240 | | } |
241 | | |
242 | | /* |
243 | | * Determine whether the control segment from the previous postmaster |
244 | | * invocation still exists. If so, remove the dynamic shared memory |
245 | | * segments to which it refers, and then the control segment itself. |
246 | | */ |
247 | | void |
248 | | dsm_cleanup_using_control_segment(dsm_handle old_control_handle) |
249 | | { |
250 | | void *mapped_address = NULL; |
251 | | void *junk_mapped_address = NULL; |
252 | | void *impl_private = NULL; |
253 | | void *junk_impl_private = NULL; |
254 | | Size mapped_size = 0; |
255 | | Size junk_mapped_size = 0; |
256 | | uint32 nitems; |
257 | | uint32 i; |
258 | | dsm_control_header *old_control; |
259 | | |
260 | | /* |
261 | | * Try to attach the segment. If this fails, it probably just means that |
262 | | * the operating system has been rebooted and the segment no longer |
263 | | * exists, or an unrelated process has used the same shm ID. So just fall |
264 | | * out quietly. |
265 | | */ |
266 | | if (!dsm_impl_op(DSM_OP_ATTACH, old_control_handle, 0, &impl_private, |
267 | | &mapped_address, &mapped_size, DEBUG1)) |
268 | | return; |
269 | | |
270 | | /* |
271 | | * We've managed to reattach it, but the contents might not be sane. If |
272 | | * they aren't, we disregard the segment after all. |
273 | | */ |
274 | | old_control = (dsm_control_header *) mapped_address; |
275 | | if (!dsm_control_segment_sane(old_control, mapped_size)) |
276 | | { |
277 | | dsm_impl_op(DSM_OP_DETACH, old_control_handle, 0, &impl_private, |
278 | | &mapped_address, &mapped_size, LOG); |
279 | | return; |
280 | | } |
281 | | |
282 | | /* |
283 | | * OK, the control segment looks basically valid, so we can use it to get |
284 | | * a list of segments that need to be removed. |
285 | | */ |
286 | | nitems = old_control->nitems; |
287 | | for (i = 0; i < nitems; ++i) |
288 | | { |
289 | | dsm_handle handle; |
290 | | uint32 refcnt; |
291 | | |
292 | | /* If the reference count is 0, the slot is actually unused. */ |
293 | | refcnt = old_control->item[i].refcnt; |
294 | | if (refcnt == 0) |
295 | | continue; |
296 | | |
297 | | /* If it was using the main shmem area, there is nothing to do. */ |
298 | | handle = old_control->item[i].handle; |
299 | | if (is_main_region_dsm_handle(handle)) |
300 | | continue; |
301 | | |
302 | | /* Log debugging information. */ |
303 | | elog(DEBUG2, "cleaning up orphaned dynamic shared memory with ID %u (reference count %u)", |
304 | | handle, refcnt); |
305 | | |
306 | | /* Destroy the referenced segment. */ |
307 | | dsm_impl_op(DSM_OP_DESTROY, handle, 0, &junk_impl_private, |
308 | | &junk_mapped_address, &junk_mapped_size, LOG); |
309 | | } |
310 | | |
311 | | /* Destroy the old control segment, too. */ |
312 | | elog(DEBUG2, |
313 | | "cleaning up dynamic shared memory control segment with ID %u", |
314 | | old_control_handle); |
315 | | dsm_impl_op(DSM_OP_DESTROY, old_control_handle, 0, &impl_private, |
316 | | &mapped_address, &mapped_size, LOG); |
317 | | } |
318 | | |
319 | | /* |
320 | | * When we're using the mmap shared memory implementation, "shared memory" |
321 | | * segments might even manage to survive an operating system reboot. |
322 | | * But there's no guarantee as to exactly what will survive: some segments |
323 | | * may survive, and others may not, and the contents of some may be out |
324 | | * of date. In particular, the control segment may be out of date, so we |
325 | | * can't rely on it to figure out what to remove. However, since we know |
326 | | * what directory contains the files we used as shared memory, we can simply |
327 | | * scan the directory and blow everything away that shouldn't be there. |
328 | | */ |
329 | | static void |
330 | | dsm_cleanup_for_mmap(void) |
331 | 0 | { |
332 | 0 | DIR *dir; |
333 | 0 | struct dirent *dent; |
334 | | |
335 | | /* Scan the directory for something with a name of the correct format. */ |
336 | 0 | dir = AllocateDir(PG_DYNSHMEM_DIR); |
337 | |
|
338 | 0 | while ((dent = ReadDir(dir, PG_DYNSHMEM_DIR)) != NULL) |
339 | 0 | { |
340 | 0 | if (strncmp(dent->d_name, PG_DYNSHMEM_MMAP_FILE_PREFIX, |
341 | 0 | strlen(PG_DYNSHMEM_MMAP_FILE_PREFIX)) == 0) |
342 | 0 | { |
343 | 0 | char buf[MAXPGPATH + sizeof(PG_DYNSHMEM_DIR)]; |
344 | |
|
345 | 0 | snprintf(buf, sizeof(buf), PG_DYNSHMEM_DIR "/%s", dent->d_name); |
346 | |
|
347 | 0 | elog(DEBUG2, "removing file \"%s\"", buf); |
348 | | |
349 | | /* We found a matching file; so remove it. */ |
350 | 0 | if (unlink(buf) != 0) |
351 | 0 | ereport(ERROR, |
352 | 0 | (errcode_for_file_access(), |
353 | 0 | errmsg("could not remove file \"%s\": %m", buf))); |
354 | 0 | } |
355 | 0 | } |
356 | | |
357 | | /* Cleanup complete. */ |
358 | 0 | FreeDir(dir); |
359 | 0 | } |
360 | | |
361 | | /* |
362 | | * At shutdown time, we iterate over the control segment and remove all |
363 | | * remaining dynamic shared memory segments. We avoid throwing errors here; |
364 | | * the postmaster is shutting down either way, and this is just non-critical |
365 | | * resource cleanup. |
366 | | */ |
367 | | static void |
368 | | dsm_postmaster_shutdown(int code, Datum arg) |
369 | | { |
370 | | uint32 nitems; |
371 | | uint32 i; |
372 | | void *dsm_control_address; |
373 | | void *junk_mapped_address = NULL; |
374 | | void *junk_impl_private = NULL; |
375 | | Size junk_mapped_size = 0; |
376 | | PGShmemHeader *shim = (PGShmemHeader *) DatumGetPointer(arg); |
377 | | |
378 | | /* |
379 | | * If some other backend exited uncleanly, it might have corrupted the |
380 | | * control segment while it was dying. In that case, we warn and ignore |
381 | | * the contents of the control segment. This may end up leaving behind |
382 | | * stray shared memory segments, but there's not much we can do about that |
383 | | * if the metadata is gone. |
384 | | */ |
385 | | nitems = dsm_control->nitems; |
386 | | if (!dsm_control_segment_sane(dsm_control, dsm_control_mapped_size)) |
387 | | { |
388 | | ereport(LOG, |
389 | | (errmsg("dynamic shared memory control segment is corrupt"))); |
390 | | return; |
391 | | } |
392 | | |
393 | | /* Remove any remaining segments. */ |
394 | | for (i = 0; i < nitems; ++i) |
395 | | { |
396 | | dsm_handle handle; |
397 | | |
398 | | /* If the reference count is 0, the slot is actually unused. */ |
399 | | if (dsm_control->item[i].refcnt == 0) |
400 | | continue; |
401 | | |
402 | | handle = dsm_control->item[i].handle; |
403 | | if (is_main_region_dsm_handle(handle)) |
404 | | continue; |
405 | | |
406 | | /* Log debugging information. */ |
407 | | elog(DEBUG2, "cleaning up orphaned dynamic shared memory with ID %u", |
408 | | handle); |
409 | | |
410 | | /* Destroy the segment. */ |
411 | | dsm_impl_op(DSM_OP_DESTROY, handle, 0, &junk_impl_private, |
412 | | &junk_mapped_address, &junk_mapped_size, LOG); |
413 | | } |
414 | | |
415 | | /* Remove the control segment itself. */ |
416 | | elog(DEBUG2, |
417 | | "cleaning up dynamic shared memory control segment with ID %u", |
418 | | dsm_control_handle); |
419 | | dsm_control_address = dsm_control; |
420 | | dsm_impl_op(DSM_OP_DESTROY, dsm_control_handle, 0, |
421 | | &dsm_control_impl_private, &dsm_control_address, |
422 | | &dsm_control_mapped_size, LOG); |
423 | | dsm_control = dsm_control_address; |
424 | | shim->dsm_control = 0; |
425 | | } |
426 | | |
427 | | /* |
428 | | * Prepare this backend for dynamic shared memory usage. Under EXEC_BACKEND, |
429 | | * we must reread the state file and map the control segment; in other cases, |
430 | | * we'll have inherited the postmaster's mapping and global variables. |
431 | | */ |
432 | | static void |
433 | | dsm_backend_startup(void) |
434 | 0 | { |
435 | | #ifdef EXEC_BACKEND |
436 | | if (IsUnderPostmaster) |
437 | | { |
438 | | void *control_address = NULL; |
439 | | |
440 | | /* Attach control segment. */ |
441 | | Assert(dsm_control_handle != 0); |
442 | | dsm_impl_op(DSM_OP_ATTACH, dsm_control_handle, 0, |
443 | | &dsm_control_impl_private, &control_address, |
444 | | &dsm_control_mapped_size, ERROR); |
445 | | dsm_control = control_address; |
446 | | /* If control segment doesn't look sane, something is badly wrong. */ |
447 | | if (!dsm_control_segment_sane(dsm_control, dsm_control_mapped_size)) |
448 | | { |
449 | | dsm_impl_op(DSM_OP_DETACH, dsm_control_handle, 0, |
450 | | &dsm_control_impl_private, &control_address, |
451 | | &dsm_control_mapped_size, WARNING); |
452 | | ereport(FATAL, |
453 | | (errcode(ERRCODE_INTERNAL_ERROR), |
454 | | errmsg("dynamic shared memory control segment is not valid"))); |
455 | | } |
456 | | } |
457 | | #endif |
458 | |
|
459 | 0 | dsm_init_done = true; |
460 | 0 | } |
461 | | |
462 | | #ifdef EXEC_BACKEND |
463 | | /* |
464 | | * When running under EXEC_BACKEND, we get a callback here when the main |
465 | | * shared memory segment is re-attached, so that we can record the control |
466 | | * handle retrieved from it. |
467 | | */ |
468 | | void |
469 | | dsm_set_control_handle(dsm_handle h) |
470 | | { |
471 | | Assert(dsm_control_handle == 0 && h != 0); |
472 | | dsm_control_handle = h; |
473 | | } |
474 | | #endif |
475 | | |
476 | | /* |
477 | | * Reserve space in the main shared memory segment for DSM segments. |
478 | | */ |
479 | | static void |
480 | | dsm_main_space_request(void *arg) |
481 | 0 | { |
482 | 0 | dsm_main_space_size = 1024 * 1024 * (size_t) min_dynamic_shared_memory; |
483 | |
|
484 | 0 | if (dsm_main_space_size == 0) |
485 | 0 | return; |
486 | | |
487 | 0 | ShmemRequestStruct(.name = "Preallocated DSM", |
488 | 0 | .size = dsm_main_space_size, |
489 | 0 | .ptr = &dsm_main_space_begin, |
490 | 0 | ); |
491 | 0 | } |
492 | | |
493 | | static void |
494 | | dsm_main_space_init(void *arg) |
495 | 0 | { |
496 | 0 | FreePageManager *fpm = (FreePageManager *) dsm_main_space_begin; |
497 | 0 | size_t first_page = 0; |
498 | 0 | size_t pages; |
499 | |
|
500 | 0 | if (dsm_main_space_size == 0) |
501 | 0 | return; |
502 | | |
503 | | /* Reserve space for the FreePageManager. */ |
504 | 0 | while (first_page * FPM_PAGE_SIZE < sizeof(FreePageManager)) |
505 | 0 | ++first_page; |
506 | | |
507 | | /* Initialize it and give it all the rest of the space. */ |
508 | 0 | FreePageManagerInitialize(fpm, dsm_main_space_begin); |
509 | 0 | pages = (dsm_main_space_size / FPM_PAGE_SIZE) - first_page; |
510 | 0 | FreePageManagerPut(fpm, first_page, pages); |
511 | 0 | } |
512 | | |
513 | | /* |
514 | | * Create a new dynamic shared memory segment. |
515 | | * |
516 | | * If there is a non-NULL CurrentResourceOwner, the new segment is associated |
517 | | * with it and must be detached before the resource owner releases, or a |
518 | | * warning will be logged. If CurrentResourceOwner is NULL, the segment |
519 | | * remains attached until explicitly detached or the session ends. |
520 | | * Creating with a NULL CurrentResourceOwner is equivalent to creating |
521 | | * with a non-NULL CurrentResourceOwner and then calling dsm_pin_mapping. |
522 | | */ |
523 | | dsm_segment * |
524 | | dsm_create(Size size, int flags) |
525 | 0 | { |
526 | 0 | dsm_segment *seg; |
527 | 0 | uint32 i; |
528 | 0 | uint32 nitems; |
529 | 0 | size_t npages = 0; |
530 | 0 | size_t first_page = 0; |
531 | 0 | FreePageManager *dsm_main_space_fpm = dsm_main_space_begin; |
532 | 0 | bool using_main_dsm_region = false; |
533 | | |
534 | | /* |
535 | | * Unsafe in postmaster. It might seem pointless to allow use of dsm in |
536 | | * single user mode, but otherwise some subsystems will need dedicated |
537 | | * single user mode code paths. |
538 | | */ |
539 | 0 | Assert(IsUnderPostmaster || !IsPostmasterEnvironment); |
540 | |
|
541 | 0 | if (!dsm_init_done) |
542 | 0 | dsm_backend_startup(); |
543 | | |
544 | | /* Create a new segment descriptor. */ |
545 | 0 | seg = dsm_create_descriptor(); |
546 | | |
547 | | /* |
548 | | * Lock the control segment while we try to allocate from the main shared |
549 | | * memory area, if configured. |
550 | | */ |
551 | 0 | if (dsm_main_space_fpm) |
552 | 0 | { |
553 | 0 | npages = size / FPM_PAGE_SIZE; |
554 | 0 | if (size % FPM_PAGE_SIZE > 0) |
555 | 0 | ++npages; |
556 | |
|
557 | 0 | LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE); |
558 | 0 | if (FreePageManagerGet(dsm_main_space_fpm, npages, &first_page)) |
559 | 0 | { |
560 | | /* We can carve out a piece of the main shared memory segment. */ |
561 | 0 | seg->mapped_address = (char *) dsm_main_space_begin + |
562 | 0 | first_page * FPM_PAGE_SIZE; |
563 | 0 | seg->mapped_size = npages * FPM_PAGE_SIZE; |
564 | 0 | using_main_dsm_region = true; |
565 | | /* We'll choose a handle below. */ |
566 | 0 | } |
567 | 0 | } |
568 | |
|
569 | 0 | if (!using_main_dsm_region) |
570 | 0 | { |
571 | | /* |
572 | | * We need to create a new memory segment. Loop until we find an |
573 | | * unused segment identifier. |
574 | | */ |
575 | 0 | if (dsm_main_space_fpm) |
576 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
577 | 0 | for (;;) |
578 | 0 | { |
579 | 0 | Assert(seg->mapped_address == NULL && seg->mapped_size == 0); |
580 | | /* Use even numbers only */ |
581 | 0 | seg->handle = pg_prng_uint32(&pg_global_prng_state) << 1; |
582 | 0 | if (seg->handle == DSM_HANDLE_INVALID) /* Reserve sentinel */ |
583 | 0 | continue; |
584 | 0 | if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private, |
585 | 0 | &seg->mapped_address, &seg->mapped_size, ERROR)) |
586 | 0 | break; |
587 | 0 | } |
588 | 0 | LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE); |
589 | 0 | } |
590 | | |
591 | | /* Search the control segment for an unused slot. */ |
592 | 0 | nitems = dsm_control->nitems; |
593 | 0 | for (i = 0; i < nitems; ++i) |
594 | 0 | { |
595 | 0 | if (dsm_control->item[i].refcnt == 0) |
596 | 0 | { |
597 | 0 | if (using_main_dsm_region) |
598 | 0 | { |
599 | 0 | seg->handle = make_main_region_dsm_handle(i); |
600 | 0 | dsm_control->item[i].first_page = first_page; |
601 | 0 | dsm_control->item[i].npages = npages; |
602 | 0 | } |
603 | 0 | else |
604 | 0 | Assert(!is_main_region_dsm_handle(seg->handle)); |
605 | 0 | dsm_control->item[i].handle = seg->handle; |
606 | | /* refcnt of 1 triggers destruction, so start at 2 */ |
607 | 0 | dsm_control->item[i].refcnt = 2; |
608 | 0 | dsm_control->item[i].impl_private_pm_handle = NULL; |
609 | 0 | dsm_control->item[i].pinned = false; |
610 | 0 | seg->control_slot = i; |
611 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
612 | 0 | return seg; |
613 | 0 | } |
614 | 0 | } |
615 | | |
616 | | /* Verify that we can support an additional mapping. */ |
617 | 0 | if (nitems >= dsm_control->maxitems) |
618 | 0 | { |
619 | 0 | if (using_main_dsm_region) |
620 | 0 | FreePageManagerPut(dsm_main_space_fpm, first_page, npages); |
621 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
622 | 0 | if (!using_main_dsm_region) |
623 | 0 | dsm_impl_op(DSM_OP_DESTROY, seg->handle, 0, &seg->impl_private, |
624 | 0 | &seg->mapped_address, &seg->mapped_size, WARNING); |
625 | 0 | if (seg->resowner != NULL) |
626 | 0 | ResourceOwnerForgetDSM(seg->resowner, seg); |
627 | 0 | dlist_delete(&seg->node); |
628 | 0 | pfree(seg); |
629 | |
|
630 | 0 | if ((flags & DSM_CREATE_NULL_IF_MAXSEGMENTS) != 0) |
631 | 0 | return NULL; |
632 | 0 | ereport(ERROR, |
633 | 0 | (errcode(ERRCODE_INSUFFICIENT_RESOURCES), |
634 | 0 | errmsg("too many dynamic shared memory segments"))); |
635 | 0 | } |
636 | | |
637 | | /* Enter the handle into a new array slot. */ |
638 | 0 | if (using_main_dsm_region) |
639 | 0 | { |
640 | 0 | seg->handle = make_main_region_dsm_handle(nitems); |
641 | 0 | dsm_control->item[i].first_page = first_page; |
642 | 0 | dsm_control->item[i].npages = npages; |
643 | 0 | } |
644 | 0 | dsm_control->item[nitems].handle = seg->handle; |
645 | | /* refcnt of 1 triggers destruction, so start at 2 */ |
646 | 0 | dsm_control->item[nitems].refcnt = 2; |
647 | 0 | dsm_control->item[nitems].impl_private_pm_handle = NULL; |
648 | 0 | dsm_control->item[nitems].pinned = false; |
649 | 0 | seg->control_slot = nitems; |
650 | 0 | dsm_control->nitems++; |
651 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
652 | |
|
653 | 0 | return seg; |
654 | 0 | } |
655 | | |
656 | | /* |
657 | | * Attach a dynamic shared memory segment. |
658 | | * |
659 | | * See comments for dsm_segment_handle() for an explanation of how this |
660 | | * is intended to be used. |
661 | | * |
662 | | * This function will return NULL if the segment isn't known to the system. |
663 | | * This can happen if we're asked to attach the segment, but then everyone |
664 | | * else detaches it (causing it to be destroyed) before we get around to |
665 | | * attaching it. |
666 | | * |
667 | | * If there is a non-NULL CurrentResourceOwner, the attached segment is |
668 | | * associated with it and must be detached before the resource owner releases, |
669 | | * or a warning will be logged. Otherwise the segment remains attached until |
670 | | * explicitly detached or the session ends. See the note atop dsm_create(). |
671 | | */ |
672 | | dsm_segment * |
673 | | dsm_attach(dsm_handle h) |
674 | 0 | { |
675 | 0 | dsm_segment *seg; |
676 | 0 | dlist_iter iter; |
677 | 0 | uint32 i; |
678 | 0 | uint32 nitems; |
679 | | |
680 | | /* Unsafe in postmaster (and pointless in a stand-alone backend). */ |
681 | 0 | Assert(IsUnderPostmaster); |
682 | |
|
683 | 0 | if (!dsm_init_done) |
684 | 0 | dsm_backend_startup(); |
685 | | |
686 | | /* |
687 | | * Since this is just a debugging cross-check, we could leave it out |
688 | | * altogether, or include it only in assert-enabled builds. But since the |
689 | | * list of attached segments should normally be very short, let's include |
690 | | * it always for right now. |
691 | | * |
692 | | * If you're hitting this error, you probably want to attempt to find an |
693 | | * existing mapping via dsm_find_mapping() before calling dsm_attach() to |
694 | | * create a new one. |
695 | | */ |
696 | 0 | dlist_foreach(iter, &dsm_segment_list) |
697 | 0 | { |
698 | 0 | seg = dlist_container(dsm_segment, node, iter.cur); |
699 | 0 | if (seg->handle == h) |
700 | 0 | elog(ERROR, "can't attach the same segment more than once"); |
701 | 0 | } |
702 | | |
703 | | /* Create a new segment descriptor. */ |
704 | 0 | seg = dsm_create_descriptor(); |
705 | 0 | seg->handle = h; |
706 | | |
707 | | /* Bump reference count for this segment in shared memory. */ |
708 | 0 | LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE); |
709 | 0 | nitems = dsm_control->nitems; |
710 | 0 | for (i = 0; i < nitems; ++i) |
711 | 0 | { |
712 | | /* |
713 | | * If the reference count is 0, the slot is actually unused. If the |
714 | | * reference count is 1, the slot is still in use, but the segment is |
715 | | * in the process of going away; even if the handle matches, another |
716 | | * slot may already have started using the same handle value by |
717 | | * coincidence so we have to keep searching. |
718 | | */ |
719 | 0 | if (dsm_control->item[i].refcnt <= 1) |
720 | 0 | continue; |
721 | | |
722 | | /* If the handle doesn't match, it's not the slot we want. */ |
723 | 0 | if (dsm_control->item[i].handle != seg->handle) |
724 | 0 | continue; |
725 | | |
726 | | /* Otherwise we've found a match. */ |
727 | 0 | dsm_control->item[i].refcnt++; |
728 | 0 | seg->control_slot = i; |
729 | 0 | if (is_main_region_dsm_handle(seg->handle)) |
730 | 0 | { |
731 | 0 | seg->mapped_address = (char *) dsm_main_space_begin + |
732 | 0 | dsm_control->item[i].first_page * FPM_PAGE_SIZE; |
733 | 0 | seg->mapped_size = dsm_control->item[i].npages * FPM_PAGE_SIZE; |
734 | 0 | } |
735 | 0 | break; |
736 | 0 | } |
737 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
738 | | |
739 | | /* |
740 | | * If we didn't find the handle we're looking for in the control segment, |
741 | | * it probably means that everyone else who had it mapped, including the |
742 | | * original creator, died before we got to this point. It's up to the |
743 | | * caller to decide what to do about that. |
744 | | */ |
745 | 0 | if (seg->control_slot == INVALID_CONTROL_SLOT) |
746 | 0 | { |
747 | 0 | dsm_detach(seg); |
748 | 0 | return NULL; |
749 | 0 | } |
750 | | |
751 | | /* Here's where we actually try to map the segment. */ |
752 | 0 | if (!is_main_region_dsm_handle(seg->handle)) |
753 | 0 | dsm_impl_op(DSM_OP_ATTACH, seg->handle, 0, &seg->impl_private, |
754 | 0 | &seg->mapped_address, &seg->mapped_size, ERROR); |
755 | |
|
756 | 0 | return seg; |
757 | 0 | } |
758 | | |
759 | | /* |
760 | | * At backend shutdown time, detach any segments that are still attached. |
761 | | * (This is similar to dsm_detach_all, except that there's no reason to |
762 | | * unmap the control segment before exiting, so we don't bother.) |
763 | | */ |
764 | | void |
765 | | dsm_backend_shutdown(void) |
766 | 0 | { |
767 | 0 | while (!dlist_is_empty(&dsm_segment_list)) |
768 | 0 | { |
769 | 0 | dsm_segment *seg; |
770 | |
|
771 | 0 | seg = dlist_head_element(dsm_segment, node, &dsm_segment_list); |
772 | 0 | dsm_detach(seg); |
773 | 0 | } |
774 | 0 | } |
775 | | |
776 | | /* |
777 | | * Detach all shared memory segments, including the control segments. This |
778 | | * should be called, along with PGSharedMemoryDetach, in processes that |
779 | | * might inherit mappings but are not intended to be connected to dynamic |
780 | | * shared memory. |
781 | | */ |
782 | | void |
783 | | dsm_detach_all(void) |
784 | 0 | { |
785 | 0 | void *control_address = dsm_control; |
786 | |
|
787 | 0 | while (!dlist_is_empty(&dsm_segment_list)) |
788 | 0 | { |
789 | 0 | dsm_segment *seg; |
790 | |
|
791 | 0 | seg = dlist_head_element(dsm_segment, node, &dsm_segment_list); |
792 | 0 | dsm_detach(seg); |
793 | 0 | } |
794 | |
|
795 | 0 | if (control_address != NULL) |
796 | 0 | dsm_impl_op(DSM_OP_DETACH, dsm_control_handle, 0, |
797 | 0 | &dsm_control_impl_private, &control_address, |
798 | 0 | &dsm_control_mapped_size, ERROR); |
799 | 0 | } |
800 | | |
801 | | /* |
802 | | * Detach from a shared memory segment, destroying the segment if we |
803 | | * remove the last reference. |
804 | | * |
805 | | * This function should never fail. It will often be invoked when aborting |
806 | | * a transaction, and a further error won't serve any purpose. It's not a |
807 | | * complete disaster if we fail to unmap or destroy the segment; it means a |
808 | | * resource leak, but that doesn't necessarily preclude further operations. |
809 | | */ |
810 | | void |
811 | | dsm_detach(dsm_segment *seg) |
812 | 0 | { |
813 | | /* |
814 | | * Invoke registered callbacks. Just in case one of those callbacks |
815 | | * throws a further error that brings us back here, pop the callback |
816 | | * before invoking it, to avoid infinite error recursion. Don't allow |
817 | | * interrupts while running the individual callbacks in non-error code |
818 | | * paths, to avoid leaving cleanup work unfinished if we're interrupted by |
819 | | * a statement timeout or similar. |
820 | | */ |
821 | 0 | HOLD_INTERRUPTS(); |
822 | 0 | while (!slist_is_empty(&seg->on_detach)) |
823 | 0 | { |
824 | 0 | slist_node *node; |
825 | 0 | dsm_segment_detach_callback *cb; |
826 | 0 | on_dsm_detach_callback function; |
827 | 0 | Datum arg; |
828 | |
|
829 | 0 | node = slist_pop_head_node(&seg->on_detach); |
830 | 0 | cb = slist_container(dsm_segment_detach_callback, node, node); |
831 | 0 | function = cb->function; |
832 | 0 | arg = cb->arg; |
833 | 0 | pfree(cb); |
834 | |
|
835 | 0 | function(seg, arg); |
836 | 0 | } |
837 | 0 | RESUME_INTERRUPTS(); |
838 | | |
839 | | /* |
840 | | * Try to remove the mapping, if one exists. Normally, there will be, but |
841 | | * maybe not, if we failed partway through a create or attach operation. |
842 | | * We remove the mapping before decrementing the reference count so that |
843 | | * the process that sees a zero reference count can be certain that no |
844 | | * remaining mappings exist. Even if this fails, we pretend that it |
845 | | * works, because retrying is likely to fail in the same way. |
846 | | */ |
847 | 0 | if (seg->mapped_address != NULL) |
848 | 0 | { |
849 | 0 | if (!is_main_region_dsm_handle(seg->handle)) |
850 | 0 | dsm_impl_op(DSM_OP_DETACH, seg->handle, 0, &seg->impl_private, |
851 | 0 | &seg->mapped_address, &seg->mapped_size, WARNING); |
852 | 0 | seg->impl_private = NULL; |
853 | 0 | seg->mapped_address = NULL; |
854 | 0 | seg->mapped_size = 0; |
855 | 0 | } |
856 | | |
857 | | /* Reduce reference count, if we previously increased it. */ |
858 | 0 | if (seg->control_slot != INVALID_CONTROL_SLOT) |
859 | 0 | { |
860 | 0 | uint32 refcnt; |
861 | 0 | uint32 control_slot = seg->control_slot; |
862 | |
|
863 | 0 | LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE); |
864 | 0 | Assert(dsm_control->item[control_slot].handle == seg->handle); |
865 | 0 | Assert(dsm_control->item[control_slot].refcnt > 1); |
866 | 0 | refcnt = --dsm_control->item[control_slot].refcnt; |
867 | 0 | seg->control_slot = INVALID_CONTROL_SLOT; |
868 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
869 | | |
870 | | /* If new reference count is 1, try to destroy the segment. */ |
871 | 0 | if (refcnt == 1) |
872 | 0 | { |
873 | | /* A pinned segment should never reach 1. */ |
874 | 0 | Assert(!dsm_control->item[control_slot].pinned); |
875 | | |
876 | | /* |
877 | | * If we fail to destroy the segment here, or are killed before we |
878 | | * finish doing so, the reference count will remain at 1, which |
879 | | * will mean that nobody else can attach to the segment. At |
880 | | * postmaster shutdown time, or when a new postmaster is started |
881 | | * after a hard kill, another attempt will be made to remove the |
882 | | * segment. |
883 | | * |
884 | | * The main case we're worried about here is being killed by a |
885 | | * signal before we can finish removing the segment. In that |
886 | | * case, it's important to be sure that the segment still gets |
887 | | * removed. If we actually fail to remove the segment for some |
888 | | * other reason, the postmaster may not have any better luck than |
889 | | * we did. There's not much we can do about that, though. |
890 | | */ |
891 | 0 | if (is_main_region_dsm_handle(seg->handle) || |
892 | 0 | dsm_impl_op(DSM_OP_DESTROY, seg->handle, 0, &seg->impl_private, |
893 | 0 | &seg->mapped_address, &seg->mapped_size, WARNING)) |
894 | 0 | { |
895 | 0 | LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE); |
896 | 0 | if (is_main_region_dsm_handle(seg->handle)) |
897 | 0 | FreePageManagerPut((FreePageManager *) dsm_main_space_begin, |
898 | 0 | dsm_control->item[control_slot].first_page, |
899 | 0 | dsm_control->item[control_slot].npages); |
900 | 0 | Assert(dsm_control->item[control_slot].handle == seg->handle); |
901 | 0 | Assert(dsm_control->item[control_slot].refcnt == 1); |
902 | 0 | dsm_control->item[control_slot].refcnt = 0; |
903 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
904 | 0 | } |
905 | 0 | } |
906 | 0 | } |
907 | | |
908 | | /* Clean up our remaining backend-private data structures. */ |
909 | 0 | if (seg->resowner != NULL) |
910 | 0 | ResourceOwnerForgetDSM(seg->resowner, seg); |
911 | 0 | dlist_delete(&seg->node); |
912 | 0 | pfree(seg); |
913 | 0 | } |
914 | | |
915 | | /* |
916 | | * Keep a dynamic shared memory mapping until end of session. |
917 | | * |
918 | | * By default, mappings are owned by the current resource owner, which |
919 | | * typically means they stick around for the duration of the current query |
920 | | * only. |
921 | | */ |
922 | | void |
923 | | dsm_pin_mapping(dsm_segment *seg) |
924 | 0 | { |
925 | 0 | if (seg->resowner != NULL) |
926 | 0 | { |
927 | 0 | ResourceOwnerForgetDSM(seg->resowner, seg); |
928 | 0 | seg->resowner = NULL; |
929 | 0 | } |
930 | 0 | } |
931 | | |
932 | | /* |
933 | | * Arrange to remove a dynamic shared memory mapping at cleanup time. |
934 | | * |
935 | | * dsm_pin_mapping() can be used to preserve a mapping for the entire |
936 | | * lifetime of a process; this function reverses that decision, making |
937 | | * the segment owned by the current resource owner. This may be useful |
938 | | * just before performing some operation that will invalidate the segment |
939 | | * for future use by this backend. |
940 | | */ |
941 | | void |
942 | | dsm_unpin_mapping(dsm_segment *seg) |
943 | 0 | { |
944 | 0 | Assert(seg->resowner == NULL); |
945 | 0 | ResourceOwnerEnlarge(CurrentResourceOwner); |
946 | 0 | seg->resowner = CurrentResourceOwner; |
947 | 0 | ResourceOwnerRememberDSM(seg->resowner, seg); |
948 | 0 | } |
949 | | |
950 | | /* |
951 | | * Keep a dynamic shared memory segment until postmaster shutdown, or until |
952 | | * dsm_unpin_segment is called. |
953 | | * |
954 | | * This function should not be called more than once per segment, unless the |
955 | | * segment is explicitly unpinned with dsm_unpin_segment in between calls. |
956 | | * |
957 | | * Note that this function does not arrange for the current process to |
958 | | * keep the segment mapped indefinitely; if that behavior is desired, |
959 | | * dsm_pin_mapping() should be used from each process that needs to |
960 | | * retain the mapping. |
961 | | */ |
962 | | void |
963 | | dsm_pin_segment(dsm_segment *seg) |
964 | 0 | { |
965 | 0 | void *handle = NULL; |
966 | | |
967 | | /* |
968 | | * Bump reference count for this segment in shared memory. This will |
969 | | * ensure that even if there is no session which is attached to this |
970 | | * segment, it will remain until postmaster shutdown or an explicit call |
971 | | * to unpin. |
972 | | */ |
973 | 0 | LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE); |
974 | 0 | if (dsm_control->item[seg->control_slot].pinned) |
975 | 0 | elog(ERROR, "cannot pin a segment that is already pinned"); |
976 | 0 | if (!is_main_region_dsm_handle(seg->handle)) |
977 | 0 | dsm_impl_pin_segment(seg->handle, seg->impl_private, &handle); |
978 | 0 | dsm_control->item[seg->control_slot].pinned = true; |
979 | 0 | dsm_control->item[seg->control_slot].refcnt++; |
980 | 0 | dsm_control->item[seg->control_slot].impl_private_pm_handle = handle; |
981 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
982 | 0 | } |
983 | | |
984 | | /* |
985 | | * Unpin a dynamic shared memory segment that was previously pinned with |
986 | | * dsm_pin_segment. This function should not be called unless dsm_pin_segment |
987 | | * was previously called for this segment. |
988 | | * |
989 | | * The argument is a dsm_handle rather than a dsm_segment in case you want |
990 | | * to unpin a segment to which you haven't attached. This turns out to be |
991 | | * useful if, for example, a reference to one shared memory segment is stored |
992 | | * within another shared memory segment. You might want to unpin the |
993 | | * referenced segment before destroying the referencing segment. |
994 | | */ |
995 | | void |
996 | | dsm_unpin_segment(dsm_handle handle) |
997 | 0 | { |
998 | 0 | uint32 control_slot = INVALID_CONTROL_SLOT; |
999 | 0 | bool destroy = false; |
1000 | 0 | uint32 i; |
1001 | | |
1002 | | /* Find the control slot for the given handle. */ |
1003 | 0 | LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE); |
1004 | 0 | for (i = 0; i < dsm_control->nitems; ++i) |
1005 | 0 | { |
1006 | | /* Skip unused slots and segments that are concurrently going away. */ |
1007 | 0 | if (dsm_control->item[i].refcnt <= 1) |
1008 | 0 | continue; |
1009 | | |
1010 | | /* If we've found our handle, we can stop searching. */ |
1011 | 0 | if (dsm_control->item[i].handle == handle) |
1012 | 0 | { |
1013 | 0 | control_slot = i; |
1014 | 0 | break; |
1015 | 0 | } |
1016 | 0 | } |
1017 | | |
1018 | | /* |
1019 | | * We should definitely have found the slot, and it should not already be |
1020 | | * in the process of going away, because this function should only be |
1021 | | * called on a segment which is pinned. |
1022 | | */ |
1023 | 0 | if (control_slot == INVALID_CONTROL_SLOT) |
1024 | 0 | elog(ERROR, "cannot unpin unknown segment handle"); |
1025 | 0 | if (!dsm_control->item[control_slot].pinned) |
1026 | 0 | elog(ERROR, "cannot unpin a segment that is not pinned"); |
1027 | 0 | Assert(dsm_control->item[control_slot].refcnt > 1); |
1028 | | |
1029 | | /* |
1030 | | * Allow implementation-specific code to run. We have to do this before |
1031 | | * releasing the lock, because impl_private_pm_handle may get modified by |
1032 | | * dsm_impl_unpin_segment. |
1033 | | */ |
1034 | 0 | if (!is_main_region_dsm_handle(handle)) |
1035 | 0 | dsm_impl_unpin_segment(handle, |
1036 | 0 | &dsm_control->item[control_slot].impl_private_pm_handle); |
1037 | | |
1038 | | /* Note that 1 means no references (0 means unused slot). */ |
1039 | 0 | if (--dsm_control->item[control_slot].refcnt == 1) |
1040 | 0 | destroy = true; |
1041 | 0 | dsm_control->item[control_slot].pinned = false; |
1042 | | |
1043 | | /* Now we can release the lock. */ |
1044 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
1045 | | |
1046 | | /* Clean up resources if that was the last reference. */ |
1047 | 0 | if (destroy) |
1048 | 0 | { |
1049 | 0 | void *junk_impl_private = NULL; |
1050 | 0 | void *junk_mapped_address = NULL; |
1051 | 0 | Size junk_mapped_size = 0; |
1052 | | |
1053 | | /* |
1054 | | * For an explanation of how error handling works in this case, see |
1055 | | * comments in dsm_detach. Note that if we reach this point, the |
1056 | | * current process certainly does not have the segment mapped, because |
1057 | | * if it did, the reference count would have still been greater than 1 |
1058 | | * even after releasing the reference count held by the pin. The fact |
1059 | | * that there can't be a dsm_segment for this handle makes it OK to |
1060 | | * pass the mapped size, mapped address, and private data as NULL |
1061 | | * here. |
1062 | | */ |
1063 | 0 | if (is_main_region_dsm_handle(handle) || |
1064 | 0 | dsm_impl_op(DSM_OP_DESTROY, handle, 0, &junk_impl_private, |
1065 | 0 | &junk_mapped_address, &junk_mapped_size, WARNING)) |
1066 | 0 | { |
1067 | 0 | LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE); |
1068 | 0 | if (is_main_region_dsm_handle(handle)) |
1069 | 0 | FreePageManagerPut((FreePageManager *) dsm_main_space_begin, |
1070 | 0 | dsm_control->item[control_slot].first_page, |
1071 | 0 | dsm_control->item[control_slot].npages); |
1072 | 0 | Assert(dsm_control->item[control_slot].handle == handle); |
1073 | 0 | Assert(dsm_control->item[control_slot].refcnt == 1); |
1074 | 0 | dsm_control->item[control_slot].refcnt = 0; |
1075 | 0 | LWLockRelease(DynamicSharedMemoryControlLock); |
1076 | 0 | } |
1077 | 0 | } |
1078 | 0 | } |
1079 | | |
1080 | | /* |
1081 | | * Find an existing mapping for a shared memory segment, if there is one. |
1082 | | */ |
1083 | | dsm_segment * |
1084 | | dsm_find_mapping(dsm_handle handle) |
1085 | 0 | { |
1086 | 0 | dlist_iter iter; |
1087 | 0 | dsm_segment *seg; |
1088 | |
|
1089 | 0 | dlist_foreach(iter, &dsm_segment_list) |
1090 | 0 | { |
1091 | 0 | seg = dlist_container(dsm_segment, node, iter.cur); |
1092 | 0 | if (seg->handle == handle) |
1093 | 0 | return seg; |
1094 | 0 | } |
1095 | | |
1096 | 0 | return NULL; |
1097 | 0 | } |
1098 | | |
1099 | | /* |
1100 | | * Get the address at which a dynamic shared memory segment is mapped. |
1101 | | */ |
1102 | | void * |
1103 | | dsm_segment_address(dsm_segment *seg) |
1104 | 0 | { |
1105 | 0 | Assert(seg->mapped_address != NULL); |
1106 | 0 | return seg->mapped_address; |
1107 | 0 | } |
1108 | | |
1109 | | /* |
1110 | | * Get the size of a mapping. |
1111 | | */ |
1112 | | Size |
1113 | | dsm_segment_map_length(dsm_segment *seg) |
1114 | 0 | { |
1115 | 0 | Assert(seg->mapped_address != NULL); |
1116 | 0 | return seg->mapped_size; |
1117 | 0 | } |
1118 | | |
1119 | | /* |
1120 | | * Get a handle for a mapping. |
1121 | | * |
1122 | | * To establish communication via dynamic shared memory between two backends, |
1123 | | * one of them should first call dsm_create() to establish a new shared |
1124 | | * memory mapping. That process should then call dsm_segment_handle() to |
1125 | | * obtain a handle for the mapping, and pass that handle to the |
1126 | | * coordinating backend via some means (e.g. bgw_main_arg, or via the |
1127 | | * main shared memory segment). The recipient, once in possession of the |
1128 | | * handle, should call dsm_attach(). |
1129 | | */ |
1130 | | dsm_handle |
1131 | | dsm_segment_handle(dsm_segment *seg) |
1132 | 0 | { |
1133 | 0 | return seg->handle; |
1134 | 0 | } |
1135 | | |
1136 | | /* |
1137 | | * Register an on-detach callback for a dynamic shared memory segment. |
1138 | | */ |
1139 | | void |
1140 | | on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function, Datum arg) |
1141 | 0 | { |
1142 | 0 | dsm_segment_detach_callback *cb; |
1143 | |
|
1144 | 0 | cb = MemoryContextAlloc(TopMemoryContext, |
1145 | 0 | sizeof(dsm_segment_detach_callback)); |
1146 | 0 | cb->function = function; |
1147 | 0 | cb->arg = arg; |
1148 | 0 | slist_push_head(&seg->on_detach, &cb->node); |
1149 | 0 | } |
1150 | | |
1151 | | /* |
1152 | | * Unregister an on-detach callback for a dynamic shared memory segment. |
1153 | | */ |
1154 | | void |
1155 | | cancel_on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function, |
1156 | | Datum arg) |
1157 | 0 | { |
1158 | 0 | slist_mutable_iter iter; |
1159 | |
|
1160 | 0 | slist_foreach_modify(iter, &seg->on_detach) |
1161 | 0 | { |
1162 | 0 | dsm_segment_detach_callback *cb; |
1163 | |
|
1164 | 0 | cb = slist_container(dsm_segment_detach_callback, node, iter.cur); |
1165 | 0 | if (cb->function == function && cb->arg == arg) |
1166 | 0 | { |
1167 | 0 | slist_delete_current(&iter); |
1168 | 0 | pfree(cb); |
1169 | 0 | break; |
1170 | 0 | } |
1171 | 0 | } |
1172 | 0 | } |
1173 | | |
1174 | | /* |
1175 | | * Discard all registered on-detach callbacks without executing them. |
1176 | | */ |
1177 | | void |
1178 | | reset_on_dsm_detach(void) |
1179 | 0 | { |
1180 | 0 | dlist_iter iter; |
1181 | |
|
1182 | 0 | dlist_foreach(iter, &dsm_segment_list) |
1183 | 0 | { |
1184 | 0 | dsm_segment *seg = dlist_container(dsm_segment, node, iter.cur); |
1185 | | |
1186 | | /* Throw away explicit on-detach actions one by one. */ |
1187 | 0 | while (!slist_is_empty(&seg->on_detach)) |
1188 | 0 | { |
1189 | 0 | slist_node *node; |
1190 | 0 | dsm_segment_detach_callback *cb; |
1191 | |
|
1192 | 0 | node = slist_pop_head_node(&seg->on_detach); |
1193 | 0 | cb = slist_container(dsm_segment_detach_callback, node, node); |
1194 | 0 | pfree(cb); |
1195 | 0 | } |
1196 | | |
1197 | | /* |
1198 | | * Decrementing the reference count is a sort of implicit on-detach |
1199 | | * action; make sure we don't do that, either. |
1200 | | */ |
1201 | 0 | seg->control_slot = INVALID_CONTROL_SLOT; |
1202 | 0 | } |
1203 | 0 | } |
1204 | | |
1205 | | /* |
1206 | | * Create a segment descriptor. |
1207 | | */ |
1208 | | static dsm_segment * |
1209 | | dsm_create_descriptor(void) |
1210 | 0 | { |
1211 | 0 | dsm_segment *seg; |
1212 | |
|
1213 | 0 | if (CurrentResourceOwner) |
1214 | 0 | ResourceOwnerEnlarge(CurrentResourceOwner); |
1215 | |
|
1216 | 0 | seg = MemoryContextAlloc(TopMemoryContext, sizeof(dsm_segment)); |
1217 | 0 | dlist_push_head(&dsm_segment_list, &seg->node); |
1218 | | |
1219 | | /* seg->handle must be initialized by the caller */ |
1220 | 0 | seg->control_slot = INVALID_CONTROL_SLOT; |
1221 | 0 | seg->impl_private = NULL; |
1222 | 0 | seg->mapped_address = NULL; |
1223 | 0 | seg->mapped_size = 0; |
1224 | |
|
1225 | 0 | seg->resowner = CurrentResourceOwner; |
1226 | 0 | if (CurrentResourceOwner) |
1227 | 0 | ResourceOwnerRememberDSM(CurrentResourceOwner, seg); |
1228 | |
|
1229 | 0 | slist_init(&seg->on_detach); |
1230 | |
|
1231 | 0 | return seg; |
1232 | 0 | } |
1233 | | |
1234 | | /* |
1235 | | * Sanity check a control segment. |
1236 | | * |
1237 | | * The goal here isn't to detect everything that could possibly be wrong with |
1238 | | * the control segment; there's not enough information for that. Rather, the |
1239 | | * goal is to make sure that someone can iterate over the items in the segment |
1240 | | * without overrunning the end of the mapping and crashing. We also check |
1241 | | * the magic number since, if that's messed up, this may not even be one of |
1242 | | * our segments at all. |
1243 | | */ |
1244 | | static bool |
1245 | | dsm_control_segment_sane(dsm_control_header *control, Size mapped_size) |
1246 | 0 | { |
1247 | 0 | if (mapped_size < offsetof(dsm_control_header, item)) |
1248 | 0 | return false; /* Mapped size too short to read header. */ |
1249 | 0 | if (control->magic != PG_DYNSHMEM_CONTROL_MAGIC) |
1250 | 0 | return false; /* Magic number doesn't match. */ |
1251 | 0 | if (dsm_control_bytes_needed(control->maxitems) > mapped_size) |
1252 | 0 | return false; /* Max item count won't fit in map. */ |
1253 | 0 | if (control->nitems > control->maxitems) |
1254 | 0 | return false; /* Overfull. */ |
1255 | 0 | return true; |
1256 | 0 | } |
1257 | | |
1258 | | /* |
1259 | | * Compute the number of control-segment bytes needed to store a given |
1260 | | * number of items. |
1261 | | */ |
1262 | | static uint64 |
1263 | | dsm_control_bytes_needed(uint32 nitems) |
1264 | 0 | { |
1265 | 0 | return offsetof(dsm_control_header, item) |
1266 | 0 | + sizeof(dsm_control_item) * (uint64) nitems; |
1267 | 0 | } |
1268 | | |
1269 | | static inline dsm_handle |
1270 | | make_main_region_dsm_handle(int slot) |
1271 | 0 | { |
1272 | 0 | dsm_handle handle; |
1273 | | |
1274 | | /* |
1275 | | * We need to create a handle that doesn't collide with any existing extra |
1276 | | * segment created by dsm_impl_op(), so we'll make it odd. It also |
1277 | | * mustn't collide with any other main area pseudo-segment, so we'll |
1278 | | * include the slot number in some of the bits. We also want to make an |
1279 | | * effort to avoid newly created and recently destroyed handles from being |
1280 | | * confused, so we'll make the rest of the bits random. |
1281 | | */ |
1282 | 0 | handle = 1; |
1283 | 0 | handle |= slot << 1; |
1284 | 0 | handle |= pg_prng_uint32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1); |
1285 | 0 | return handle; |
1286 | 0 | } |
1287 | | |
1288 | | static inline bool |
1289 | | is_main_region_dsm_handle(dsm_handle handle) |
1290 | 0 | { |
1291 | 0 | return handle & 1; |
1292 | 0 | } |
1293 | | |
1294 | | /* ResourceOwner callbacks */ |
1295 | | |
1296 | | static void |
1297 | | ResOwnerReleaseDSM(Datum res) |
1298 | 0 | { |
1299 | 0 | dsm_segment *seg = (dsm_segment *) DatumGetPointer(res); |
1300 | |
|
1301 | 0 | seg->resowner = NULL; |
1302 | 0 | dsm_detach(seg); |
1303 | 0 | } |
1304 | | static char * |
1305 | | ResOwnerPrintDSM(Datum res) |
1306 | 0 | { |
1307 | 0 | dsm_segment *seg = (dsm_segment *) DatumGetPointer(res); |
1308 | |
|
1309 | 0 | return psprintf("dynamic shared memory segment %u", |
1310 | 0 | dsm_segment_handle(seg)); |
1311 | 0 | } |