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 63 5436 : DSMRegistryShmemSize(void) 64 : { 65 5436 : return MAXALIGN(sizeof(DSMRegistryCtxStruct)); 66 : } 67 : 68 : void 69 1902 : DSMRegistryShmemInit(void) 70 : { 71 : bool found; 72 : 73 1902 : DSMRegistryCtx = (DSMRegistryCtxStruct *) 74 1902 : ShmemInitStruct("DSM Registry Data", 75 : DSMRegistryShmemSize(), 76 : &found); 77 : 78 1902 : if (!found) 79 : { 80 1902 : DSMRegistryCtx->dsah = DSA_HANDLE_INVALID; 81 1902 : DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID; 82 : } 83 1902 : } 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 44 : init_dsm_registry(void) 92 : { 93 : /* Quick exit if we already did this. */ 94 44 : if (dsm_registry_table) 95 0 : return; 96 : 97 : /* Otherwise, use a lock to ensure only one process creates the table. */ 98 44 : LWLockAcquire(DSMRegistryLock, LW_EXCLUSIVE); 99 : 100 44 : if (DSMRegistryCtx->dshh == DSHASH_HANDLE_INVALID) 101 : { 102 : /* Initialize dynamic shared hash table for registry. */ 103 18 : dsm_registry_dsa = dsa_create(LWTRANCHE_DSM_REGISTRY_DSA); 104 18 : dsa_pin(dsm_registry_dsa); 105 18 : dsa_pin_mapping(dsm_registry_dsa); 106 18 : dsm_registry_table = dshash_create(dsm_registry_dsa, &dsh_params, NULL); 107 : 108 : /* Store handles in shared memory for other backends to use. */ 109 18 : DSMRegistryCtx->dsah = dsa_get_handle(dsm_registry_dsa); 110 18 : DSMRegistryCtx->dshh = dshash_get_hash_table_handle(dsm_registry_table); 111 : } 112 : else 113 : { 114 : /* Attach to existing dynamic shared hash table. */ 115 26 : dsm_registry_dsa = dsa_attach(DSMRegistryCtx->dsah); 116 26 : dsa_pin_mapping(dsm_registry_dsa); 117 26 : dsm_registry_table = dshash_attach(dsm_registry_dsa, &dsh_params, 118 26 : DSMRegistryCtx->dshh, NULL); 119 : } 120 : 121 44 : 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 44 : 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 : Assert(found); 139 : 140 44 : if (!name || *name == '\0') 141 0 : ereport(ERROR, 142 : (errmsg("DSM segment name cannot be empty"))); 143 : 144 44 : if (strlen(name) >= offsetof(DSMRegistryEntry, handle)) 145 0 : ereport(ERROR, 146 : (errmsg("DSM segment name too long"))); 147 : 148 44 : if (size == 0) 149 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. */ 153 44 : oldcontext = MemoryContextSwitchTo(TopMemoryContext); 154 : 155 : /* Connect to the registry. */ 156 44 : init_dsm_registry(); 157 : 158 44 : entry = dshash_find_or_insert(dsm_registry_table, name, found); 159 44 : if (!(*found)) 160 : { 161 : /* Initialize the segment. */ 162 18 : dsm_segment *seg = dsm_create(size, 0); 163 : 164 18 : dsm_pin_segment(seg); 165 18 : dsm_pin_mapping(seg); 166 18 : entry->handle = dsm_segment_handle(seg); 167 18 : entry->size = size; 168 18 : ret = dsm_segment_address(seg); 169 : 170 18 : if (init_callback) 171 18 : (*init_callback) (ret); 172 : } 173 26 : else if (entry->size != size) 174 : { 175 0 : ereport(ERROR, 176 : (errmsg("requested DSM segment size does not match size of " 177 : "existing segment"))); 178 : } 179 : else 180 : { 181 26 : dsm_segment *seg = dsm_find_mapping(entry->handle); 182 : 183 : /* If the existing segment is not already attached, attach it now. */ 184 26 : if (seg == NULL) 185 : { 186 26 : seg = dsm_attach(entry->handle); 187 26 : if (seg == NULL) 188 0 : elog(ERROR, "could not map dynamic shared memory segment"); 189 : 190 26 : dsm_pin_mapping(seg); 191 : } 192 : 193 26 : ret = dsm_segment_address(seg); 194 : } 195 : 196 44 : dshash_release_lock(dsm_registry_table, entry); 197 44 : MemoryContextSwitchTo(oldcontext); 198 : 199 44 : return ret; 200 : }