Line data Source code
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 : #define PG_DYNSHMEM_CONTROL_MAGIC 0x9a503d32
52 :
53 : #define PG_DYNSHMEM_FIXED_SLOTS 64
54 : #define PG_DYNSHMEM_SLOTS_PER_BACKEND 5
55 :
56 : #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 4051 : ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
171 : {
172 4051 : ResourceOwnerRemember(owner, PointerGetDatum(seg), &dsm_resowner_desc);
173 4051 : }
174 : static inline void
175 4051 : ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
176 : {
177 4051 : ResourceOwnerForget(owner, PointerGetDatum(seg), &dsm_resowner_desc);
178 4051 : }
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 1230 : dsm_postmaster_startup(PGShmemHeader *shim)
188 : {
189 1230 : 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 1230 : if (dynamic_shared_memory_type == DSM_IMPL_MMAP)
202 0 : dsm_cleanup_for_mmap();
203 :
204 : /* Determine size for new control segment. */
205 1230 : maxitems = PG_DYNSHMEM_FIXED_SLOTS
206 1230 : + PG_DYNSHMEM_SLOTS_PER_BACKEND * MaxBackends;
207 1230 : elog(DEBUG2, "dynamic shared memory system will support %u segments",
208 : maxitems);
209 1230 : 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 0 : Assert(dsm_control_address == NULL);
219 : Assert(dsm_control_mapped_size == 0);
220 : /* Use even numbers only */
221 1230 : dsm_control_handle = pg_prng_uint32(&pg_global_prng_state) << 1;
222 1230 : if (dsm_control_handle == DSM_HANDLE_INVALID)
223 0 : continue;
224 1230 : 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 1230 : break;
228 : }
229 1230 : dsm_control = dsm_control_address;
230 1230 : on_shmem_exit(dsm_postmaster_shutdown, PointerGetDatum(shim));
231 1230 : elog(DEBUG2,
232 : "created dynamic shared memory control segment %u (%zu bytes)",
233 : dsm_control_handle, segsize);
234 1230 : shim->dsm_control = dsm_control_handle;
235 :
236 : /* Initialize control segment. */
237 1230 : dsm_control->magic = PG_DYNSHMEM_CONTROL_MAGIC;
238 1230 : dsm_control->nitems = 0;
239 1230 : dsm_control->maxitems = maxitems;
240 1230 : }
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 2 : dsm_cleanup_using_control_segment(dsm_handle old_control_handle)
249 : {
250 2 : void *mapped_address = NULL;
251 2 : void *junk_mapped_address = NULL;
252 2 : void *impl_private = NULL;
253 2 : void *junk_impl_private = NULL;
254 2 : Size mapped_size = 0;
255 2 : 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 2 : if (!dsm_impl_op(DSM_OP_ATTACH, old_control_handle, 0, &impl_private,
267 : &mapped_address, &mapped_size, DEBUG1))
268 0 : 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 2 : old_control = (dsm_control_header *) mapped_address;
275 2 : if (!dsm_control_segment_sane(old_control, mapped_size))
276 : {
277 0 : dsm_impl_op(DSM_OP_DETACH, old_control_handle, 0, &impl_private,
278 : &mapped_address, &mapped_size, LOG);
279 0 : 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 2 : nitems = old_control->nitems;
287 4 : 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 2 : refcnt = old_control->item[i].refcnt;
294 2 : if (refcnt == 0)
295 0 : continue;
296 :
297 : /* If it was using the main shmem area, there is nothing to do. */
298 2 : handle = old_control->item[i].handle;
299 2 : if (is_main_region_dsm_handle(handle))
300 0 : continue;
301 :
302 : /* Log debugging information. */
303 2 : elog(DEBUG2, "cleaning up orphaned dynamic shared memory with ID %u (reference count %u)",
304 : handle, refcnt);
305 :
306 : /* Destroy the referenced segment. */
307 2 : 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 2 : elog(DEBUG2,
313 : "cleaning up dynamic shared memory control segment with ID %u",
314 : old_control_handle);
315 2 : 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 0 : dsm_cleanup_for_mmap(void)
331 : {
332 : DIR *dir;
333 : 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 : {
340 0 : if (strncmp(dent->d_name, PG_DYNSHMEM_MMAP_FILE_PREFIX,
341 : strlen(PG_DYNSHMEM_MMAP_FILE_PREFIX)) == 0)
342 : {
343 : 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 : (errcode_for_file_access(),
353 : errmsg("could not remove file \"%s\": %m", buf)));
354 : }
355 : }
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 1230 : dsm_postmaster_shutdown(int code, Datum arg)
369 : {
370 : uint32 nitems;
371 : uint32 i;
372 : void *dsm_control_address;
373 1230 : void *junk_mapped_address = NULL;
374 1230 : void *junk_impl_private = NULL;
375 1230 : Size junk_mapped_size = 0;
376 1230 : 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 1230 : nitems = dsm_control->nitems;
386 1230 : if (!dsm_control_segment_sane(dsm_control, dsm_control_mapped_size))
387 : {
388 0 : ereport(LOG,
389 : (errmsg("dynamic shared memory control segment is corrupt")));
390 0 : return;
391 : }
392 :
393 : /* Remove any remaining segments. */
394 2540 : 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 1310 : if (dsm_control->item[i].refcnt == 0)
400 66 : continue;
401 :
402 1244 : handle = dsm_control->item[i].handle;
403 1244 : if (is_main_region_dsm_handle(handle))
404 0 : continue;
405 :
406 : /* Log debugging information. */
407 1244 : elog(DEBUG2, "cleaning up orphaned dynamic shared memory with ID %u",
408 : handle);
409 :
410 : /* Destroy the segment. */
411 1244 : 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 1230 : elog(DEBUG2,
417 : "cleaning up dynamic shared memory control segment with ID %u",
418 : dsm_control_handle);
419 1230 : dsm_control_address = dsm_control;
420 1230 : 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 1230 : dsm_control = dsm_control_address;
424 1230 : 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 22008 : dsm_backend_startup(void)
434 : {
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 22008 : dsm_init_done = true;
460 22008 : }
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 1233 : dsm_main_space_request(void *arg)
481 : {
482 1233 : dsm_main_space_size = 1024 * 1024 * (size_t) min_dynamic_shared_memory;
483 :
484 1233 : if (dsm_main_space_size == 0)
485 1233 : return;
486 :
487 0 : ShmemRequestStruct(.name = "Preallocated DSM",
488 : .size = dsm_main_space_size,
489 : .ptr = &dsm_main_space_begin,
490 : );
491 : }
492 :
493 : static void
494 1230 : dsm_main_space_init(void *arg)
495 : {
496 1230 : FreePageManager *fpm = (FreePageManager *) dsm_main_space_begin;
497 1230 : size_t first_page = 0;
498 : size_t pages;
499 :
500 1230 : if (dsm_main_space_size == 0)
501 1230 : 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 : }
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 2206 : dsm_create(Size size, int flags)
525 : {
526 : dsm_segment *seg;
527 : uint32 i;
528 : uint32 nitems;
529 2206 : size_t npages = 0;
530 2206 : size_t first_page = 0;
531 2206 : FreePageManager *dsm_main_space_fpm = dsm_main_space_begin;
532 2206 : 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 : Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
540 :
541 2206 : if (!dsm_init_done)
542 1029 : dsm_backend_startup();
543 :
544 : /* Create a new segment descriptor. */
545 2206 : 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 2206 : if (dsm_main_space_fpm)
552 : {
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 : {
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 : }
567 : }
568 :
569 2206 : if (!using_main_dsm_region)
570 : {
571 : /*
572 : * We need to create a new memory segment. Loop until we find an
573 : * unused segment identifier.
574 : */
575 2206 : if (dsm_main_space_fpm)
576 0 : LWLockRelease(DynamicSharedMemoryControlLock);
577 : for (;;)
578 : {
579 0 : Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
580 : /* Use even numbers only */
581 2206 : seg->handle = pg_prng_uint32(&pg_global_prng_state) << 1;
582 2206 : if (seg->handle == DSM_HANDLE_INVALID) /* Reserve sentinel */
583 0 : continue;
584 2206 : if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
585 : &seg->mapped_address, &seg->mapped_size, ERROR))
586 2206 : break;
587 : }
588 2206 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
589 : }
590 :
591 : /* Search the control segment for an unused slot. */
592 2206 : nitems = dsm_control->nitems;
593 6657 : for (i = 0; i < nitems; ++i)
594 : {
595 5450 : if (dsm_control->item[i].refcnt == 0)
596 : {
597 999 : if (using_main_dsm_region)
598 : {
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 : }
603 : else
604 : Assert(!is_main_region_dsm_handle(seg->handle));
605 999 : dsm_control->item[i].handle = seg->handle;
606 : /* refcnt of 1 triggers destruction, so start at 2 */
607 999 : dsm_control->item[i].refcnt = 2;
608 999 : dsm_control->item[i].impl_private_pm_handle = NULL;
609 999 : dsm_control->item[i].pinned = false;
610 999 : seg->control_slot = i;
611 999 : LWLockRelease(DynamicSharedMemoryControlLock);
612 999 : return seg;
613 : }
614 : }
615 :
616 : /* Verify that we can support an additional mapping. */
617 1207 : if (nitems >= dsm_control->maxitems)
618 : {
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 : &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 : (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
634 : errmsg("too many dynamic shared memory segments")));
635 : }
636 :
637 : /* Enter the handle into a new array slot. */
638 1207 : if (using_main_dsm_region)
639 : {
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 : }
644 1207 : dsm_control->item[nitems].handle = seg->handle;
645 : /* refcnt of 1 triggers destruction, so start at 2 */
646 1207 : dsm_control->item[nitems].refcnt = 2;
647 1207 : dsm_control->item[nitems].impl_private_pm_handle = NULL;
648 1207 : dsm_control->item[nitems].pinned = false;
649 1207 : seg->control_slot = nitems;
650 1207 : dsm_control->nitems++;
651 1207 : LWLockRelease(DynamicSharedMemoryControlLock);
652 :
653 1207 : return seg;
654 : }
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 26346 : dsm_attach(dsm_handle h)
674 : {
675 : dsm_segment *seg;
676 : dlist_iter iter;
677 : uint32 i;
678 : uint32 nitems;
679 :
680 : /* Unsafe in postmaster (and pointless in a stand-alone backend). */
681 : Assert(IsUnderPostmaster);
682 :
683 26346 : if (!dsm_init_done)
684 20979 : 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 35259 : dlist_foreach(iter, &dsm_segment_list)
697 : {
698 8913 : seg = dlist_container(dsm_segment, node, iter.cur);
699 8913 : if (seg->handle == h)
700 0 : elog(ERROR, "can't attach the same segment more than once");
701 : }
702 :
703 : /* Create a new segment descriptor. */
704 26346 : seg = dsm_create_descriptor();
705 26346 : seg->handle = h;
706 :
707 : /* Bump reference count for this segment in shared memory. */
708 26346 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
709 26346 : nitems = dsm_control->nitems;
710 43197 : for (i = 0; i < nitems; ++i)
711 : {
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 43197 : if (dsm_control->item[i].refcnt <= 1)
720 136 : continue;
721 :
722 : /* If the handle doesn't match, it's not the slot we want. */
723 43061 : if (dsm_control->item[i].handle != seg->handle)
724 16715 : continue;
725 :
726 : /* Otherwise we've found a match. */
727 26346 : dsm_control->item[i].refcnt++;
728 26346 : seg->control_slot = i;
729 26346 : if (is_main_region_dsm_handle(seg->handle))
730 : {
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 : }
735 26346 : break;
736 : }
737 26346 : 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 26346 : if (seg->control_slot == INVALID_CONTROL_SLOT)
746 : {
747 0 : dsm_detach(seg);
748 0 : return NULL;
749 : }
750 :
751 : /* Here's where we actually try to map the segment. */
752 26346 : if (!is_main_region_dsm_handle(seg->handle))
753 26346 : dsm_impl_op(DSM_OP_ATTACH, seg->handle, 0, &seg->impl_private,
754 : &seg->mapped_address, &seg->mapped_size, ERROR);
755 :
756 26346 : return seg;
757 : }
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 52442 : dsm_backend_shutdown(void)
766 : {
767 53201 : while (!dlist_is_empty(&dsm_segment_list))
768 : {
769 : dsm_segment *seg;
770 :
771 759 : seg = dlist_head_element(dsm_segment, node, &dsm_segment_list);
772 759 : dsm_detach(seg);
773 : }
774 52442 : }
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 1 : dsm_detach_all(void)
784 : {
785 1 : void *control_address = dsm_control;
786 :
787 1 : while (!dlist_is_empty(&dsm_segment_list))
788 : {
789 : dsm_segment *seg;
790 :
791 0 : seg = dlist_head_element(dsm_segment, node, &dsm_segment_list);
792 0 : dsm_detach(seg);
793 : }
794 :
795 1 : if (control_address != NULL)
796 1 : dsm_impl_op(DSM_OP_DETACH, dsm_control_handle, 0,
797 : &dsm_control_impl_private, &control_address,
798 : &dsm_control_mapped_size, ERROR);
799 1 : }
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 28552 : dsm_detach(dsm_segment *seg)
812 : {
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 28552 : HOLD_INTERRUPTS();
822 40518 : while (!slist_is_empty(&seg->on_detach))
823 : {
824 : slist_node *node;
825 : dsm_segment_detach_callback *cb;
826 : on_dsm_detach_callback function;
827 : Datum arg;
828 :
829 11966 : node = slist_pop_head_node(&seg->on_detach);
830 11966 : cb = slist_container(dsm_segment_detach_callback, node, node);
831 11966 : function = cb->function;
832 11966 : arg = cb->arg;
833 11966 : pfree(cb);
834 :
835 11966 : function(seg, arg);
836 : }
837 28552 : 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 28552 : if (seg->mapped_address != NULL)
848 : {
849 28552 : if (!is_main_region_dsm_handle(seg->handle))
850 28552 : dsm_impl_op(DSM_OP_DETACH, seg->handle, 0, &seg->impl_private,
851 : &seg->mapped_address, &seg->mapped_size, WARNING);
852 28552 : seg->impl_private = NULL;
853 28552 : seg->mapped_address = NULL;
854 28552 : seg->mapped_size = 0;
855 : }
856 :
857 : /* Reduce reference count, if we previously increased it. */
858 28552 : if (seg->control_slot != INVALID_CONTROL_SLOT)
859 : {
860 : uint32 refcnt;
861 28552 : uint32 control_slot = seg->control_slot;
862 :
863 28552 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
864 : Assert(dsm_control->item[control_slot].handle == seg->handle);
865 : Assert(dsm_control->item[control_slot].refcnt > 1);
866 28552 : refcnt = --dsm_control->item[control_slot].refcnt;
867 28552 : seg->control_slot = INVALID_CONTROL_SLOT;
868 28552 : LWLockRelease(DynamicSharedMemoryControlLock);
869 :
870 : /* If new reference count is 1, try to destroy the segment. */
871 28552 : if (refcnt == 1)
872 : {
873 : /* A pinned segment should never reach 1. */
874 : 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 1764 : if (is_main_region_dsm_handle(seg->handle) ||
892 882 : dsm_impl_op(DSM_OP_DESTROY, seg->handle, 0, &seg->impl_private,
893 : &seg->mapped_address, &seg->mapped_size, WARNING))
894 : {
895 882 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
896 882 : 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 : Assert(dsm_control->item[control_slot].handle == seg->handle);
901 : Assert(dsm_control->item[control_slot].refcnt == 1);
902 882 : dsm_control->item[control_slot].refcnt = 0;
903 882 : LWLockRelease(DynamicSharedMemoryControlLock);
904 : }
905 : }
906 : }
907 :
908 : /* Clean up our remaining backend-private data structures. */
909 28552 : if (seg->resowner != NULL)
910 1387 : ResourceOwnerForgetDSM(seg->resowner, seg);
911 28552 : dlist_delete(&seg->node);
912 28552 : pfree(seg);
913 28552 : }
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 2669 : dsm_pin_mapping(dsm_segment *seg)
924 : {
925 2669 : if (seg->resowner != NULL)
926 : {
927 2664 : ResourceOwnerForgetDSM(seg->resowner, seg);
928 2664 : seg->resowner = NULL;
929 : }
930 2669 : }
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 0 : dsm_unpin_mapping(dsm_segment *seg)
943 : {
944 : 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 1395 : dsm_pin_segment(dsm_segment *seg)
964 : {
965 1395 : 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 1395 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
974 1395 : if (dsm_control->item[seg->control_slot].pinned)
975 0 : elog(ERROR, "cannot pin a segment that is already pinned");
976 1395 : if (!is_main_region_dsm_handle(seg->handle))
977 1395 : dsm_impl_pin_segment(seg->handle, seg->impl_private, &handle);
978 1395 : dsm_control->item[seg->control_slot].pinned = true;
979 1395 : dsm_control->item[seg->control_slot].refcnt++;
980 1395 : dsm_control->item[seg->control_slot].impl_private_pm_handle = handle;
981 1395 : LWLockRelease(DynamicSharedMemoryControlLock);
982 1395 : }
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 254 : dsm_unpin_segment(dsm_handle handle)
997 : {
998 254 : uint32 control_slot = INVALID_CONTROL_SLOT;
999 254 : bool destroy = false;
1000 : uint32 i;
1001 :
1002 : /* Find the control slot for the given handle. */
1003 254 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
1004 1463 : for (i = 0; i < dsm_control->nitems; ++i)
1005 : {
1006 : /* Skip unused slots and segments that are concurrently going away. */
1007 1463 : if (dsm_control->item[i].refcnt <= 1)
1008 50 : continue;
1009 :
1010 : /* If we've found our handle, we can stop searching. */
1011 1413 : if (dsm_control->item[i].handle == handle)
1012 : {
1013 254 : control_slot = i;
1014 254 : break;
1015 : }
1016 : }
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 254 : if (control_slot == INVALID_CONTROL_SLOT)
1024 0 : elog(ERROR, "cannot unpin unknown segment handle");
1025 254 : if (!dsm_control->item[control_slot].pinned)
1026 0 : elog(ERROR, "cannot unpin a segment that is not pinned");
1027 : 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 254 : if (!is_main_region_dsm_handle(handle))
1035 254 : dsm_impl_unpin_segment(handle,
1036 254 : &dsm_control->item[control_slot].impl_private_pm_handle);
1037 :
1038 : /* Note that 1 means no references (0 means unused slot). */
1039 254 : if (--dsm_control->item[control_slot].refcnt == 1)
1040 183 : destroy = true;
1041 254 : dsm_control->item[control_slot].pinned = false;
1042 :
1043 : /* Now we can release the lock. */
1044 254 : LWLockRelease(DynamicSharedMemoryControlLock);
1045 :
1046 : /* Clean up resources if that was the last reference. */
1047 254 : if (destroy)
1048 : {
1049 183 : void *junk_impl_private = NULL;
1050 183 : void *junk_mapped_address = NULL;
1051 183 : 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 366 : if (is_main_region_dsm_handle(handle) ||
1064 183 : dsm_impl_op(DSM_OP_DESTROY, handle, 0, &junk_impl_private,
1065 : &junk_mapped_address, &junk_mapped_size, WARNING))
1066 : {
1067 183 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
1068 183 : 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 : Assert(dsm_control->item[control_slot].handle == handle);
1073 : Assert(dsm_control->item[control_slot].refcnt == 1);
1074 183 : dsm_control->item[control_slot].refcnt = 0;
1075 183 : LWLockRelease(DynamicSharedMemoryControlLock);
1076 : }
1077 : }
1078 254 : }
1079 :
1080 : /*
1081 : * Find an existing mapping for a shared memory segment, if there is one.
1082 : */
1083 : dsm_segment *
1084 132 : dsm_find_mapping(dsm_handle handle)
1085 : {
1086 : dlist_iter iter;
1087 : dsm_segment *seg;
1088 :
1089 392 : dlist_foreach(iter, &dsm_segment_list)
1090 : {
1091 265 : seg = dlist_container(dsm_segment, node, iter.cur);
1092 265 : if (seg->handle == handle)
1093 5 : return seg;
1094 : }
1095 :
1096 127 : return NULL;
1097 : }
1098 :
1099 : /*
1100 : * Get the address at which a dynamic shared memory segment is mapped.
1101 : */
1102 : void *
1103 29122 : dsm_segment_address(dsm_segment *seg)
1104 : {
1105 : Assert(seg->mapped_address != NULL);
1106 29122 : return seg->mapped_address;
1107 : }
1108 :
1109 : /*
1110 : * Get the size of a mapping.
1111 : */
1112 : Size
1113 0 : dsm_segment_map_length(dsm_segment *seg)
1114 : {
1115 : Assert(seg->mapped_address != NULL);
1116 0 : return seg->mapped_size;
1117 : }
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 2949 : dsm_segment_handle(dsm_segment *seg)
1132 : {
1133 2949 : return seg->handle;
1134 : }
1135 :
1136 : /*
1137 : * Register an on-detach callback for a dynamic shared memory segment.
1138 : */
1139 : void
1140 17667 : on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function, Datum arg)
1141 : {
1142 : dsm_segment_detach_callback *cb;
1143 :
1144 17667 : cb = MemoryContextAlloc(TopMemoryContext,
1145 : sizeof(dsm_segment_detach_callback));
1146 17667 : cb->function = function;
1147 17667 : cb->arg = arg;
1148 17667 : slist_push_head(&seg->on_detach, &cb->node);
1149 17667 : }
1150 :
1151 : /*
1152 : * Unregister an on-detach callback for a dynamic shared memory segment.
1153 : */
1154 : void
1155 5701 : cancel_on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function,
1156 : Datum arg)
1157 : {
1158 : slist_mutable_iter iter;
1159 :
1160 18767 : slist_foreach_modify(iter, &seg->on_detach)
1161 : {
1162 : dsm_segment_detach_callback *cb;
1163 :
1164 18767 : cb = slist_container(dsm_segment_detach_callback, node, iter.cur);
1165 18767 : if (cb->function == function && cb->arg == arg)
1166 : {
1167 5701 : slist_delete_current(&iter);
1168 5701 : pfree(cb);
1169 5701 : break;
1170 : }
1171 : }
1172 5701 : }
1173 :
1174 : /*
1175 : * Discard all registered on-detach callbacks without executing them.
1176 : */
1177 : void
1178 24961 : reset_on_dsm_detach(void)
1179 : {
1180 : dlist_iter iter;
1181 :
1182 24961 : dlist_foreach(iter, &dsm_segment_list)
1183 : {
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 : {
1189 : slist_node *node;
1190 : 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 : }
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 : }
1203 24961 : }
1204 :
1205 : /*
1206 : * Create a segment descriptor.
1207 : */
1208 : static dsm_segment *
1209 28552 : dsm_create_descriptor(void)
1210 : {
1211 : dsm_segment *seg;
1212 :
1213 28552 : if (CurrentResourceOwner)
1214 4051 : ResourceOwnerEnlarge(CurrentResourceOwner);
1215 :
1216 28552 : seg = MemoryContextAlloc(TopMemoryContext, sizeof(dsm_segment));
1217 28552 : dlist_push_head(&dsm_segment_list, &seg->node);
1218 :
1219 : /* seg->handle must be initialized by the caller */
1220 28552 : seg->control_slot = INVALID_CONTROL_SLOT;
1221 28552 : seg->impl_private = NULL;
1222 28552 : seg->mapped_address = NULL;
1223 28552 : seg->mapped_size = 0;
1224 :
1225 28552 : seg->resowner = CurrentResourceOwner;
1226 28552 : if (CurrentResourceOwner)
1227 4051 : ResourceOwnerRememberDSM(CurrentResourceOwner, seg);
1228 :
1229 28552 : slist_init(&seg->on_detach);
1230 :
1231 28552 : return seg;
1232 : }
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 1232 : dsm_control_segment_sane(dsm_control_header *control, Size mapped_size)
1246 : {
1247 1232 : if (mapped_size < offsetof(dsm_control_header, item))
1248 0 : return false; /* Mapped size too short to read header. */
1249 1232 : if (control->magic != PG_DYNSHMEM_CONTROL_MAGIC)
1250 0 : return false; /* Magic number doesn't match. */
1251 1232 : if (dsm_control_bytes_needed(control->maxitems) > mapped_size)
1252 0 : return false; /* Max item count won't fit in map. */
1253 1232 : if (control->nitems > control->maxitems)
1254 0 : return false; /* Overfull. */
1255 1232 : return true;
1256 : }
1257 :
1258 : /*
1259 : * Compute the number of control-segment bytes needed to store a given
1260 : * number of items.
1261 : */
1262 : static uint64
1263 2462 : dsm_control_bytes_needed(uint32 nitems)
1264 : {
1265 : return offsetof(dsm_control_header, item)
1266 2462 : + sizeof(dsm_control_item) * (uint64) nitems;
1267 : }
1268 :
1269 : static inline dsm_handle
1270 0 : make_main_region_dsm_handle(int slot)
1271 : {
1272 : 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 : }
1287 :
1288 : static inline bool
1289 86269 : is_main_region_dsm_handle(dsm_handle handle)
1290 : {
1291 86269 : return handle & 1;
1292 : }
1293 :
1294 : /* ResourceOwner callbacks */
1295 :
1296 : static void
1297 0 : ResOwnerReleaseDSM(Datum res)
1298 : {
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 0 : ResOwnerPrintDSM(Datum res)
1306 : {
1307 0 : dsm_segment *seg = (dsm_segment *) DatumGetPointer(res);
1308 :
1309 0 : return psprintf("dynamic shared memory segment %u",
1310 : dsm_segment_handle(seg));
1311 : }
|