1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <assert.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include "nscd_db.h" 30 #include "nscd_log.h" 31 32 /* 33 * Access control structure for a piece of nscd data. This structure 34 * is always tagged before the nscd data. nscd_alloc, which should 35 * be used to allocate memory that requires access control or usage 36 * count control, will initialize this access control structure at the 37 * start of the memory returned to the caller. 38 */ 39 struct nscd_access_s { 40 void *data; /* addr of real data */ 41 void (*free_func)(nscd_acc_data_t *data); /* destructor */ 42 mutex_t mutex; /* protect this structure */ 43 mutex_t *data_mutex; 44 rwlock_t *data_rwlock; 45 cond_t *data_cond; 46 int nUse; /* usage count */ 47 int type; 48 int delete; /* no longer available */ 49 nscd_seq_num_t seq_num; /* sequence number */ 50 }; 51 52 /* size should be in multiple of 8 */ 53 static int sizeof_access = roundup(sizeof (nscd_access_t)); 54 55 #define ABORT_DUE_TO_NO_VALID_NSCD_ACCESS_DATA 0 56 #define ASSERT_ACCESS_DATA \ 57 if (access->data != data) \ 58 assert(ABORT_DUE_TO_NO_VALID_NSCD_ACCESS_DATA) 59 60 #define SET_ACCESS_PTR \ 61 access = (nscd_access_t *) \ 62 ((void *)((char *)data - sizeof_access)) 63 64 static void _nscd_free(nscd_acc_data_t *data); 65 66 /* 67 * FUNCTION: _nscd_release 68 * 69 * Decrements the usage count maintained in the access data 70 * tagged before 'data'. Delete the nscd data item if the delete 71 * flag is set and the usage count reaches 0. 72 */ 73 void 74 _nscd_release( 75 nscd_acc_data_t *data) 76 { 77 nscd_access_t *access; 78 char *me = "_nscd_release"; 79 80 if (data == NULL) 81 return; 82 83 SET_ACCESS_PTR; 84 85 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 86 (me, "data = %p, access->data = %p, " 87 "seq = %lld, nUse = %d\n", 88 data, access->data, access->seq_num, access->nUse); 89 ASSERT_ACCESS_DATA; 90 91 (void) mutex_lock(&access->mutex); 92 access->nUse--; 93 if (access->nUse < 0) { 94 #define ACCESS_NUSE_LESS_THAN_ZERO 0 95 assert(ACCESS_NUSE_LESS_THAN_ZERO); 96 } 97 if (access->nUse <= 0 && 98 access->delete == 1) { 99 100 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 101 (me, "deleting data %p\n", access->data); 102 (access->free_func)(access->data); 103 104 /* 105 * if we get here, no other thread could be 106 * holding the access->mutex lock, It is safe 107 * to free the memory containing the mutex 108 * structure. No mutex_unlock is necessary. 109 */ 110 _nscd_free(data); 111 } else 112 (void) mutex_unlock(&access->mutex); 113 } 114 115 116 /* 117 * FUNCTION: _nscd_destroy 118 * 119 * Marks the nscd data item as to-be-deleted and then releases 120 * (If the usage count happens to be zero, then _nscd_release() 121 * will destroy the data.) 122 * 123 * Note that _nscd_destroy should only be called if the 124 * caller has created the nscd data with _nscd_alloc 125 * (with the exception of _nscd_set). That nscd data 126 * item should be private to the caller. 127 */ 128 static void 129 _nscd_destroy( 130 nscd_acc_data_t *data) 131 { 132 nscd_access_t *access; 133 char *me = "_nscd_destroy"; 134 135 if (data == NULL) 136 return; 137 138 SET_ACCESS_PTR; 139 140 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 141 (me, "data = %p, access->data = %p\n", data, access->data); 142 ASSERT_ACCESS_DATA; 143 144 (void) mutex_lock(&access->mutex); 145 access->delete = 1; 146 (void) mutex_unlock(&access->mutex); 147 148 _nscd_release(data); 149 } 150 151 /* 152 * FUNCTION: _nscd_get 153 * 154 * Increment the usage count by one if 'data' can 155 * be found in the internal address database. 156 */ 157 nscd_acc_data_t * 158 _nscd_get( 159 nscd_acc_data_t *data) 160 { 161 nscd_access_t *access; 162 void *ret = data; 163 rwlock_t *addr_rwlock; 164 char *me = "_nscd_get"; 165 166 if (data == NULL) 167 return (NULL); 168 169 SET_ACCESS_PTR; 170 171 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 172 (me, "data = %p, access->data = %p, seq#= %lld, nUse = %d\n", 173 data, access->data, access->seq_num, access->nUse); 174 ASSERT_ACCESS_DATA; 175 176 /* 177 * see if this addr is still valid, 178 * if so, _nscd_is_int_addr will 179 * do a read lock on the returned 180 * multiple readers/single writer lock 181 * to prevent the access data from being 182 * deleted while it is being accessed. 183 */ 184 if ((addr_rwlock = _nscd_is_int_addr(data, 185 access->seq_num)) == NULL) { 186 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 187 (me, "internal address %p not found\n", data); 188 assert(addr_rwlock != NULL); 189 return (NULL); 190 } 191 192 (void) mutex_lock(&access->mutex); 193 if (access->delete == 1) 194 ret = NULL; 195 else 196 access->nUse++; 197 (void) mutex_unlock(&access->mutex); 198 199 /* 200 * done with the multiple readers/single writer lock 201 */ 202 (void) rw_unlock(addr_rwlock); 203 204 return (ret); 205 } 206 207 /* 208 * FUNCTION: _nscd_set 209 * 210 * _nscd_set sets the address of a nscd data item 211 * to 'new' and delete the old nscd data (old). 212 * The pointer 'new' is returned. 213 */ 214 nscd_acc_data_t * 215 _nscd_set( 216 nscd_acc_data_t *old, 217 nscd_acc_data_t *new) 218 { 219 nscd_acc_data_t *old_data, *new_data; 220 char *me = "_nscd_set"; 221 222 if (new == old) 223 return (old); 224 225 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 226 (me, "new = %p, old = %p\n", new, old); 227 228 old_data = _nscd_get(old); 229 new_data = _nscd_get(new); 230 231 if (old_data != new_data) { 232 233 _nscd_destroy(old_data); 234 _nscd_release(new_data); 235 return (new_data); 236 } 237 238 /* if old_data == new_data, both must be NULL */ 239 return (NULL); 240 } 241 242 /* 243 * FUNCTION: _nscd_rdlock 244 * 245 * Lock (rw_rdlock) a nscd data item for reading. The caller 246 * needs to call _nscd_rw_unlock() to unlock the data item 247 * when done using the data. 248 */ 249 nscd_acc_data_t * 250 _nscd_rdlock( 251 nscd_acc_data_t *data) 252 { 253 nscd_access_t *access; 254 void *ret; 255 char *me = "_nscd_rdlock"; 256 257 ret = _nscd_get(data); 258 259 if (ret == NULL) 260 return (NULL); 261 262 SET_ACCESS_PTR; 263 264 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 265 (me, "data = %p, access->data = %p\n", data, access->data); 266 ASSERT_ACCESS_DATA; 267 268 assert(access->data_rwlock != NULL); 269 270 (void) rw_rdlock(access->data_rwlock); 271 272 return (ret); 273 } 274 275 /* 276 * FUNCTION: _nscd_wrlock 277 * 278 * Lock (rw_wrlock) a nscd data item for writing. The caller 279 * needs to call _nscd_rw_unlock() to unlock the data item 280 * when done using the data. 281 */ 282 nscd_acc_data_t * 283 _nscd_wrlock( 284 nscd_acc_data_t *data) 285 { 286 nscd_access_t *access; 287 void *ret; 288 char *me = "_nscd_wrlock"; 289 290 ret = _nscd_get(data); 291 292 if (ret == NULL) 293 return (NULL); 294 295 SET_ACCESS_PTR; 296 297 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 298 (me, "data = %p, access->data = %p\n", data, access->data); 299 ASSERT_ACCESS_DATA; 300 301 assert(access->data_rwlock != NULL); 302 303 (void) rw_wrlock(access->data_rwlock); 304 305 return (ret); 306 } 307 308 /* 309 * FUNCTION: _nscd_rw_unlock 310 * 311 * Unlock (rw_unlock) a locked nscd data item. 312 */ 313 void 314 _nscd_rw_unlock( 315 nscd_acc_data_t *data) 316 { 317 nscd_access_t *access; 318 char *me = "_nscd_rw_unlock"; 319 320 if (data == NULL) 321 return; 322 323 SET_ACCESS_PTR; 324 325 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 326 (me, "data = %p, access->data = %p\n", 327 data, access->data); 328 ASSERT_ACCESS_DATA; 329 330 assert(access->data_rwlock != NULL); 331 332 (void) rw_unlock(access->data_rwlock); 333 _nscd_release(data); 334 } 335 336 /* 337 * FUNCTION: _nscd_rw_unlock_no_release 338 * 339 * Unlock (rw_unlock) a locked nscd data item but without release 340 * it, i.e., without decrement the usage count to indicate that 341 * the data item is still being referenced. 342 */ 343 void 344 _nscd_rw_unlock_no_release( 345 nscd_acc_data_t *data) 346 { 347 nscd_access_t *access; 348 349 if (data == NULL) 350 return; 351 352 SET_ACCESS_PTR; 353 ASSERT_ACCESS_DATA; 354 355 assert(access->data_rwlock != NULL); 356 357 (void) rw_unlock(access->data_rwlock); 358 } 359 360 /* 361 * FUNCTION: _nscd_mutex_lock 362 * 363 * Lock (mutex_lock) a nscd data item. The caller needs 364 * to call _nscd_mutex_unlock() to unlock the data item 365 * when done using the data. 366 */ 367 nscd_acc_data_t * 368 _nscd_mutex_lock( 369 nscd_acc_data_t *data) 370 { 371 nscd_access_t *access; 372 void *ret; 373 char *me = "_nscd_mutex_lock"; 374 375 ret = _nscd_get(data); 376 377 if (ret == NULL) 378 return (NULL); 379 380 SET_ACCESS_PTR; 381 382 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 383 (me, "data = %p, access->data = %p\n", data, access->data); 384 ASSERT_ACCESS_DATA; 385 386 assert(access->data_mutex != NULL); 387 388 (void) mutex_lock(access->data_mutex); 389 390 return (ret); 391 } 392 393 394 /* 395 * FUNCTION: _nscd_mutex_unlock 396 * 397 * Unlock a locked nscd data item (that were locked by _nscd_mutex_lock).. 398 */ 399 void 400 _nscd_mutex_unlock( 401 nscd_acc_data_t *data) 402 { 403 nscd_access_t *access; 404 char *me = "_nscd_mutex_unlock"; 405 406 if (data == NULL) 407 return; 408 409 SET_ACCESS_PTR; 410 411 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 412 (me, "data = %p, access->data = %p\n", data, access->data); 413 ASSERT_ACCESS_DATA; 414 415 assert(access->data_mutex != NULL); 416 417 (void) mutex_unlock(access->data_mutex); 418 _nscd_release(data); 419 } 420 421 /* 422 * FUNCTION: _nscd_cond_wait 423 * 424 * Perform a condition wait with the cond_t and mutex_t associated 425 * with data. 426 */ 427 void 428 _nscd_cond_wait( 429 nscd_acc_data_t *data, cond_t *cond) 430 { 431 nscd_access_t *access; 432 char *me = "_nscd_cond_wait"; 433 434 if (data == NULL) 435 return; 436 437 SET_ACCESS_PTR; 438 439 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 440 (me, "data = %p, access->data = %p\n", data, access->data); 441 ASSERT_ACCESS_DATA; 442 443 assert(access->data_cond != NULL && access->data_mutex != NULL); 444 445 if (cond == NULL) 446 (void) cond_wait(access->data_cond, access->data_mutex); 447 else 448 (void) cond_wait(cond, access->data_mutex); 449 } 450 451 /* 452 * FUNCTION: _nscd_cond_signal 453 * 454 * Perform a condition signal with the cond_t associated with 'data'. 455 */ 456 void 457 _nscd_cond_signal( 458 nscd_acc_data_t *data) 459 { 460 nscd_access_t *access; 461 char *me = "_nscd_cond_signal"; 462 463 if (data == NULL) 464 return; 465 466 SET_ACCESS_PTR; 467 468 _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) 469 (me, "data = %p, access->data = %p\n", data, access->data); 470 ASSERT_ACCESS_DATA; 471 472 assert(access->data_cond != NULL); 473 474 (void) cond_signal(access->data_cond); 475 } 476 477 /* 478 * FUNCTION: _nscd_alloc 479 * 480 * Allocate a piece of nscd memory. 'data_free' 481 * is the function to invoke to free the data 482 * stored in this memory, i.e., the desctrctor. 483 * 'option' indicate whether a mutex or a 484 * readers/writer (or both, or none) should also 485 * be allocated. 486 */ 487 nscd_acc_data_t * 488 _nscd_alloc( 489 int type, 490 size_t size, 491 void (*data_free)(nscd_acc_data_t *data), 492 int option) 493 { 494 nscd_access_t *access; 495 nscd_acc_data_t *ptr; 496 nscd_seq_num_t seq_num; 497 rwlock_t *rwlock = NULL; 498 mutex_t *mutex = NULL; 499 cond_t *cond = NULL; 500 501 if ((ptr = (nscd_acc_data_t *)calloc(1, 502 size + sizeof_access)) == NULL) 503 return (NULL); 504 if (option & NSCD_ALLOC_MUTEX) { 505 if ((mutex = (mutex_t *)calloc(1, sizeof (mutex_t))) == 506 NULL) { 507 free(ptr); 508 return (NULL); 509 } else 510 (void) mutex_init(mutex, USYNC_THREAD, NULL); 511 } 512 if (option & NSCD_ALLOC_RWLOCK) { 513 if ((rwlock = (rwlock_t *)calloc(1, sizeof (rwlock_t))) == 514 NULL) { 515 free(ptr); 516 free(mutex); 517 return (NULL); 518 } else 519 (void) rwlock_init(rwlock, USYNC_THREAD, NULL); 520 } 521 if (option & NSCD_ALLOC_COND) { 522 if ((cond = (cond_t *)calloc(1, sizeof (cond_t))) == 523 NULL) { 524 free(ptr); 525 free(mutex); 526 free(rwlock); 527 return (NULL); 528 } else 529 (void) cond_init(cond, USYNC_THREAD, NULL); 530 } 531 532 /* get current sequence number */ 533 seq_num = _nscd_get_seq_num(); 534 535 access = (nscd_access_t *)ptr; 536 access->data = (char *)ptr + sizeof_access; 537 access->data_mutex = mutex; 538 access->data_rwlock = rwlock; 539 access->data_cond = cond; 540 access->nUse = 0; 541 access->delete = 0; 542 access->type = type; 543 access->free_func = data_free; 544 access->seq_num = seq_num; 545 546 /* add the address to the internal address database */ 547 if (_nscd_add_int_addr(access->data, type, 548 seq_num) != NSCD_SUCCESS) { 549 free(ptr); 550 return (NULL); 551 } 552 553 return (access->data); 554 } 555 556 /* 557 * FUNCTION: _nscd_free 558 * 559 * Free a piece of nscd memory. 560 */ 561 static void 562 _nscd_free( 563 nscd_acc_data_t *data) 564 { 565 nscd_access_t *access; 566 567 if (data == NULL) 568 return; 569 570 SET_ACCESS_PTR; 571 ASSERT_ACCESS_DATA; 572 573 /* remove the address from the internal address database */ 574 _nscd_del_int_addr(access->data, access->seq_num); 575 576 if (access->data_mutex) 577 free(access->data_mutex); 578 if (access->data_rwlock) 579 free(access->data_rwlock); 580 if (access->data_cond) 581 free(access->data_cond); 582 583 (void) memset(access, 0, sizeof (*access)); 584 585 free(access); 586 } 587