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 */
41struct 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 */
55static 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
66static 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 */
75void
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 */
130static 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 */
159nscd_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 */
216nscd_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 */
251nscd_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 */
284nscd_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 */
315void
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 */
345void
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 */
369nscd_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 */
401void
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 */
429void
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 */
458void
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 */
489nscd_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 */
563static 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