xref: /illumos-gate/usr/src/cmd/nscd/nscd_access.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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