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