Age Owner Branch data TLA 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
158 heikki.linnakangas@i 160 :GNC 2407 : ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
161 : : {
162 : 2407 : ResourceOwnerRemember(owner, PointerGetDatum(seg), &dsm_resowner_desc);
163 : 2407 : }
164 : : static inline void
165 : 2407 : ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
166 : : {
167 : 2407 : ResourceOwnerForget(owner, PointerGetDatum(seg), &dsm_resowner_desc);
168 : 2407 : }
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
3659 rhaas@postgresql.org 177 :CBC 898 : dsm_postmaster_startup(PGShmemHeader *shim)
178 : : {
3840 179 : 898 : void *dsm_control_address = NULL;
180 : : uint32 maxitems;
181 : : Size segsize;
182 : :
183 [ - + ]: 898 : 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 : : */
3659 191 [ - + ]: 898 : if (dynamic_shared_memory_type == DSM_IMPL_MMAP)
3659 rhaas@postgresql.org 192 :UBC 0 : dsm_cleanup_for_mmap();
193 : :
194 : : /* Determine size for new control segment. */
3840 rhaas@postgresql.org 195 :CBC 898 : maxitems = PG_DYNSHMEM_FIXED_SLOTS
733 196 : 898 : + PG_DYNSHMEM_SLOTS_PER_BACKEND * MaxBackends;
3840 197 [ + + ]: 898 : elog(DEBUG2, "dynamic shared memory system will support %u segments",
198 : : maxitems);
199 : 898 : 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 [ - + ]: 898 : Assert(dsm_control_address == NULL);
209 [ - + ]: 898 : Assert(dsm_control_mapped_size == 0);
210 : : /* Use even numbers only */
868 tgl@sss.pgh.pa.us 211 : 898 : dsm_control_handle = pg_prng_uint32(&pg_global_prng_state) << 1;
2707 rhaas@postgresql.org 212 [ - + ]: 898 : if (dsm_control_handle == DSM_HANDLE_INVALID)
3659 rhaas@postgresql.org 213 :UBC 0 : continue;
3840 rhaas@postgresql.org 214 [ + - ]:CBC 898 : 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 : 898 : break;
218 : : }
219 : 898 : dsm_control = dsm_control_address;
3659 220 : 898 : on_shmem_exit(dsm_postmaster_shutdown, PointerGetDatum(shim));
3814 221 [ + + ]: 898 : elog(DEBUG2,
222 : : "created dynamic shared memory control segment %u (%zu bytes)",
223 : : dsm_control_handle, segsize);
3659 224 : 898 : shim->dsm_control = dsm_control_handle;
225 : :
226 : : /* Initialize control segment. */
3840 227 : 898 : dsm_control->magic = PG_DYNSHMEM_CONTROL_MAGIC;
228 : 898 : dsm_control->nitems = 0;
229 : 898 : dsm_control->maxitems = maxitems;
230 : 898 : }
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
3659 238 : 3 : dsm_cleanup_using_control_segment(dsm_handle old_control_handle)
239 : : {
3840 240 : 3 : void *mapped_address = NULL;
241 : 3 : void *junk_mapped_address = NULL;
242 : 3 : void *impl_private = NULL;
243 : 3 : void *junk_impl_private = NULL;
3821 244 : 3 : Size mapped_size = 0;
245 : 3 : 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 : : */
3840 256 [ - + ]: 3 : if (!dsm_impl_op(DSM_OP_ATTACH, old_control_handle, 0, &impl_private,
257 : : &mapped_address, &mapped_size, DEBUG1))
3840 rhaas@postgresql.org 258 :UBC 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 : : */
3840 rhaas@postgresql.org 264 :CBC 3 : old_control = (dsm_control_header *) mapped_address;
265 [ - + ]: 3 : if (!dsm_control_segment_sane(old_control, mapped_size))
266 : : {
3840 rhaas@postgresql.org 267 :UBC 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 : : */
3840 rhaas@postgresql.org 276 :CBC 3 : nitems = old_control->nitems;
277 [ + + ]: 8 : 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 : 5 : refcnt = old_control->item[i].refcnt;
284 [ + + ]: 5 : if (refcnt == 0)
3840 rhaas@postgresql.org 285 :GBC 2 : continue;
286 : :
287 : : /* If it was using the main shmem area, there is nothing to do. */
3840 rhaas@postgresql.org 288 :CBC 3 : handle = old_control->item[i].handle;
1353 tmunro@postgresql.or 289 [ - + ]: 3 : if (is_main_region_dsm_handle(handle))
1353 tmunro@postgresql.or 290 :UBC 0 : continue;
291 : :
292 : : /* Log debugging information. */
3840 rhaas@postgresql.org 293 [ - + ]:CBC 3 : elog(DEBUG2, "cleaning up orphaned dynamic shared memory with ID %u (reference count %u)",
294 : : handle, refcnt);
295 : :
296 : : /* Destroy the referenced segment. */
297 : 3 : 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 [ - + ]: 3 : elog(DEBUG2,
303 : : "cleaning up dynamic shared memory control segment with ID %u",
304 : : old_control_handle);
305 : 3 : 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
3840 rhaas@postgresql.org 320 :UBC 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. */
2323 tgl@sss.pgh.pa.us 326 : 0 : dir = AllocateDir(PG_DYNSHMEM_DIR);
327 : :
3779 rhaas@postgresql.org 328 [ # # ]: 0 : while ((dent = ReadDir(dir, PG_DYNSHMEM_DIR)) != NULL)
329 : : {
3840 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 : :
2560 peter_e@gmx.net 335 : 0 : snprintf(buf, sizeof(buf), PG_DYNSHMEM_DIR "/%s", dent->d_name);
336 : :
3840 rhaas@postgresql.org 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. */
3779 348 : 0 : FreeDir(dir);
3840 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
3840 rhaas@postgresql.org 358 :CBC 898 : dsm_postmaster_shutdown(int code, Datum arg)
359 : : {
360 : : uint32 nitems;
361 : : uint32 i;
362 : : void *dsm_control_address;
363 : 898 : void *junk_mapped_address = NULL;
364 : 898 : void *junk_impl_private = NULL;
3821 365 : 898 : Size junk_mapped_size = 0;
3659 366 : 898 : 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 : : */
3840 375 : 898 : nitems = dsm_control->nitems;
376 [ - + ]: 898 : if (!dsm_control_segment_sane(dsm_control, dsm_control_mapped_size))
377 : : {
3840 rhaas@postgresql.org 378 [ # # ]:UBC 0 : ereport(LOG,
379 : : (errmsg("dynamic shared memory control segment is corrupt")));
380 : 0 : return;
381 : : }
382 : :
383 : : /* Remove any remaining segments. */
3840 rhaas@postgresql.org 384 [ + + ]:CBC 1817 : 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 [ + + ]: 919 : if (dsm_control->item[i].refcnt == 0)
390 : 48 : continue;
391 : :
392 : 871 : handle = dsm_control->item[i].handle;
1353 tmunro@postgresql.or 393 [ - + ]: 871 : if (is_main_region_dsm_handle(handle))
1353 tmunro@postgresql.or 394 :UBC 0 : continue;
395 : :
396 : : /* Log debugging information. */
3840 rhaas@postgresql.org 397 [ + + ]:CBC 871 : elog(DEBUG2, "cleaning up orphaned dynamic shared memory with ID %u",
398 : : handle);
399 : :
400 : : /* Destroy the segment. */
401 : 871 : 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 [ + + ]: 898 : elog(DEBUG2,
407 : : "cleaning up dynamic shared memory control segment with ID %u",
408 : : dsm_control_handle);
409 : 898 : dsm_control_address = dsm_control;
410 : 898 : 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 : 898 : dsm_control = dsm_control_address;
3659 414 : 898 : 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
3840 423 : 16712 : 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 : 16712 : dsm_init_done = true;
450 : 16712 : }
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
1353 tmunro@postgresql.or 470 : 2577 : dsm_estimate_size(void)
471 : : {
472 : 2577 : 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 : 898 : dsm_shmem_init(void)
480 : : {
481 : 898 : size_t size = dsm_estimate_size();
482 : : bool found;
483 : :
484 [ + - ]: 898 : if (size == 0)
485 : 898 : return;
486 : :
1353 tmunro@postgresql.or 487 :UBC 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 *
3314 rhaas@postgresql.org 516 :CBC 1498 : dsm_create(Size size, int flags)
517 : : {
518 : : dsm_segment *seg;
519 : : uint32 i;
520 : : uint32 nitems;
1353 tmunro@postgresql.or 521 : 1498 : size_t npages = 0;
522 : 1498 : size_t first_page = 0;
523 : 1498 : FreePageManager *dsm_main_space_fpm = dsm_main_space_begin;
524 : 1498 : 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 : : */
739 andres@anarazel.de 531 [ + + - + ]: 1498 : Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
532 : :
3840 rhaas@postgresql.org 533 [ + + ]: 1498 : if (!dsm_init_done)
534 : 794 : dsm_backend_startup();
535 : :
536 : : /* Create a new segment descriptor. */
3314 537 : 1498 : 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 : : */
1353 tmunro@postgresql.or 543 [ - + ]: 1498 : if (dsm_main_space_fpm)
544 : : {
1353 tmunro@postgresql.or 545 :UBC 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 : :
1353 tmunro@postgresql.or 561 [ + - ]:CBC 1498 : 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 [ - + ]: 1498 : if (dsm_main_space_fpm)
1353 tmunro@postgresql.or 568 :UBC 0 : LWLockRelease(DynamicSharedMemoryControlLock);
569 : : for (;;)
570 : : {
1353 tmunro@postgresql.or 571 [ + - - + ]:CBC 1498 : Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
572 : : /* Use even numbers only */
868 tgl@sss.pgh.pa.us 573 : 1498 : seg->handle = pg_prng_uint32(&pg_global_prng_state) << 1;
1353 tmunro@postgresql.or 574 [ - + ]: 1498 : if (seg->handle == DSM_HANDLE_INVALID) /* Reserve sentinel */
1353 tmunro@postgresql.or 575 :UBC 0 : continue;
1353 tmunro@postgresql.or 576 [ + - ]:CBC 1498 : if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
577 : : &seg->mapped_address, &seg->mapped_size, ERROR))
578 : 1498 : break;
579 : : }
580 : 1498 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
581 : : }
582 : :
583 : : /* Search the control segment for an unused slot. */
3840 rhaas@postgresql.org 584 : 1498 : nitems = dsm_control->nitems;
585 [ + + ]: 3670 : for (i = 0; i < nitems; ++i)
586 : : {
587 [ + + ]: 2752 : if (dsm_control->item[i].refcnt == 0)
588 : : {
1353 tmunro@postgresql.or 589 [ - + ]: 580 : if (using_main_dsm_region)
590 : : {
1353 tmunro@postgresql.or 591 :UBC 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
1353 tmunro@postgresql.or 596 [ - + ]:CBC 580 : Assert(!is_main_region_dsm_handle(seg->handle));
3840 rhaas@postgresql.org 597 : 580 : dsm_control->item[i].handle = seg->handle;
598 : : /* refcnt of 1 triggers destruction, so start at 2 */
599 : 580 : dsm_control->item[i].refcnt = 2;
2791 600 : 580 : dsm_control->item[i].impl_private_pm_handle = NULL;
601 : 580 : dsm_control->item[i].pinned = false;
3840 602 : 580 : seg->control_slot = i;
603 : 580 : LWLockRelease(DynamicSharedMemoryControlLock);
604 : 580 : return seg;
605 : : }
606 : : }
607 : :
608 : : /* Verify that we can support an additional mapping. */
609 [ - + ]: 918 : if (nitems >= dsm_control->maxitems)
610 : : {
1353 tmunro@postgresql.or 611 [ # # ]:UBC 0 : if (using_main_dsm_region)
612 : 0 : FreePageManagerPut(dsm_main_space_fpm, first_page, npages);
1534 613 : 0 : LWLockRelease(DynamicSharedMemoryControlLock);
1353 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);
1534 617 [ # # ]: 0 : if (seg->resowner != NULL)
618 : 0 : ResourceOwnerForgetDSM(seg->resowner, seg);
619 : 0 : dlist_delete(&seg->node);
620 : 0 : pfree(seg);
621 : :
3314 rhaas@postgresql.org 622 [ # # ]: 0 : if ((flags & DSM_CREATE_NULL_IF_MAXSEGMENTS) != 0)
623 : 0 : return NULL;
3840 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. */
1353 tmunro@postgresql.or 630 [ - + ]:CBC 918 : if (using_main_dsm_region)
631 : : {
1353 tmunro@postgresql.or 632 :UBC 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 : : }
3840 rhaas@postgresql.org 636 :CBC 918 : dsm_control->item[nitems].handle = seg->handle;
637 : : /* refcnt of 1 triggers destruction, so start at 2 */
638 : 918 : dsm_control->item[nitems].refcnt = 2;
2791 639 : 918 : dsm_control->item[nitems].impl_private_pm_handle = NULL;
640 : 918 : dsm_control->item[nitems].pinned = false;
3840 641 : 918 : seg->control_slot = nitems;
642 : 918 : dsm_control->nitems++;
643 : 918 : LWLockRelease(DynamicSharedMemoryControlLock);
644 : :
645 : 918 : 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 : 19056 : 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 [ - + ]: 19056 : Assert(IsUnderPostmaster);
674 : :
675 [ + + ]: 19056 : if (!dsm_init_done)
676 : 15918 : 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 [ + - + + ]: 24304 : dlist_foreach(iter, &dsm_segment_list)
689 : : {
690 : 5248 : seg = dlist_container(dsm_segment, node, iter.cur);
691 [ - + ]: 5248 : if (seg->handle == h)
3840 rhaas@postgresql.org 692 [ # # ]:UBC 0 : elog(ERROR, "can't attach the same segment more than once");
693 : : }
694 : :
695 : : /* Create a new segment descriptor. */
3840 rhaas@postgresql.org 696 :CBC 19056 : seg = dsm_create_descriptor();
697 : 19056 : seg->handle = h;
698 : :
699 : : /* Bump reference count for this segment in shared memory. */
700 : 19056 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
701 : 19056 : nitems = dsm_control->nitems;
702 [ + - ]: 26623 : 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 : : */
1885 tmunro@postgresql.or 711 [ + + ]: 26623 : if (dsm_control->item[i].refcnt <= 1)
3840 rhaas@postgresql.org 712 : 10 : continue;
713 : :
714 : : /* If the handle doesn't match, it's not the slot we want. */
3631 715 [ + + ]: 26613 : if (dsm_control->item[i].handle != seg->handle)
716 : 7557 : continue;
717 : :
718 : : /* Otherwise we've found a match. */
719 : 19056 : dsm_control->item[i].refcnt++;
720 : 19056 : seg->control_slot = i;
1353 tmunro@postgresql.or 721 [ - + ]: 19056 : if (is_main_region_dsm_handle(seg->handle))
722 : : {
1353 tmunro@postgresql.or 723 :UBC 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 : : }
3631 rhaas@postgresql.org 727 :CBC 19056 : break;
728 : : }
3840 729 : 19056 : 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 [ - + ]: 19056 : if (seg->control_slot == INVALID_CONTROL_SLOT)
738 : : {
3840 rhaas@postgresql.org 739 :UBC 0 : dsm_detach(seg);
740 : 0 : return NULL;
741 : : }
742 : :
743 : : /* Here's where we actually try to map the segment. */
1353 tmunro@postgresql.or 744 [ + - ]:CBC 19056 : if (!is_main_region_dsm_handle(seg->handle))
745 : 19056 : dsm_impl_op(DSM_OP_ATTACH, seg->handle, 0, &seg->impl_private,
746 : : &seg->mapped_address, &seg->mapped_size, ERROR);
747 : :
3840 rhaas@postgresql.org 748 : 19056 : 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
3770 757 : 38166 : dsm_backend_shutdown(void)
758 : : {
3840 759 [ + + ]: 38423 : while (!dlist_is_empty(&dsm_segment_list))
760 : : {
761 : : dsm_segment *seg;
762 : :
763 : 257 : seg = dlist_head_element(dsm_segment, node, &dsm_segment_list);
764 : 257 : dsm_detach(seg);
765 : : }
766 : 38166 : }
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
3680 rhaas@postgresql.org 775 :LBC (1) : dsm_detach_all(void)
776 : : {
3631 bruce@momjian.us 777 : (1) : void *control_address = dsm_control;
778 : :
3680 rhaas@postgresql.org 779 [ # # ]: (1) : while (!dlist_is_empty(&dsm_segment_list))
780 : : {
781 : : dsm_segment *seg;
782 : :
3680 rhaas@postgresql.org 783 :UBC 0 : seg = dlist_head_element(dsm_segment, node, &dsm_segment_list);
784 : 0 : dsm_detach(seg);
785 : : }
786 : :
3680 rhaas@postgresql.org 787 [ # # ]:LBC (1) : if (control_address != NULL)
788 : (1) : dsm_impl_op(DSM_OP_DETACH, dsm_control_handle, 0,
789 : : &dsm_control_impl_private, &control_address,
790 : : &dsm_control_mapped_size, ERROR);
791 : (1) : }
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
3840 rhaas@postgresql.org 803 :CBC 19970 : 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 : : */
1154 tmunro@postgresql.or 813 : 19970 : HOLD_INTERRUPTS();
3770 rhaas@postgresql.org 814 [ + + ]: 27658 : 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 : 7688 : node = slist_pop_head_node(&seg->on_detach);
822 : 7688 : cb = slist_container(dsm_segment_detach_callback, node, node);
823 : 7688 : function = cb->function;
824 : 7688 : arg = cb->arg;
825 : 7688 : pfree(cb);
826 : :
827 : 7688 : function(seg, arg);
828 : : }
1154 tmunro@postgresql.or 829 [ - + ]: 19970 : 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 : : */
3840 rhaas@postgresql.org 839 [ + - ]: 19970 : if (seg->mapped_address != NULL)
840 : : {
1353 tmunro@postgresql.or 841 [ + - ]: 19970 : if (!is_main_region_dsm_handle(seg->handle))
842 : 19970 : dsm_impl_op(DSM_OP_DETACH, seg->handle, 0, &seg->impl_private,
843 : : &seg->mapped_address, &seg->mapped_size, WARNING);
3840 rhaas@postgresql.org 844 : 19970 : seg->impl_private = NULL;
845 : 19970 : seg->mapped_address = NULL;
846 : 19970 : seg->mapped_size = 0;
847 : : }
848 : :
849 : : /* Reduce reference count, if we previously increased it. */
850 [ + - ]: 19970 : if (seg->control_slot != INVALID_CONTROL_SLOT)
851 : : {
852 : : uint32 refcnt;
3631 bruce@momjian.us 853 : 19970 : uint32 control_slot = seg->control_slot;
854 : :
3840 rhaas@postgresql.org 855 : 19970 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
856 [ - + ]: 19970 : Assert(dsm_control->item[control_slot].handle == seg->handle);
857 [ - + ]: 19970 : Assert(dsm_control->item[control_slot].refcnt > 1);
858 : 19970 : refcnt = --dsm_control->item[control_slot].refcnt;
859 : 19970 : seg->control_slot = INVALID_CONTROL_SLOT;
860 : 19970 : LWLockRelease(DynamicSharedMemoryControlLock);
861 : :
862 : : /* If new reference count is 1, try to destroy the segment. */
863 [ + + ]: 19970 : if (refcnt == 1)
864 : : {
865 : : /* A pinned segment should never reach 1. */
2791 866 [ - + ]: 512 : 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 : : */
1353 tmunro@postgresql.or 883 [ + - + - ]: 1024 : if (is_main_region_dsm_handle(seg->handle) ||
884 : 512 : dsm_impl_op(DSM_OP_DESTROY, seg->handle, 0, &seg->impl_private,
885 : : &seg->mapped_address, &seg->mapped_size, WARNING))
886 : : {
3840 rhaas@postgresql.org 887 : 512 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
1353 tmunro@postgresql.or 888 [ - + ]: 512 : if (is_main_region_dsm_handle(seg->handle))
1353 tmunro@postgresql.or 889 :UBC 0 : FreePageManagerPut((FreePageManager *) dsm_main_space_begin,
890 : 0 : dsm_control->item[control_slot].first_page,
891 : 0 : dsm_control->item[control_slot].npages);
3840 rhaas@postgresql.org 892 [ - + ]:CBC 512 : Assert(dsm_control->item[control_slot].handle == seg->handle);
893 [ - + ]: 512 : Assert(dsm_control->item[control_slot].refcnt == 1);
894 : 512 : dsm_control->item[control_slot].refcnt = 0;
895 : 512 : LWLockRelease(DynamicSharedMemoryControlLock);
896 : : }
897 : : }
898 : : }
899 : :
900 : : /* Clean up our remaining backend-private data structures. */
901 [ + + ]: 19970 : if (seg->resowner != NULL)
902 : 898 : ResourceOwnerForgetDSM(seg->resowner, seg);
903 : 19970 : dlist_delete(&seg->node);
904 : 19970 : pfree(seg);
905 : 19970 : }
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
3454 915 : 1512 : dsm_pin_mapping(dsm_segment *seg)
916 : : {
3840 917 [ + + ]: 1512 : if (seg->resowner != NULL)
918 : : {
919 : 1509 : ResourceOwnerForgetDSM(seg->resowner, seg);
920 : 1509 : seg->resowner = NULL;
921 : : }
922 : 1512 : }
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
3454 rhaas@postgresql.org 934 :UBC 0 : dsm_unpin_mapping(dsm_segment *seg)
935 : : {
936 [ # # ]: 0 : Assert(seg->resowner == NULL);
158 heikki.linnakangas@i 937 :UNC 0 : ResourceOwnerEnlarge(CurrentResourceOwner);
3454 rhaas@postgresql.org 938 :UBC 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
3454 rhaas@postgresql.org 955 :CBC 1008 : dsm_pin_segment(dsm_segment *seg)
956 : : {
175 tmunro@postgresql.or 957 : 1008 : 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 : : */
3688 rhaas@postgresql.org 965 : 1008 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
2791 966 [ - + ]: 1008 : if (dsm_control->item[seg->control_slot].pinned)
2791 rhaas@postgresql.org 967 [ # # ]:UBC 0 : elog(ERROR, "cannot pin a segment that is already pinned");
175 tmunro@postgresql.or 968 [ + - ]:CBC 1008 : if (!is_main_region_dsm_handle(seg->handle))
969 : 1008 : dsm_impl_pin_segment(seg->handle, seg->impl_private, &handle);
2791 rhaas@postgresql.org 970 : 1008 : dsm_control->item[seg->control_slot].pinned = true;
3688 971 : 1008 : dsm_control->item[seg->control_slot].refcnt++;
2791 972 : 1008 : dsm_control->item[seg->control_slot].impl_private_pm_handle = handle;
3688 973 : 1008 : LWLockRelease(DynamicSharedMemoryControlLock);
2791 974 : 1008 : }
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 : 139 : dsm_unpin_segment(dsm_handle handle)
989 : : {
990 : 139 : uint32 control_slot = INVALID_CONTROL_SLOT;
991 : 139 : bool destroy = false;
992 : : uint32 i;
993 : :
994 : : /* Find the control slot for the given handle. */
995 : 139 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
996 [ + - ]: 763 : for (i = 0; i < dsm_control->nitems; ++i)
997 : : {
998 : : /* Skip unused slots and segments that are concurrently going away. */
1882 tmunro@postgresql.or 999 [ + + ]: 763 : if (dsm_control->item[i].refcnt <= 1)
2791 rhaas@postgresql.org 1000 : 25 : continue;
1001 : :
1002 : : /* If we've found our handle, we can stop searching. */
1003 [ + + ]: 738 : if (dsm_control->item[i].handle == handle)
1004 : : {
1005 : 139 : control_slot = i;
1006 : 139 : 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 [ - + ]: 139 : if (control_slot == INVALID_CONTROL_SLOT)
2791 rhaas@postgresql.org 1016 [ # # ]:UBC 0 : elog(ERROR, "cannot unpin unknown segment handle");
2791 rhaas@postgresql.org 1017 [ - + ]:CBC 139 : if (!dsm_control->item[control_slot].pinned)
2791 rhaas@postgresql.org 1018 [ # # ]:UBC 0 : elog(ERROR, "cannot unpin a segment that is not pinned");
2791 rhaas@postgresql.org 1019 [ - + ]:CBC 139 : 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 : : */
175 tmunro@postgresql.or 1026 [ + - ]: 139 : if (!is_main_region_dsm_handle(handle))
1027 : 139 : dsm_impl_unpin_segment(handle,
1028 : 139 : &dsm_control->item[control_slot].impl_private_pm_handle);
1029 : :
1030 : : /* Note that 1 means no references (0 means unused slot). */
2791 rhaas@postgresql.org 1031 [ + + ]: 139 : if (--dsm_control->item[control_slot].refcnt == 1)
1032 : 116 : destroy = true;
1033 : 139 : dsm_control->item[control_slot].pinned = false;
1034 : :
1035 : : /* Now we can release the lock. */
1036 : 139 : LWLockRelease(DynamicSharedMemoryControlLock);
1037 : :
1038 : : /* Clean up resources if that was the last reference. */
1039 [ + + ]: 139 : if (destroy)
1040 : : {
1041 : 116 : void *junk_impl_private = NULL;
1042 : 116 : void *junk_mapped_address = NULL;
1043 : 116 : 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 : : */
1353 tmunro@postgresql.or 1055 [ + - + - ]: 232 : if (is_main_region_dsm_handle(handle) ||
1056 : 116 : dsm_impl_op(DSM_OP_DESTROY, handle, 0, &junk_impl_private,
1057 : : &junk_mapped_address, &junk_mapped_size, WARNING))
1058 : : {
2791 rhaas@postgresql.org 1059 : 116 : LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
1353 tmunro@postgresql.or 1060 [ - + ]: 116 : if (is_main_region_dsm_handle(handle))
1353 tmunro@postgresql.or 1061 :UBC 0 : FreePageManagerPut((FreePageManager *) dsm_main_space_begin,
1062 : 0 : dsm_control->item[control_slot].first_page,
1063 : 0 : dsm_control->item[control_slot].npages);
2791 rhaas@postgresql.org 1064 [ - + ]:CBC 116 : Assert(dsm_control->item[control_slot].handle == handle);
1065 [ - + ]: 116 : Assert(dsm_control->item[control_slot].refcnt == 1);
1066 : 116 : dsm_control->item[control_slot].refcnt = 0;
1067 : 116 : LWLockRelease(DynamicSharedMemoryControlLock);
1068 : : }
1069 : : }
3688 1070 : 139 : }
1071 : :
1072 : : /*
1073 : : * Find an existing mapping for a shared memory segment, if there is one.
1074 : : */
1075 : : dsm_segment *
573 pg@bowt.ie 1076 :GBC 9 : dsm_find_mapping(dsm_handle handle)
1077 : : {
1078 : : dlist_iter iter;
1079 : : dsm_segment *seg;
1080 : :
3840 rhaas@postgresql.org 1081 [ + - + + ]: 26 : dlist_foreach(iter, &dsm_segment_list)
1082 : : {
1083 : 17 : seg = dlist_container(dsm_segment, node, iter.cur);
573 pg@bowt.ie 1084 [ - + ]: 17 : if (seg->handle == handle)
3840 rhaas@postgresql.org 1085 :UBC 0 : return seg;
1086 : : }
1087 : :
3840 rhaas@postgresql.org 1088 :GBC 9 : return NULL;
1089 : : }
1090 : :
1091 : : /*
1092 : : * Get the address at which a dynamic shared memory segment is mapped.
1093 : : */
1094 : : void *
3840 rhaas@postgresql.org 1095 :CBC 20765 : dsm_segment_address(dsm_segment *seg)
1096 : : {
1097 [ - + ]: 20765 : Assert(seg->mapped_address != NULL);
1098 : 20765 : return seg->mapped_address;
1099 : : }
1100 : :
1101 : : /*
1102 : : * Get the size of a mapping.
1103 : : */
1104 : : Size
3840 rhaas@postgresql.org 1105 :UBC 0 : dsm_segment_map_length(dsm_segment *seg)
1106 : : {
1107 [ # # ]: 0 : 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
3840 rhaas@postgresql.org 1123 :CBC 1983 : dsm_segment_handle(dsm_segment *seg)
1124 : : {
1125 : 1983 : return seg->handle;
1126 : : }
1127 : :
1128 : : /*
1129 : : * Register an on-detach callback for a dynamic shared memory segment.
1130 : : */
1131 : : void
3770 1132 : 11553 : on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function, Datum arg)
1133 : : {
1134 : : dsm_segment_detach_callback *cb;
1135 : :
1136 : 11553 : cb = MemoryContextAlloc(TopMemoryContext,
1137 : : sizeof(dsm_segment_detach_callback));
1138 : 11553 : cb->function = function;
1139 : 11553 : cb->arg = arg;
1140 : 11553 : slist_push_head(&seg->on_detach, &cb->node);
1141 : 11553 : }
1142 : :
1143 : : /*
1144 : : * Unregister an on-detach callback for a dynamic shared memory segment.
1145 : : */
1146 : : void
1147 : 3840 : cancel_on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function,
1148 : : Datum arg)
1149 : : {
1150 : : slist_mutable_iter iter;
1151 : :
1152 [ + - + - : 12705 : slist_foreach_modify(iter, &seg->on_detach)
+ - ]
1153 : : {
1154 : : dsm_segment_detach_callback *cb;
1155 : :
1156 : 12705 : cb = slist_container(dsm_segment_detach_callback, node, iter.cur);
1157 [ + + + + ]: 12705 : if (cb->function == function && cb->arg == arg)
1158 : : {
1159 : 3840 : slist_delete_current(&iter);
1160 : 3840 : pfree(cb);
1161 : 3840 : break;
1162 : : }
1163 : : }
1164 : 3840 : }
1165 : :
1166 : : /*
1167 : : * Discard all registered on-detach callbacks without executing them.
1168 : : */
1169 : : void
3688 1170 : 19697 : reset_on_dsm_detach(void)
1171 : : {
1172 : : dlist_iter iter;
1173 : :
1174 [ + - - + ]: 19697 : dlist_foreach(iter, &dsm_segment_list)
1175 : : {
3688 rhaas@postgresql.org 1176 :UBC 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 : : }
3688 rhaas@postgresql.org 1195 :CBC 19697 : }
1196 : :
1197 : : /*
1198 : : * Create a segment descriptor.
1199 : : */
1200 : : static dsm_segment *
3840 1201 : 20554 : dsm_create_descriptor(void)
1202 : : {
1203 : : dsm_segment *seg;
1204 : :
2578 alvherre@alvh.no-ip. 1205 [ + + ]: 20554 : if (CurrentResourceOwner)
158 heikki.linnakangas@i 1206 :GNC 2407 : ResourceOwnerEnlarge(CurrentResourceOwner);
1207 : :
3840 rhaas@postgresql.org 1208 :CBC 20554 : seg = MemoryContextAlloc(TopMemoryContext, sizeof(dsm_segment));
1209 : 20554 : dlist_push_head(&dsm_segment_list, &seg->node);
1210 : :
1211 : : /* seg->handle must be initialized by the caller */
1212 : 20554 : seg->control_slot = INVALID_CONTROL_SLOT;
1213 : 20554 : seg->impl_private = NULL;
1214 : 20554 : seg->mapped_address = NULL;
1215 : 20554 : seg->mapped_size = 0;
1216 : :
1217 : 20554 : seg->resowner = CurrentResourceOwner;
2578 alvherre@alvh.no-ip. 1218 [ + + ]: 20554 : if (CurrentResourceOwner)
1219 : 2407 : ResourceOwnerRememberDSM(CurrentResourceOwner, seg);
1220 : :
3770 rhaas@postgresql.org 1221 : 20554 : slist_init(&seg->on_detach);
1222 : :
3840 1223 : 20554 : 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
3821 1237 : 901 : dsm_control_segment_sane(dsm_control_header *control, Size mapped_size)
1238 : : {
3840 1239 [ - + ]: 901 : if (mapped_size < offsetof(dsm_control_header, item))
3840 rhaas@postgresql.org 1240 :UBC 0 : return false; /* Mapped size too short to read header. */
3840 rhaas@postgresql.org 1241 [ - + ]:CBC 901 : if (control->magic != PG_DYNSHMEM_CONTROL_MAGIC)
3840 rhaas@postgresql.org 1242 :UBC 0 : return false; /* Magic number doesn't match. */
3840 rhaas@postgresql.org 1243 [ - + ]:CBC 901 : if (dsm_control_bytes_needed(control->maxitems) > mapped_size)
3840 rhaas@postgresql.org 1244 :UBC 0 : return false; /* Max item count won't fit in map. */
3840 rhaas@postgresql.org 1245 [ - + ]:CBC 901 : if (control->nitems > control->maxitems)
3840 rhaas@postgresql.org 1246 :UBC 0 : return false; /* Overfull. */
3840 rhaas@postgresql.org 1247 :CBC 901 : 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 : 1799 : dsm_control_bytes_needed(uint32 nitems)
1256 : : {
1257 : : return offsetof(dsm_control_header, item)
2489 tgl@sss.pgh.pa.us 1258 : 1799 : + sizeof(dsm_control_item) * (uint64) nitems;
1259 : : }
1260 : :
1261 : : static inline dsm_handle
1353 tmunro@postgresql.or 1262 :UBC 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;
868 tgl@sss.pgh.pa.us 1276 : 0 : handle |= pg_prng_uint32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
1353 tmunro@postgresql.or 1277 : 0 : return handle;
1278 : : }
1279 : :
1280 : : static inline bool
1353 tmunro@postgresql.or 1281 :CBC 61939 : is_main_region_dsm_handle(dsm_handle handle)
1282 : : {
1283 : 61939 : return handle & 1;
1284 : : }
1285 : :
1286 : : /* ResourceOwner callbacks */
1287 : :
1288 : : static void
158 heikki.linnakangas@i 1289 :UNC 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 : : }
|