Coverage Report

Created: 2026-09-28 06:55

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