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