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