xref: /illumos-gate/usr/src/cmd/svc/configd/snapshot.c (revision 2a8bcb4e)
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 #include <assert.h>
28 #include <pthread.h>
29 #include <stdlib.h>
30 #include <strings.h>
31 #include "configd.h"
32 #include "repcache_protocol.h"
33 
34 typedef struct snapshot_bucket {
35 	pthread_mutex_t	sb_lock;
36 	rc_snapshot_t	*sb_head;
37 
38 	char		sb_pad[64 - sizeof (pthread_mutex_t) -
39 			    sizeof (rc_snapshot_t *)];
40 } snapshot_bucket_t;
41 
42 #define	SN_HASH_SIZE	64
43 #define	SN_HASH_MASK	(SN_HASH_SIZE - 1)
44 
45 #pragma align 64(snapshot_hash)
46 static snapshot_bucket_t snapshot_hash[SN_HASH_SIZE];
47 
48 #define	SNAPSHOT_BUCKET(h)	(&snapshot_hash[(h) & SN_HASH_MASK])
49 
50 static rc_snapshot_t *
snapshot_alloc(void)51 snapshot_alloc(void)
52 {
53 	rc_snapshot_t *sp;
54 	sp = uu_zalloc(sizeof (*sp));
55 
56 	(void) pthread_mutex_init(&sp->rs_lock, NULL);
57 	(void) pthread_cond_init(&sp->rs_cv, NULL);
58 
59 	sp->rs_refcnt++;
60 	return (sp);
61 }
62 
63 static void
snapshot_free(rc_snapshot_t * sp)64 snapshot_free(rc_snapshot_t *sp)
65 {
66 	rc_snaplevel_t *lvl, *next;
67 
68 	assert(sp->rs_refcnt == 0 && sp->rs_childref == 0);
69 
70 	(void) pthread_mutex_destroy(&sp->rs_lock);
71 	(void) pthread_cond_destroy(&sp->rs_cv);
72 
73 	for (lvl = sp->rs_levels; lvl != NULL; lvl = next) {
74 		next = lvl->rsl_next;
75 
76 		assert(lvl->rsl_parent == sp);
77 		lvl->rsl_parent = NULL;
78 
79 		if (lvl->rsl_service)
80 			free((char *)lvl->rsl_service);
81 		if (lvl->rsl_instance)
82 			free((char *)lvl->rsl_instance);
83 
84 		uu_free(lvl);
85 	}
86 	uu_free(sp);
87 }
88 
89 static void
rc_snapshot_hold(rc_snapshot_t * sp)90 rc_snapshot_hold(rc_snapshot_t *sp)
91 {
92 	(void) pthread_mutex_lock(&sp->rs_lock);
93 	sp->rs_refcnt++;
94 	assert(sp->rs_refcnt > 0);
95 	(void) pthread_mutex_unlock(&sp->rs_lock);
96 }
97 
98 void
rc_snapshot_rele(rc_snapshot_t * sp)99 rc_snapshot_rele(rc_snapshot_t *sp)
100 {
101 	int done;
102 	(void) pthread_mutex_lock(&sp->rs_lock);
103 	assert(sp->rs_refcnt > 0);
104 	sp->rs_refcnt--;
105 	done = ((sp->rs_flags & RC_SNAPSHOT_DEAD) &&
106 	    sp->rs_refcnt == 0 && sp->rs_childref == 0);
107 	(void) pthread_mutex_unlock(&sp->rs_lock);
108 
109 	if (done)
110 		snapshot_free(sp);
111 }
112 
113 void
rc_snaplevel_hold(rc_snaplevel_t * lvl)114 rc_snaplevel_hold(rc_snaplevel_t *lvl)
115 {
116 	rc_snapshot_t *sp = lvl->rsl_parent;
117 	(void) pthread_mutex_lock(&sp->rs_lock);
118 	sp->rs_childref++;
119 	assert(sp->rs_childref > 0);
120 	(void) pthread_mutex_unlock(&sp->rs_lock);
121 }
122 
123 void
rc_snaplevel_rele(rc_snaplevel_t * lvl)124 rc_snaplevel_rele(rc_snaplevel_t *lvl)
125 {
126 	int done;
127 	rc_snapshot_t *sp = lvl->rsl_parent;
128 	(void) pthread_mutex_lock(&sp->rs_lock);
129 	assert(sp->rs_childref > 0);
130 	sp->rs_childref--;
131 	done = ((sp->rs_flags & RC_SNAPSHOT_DEAD) &&
132 	    sp->rs_refcnt == 0 && sp->rs_childref == 0);
133 	(void) pthread_mutex_unlock(&sp->rs_lock);
134 
135 	if (done)
136 		snapshot_free(sp);
137 }
138 
139 static snapshot_bucket_t *
snapshot_hold_bucket(uint32_t snap_id)140 snapshot_hold_bucket(uint32_t snap_id)
141 {
142 	snapshot_bucket_t *bp = SNAPSHOT_BUCKET(snap_id);
143 	(void) pthread_mutex_lock(&bp->sb_lock);
144 	return (bp);
145 }
146 
147 static void
snapshot_rele_bucket(snapshot_bucket_t * bp)148 snapshot_rele_bucket(snapshot_bucket_t *bp)
149 {
150 	assert(MUTEX_HELD(&bp->sb_lock));
151 	(void) pthread_mutex_unlock(&bp->sb_lock);
152 }
153 
154 static rc_snapshot_t *
snapshot_lookup_unlocked(snapshot_bucket_t * bp,uint32_t snap_id)155 snapshot_lookup_unlocked(snapshot_bucket_t *bp, uint32_t snap_id)
156 {
157 	rc_snapshot_t *sp;
158 
159 	assert(MUTEX_HELD(&bp->sb_lock));
160 	assert(bp == SNAPSHOT_BUCKET(snap_id));
161 
162 	for (sp = bp->sb_head; sp != NULL; sp = sp->rs_hash_next) {
163 		if (sp->rs_snap_id == snap_id) {
164 			rc_snapshot_hold(sp);
165 			return (sp);
166 		}
167 	}
168 	return (NULL);
169 }
170 
171 static void
snapshot_insert_unlocked(snapshot_bucket_t * bp,rc_snapshot_t * sp)172 snapshot_insert_unlocked(snapshot_bucket_t *bp, rc_snapshot_t *sp)
173 {
174 	assert(MUTEX_HELD(&bp->sb_lock));
175 	assert(bp == SNAPSHOT_BUCKET(sp->rs_snap_id));
176 
177 	assert(sp->rs_hash_next == NULL);
178 
179 	sp->rs_hash_next = bp->sb_head;
180 	bp->sb_head = sp;
181 }
182 
183 static void
snapshot_remove_unlocked(snapshot_bucket_t * bp,rc_snapshot_t * sp)184 snapshot_remove_unlocked(snapshot_bucket_t *bp, rc_snapshot_t *sp)
185 {
186 	rc_snapshot_t **spp;
187 
188 	assert(MUTEX_HELD(&bp->sb_lock));
189 	assert(bp == SNAPSHOT_BUCKET(sp->rs_snap_id));
190 
191 	assert(sp->rs_hash_next == NULL);
192 
193 	for (spp = &bp->sb_head; *spp != NULL; spp = &(*spp)->rs_hash_next)
194 		if (*spp == sp)
195 			break;
196 
197 	assert(*spp == sp);
198 	*spp = sp->rs_hash_next;
199 	sp->rs_hash_next = NULL;
200 }
201 
202 /*
203  * Look up the snapshot with id snap_id in the hash table, or create it
204  * & populate it with its snaplevels if it's not in the hash table yet.
205  *
206  * Fails with
207  *   _NO_RESOURCES
208  */
209 int
rc_snapshot_get(uint32_t snap_id,rc_snapshot_t ** snpp)210 rc_snapshot_get(uint32_t snap_id, rc_snapshot_t **snpp)
211 {
212 	snapshot_bucket_t *bp;
213 	rc_snapshot_t *sp;
214 	int r;
215 
216 	bp = snapshot_hold_bucket(snap_id);
217 	sp = snapshot_lookup_unlocked(bp, snap_id);
218 	if (sp != NULL) {
219 		snapshot_rele_bucket(bp);
220 		(void) pthread_mutex_lock(&sp->rs_lock);
221 		while (sp->rs_flags & RC_SNAPSHOT_FILLING)
222 			(void) pthread_cond_wait(&sp->rs_cv, &sp->rs_lock);
223 
224 		if (sp->rs_flags & RC_SNAPSHOT_DEAD) {
225 			(void) pthread_mutex_unlock(&sp->rs_lock);
226 			rc_snapshot_rele(sp);
227 			return (REP_PROTOCOL_FAIL_NO_RESOURCES);
228 		}
229 		assert(sp->rs_flags & RC_SNAPSHOT_READY);
230 		(void) pthread_mutex_unlock(&sp->rs_lock);
231 		*snpp = sp;
232 		return (REP_PROTOCOL_SUCCESS);
233 	}
234 	sp = snapshot_alloc();
235 	sp->rs_snap_id = snap_id;
236 	sp->rs_flags |= RC_SNAPSHOT_FILLING;
237 	snapshot_insert_unlocked(bp, sp);
238 	snapshot_rele_bucket(bp);
239 
240 	/*
241 	 * Now fill in the snapshot tree
242 	 */
243 	r = object_fill_snapshot(sp);
244 	if (r != REP_PROTOCOL_SUCCESS) {
245 		assert(r == REP_PROTOCOL_FAIL_NO_RESOURCES);
246 
247 		/*
248 		 * failed -- first remove it from the hash table, then kill it
249 		 */
250 		bp = snapshot_hold_bucket(snap_id);
251 		snapshot_remove_unlocked(bp, sp);
252 		snapshot_rele_bucket(bp);
253 
254 		(void) pthread_mutex_lock(&sp->rs_lock);
255 		sp->rs_flags &= ~RC_SNAPSHOT_FILLING;
256 		sp->rs_flags |= RC_SNAPSHOT_DEAD;
257 		(void) pthread_cond_broadcast(&sp->rs_cv);
258 		(void) pthread_mutex_unlock(&sp->rs_lock);
259 		rc_snapshot_rele(sp);		/* may free sp */
260 		return (r);
261 	}
262 	(void) pthread_mutex_lock(&sp->rs_lock);
263 	sp->rs_flags &= ~RC_SNAPSHOT_FILLING;
264 	sp->rs_flags |= RC_SNAPSHOT_READY;
265 	(void) pthread_cond_broadcast(&sp->rs_cv);
266 	(void) pthread_mutex_unlock(&sp->rs_lock);
267 	*snpp = sp;
268 	return (REP_PROTOCOL_SUCCESS);		/* pass on creation reference */
269 }
270