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
_nscd_release(nscd_acc_data_t * data)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
_nscd_destroy(nscd_acc_data_t * data)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 *
_nscd_get(nscd_acc_data_t * data)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 *
_nscd_set(nscd_acc_data_t * old,nscd_acc_data_t * new)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 *
_nscd_rdlock(nscd_acc_data_t * data)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 *
_nscd_wrlock(nscd_acc_data_t * data)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
_nscd_rw_unlock(nscd_acc_data_t * data)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
_nscd_rw_unlock_no_release(nscd_acc_data_t * data)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 *
_nscd_mutex_lock(nscd_acc_data_t * data)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
_nscd_mutex_unlock(nscd_acc_data_t * data)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
_nscd_cond_wait(nscd_acc_data_t * data,cond_t * cond)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
_nscd_cond_signal(nscd_acc_data_t * data)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 *
_nscd_alloc(int type,size_t size,void (* data_free)(nscd_acc_data_t * data),int option)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
_nscd_free(nscd_acc_data_t * data)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