xref: /illumos-gate/usr/src/cmd/svc/configd/snapshot.c (revision 7c478bd9)
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 
36 typedef 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)
48 static snapshot_bucket_t snapshot_hash[SN_HASH_SIZE];
49 
50 #define	SNAPSHOT_BUCKET(h)	(&snapshot_hash[(h) & SN_HASH_MASK])
51 
52 static rc_snapshot_t *
snapshot_alloc(void)53 snapshot_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 
65 static void
snapshot_free(rc_snapshot_t * sp)66 snapshot_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 
91 static void
rc_snapshot_hold(rc_snapshot_t * sp)92 rc_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 
100 void
rc_snapshot_rele(rc_snapshot_t * sp)101 rc_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 
115 void
rc_snaplevel_hold(rc_snaplevel_t * lvl)116 rc_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 
125 void
rc_snaplevel_rele(rc_snaplevel_t * lvl)126 rc_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 
141 static snapshot_bucket_t *
snapshot_hold_bucket(uint32_t snap_id)142 snapshot_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 
149 static void
snapshot_rele_bucket(snapshot_bucket_t * bp)150 snapshot_rele_bucket(snapshot_bucket_t *bp)
151 {
152 	assert(MUTEX_HELD(&bp->sb_lock));
153 	(void) pthread_mutex_unlock(&bp->sb_lock);
154 }
155 
156 static rc_snapshot_t *
snapshot_lookup_unlocked(snapshot_bucket_t * bp,uint32_t snap_id)157 snapshot_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 
173 static void
snapshot_insert_unlocked(snapshot_bucket_t * bp,rc_snapshot_t * sp)174 snapshot_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 
185 static void
snapshot_remove_unlocked(snapshot_bucket_t * bp,rc_snapshot_t * sp)186 snapshot_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  */
211 int
rc_snapshot_get(uint32_t snap_id,rc_snapshot_t ** snpp)212 rc_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