1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1998,2008 Oracle. All rights reserved. 5 * 6 * This code is derived from software contributed to Sleepycat Software by 7 * Frederick G.M. Roeber of Netscape Communications Corp. 8 * 9 * $Id: os_vx_map.c,v 12.14 2008/01/30 16:42:21 bostic Exp $ 10 */ 11 12#include "db_config.h" 13 14#include "db_int.h" 15 16/* 17 * DB uses memory-mapped files for two things: 18 * faster access of read-only databases, and 19 * shared memory for process synchronization and locking. 20 * The code carefully does not mix the two uses. The first-case uses are 21 * actually written such that memory-mapping isn't really required -- it's 22 * merely a convenience -- so we don't have to worry much about it. In the 23 * second case, it's solely used as a shared memory mechanism, so that's 24 * all we have to replace. 25 * 26 * All memory in VxWorks is shared, and a task can allocate memory and keep 27 * notes. So I merely have to allocate memory, remember the "filename" for 28 * that memory, and issue small-integer segment IDs which index the list of 29 * these shared-memory segments. Subsequent opens are checked against the 30 * list of already open segments. 31 */ 32typedef struct { 33 void *segment; /* Segment address. */ 34 u_int32_t size; /* Segment size. */ 35 char *name; /* Segment name. */ 36 long segid; /* Segment ID. */ 37} os_segdata_t; 38 39static os_segdata_t *__os_segdata; /* Segment table. */ 40static int __os_segdata_size; /* Segment table size. */ 41 42#define OS_SEGDATA_STARTING_SIZE 16 43#define OS_SEGDATA_INCREMENT 16 44 45static int __os_segdata_allocate 46 __P((ENV *, const char *, REGINFO *, REGION *)); 47static int __os_segdata_find_byname 48 __P((ENV *, const char *, REGINFO *, REGION *)); 49static int __os_segdata_init __P((ENV *)); 50static int __os_segdata_new __P((ENV *, int *)); 51static int __os_segdata_release __P((ENV *, REGION *, int)); 52 53/* 54 * __os_attach -- 55 * Create/join a shared memory region. 56 */ 57int 58__os_attach(env, infop, rp) 59 ENV *env; 60 REGINFO *infop; 61 REGION *rp; 62{ 63 DB_ENV *dbenv; 64 int ret; 65 66 dbenv = env->dbenv; 67 68 if (__os_segdata == NULL) 69 __os_segdata_init(env); 70 71 DB_BEGIN_SINGLE_THREAD; 72 73 /* Try to find an already existing segment. */ 74 ret = __os_segdata_find_byname(env, infop->name, infop, rp); 75 76 /* 77 * If we are trying to join a region, it is easy, either we 78 * found it and we return, or we didn't find it and we return 79 * an error that it doesn't exist. 80 */ 81 if (!F_ISSET(infop, REGION_CREATE)) { 82 if (ret != 0) { 83 __db_errx(env, "segment %s does not exist", 84 infop->name); 85 ret = EAGAIN; 86 } 87 goto out; 88 } 89 90 /* 91 * If we get here, we are trying to create the region. 92 * There are several things to consider: 93 * - if we have an error (not a found or not-found value), return. 94 * - they better have shm_key set. 95 * - if the region is already there (ret == 0 from above), 96 * assume the application crashed and we're restarting. 97 * Delete the old region. 98 * - try to create the region. 99 */ 100 if (ret != 0 && ret != ENOENT) 101 goto out; 102 103 if (dbenv->shm_key == INVALID_REGION_SEGID) { 104 __db_errx(env, "no base shared memory ID specified"); 105 ret = EAGAIN; 106 goto out; 107 } 108 if (ret == 0 && __os_segdata_release(env, rp, 1) != 0) { 109 __db_errx(env, 110 "key: %ld: shared memory region already exists", 111 dbenv->shm_key + (infop->id - 1)); 112 ret = EAGAIN; 113 goto out; 114 } 115 116 ret = __os_segdata_allocate(env, infop->name, infop, rp); 117out: 118 DB_END_SINGLE_THREAD; 119 return (ret); 120} 121 122/* 123 * __os_detach -- 124 * Detach from a shared region. 125 */ 126int 127__os_detach(env, infop, destroy) 128 ENV *env; 129 REGINFO *infop; 130 int destroy; 131{ 132 /* 133 * If just detaching, there is no mapping to discard. 134 * If destroying, remove the region. 135 */ 136 if (destroy) 137 return (__os_segdata_release(env, infop->rp, 0)); 138 return (0); 139} 140 141/* 142 * __os_mapfile -- 143 * Map in a shared memory file. 144 */ 145int 146__os_mapfile(env, path, fhp, len, is_rdonly, addrp) 147 ENV *env; 148 char *path; 149 DB_FH *fhp; 150 int is_rdonly; 151 size_t len; 152 void **addrp; 153{ 154 /* We cannot map in regular files in VxWorks. */ 155 COMPQUIET(env, NULL); 156 COMPQUIET(path, NULL); 157 COMPQUIET(fhp, NULL); 158 COMPQUIET(is_rdonly, 0); 159 COMPQUIET(len, 0); 160 COMPQUIET(addrp, NULL); 161 return (DB_OPNOTSUP); 162} 163 164/* 165 * __os_unmapfile -- 166 * Unmap the shared memory file. 167 */ 168int 169__os_unmapfile(env, addr, len) 170 ENV *env; 171 void *addr; 172 size_t len; 173{ 174 /* We cannot map in regular files in VxWorks. */ 175 COMPQUIET(env, NULL); 176 COMPQUIET(addr, NULL); 177 COMPQUIET(len, 0); 178 return (DB_OPNOTSUP); 179} 180 181/* 182 * __os_segdata_init -- 183 * Initializes the library's table of shared memory segments. 184 * Called once on the first time through __os_segdata_new(). 185 */ 186static int 187__os_segdata_init(env) 188 ENV *env; 189{ 190 int ret; 191 192 if (__os_segdata != NULL) { 193 __db_errx(env, "shared memory segment already exists"); 194 return (EEXIST); 195 } 196 197 /* 198 * The lock init call returns a locked lock. 199 */ 200 DB_BEGIN_SINGLE_THREAD; 201 __os_segdata_size = OS_SEGDATA_STARTING_SIZE; 202 ret = __os_calloc(env, 203 __os_segdata_size, sizeof(os_segdata_t), &__os_segdata); 204 DB_END_SINGLE_THREAD; 205 return (ret); 206} 207 208/* 209 * __os_segdata_destroy -- 210 * Destroys the library's table of shared memory segments. It also 211 * frees all linked data: the segments themselves, and their names. 212 * Currently not called. This function should be called if the 213 * user creates a function to unload or shutdown. 214 */ 215int 216__os_segdata_destroy(env) 217 ENV *env; 218{ 219 os_segdata_t *p; 220 int i; 221 222 if (__os_segdata == NULL) 223 return (0); 224 225 DB_BEGIN_SINGLE_THREAD; 226 for (i = 0; i < __os_segdata_size; i++) { 227 p = &__os_segdata[i]; 228 if (p->name != NULL) { 229 __os_free(env, p->name); 230 p->name = NULL; 231 } 232 if (p->segment != NULL) { 233 __os_free(env, p->segment); 234 p->segment = NULL; 235 } 236 p->size = 0; 237 } 238 239 __os_free(env, __os_segdata); 240 __os_segdata = NULL; 241 __os_segdata_size = 0; 242 DB_END_SINGLE_THREAD; 243 244 return (0); 245} 246 247/* 248 * __os_segdata_allocate -- 249 * Creates a new segment of the specified size, optionally with the 250 * specified name. 251 * 252 * Assumes it is called with the SEGDATA lock taken. 253 */ 254static int 255__os_segdata_allocate(env, name, infop, rp) 256 ENV *env; 257 const char *name; 258 REGINFO *infop; 259 REGION *rp; 260{ 261 DB_ENV *dbenv; 262 os_segdata_t *p; 263 int id, ret; 264 265 dbenv = env->dbenv; 266 267 if ((ret = __os_segdata_new(env, &id)) != 0) 268 return (ret); 269 270 p = &__os_segdata[id]; 271 if ((ret = __os_calloc(env, 1, rp->size, &p->segment)) != 0) 272 return (ret); 273 if ((ret = __os_strdup(env, name, &p->name)) != 0) { 274 __os_free(env, p->segment); 275 p->segment = NULL; 276 return (ret); 277 } 278 p->size = rp->size; 279 p->segid = dbenv->shm_key + infop->id - 1; 280 281 infop->addr = p->segment; 282 rp->segid = id; 283 284 return (0); 285} 286 287/* 288 * __os_segdata_new -- 289 * Finds a new segdata slot. Does not initialise it, so the fd returned 290 * is only valid until you call this again. 291 * 292 * Assumes it is called with the SEGDATA lock taken. 293 */ 294static int 295__os_segdata_new(env, segidp) 296 ENV *env; 297 int *segidp; 298{ 299 os_segdata_t *p; 300 int i, newsize, ret; 301 302 if (__os_segdata == NULL) { 303 __db_errx(env, "shared memory segment not initialized"); 304 return (EAGAIN); 305 } 306 307 for (i = 0; i < __os_segdata_size; i++) { 308 p = &__os_segdata[i]; 309 if (p->segment == NULL) { 310 *segidp = i; 311 return (0); 312 } 313 } 314 315 /* 316 * No more free slots, expand. 317 */ 318 newsize = __os_segdata_size + OS_SEGDATA_INCREMENT; 319 if ((ret = __os_realloc(env, newsize * sizeof(os_segdata_t), 320 &__os_segdata)) != 0) 321 return (ret); 322 memset(&__os_segdata[__os_segdata_size], 323 0, OS_SEGDATA_INCREMENT * sizeof(os_segdata_t)); 324 325 *segidp = __os_segdata_size; 326 __os_segdata_size = newsize; 327 328 return (0); 329} 330 331/* 332 * __os_segdata_find_byname -- 333 * Finds a segment by its name and shm_key. 334 * 335 * Assumes it is called with the SEGDATA lock taken. 336 */ 337static int 338__os_segdata_find_byname(env, name, infop, rp) 339 ENV *env; 340 const char *name; 341 REGINFO *infop; 342 REGION *rp; 343{ 344 DB_ENV *dbenv; 345 os_segdata_t *p; 346 long segid; 347 int i; 348 349 dbenv = env->dbenv; 350 351 if (__os_segdata == NULL) { 352 __db_errx(env, "shared memory segment not initialized"); 353 return (EAGAIN); 354 } 355 356 if (name == NULL) { 357 __db_errx(env, "no segment name given"); 358 return (EAGAIN); 359 } 360 361 /* 362 * If we are creating the region, compute the segid. 363 * If we are joining the region, we use the segid in the 364 * index we are given. 365 */ 366 if (F_ISSET(infop, REGION_CREATE)) 367 segid = dbenv->shm_key + (infop->id - 1); 368 else { 369 if (rp->segid >= __os_segdata_size || 370 rp->segid == INVALID_REGION_SEGID) { 371 __db_errx(env, "Invalid segment id given"); 372 return (EAGAIN); 373 } 374 segid = __os_segdata[rp->segid].segid; 375 } 376 for (i = 0; i < __os_segdata_size; i++) { 377 p = &__os_segdata[i]; 378 if (p->name != NULL && strcmp(name, p->name) == 0 && 379 p->segid == segid) { 380 infop->addr = p->segment; 381 rp->segid = i; 382 return (0); 383 } 384 } 385 return (ENOENT); 386} 387 388/* 389 * __os_segdata_release -- 390 * Free a segdata entry. 391 */ 392static int 393__os_segdata_release(env, rp, is_locked) 394 ENV *env; 395 REGION *rp; 396 int is_locked; 397{ 398 os_segdata_t *p; 399 400 if (__os_segdata == NULL) { 401 __db_errx(env, "shared memory segment not initialized"); 402 return (EAGAIN); 403 } 404 405 if (rp->segid < 0 || rp->segid >= __os_segdata_size) { 406 __db_errx(env, "segment id %ld out of range", rp->segid); 407 return (EINVAL); 408 } 409 410 if (is_locked == 0) 411 DB_BEGIN_SINGLE_THREAD; 412 p = &__os_segdata[rp->segid]; 413 if (p->name != NULL) { 414 __os_free(env, p->name); 415 p->name = NULL; 416 } 417 if (p->segment != NULL) { 418 __os_free(env, p->segment); 419 p->segment = NULL; 420 } 421 p->size = 0; 422 if (is_locked == 0) 423 DB_END_SINGLE_THREAD; 424 425 /* Any shrink-table logic could go here */ 426 427 return (0); 428} 429