/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include "configd.h" #include "repcache_protocol.h" typedef struct snapshot_bucket { pthread_mutex_t sb_lock; rc_snapshot_t *sb_head; char sb_pad[64 - sizeof (pthread_mutex_t) - sizeof (rc_snapshot_t *)]; } snapshot_bucket_t; #define SN_HASH_SIZE 64 #define SN_HASH_MASK (SN_HASH_SIZE - 1) #pragma align 64(snapshot_hash) static snapshot_bucket_t snapshot_hash[SN_HASH_SIZE]; #define SNAPSHOT_BUCKET(h) (&snapshot_hash[(h) & SN_HASH_MASK]) static rc_snapshot_t * snapshot_alloc(void) { rc_snapshot_t *sp; sp = uu_zalloc(sizeof (*sp)); (void) pthread_mutex_init(&sp->rs_lock, NULL); (void) pthread_cond_init(&sp->rs_cv, NULL); sp->rs_refcnt++; return (sp); } static void snapshot_free(rc_snapshot_t *sp) { rc_snaplevel_t *lvl, *next; assert(sp->rs_refcnt == 0 && sp->rs_childref == 0); (void) pthread_mutex_destroy(&sp->rs_lock); (void) pthread_cond_destroy(&sp->rs_cv); for (lvl = sp->rs_levels; lvl != NULL; lvl = next) { next = lvl->rsl_next; assert(lvl->rsl_parent == sp); lvl->rsl_parent = NULL; if (lvl->rsl_service) free((char *)lvl->rsl_service); if (lvl->rsl_instance) free((char *)lvl->rsl_instance); uu_free(lvl); } uu_free(sp); } static void rc_snapshot_hold(rc_snapshot_t *sp) { (void) pthread_mutex_lock(&sp->rs_lock); sp->rs_refcnt++; assert(sp->rs_refcnt > 0); (void) pthread_mutex_unlock(&sp->rs_lock); } void rc_snapshot_rele(rc_snapshot_t *sp) { int done; (void) pthread_mutex_lock(&sp->rs_lock); assert(sp->rs_refcnt > 0); sp->rs_refcnt--; done = ((sp->rs_flags & RC_SNAPSHOT_DEAD) && sp->rs_refcnt == 0 && sp->rs_childref == 0); (void) pthread_mutex_unlock(&sp->rs_lock); if (done) snapshot_free(sp); } void rc_snaplevel_hold(rc_snaplevel_t *lvl) { rc_snapshot_t *sp = lvl->rsl_parent; (void) pthread_mutex_lock(&sp->rs_lock); sp->rs_childref++; assert(sp->rs_childref > 0); (void) pthread_mutex_unlock(&sp->rs_lock); } void rc_snaplevel_rele(rc_snaplevel_t *lvl) { int done; rc_snapshot_t *sp = lvl->rsl_parent; (void) pthread_mutex_lock(&sp->rs_lock); assert(sp->rs_childref > 0); sp->rs_childref--; done = ((sp->rs_flags & RC_SNAPSHOT_DEAD) && sp->rs_refcnt == 0 && sp->rs_childref == 0); (void) pthread_mutex_unlock(&sp->rs_lock); if (done) snapshot_free(sp); } static snapshot_bucket_t * snapshot_hold_bucket(uint32_t snap_id) { snapshot_bucket_t *bp = SNAPSHOT_BUCKET(snap_id); (void) pthread_mutex_lock(&bp->sb_lock); return (bp); } static void snapshot_rele_bucket(snapshot_bucket_t *bp) { assert(MUTEX_HELD(&bp->sb_lock)); (void) pthread_mutex_unlock(&bp->sb_lock); } static rc_snapshot_t * snapshot_lookup_unlocked(snapshot_bucket_t *bp, uint32_t snap_id) { rc_snapshot_t *sp; assert(MUTEX_HELD(&bp->sb_lock)); assert(bp == SNAPSHOT_BUCKET(snap_id)); for (sp = bp->sb_head; sp != NULL; sp = sp->rs_hash_next) { if (sp->rs_snap_id == snap_id) { rc_snapshot_hold(sp); return (sp); } } return (NULL); } static void snapshot_insert_unlocked(snapshot_bucket_t *bp, rc_snapshot_t *sp) { assert(MUTEX_HELD(&bp->sb_lock)); assert(bp == SNAPSHOT_BUCKET(sp->rs_snap_id)); assert(sp->rs_hash_next == NULL); sp->rs_hash_next = bp->sb_head; bp->sb_head = sp; } static void snapshot_remove_unlocked(snapshot_bucket_t *bp, rc_snapshot_t *sp) { rc_snapshot_t **spp; assert(MUTEX_HELD(&bp->sb_lock)); assert(bp == SNAPSHOT_BUCKET(sp->rs_snap_id)); assert(sp->rs_hash_next == NULL); for (spp = &bp->sb_head; *spp != NULL; spp = &(*spp)->rs_hash_next) if (*spp == sp) break; assert(*spp == sp); *spp = sp->rs_hash_next; sp->rs_hash_next = NULL; } /* * Look up the snapshot with id snap_id in the hash table, or create it * & populate it with its snaplevels if it's not in the hash table yet. * * Fails with * _NO_RESOURCES */ int rc_snapshot_get(uint32_t snap_id, rc_snapshot_t **snpp) { snapshot_bucket_t *bp; rc_snapshot_t *sp; int r; bp = snapshot_hold_bucket(snap_id); sp = snapshot_lookup_unlocked(bp, snap_id); if (sp != NULL) { snapshot_rele_bucket(bp); (void) pthread_mutex_lock(&sp->rs_lock); while (sp->rs_flags & RC_SNAPSHOT_FILLING) (void) pthread_cond_wait(&sp->rs_cv, &sp->rs_lock); if (sp->rs_flags & RC_SNAPSHOT_DEAD) { (void) pthread_mutex_unlock(&sp->rs_lock); rc_snapshot_rele(sp); return (REP_PROTOCOL_FAIL_NO_RESOURCES); } assert(sp->rs_flags & RC_SNAPSHOT_READY); (void) pthread_mutex_unlock(&sp->rs_lock); *snpp = sp; return (REP_PROTOCOL_SUCCESS); } sp = snapshot_alloc(); sp->rs_snap_id = snap_id; sp->rs_flags |= RC_SNAPSHOT_FILLING; snapshot_insert_unlocked(bp, sp); snapshot_rele_bucket(bp); /* * Now fill in the snapshot tree */ r = object_fill_snapshot(sp); if (r != REP_PROTOCOL_SUCCESS) { assert(r == REP_PROTOCOL_FAIL_NO_RESOURCES); /* * failed -- first remove it from the hash table, then kill it */ bp = snapshot_hold_bucket(snap_id); snapshot_remove_unlocked(bp, sp); snapshot_rele_bucket(bp); (void) pthread_mutex_lock(&sp->rs_lock); sp->rs_flags &= ~RC_SNAPSHOT_FILLING; sp->rs_flags |= RC_SNAPSHOT_DEAD; (void) pthread_cond_broadcast(&sp->rs_cv); (void) pthread_mutex_unlock(&sp->rs_lock); rc_snapshot_rele(sp); /* may free sp */ return (r); } (void) pthread_mutex_lock(&sp->rs_lock); sp->rs_flags &= ~RC_SNAPSHOT_FILLING; sp->rs_flags |= RC_SNAPSHOT_READY; (void) pthread_cond_broadcast(&sp->rs_cv); (void) pthread_mutex_unlock(&sp->rs_lock); *snpp = sp; return (REP_PROTOCOL_SUCCESS); /* pass on creation reference */ }