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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <assert.h>
30#include <pthread.h>
31#include <stdlib.h>
32#include <strings.h>
33#include "configd.h"
34#include "repcache_protocol.h"
35
36typedef struct snapshot_bucket {
37	pthread_mutex_t	sb_lock;
38	rc_snapshot_t	*sb_head;
39
40	char		sb_pad[64 - sizeof (pthread_mutex_t) -
41			    sizeof (rc_snapshot_t *)];
42} snapshot_bucket_t;
43
44#define	SN_HASH_SIZE	64
45#define	SN_HASH_MASK	(SN_HASH_SIZE - 1)
46
47#pragma align 64(snapshot_hash)
48static snapshot_bucket_t snapshot_hash[SN_HASH_SIZE];
49
50#define	SNAPSHOT_BUCKET(h)	(&snapshot_hash[(h) & SN_HASH_MASK])
51
52static rc_snapshot_t *
53snapshot_alloc(void)
54{
55	rc_snapshot_t *sp;
56	sp = uu_zalloc(sizeof (*sp));
57
58	(void) pthread_mutex_init(&sp->rs_lock, NULL);
59	(void) pthread_cond_init(&sp->rs_cv, NULL);
60
61	sp->rs_refcnt++;
62	return (sp);
63}
64
65static void
66snapshot_free(rc_snapshot_t *sp)
67{
68	rc_snaplevel_t *lvl, *next;
69
70	assert(sp->rs_refcnt == 0 && sp->rs_childref == 0);
71
72	(void) pthread_mutex_destroy(&sp->rs_lock);
73	(void) pthread_cond_destroy(&sp->rs_cv);
74
75	for (lvl = sp->rs_levels; lvl != NULL; lvl = next) {
76		next = lvl->rsl_next;
77
78		assert(lvl->rsl_parent == sp);
79		lvl->rsl_parent = NULL;
80
81		if (lvl->rsl_service)
82			free((char *)lvl->rsl_service);
83		if (lvl->rsl_instance)
84			free((char *)lvl->rsl_instance);
85
86		uu_free(lvl);
87	}
88	uu_free(sp);
89}
90
91static void
92rc_snapshot_hold(rc_snapshot_t *sp)
93{
94	(void) pthread_mutex_lock(&sp->rs_lock);
95	sp->rs_refcnt++;
96	assert(sp->rs_refcnt > 0);
97	(void) pthread_mutex_unlock(&sp->rs_lock);
98}
99
100void
101rc_snapshot_rele(rc_snapshot_t *sp)
102{
103	int done;
104	(void) pthread_mutex_lock(&sp->rs_lock);
105	assert(sp->rs_refcnt > 0);
106	sp->rs_refcnt--;
107	done = ((sp->rs_flags & RC_SNAPSHOT_DEAD) &&
108	    sp->rs_refcnt == 0 && sp->rs_childref == 0);
109	(void) pthread_mutex_unlock(&sp->rs_lock);
110
111	if (done)
112		snapshot_free(sp);
113}
114
115void
116rc_snaplevel_hold(rc_snaplevel_t *lvl)
117{
118	rc_snapshot_t *sp = lvl->rsl_parent;
119	(void) pthread_mutex_lock(&sp->rs_lock);
120	sp->rs_childref++;
121	assert(sp->rs_childref > 0);
122	(void) pthread_mutex_unlock(&sp->rs_lock);
123}
124
125void
126rc_snaplevel_rele(rc_snaplevel_t *lvl)
127{
128	int done;
129	rc_snapshot_t *sp = lvl->rsl_parent;
130	(void) pthread_mutex_lock(&sp->rs_lock);
131	assert(sp->rs_childref > 0);
132	sp->rs_childref--;
133	done = ((sp->rs_flags & RC_SNAPSHOT_DEAD) &&
134	    sp->rs_refcnt == 0 && sp->rs_childref == 0);
135	(void) pthread_mutex_unlock(&sp->rs_lock);
136
137	if (done)
138		snapshot_free(sp);
139}
140
141static snapshot_bucket_t *
142snapshot_hold_bucket(uint32_t snap_id)
143{
144	snapshot_bucket_t *bp = SNAPSHOT_BUCKET(snap_id);
145	(void) pthread_mutex_lock(&bp->sb_lock);
146	return (bp);
147}
148
149static void
150snapshot_rele_bucket(snapshot_bucket_t *bp)
151{
152	assert(MUTEX_HELD(&bp->sb_lock));
153	(void) pthread_mutex_unlock(&bp->sb_lock);
154}
155
156static rc_snapshot_t *
157snapshot_lookup_unlocked(snapshot_bucket_t *bp, uint32_t snap_id)
158{
159	rc_snapshot_t *sp;
160
161	assert(MUTEX_HELD(&bp->sb_lock));
162	assert(bp == SNAPSHOT_BUCKET(snap_id));
163
164	for (sp = bp->sb_head; sp != NULL; sp = sp->rs_hash_next) {
165		if (sp->rs_snap_id == snap_id) {
166			rc_snapshot_hold(sp);
167			return (sp);
168		}
169	}
170	return (NULL);
171}
172
173static void
174snapshot_insert_unlocked(snapshot_bucket_t *bp, rc_snapshot_t *sp)
175{
176	assert(MUTEX_HELD(&bp->sb_lock));
177	assert(bp == SNAPSHOT_BUCKET(sp->rs_snap_id));
178
179	assert(sp->rs_hash_next == NULL);
180
181	sp->rs_hash_next = bp->sb_head;
182	bp->sb_head = sp;
183}
184
185static void
186snapshot_remove_unlocked(snapshot_bucket_t *bp, rc_snapshot_t *sp)
187{
188	rc_snapshot_t **spp;
189
190	assert(MUTEX_HELD(&bp->sb_lock));
191	assert(bp == SNAPSHOT_BUCKET(sp->rs_snap_id));
192
193	assert(sp->rs_hash_next == NULL);
194
195	for (spp = &bp->sb_head; *spp != NULL; spp = &(*spp)->rs_hash_next)
196		if (*spp == sp)
197			break;
198
199	assert(*spp == sp);
200	*spp = sp->rs_hash_next;
201	sp->rs_hash_next = NULL;
202}
203
204/*
205 * Look up the snapshot with id snap_id in the hash table, or create it
206 * & populate it with its snaplevels if it's not in the hash table yet.
207 *
208 * Fails with
209 *   _NO_RESOURCES
210 */
211int
212rc_snapshot_get(uint32_t snap_id, rc_snapshot_t **snpp)
213{
214	snapshot_bucket_t *bp;
215	rc_snapshot_t *sp;
216	int r;
217
218	bp = snapshot_hold_bucket(snap_id);
219	sp = snapshot_lookup_unlocked(bp, snap_id);
220	if (sp != NULL) {
221		snapshot_rele_bucket(bp);
222		(void) pthread_mutex_lock(&sp->rs_lock);
223		while (sp->rs_flags & RC_SNAPSHOT_FILLING)
224			(void) pthread_cond_wait(&sp->rs_cv, &sp->rs_lock);
225
226		if (sp->rs_flags & RC_SNAPSHOT_DEAD) {
227			(void) pthread_mutex_unlock(&sp->rs_lock);
228			rc_snapshot_rele(sp);
229			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
230		}
231		assert(sp->rs_flags & RC_SNAPSHOT_READY);
232		(void) pthread_mutex_unlock(&sp->rs_lock);
233		*snpp = sp;
234		return (REP_PROTOCOL_SUCCESS);
235	}
236	sp = snapshot_alloc();
237	sp->rs_snap_id = snap_id;
238	sp->rs_flags |= RC_SNAPSHOT_FILLING;
239	snapshot_insert_unlocked(bp, sp);
240	snapshot_rele_bucket(bp);
241
242	/*
243	 * Now fill in the snapshot tree
244	 */
245	r = object_fill_snapshot(sp);
246	if (r != REP_PROTOCOL_SUCCESS) {
247		assert(r == REP_PROTOCOL_FAIL_NO_RESOURCES);
248
249		/*
250		 * failed -- first remove it from the hash table, then kill it
251		 */
252		bp = snapshot_hold_bucket(snap_id);
253		snapshot_remove_unlocked(bp, sp);
254		snapshot_rele_bucket(bp);
255
256		(void) pthread_mutex_lock(&sp->rs_lock);
257		sp->rs_flags &= ~RC_SNAPSHOT_FILLING;
258		sp->rs_flags |= RC_SNAPSHOT_DEAD;
259		(void) pthread_cond_broadcast(&sp->rs_cv);
260		(void) pthread_mutex_unlock(&sp->rs_lock);
261		rc_snapshot_rele(sp);		/* may free sp */
262		return (r);
263	}
264	(void) pthread_mutex_lock(&sp->rs_lock);
265	sp->rs_flags &= ~RC_SNAPSHOT_FILLING;
266	sp->rs_flags |= RC_SNAPSHOT_READY;
267	(void) pthread_cond_broadcast(&sp->rs_cv);
268	(void) pthread_mutex_unlock(&sp->rs_lock);
269	*snpp = sp;
270	return (REP_PROTOCOL_SUCCESS);		/* pass on creation reference */
271}
272