|
| 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 | +typedefstructDSMRegistryCtxStruct |
| 36 | +{ |
| 37 | +dsa_handledsah; |
| 38 | +dshash_table_handledshh; |
| 39 | +}DSMRegistryCtxStruct; |
| 40 | + |
| 41 | +staticDSMRegistryCtxStruct*DSMRegistryCtx; |
| 42 | + |
| 43 | +typedefstructDSMRegistryEntry |
| 44 | +{ |
| 45 | +charname[64]; |
| 46 | +dsm_handlehandle; |
| 47 | +size_tsize; |
| 48 | +}DSMRegistryEntry; |
| 49 | + |
| 50 | +staticconstdshash_parametersdsh_params= { |
| 51 | +offsetof(DSMRegistryEntry,handle), |
| 52 | +sizeof(DSMRegistryEntry), |
| 53 | +dshash_memcmp, |
| 54 | +dshash_memhash, |
| 55 | +LWTRANCHE_DSM_REGISTRY_HASH |
| 56 | +}; |
| 57 | + |
| 58 | +staticdsa_area*dsm_registry_dsa; |
| 59 | +staticdshash_table*dsm_registry_table; |
| 60 | + |
| 61 | +Size |
| 62 | +DSMRegistryShmemSize(void) |
| 63 | +{ |
| 64 | +returnMAXALIGN(sizeof(DSMRegistryCtxStruct)); |
| 65 | +} |
| 66 | + |
| 67 | +void |
| 68 | +DSMRegistryShmemInit(void) |
| 69 | +{ |
| 70 | +boolfound; |
| 71 | + |
| 72 | +DSMRegistryCtx= (DSMRegistryCtxStruct*) |
| 73 | +ShmemInitStruct("DSM Registry Data", |
| 74 | +DSMRegistryShmemSize(), |
| 75 | +&found); |
| 76 | + |
| 77 | +if (!found) |
| 78 | +{ |
| 79 | +DSMRegistryCtx->dsah=DSA_HANDLE_INVALID; |
| 80 | +DSMRegistryCtx->dshh=DSHASH_HANDLE_INVALID; |
| 81 | +} |
| 82 | +} |
| 83 | + |
| 84 | +/* |
| 85 | + * Initialize or attach to the dynamic shared hash table that stores the DSM |
| 86 | + * registry entries, if not already done. This must be called before accessing |
| 87 | + * the table. |
| 88 | + */ |
| 89 | +staticvoid |
| 90 | +init_dsm_registry(void) |
| 91 | +{ |
| 92 | +/* Quick exit if we already did this. */ |
| 93 | +if (dsm_registry_table) |
| 94 | +return; |
| 95 | + |
| 96 | +/* Otherwise, use a lock to ensure only one process creates the table. */ |
| 97 | +LWLockAcquire(DSMRegistryLock,LW_EXCLUSIVE); |
| 98 | + |
| 99 | +if (DSMRegistryCtx->dshh==DSHASH_HANDLE_INVALID) |
| 100 | +{ |
| 101 | +/* Initialize dynamic shared hash table for registry. */ |
| 102 | +dsm_registry_dsa=dsa_create(LWTRANCHE_DSM_REGISTRY_DSA); |
| 103 | +dsa_pin(dsm_registry_dsa); |
| 104 | +dsa_pin_mapping(dsm_registry_dsa); |
| 105 | +dsm_registry_table=dshash_create(dsm_registry_dsa,&dsh_params,NULL); |
| 106 | + |
| 107 | +/* Store handles in shared memory for other backends to use. */ |
| 108 | +DSMRegistryCtx->dsah=dsa_get_handle(dsm_registry_dsa); |
| 109 | +DSMRegistryCtx->dshh=dshash_get_hash_table_handle(dsm_registry_table); |
| 110 | +} |
| 111 | +else |
| 112 | +{ |
| 113 | +/* Attach to existing dynamic shared hash table. */ |
| 114 | +dsm_registry_dsa=dsa_attach(DSMRegistryCtx->dsah); |
| 115 | +dsa_pin_mapping(dsm_registry_dsa); |
| 116 | +dsm_registry_table=dshash_attach(dsm_registry_dsa,&dsh_params, |
| 117 | +DSMRegistryCtx->dshh,NULL); |
| 118 | +} |
| 119 | + |
| 120 | +LWLockRelease(DSMRegistryLock); |
| 121 | +} |
| 122 | + |
| 123 | +/* |
| 124 | + * Initialize or attach a named DSM segment. |
| 125 | + * |
| 126 | + * This routine returns the address of the segment. init_callback is called to |
| 127 | + * initialize the segment when it is first created. |
| 128 | + */ |
| 129 | +void* |
| 130 | +GetNamedDSMSegment(constchar*name,size_tsize, |
| 131 | +void (*init_callback) (void*ptr),bool*found) |
| 132 | +{ |
| 133 | +DSMRegistryEntry*entry; |
| 134 | +MemoryContextoldcontext; |
| 135 | +charname_padded[offsetof(DSMRegistryEntry,handle)]= {0}; |
| 136 | +void*ret; |
| 137 | + |
| 138 | +Assert(found); |
| 139 | + |
| 140 | +if (!name||*name=='\0') |
| 141 | +ereport(ERROR, |
| 142 | +(errmsg("DSM segment name cannot be empty"))); |
| 143 | + |
| 144 | +if (strlen(name) >= offsetof(DSMRegistryEntry,handle)) |
| 145 | +ereport(ERROR, |
| 146 | +(errmsg("DSM segment name too long"))); |
| 147 | + |
| 148 | +if (size==0) |
| 149 | +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 | +oldcontext=MemoryContextSwitchTo(TopMemoryContext); |
| 154 | + |
| 155 | +/* Connect to the registry. */ |
| 156 | +init_dsm_registry(); |
| 157 | + |
| 158 | +strcpy(name_padded,name); |
| 159 | +entry=dshash_find_or_insert(dsm_registry_table,name_padded,found); |
| 160 | +if (!(*found)) |
| 161 | +{ |
| 162 | +/* Initialize the segment. */ |
| 163 | +dsm_segment*seg=dsm_create(size,0); |
| 164 | + |
| 165 | +dsm_pin_segment(seg); |
| 166 | +dsm_pin_mapping(seg); |
| 167 | +entry->handle=dsm_segment_handle(seg); |
| 168 | +entry->size=size; |
| 169 | +ret=dsm_segment_address(seg); |
| 170 | + |
| 171 | +if (init_callback) |
| 172 | +(*init_callback) (ret); |
| 173 | +} |
| 174 | +elseif (entry->size!=size) |
| 175 | +{ |
| 176 | +ereport(ERROR, |
| 177 | +(errmsg("requested DSM segment size does not match size of " |
| 178 | +"existing segment"))); |
| 179 | +} |
| 180 | +elseif (!dsm_find_mapping(entry->handle)) |
| 181 | +{ |
| 182 | +/* Attach to existing segment. */ |
| 183 | +dsm_segment*seg=dsm_attach(entry->handle); |
| 184 | + |
| 185 | +dsm_pin_mapping(seg); |
| 186 | +ret=dsm_segment_address(seg); |
| 187 | +} |
| 188 | +else |
| 189 | +{ |
| 190 | +/* Return address of an already-attached segment. */ |
| 191 | +ret=dsm_segment_address(dsm_find_mapping(entry->handle)); |
| 192 | +} |
| 193 | + |
| 194 | +dshash_release_lock(dsm_registry_table,entry); |
| 195 | +MemoryContextSwitchTo(oldcontext); |
| 196 | + |
| 197 | +returnret; |
| 198 | +} |