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