Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * dsm_registry.c
4 : : * Functions for interfacing with the dynamic shared memory registry.
5 : : *
6 : : * This provides a way for libraries to use shared memory without needing
7 : : * to request it at startup time via a shmem_request_hook. The registry
8 : : * stores dynamic shared memory (DSM) segment handles keyed by a
9 : : * library-specified string.
10 : : *
11 : : * The registry is accessed by calling GetNamedDSMSegment(). If a segment
12 : : * with the provided name does not yet exist, it is created and initialized
13 : : * with the provided init_callback callback function. Otherwise,
14 : : * GetNamedDSMSegment() simply ensures that the segment is attached to the
15 : : * current backend. This function guarantees that only one backend
16 : : * initializes the segment and that all other backends just attach it.
17 : : *
18 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
19 : : * Portions Copyright (c) 1994, Regents of the University of California
20 : : *
21 : : * IDENTIFICATION
22 : : * src/backend/storage/ipc/dsm_registry.c
23 : : *
24 : : *-------------------------------------------------------------------------
25 : : */
26 : :
27 : : #include "postgres.h"
28 : :
29 : : #include "lib/dshash.h"
30 : : #include "storage/dsm_registry.h"
31 : : #include "storage/lwlock.h"
32 : : #include "storage/shmem.h"
33 : : #include "utils/memutils.h"
34 : :
35 : : typedef struct DSMRegistryCtxStruct
36 : : {
37 : : dsa_handle dsah;
38 : : dshash_table_handle dshh;
39 : : } DSMRegistryCtxStruct;
40 : :
41 : : static DSMRegistryCtxStruct *DSMRegistryCtx;
42 : :
43 : : typedef struct DSMRegistryEntry
44 : : {
45 : : char name[64];
46 : : dsm_handle handle;
47 : : size_t size;
48 : : } DSMRegistryEntry;
49 : :
50 : : static const dshash_parameters dsh_params = {
51 : : offsetof(DSMRegistryEntry, handle),
52 : : sizeof(DSMRegistryEntry),
53 : : dshash_strcmp,
54 : : dshash_strhash,
55 : : dshash_strcpy,
56 : : LWTRANCHE_DSM_REGISTRY_HASH
57 : : };
58 : :
59 : : static dsa_area *dsm_registry_dsa;
60 : : static dshash_table *dsm_registry_table;
61 : :
62 : : Size
86 nathan@postgresql.or 63 :GNC 2577 : DSMRegistryShmemSize(void)
64 : : {
65 : 2577 : return MAXALIGN(sizeof(DSMRegistryCtxStruct));
66 : : }
67 : :
68 : : void
69 : 898 : DSMRegistryShmemInit(void)
70 : : {
71 : : bool found;
72 : :
73 : 898 : DSMRegistryCtx = (DSMRegistryCtxStruct *)
74 : 898 : ShmemInitStruct("DSM Registry Data",
75 : : DSMRegistryShmemSize(),
76 : : &found);
77 : :
78 [ + - ]: 898 : if (!found)
79 : : {
80 : 898 : DSMRegistryCtx->dsah = DSA_HANDLE_INVALID;
81 : 898 : DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID;
82 : : }
83 : 898 : }
84 : :
85 : : /*
86 : : * Initialize or attach to the dynamic shared hash table that stores the DSM
87 : : * registry entries, if not already done. This must be called before accessing
88 : : * the table.
89 : : */
90 : : static void
91 : 16 : init_dsm_registry(void)
92 : : {
93 : : /* Quick exit if we already did this. */
94 [ - + ]: 16 : if (dsm_registry_table)
86 nathan@postgresql.or 95 :UNC 0 : return;
96 : :
97 : : /* Otherwise, use a lock to ensure only one process creates the table. */
86 nathan@postgresql.or 98 :GNC 16 : LWLockAcquire(DSMRegistryLock, LW_EXCLUSIVE);
99 : :
100 [ + + ]: 16 : if (DSMRegistryCtx->dshh == DSHASH_HANDLE_INVALID)
101 : : {
102 : : /* Initialize dynamic shared hash table for registry. */
103 : 7 : dsm_registry_dsa = dsa_create(LWTRANCHE_DSM_REGISTRY_DSA);
104 : 7 : dsa_pin(dsm_registry_dsa);
105 : 7 : dsa_pin_mapping(dsm_registry_dsa);
106 : 7 : dsm_registry_table = dshash_create(dsm_registry_dsa, &dsh_params, NULL);
107 : :
108 : : /* Store handles in shared memory for other backends to use. */
109 : 7 : DSMRegistryCtx->dsah = dsa_get_handle(dsm_registry_dsa);
110 : 7 : DSMRegistryCtx->dshh = dshash_get_hash_table_handle(dsm_registry_table);
111 : : }
112 : : else
113 : : {
114 : : /* Attach to existing dynamic shared hash table. */
115 : 9 : dsm_registry_dsa = dsa_attach(DSMRegistryCtx->dsah);
116 : 9 : dsa_pin_mapping(dsm_registry_dsa);
117 : 9 : dsm_registry_table = dshash_attach(dsm_registry_dsa, &dsh_params,
118 : 9 : DSMRegistryCtx->dshh, NULL);
119 : : }
120 : :
121 : 16 : LWLockRelease(DSMRegistryLock);
122 : : }
123 : :
124 : : /*
125 : : * Initialize or attach a named DSM segment.
126 : : *
127 : : * This routine returns the address of the segment. init_callback is called to
128 : : * initialize the segment when it is first created.
129 : : */
130 : : void *
131 : 16 : GetNamedDSMSegment(const char *name, size_t size,
132 : : void (*init_callback) (void *ptr), bool *found)
133 : : {
134 : : DSMRegistryEntry *entry;
135 : : MemoryContext oldcontext;
136 : : void *ret;
137 : :
138 [ - + ]: 16 : Assert(found);
139 : :
140 [ + - - + ]: 16 : if (!name || *name == '\0')
86 nathan@postgresql.or 141 [ # # ]:UNC 0 : ereport(ERROR,
142 : : (errmsg("DSM segment name cannot be empty")));
143 : :
86 nathan@postgresql.or 144 [ - + ]:GNC 16 : if (strlen(name) >= offsetof(DSMRegistryEntry, handle))
86 nathan@postgresql.or 145 [ # # ]:UNC 0 : ereport(ERROR,
146 : : (errmsg("DSM segment name too long")));
147 : :
86 nathan@postgresql.or 148 [ - + ]:GNC 16 : if (size == 0)
86 nathan@postgresql.or 149 [ # # ]:UNC 0 : ereport(ERROR,
150 : : (errmsg("DSM segment size must be nonzero")));
151 : :
152 : : /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
86 nathan@postgresql.or 153 :GNC 16 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
154 : :
155 : : /* Connect to the registry. */
156 : 16 : init_dsm_registry();
157 : :
48 158 : 16 : entry = dshash_find_or_insert(dsm_registry_table, name, found);
86 159 [ + + ]: 16 : if (!(*found))
160 : : {
161 : : /* Initialize the segment. */
162 : 7 : dsm_segment *seg = dsm_create(size, 0);
163 : :
164 : 7 : dsm_pin_segment(seg);
165 : 7 : dsm_pin_mapping(seg);
166 : 7 : entry->handle = dsm_segment_handle(seg);
167 : 7 : entry->size = size;
168 : 7 : ret = dsm_segment_address(seg);
169 : :
170 [ + - ]: 7 : if (init_callback)
171 : 7 : (*init_callback) (ret);
172 : : }
173 [ - + ]: 9 : else if (entry->size != size)
174 : : {
86 nathan@postgresql.or 175 [ # # ]:UNC 0 : ereport(ERROR,
176 : : (errmsg("requested DSM segment size does not match size of "
177 : : "existing segment")));
178 : : }
179 : : else
180 : : {
83 nathan@postgresql.or 181 :GNC 9 : dsm_segment *seg = dsm_find_mapping(entry->handle);
182 : :
183 : : /* If the existing segment is not already attached, attach it now. */
184 [ + - ]: 9 : if (seg == NULL)
185 : : {
186 : 9 : seg = dsm_attach(entry->handle);
187 [ - + ]: 9 : if (seg == NULL)
83 nathan@postgresql.or 188 [ # # ]:UNC 0 : elog(ERROR, "could not map dynamic shared memory segment");
189 : :
83 nathan@postgresql.or 190 :GNC 9 : dsm_pin_mapping(seg);
191 : : }
192 : :
86 193 : 9 : ret = dsm_segment_address(seg);
194 : : }
195 : :
196 : 16 : dshash_release_lock(dsm_registry_table, entry);
197 : 16 : MemoryContextSwitchTo(oldcontext);
198 : :
199 : 16 : return ret;
200 : : }
|