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