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