FreeBSD/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c
/*
 * dbuf_rele() for an already-locked dbuf.  This is necessary to allow
 * db_dirtycnt and db_holds to be updated atomically.
 */
dbuf_rele() for an already-locked dbuf.  This is necessary to allow
db_dirtycnt and db_holds to be updated atomically.
dbuf_rele () for an already - locked dbuf . This is necessary to allow db_dirtycnt and db_holds to be updated atomically .
void
dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag)
{
	int64_t holds;
	ASSERT(MUTEX_HELD(&db->db_mtx));
	DBUF_VERIFY(db);
	holds = refcount_remove(&db->db_holds, tag);
	ASSERT(holds >= 0);
	if (db->db_buf && holds == (db->db_level == 0 ? db->db_dirtycnt : 0))
		arc_buf_freeze(db->db_buf);
	if (holds == db->db_dirtycnt &&
	    db->db_level == 0 && db->db_user_immediate_evict)
		dbuf_evict_user(db);
	if (holds == 0) {
		if (db->db_blkid == DMU_BONUS_BLKID) {
			dnode_t *dn;
			boolean_t evict_dbuf = db->db_pending_evict;
			DB_DNODE_ENTER(db);
			dn = DB_DNODE(db);
			atomic_dec_32(&dn->dn_dbufs_count);
			DB_DNODE_EXIT(db);
			mutex_exit(&db->db_mtx);
			if (evict_dbuf)
				dnode_evict_bonus(dn);
			dnode_rele(dn, db);
		} else if (db->db_buf == NULL) {
			ASSERT(db->db_state == DB_UNCACHED ||
			    db->db_state == DB_NOFILL);
			dbuf_evict(db);
		} else if (arc_released(db->db_buf)) {
			arc_buf_t *buf = db->db_buf;
			dbuf_clear_data(db);
			VERIFY(arc_buf_remove_ref(buf, db));
			dbuf_evict(db);
		} else {
			VERIFY(!arc_buf_remove_ref(db->db_buf, db));
			if (!DBUF_IS_CACHEABLE(db)) {
				if (db->db_blkptr != NULL &&
				    !BP_IS_HOLE(db->db_blkptr) &&
				    !BP_IS_EMBEDDED(db->db_blkptr)) {
					spa_t *spa =
					    dmu_objset_spa(db->db_objset);
					blkptr_t bp = *db->db_blkptr;
					dbuf_clear(db);
					arc_freed(spa, &bp);
				} else {
					dbuf_clear(db);
				}
			} else if (db->db_pending_evict ||
			    arc_buf_eviction_needed(db->db_buf)) {
				dbuf_clear(db);
			} else {
				mutex_exit(&db->db_mtx);
			}
		}
	} else {
		mutex_exit(&db->db_mtx);
	}
}
void dbuf_rele_and_unlock ( dmu_buf_impl_t * db , void * tag ) { int64_t holds ; ASSERT ( MUTEX_HELD ( & db -> db_mtx ) ) ; DBUF_VERIFY ( db ) ; holds = refcount_remove ( & db -> db_holds , tag ) ; ASSERT ( holds >= 0 ) ; if ( db -> db_buf && holds == ( db -> db_level == 0 ? db -> db_dirtycnt : 0 ) ) arc_buf_freeze ( db -> db_buf ) ; if ( holds == db -> db_dirtycnt && db -> db_level == 0 && db -> db_user_immediate_evict ) dbuf_evict_user ( db ) ; if ( holds == 0 ) { if ( db -> db_blkid == DMU_BONUS_BLKID ) { dnode_t * dn ; boolean_t evict_dbuf = db -> db_pending_evict ; DB_DNODE_ENTER ( db ) ; dn = DB_DNODE ( db ) ; atomic_dec_32 ( & dn -> dn_dbufs_count ) ; DB_DNODE_EXIT ( db ) ; mutex_exit ( & db -> db_mtx ) ; if ( evict_dbuf ) dnode_evict_bonus ( dn ) ; dnode_rele ( dn , db ) ; } else if ( db -> db_buf == NULL ) { ASSERT ( db -> db_state == DB_UNCACHED || db -> db_state == DB_NOFILL ) ; dbuf_evict ( db ) ; } else if ( arc_released ( db -> db_buf ) ) { arc_buf_t * buf = db -> db_buf ; dbuf_clear_data ( db ) ; VERIFY ( arc_buf_remove_ref ( buf , db ) ) ; dbuf_evict ( db ) ; } else { VERIFY ( ! arc_buf_remove_ref ( db -> db_buf , db ) ) ; if ( ! DBUF_IS_CACHEABLE ( db ) ) { if ( db -> db_blkptr != NULL && ! BP_IS_HOLE ( db -> db_blkptr ) && ! BP_IS_EMBEDDED ( db -> db_blkptr ) ) { spa_t * spa = dmu_objset_spa ( db -> db_objset ) ; blkptr_t bp = * db -> db_blkptr ; dbuf_clear ( db ) ; arc_freed ( spa , & bp ) ; } else { dbuf_clear ( db ) ; } } else if ( db -> db_pending_evict || arc_buf_eviction_needed ( db -> db_buf ) ) { dbuf_clear ( db ) ; } else { mutex_exit ( & db -> db_mtx ) ; } } } else { mutex_exit ( & db -> db_mtx ) ; } }
dbuf_rele() for an already-locked dbuf.
dbuf_rele () for an already - locked dbuf .
/*
	 * Remove the reference to the dbuf before removing its hold on the
	 * dnode so we can guarantee in dnode_move() that a referenced bonus
	 * buffer has a corresponding dnode hold.
	 */ /*
	 * We can't freeze indirects if there is a possibility that they
	 * may be modified in the current syncing context.
	 */ /*
			 * If the dnode moves here, we cannot cross this
			 * barrier until the move completes.
			 */ /*
			 * Decrementing the dbuf count means that the bonus
			 * buffer's dnode hold is no longer discounted in
			 * dnode_move(). The dnode cannot move until after
			 * the dnode_rele() below.
			 */ /*
			 * Do not reference db after its lock is dropped.
			 * Another thread may evict it.
			 */ /*
			 * This is a special case: we never associated this
			 * dbuf with any data allocated from the ARC.
			 */ /*
			 * This dbuf has anonymous data associated with it.
			 */ /*
			 * A dbuf will be eligible for eviction if either the
			 * 'primarycache' property is set or a duplicate
			 * copy of this buffer is already cached in the arc.
			 *
			 * In the case of the 'primarycache' a buffer
			 * is considered for eviction if it matches the
			 * criteria set in the property.
			 *
			 * To decide if our buffer is considered a
			 * duplicate, we must call into the arc to determine
			 * if multiple buffers are referencing the same
			 * block on-disk. If so, then we simply evict
			 * ourselves.
			 */