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