sys/vfs/hammer2/hammer2_flush.c
void
hammer2_chain_flush(hammer2_mount_t *hmp, hammer2_chain_t *chain,
		    hammer2_tid_t modify_tid)
{
	hammer2_chain_t *parent;
	hammer2_chain_t *scan;
	hammer2_blockref_t *base;
	hammer2_flush_info_t info;
	int count;
	int reflush;

	/*
	 * Execute the recursive flush and handle deferrals.
	 *
	 * Chains can be ridiculously long (thousands deep), so to
	 * avoid blowing out the kernel stack the recursive flush has a
	 * depth limit.  Elements at the limit are placed on a list
	 * for re-execution after the stack has been popped.
	 */
	bzero(&info, sizeof(info));
	TAILQ_INIT(&info.flush_list);

	if (modify_tid == 0) {
		hammer2_voldata_lock(hmp);
		info.modify_tid = hmp->voldata.alloc_tid++;
		atomic_set_int(&hmp->vchain.flags, HAMMER2_CHAIN_MODIFIED_AUX);
		hammer2_voldata_unlock(hmp);
	} else {
		info.modify_tid = modify_tid;
	}
	reflush = 1;

	while (reflush) {
		/*
		 * Primary recursion
		 */
		hammer2_chain_flush_pass1(hmp, chain, &info);
		reflush = 0;

		while ((scan = TAILQ_FIRST(&info.flush_list)) != NULL) {
			/*
			 * Secondary recursion.  Note that a reference is
			 * retained from the element's presence on the
			 * deferral list.
			 */
			KKASSERT(scan->flags & HAMMER2_CHAIN_DEFERRED);
			TAILQ_REMOVE(&info.flush_list, scan, flush_node);
			atomic_clear_int(&scan->flags, HAMMER2_CHAIN_DEFERRED);

			/*
			 * Now that we've popped back up we can do a secondary
			 * recursion on the deferred elements.
			 */
			if (hammer2_debug & 0x0040)
				kprintf("defered flush %p\n", scan);
			hammer2_chain_lock(hmp, scan, HAMMER2_RESOLVE_MAYBE);
			hammer2_chain_flush(hmp, scan, info.modify_tid);
			hammer2_chain_unlock(hmp, scan);

			/*
			 * Only flag a reflush if SUBMODIFIED is no longer
			 * set.  If SUBMODIFIED is set the element will just
			 * wind up on our flush_list again.
			 */
			if ((scan->flags & (HAMMER2_CHAIN_SUBMODIFIED |
					    HAMMER2_CHAIN_MODIFIED |
					    HAMMER2_CHAIN_MODIFIED_AUX)) == 0) {
				reflush = 1;
			}
			hammer2_chain_drop(hmp, scan);
		}
		if ((hammer2_debug & 0x0040) && reflush)
			kprintf("reflush %p\n", chain);
	}

	/*
	 * The SUBMODIFIED bit must propagate upward if the chain could not
	 * be completely flushed.
	 */
	if (chain->flags & (HAMMER2_CHAIN_SUBMODIFIED |
			    HAMMER2_CHAIN_MODIFIED |
			    HAMMER2_CHAIN_MODIFIED_AUX |
			    HAMMER2_CHAIN_MOVED)) {
		hammer2_chain_parent_setsubmod(hmp, chain);
	}

	/*
	 * If the only thing left is a simple bref update try to
	 * pro-actively update the parent, otherwise return early.
	 */
	parent = chain->parent;
	if (parent == NULL) {
		return;
	}
	if (chain->bref.type != HAMMER2_BREF_TYPE_INODE ||
	    (chain->flags & (HAMMER2_CHAIN_SUBMODIFIED |
			     HAMMER2_CHAIN_MODIFIED |
			     HAMMER2_CHAIN_MODIFIED_AUX |
			     HAMMER2_CHAIN_MOVED)) != HAMMER2_CHAIN_MOVED) {
		return;
	}

	/*
	 * We are locking backwards so allow the lock to fail.
	 */
	if (ccms_thread_lock_nonblock(&parent->cst, CCMS_STATE_EXCLUSIVE))
		return;

	/*
	 * We are updating brefs but we have to call chain_modify()
	 * because our caller is not being run from a recursive flush.
	 *
	 * This will also chain up the parent list and set the SUBMODIFIED
	 * flag.
	 *
	 * We do not want to set HAMMER2_CHAIN_MODIFY_TID here because the
	 * modification is only related to updating a bref in the parent.
	 *
	 * When updating the blockset embedded in the volume header we must
	 * also update voldata.mirror_tid.
	 */
	hammer2_chain_lock(hmp, parent, HAMMER2_RESOLVE_MAYBE);
	hammer2_chain_modify(hmp, parent, HAMMER2_MODIFY_NO_MODIFY_TID);

	switch(parent->bref.type) {
	case HAMMER2_BREF_TYPE_INODE:
		base = &parent->data->ipdata.u.blockset.
			blockref[0];
		count = HAMMER2_SET_COUNT;
		break;
	case HAMMER2_BREF_TYPE_INDIRECT:
		base = &parent->data->npdata.blockref[0];
		count = parent->bytes /
			sizeof(hammer2_blockref_t);
		break;
	case HAMMER2_BREF_TYPE_VOLUME:
		base = &hmp->voldata.sroot_blockset.blockref[0];
		count = HAMMER2_SET_COUNT;
		if (chain->flags & HAMMER2_CHAIN_MOVED) {
			if (hmp->voldata.mirror_tid < chain->bref.mirror_tid) {
				hmp->voldata.mirror_tid =
					chain->bref.mirror_tid;
			}
		}
		break;
	default:
		base = NULL;
		panic("hammer2_chain_flush: "
		      "unrecognized blockref type: %d",
		      parent->bref.type);
	}

	/*
	 * Update the blockref in the parent.  We do not have to set
	 * MOVED in the parent because the parent has been marked modified,
	 * so the flush sequence will pick up the bref change.
	 *
	 * We do have to propagate mirror_tid upward.
	 */
	KKASSERT(chain->index >= 0 &&
		 chain->index < count);
	KKASSERT(chain->parent == parent);
	if (chain->flags & HAMMER2_CHAIN_MOVED) {
		base[chain->index] = chain->bref_flush;
		if (parent->bref.mirror_tid < chain->bref_flush.mirror_tid)
			parent->bref.mirror_tid = chain->bref_flush.mirror_tid;
		atomic_clear_int(&chain->flags, HAMMER2_CHAIN_MOVED);
		hammer2_chain_drop(hmp, chain);
	} else if (bcmp(&base[chain->index], &chain->bref_flush,
		   sizeof(chain->bref)) != 0) {
		panic("hammer2: unflagged bref update(2)");
	}
	ccms_thread_unlock(&parent->cst);		/* release manual op */
	hammer2_chain_unlock(hmp, parent);
}
/*
 * Stand-alone flush.  If the chain is unable to completely flush we have
 * to be sure that SUBMODIFIED propagates up the parent chain.  We must not
 * clear the MOVED bit after flushing in this situation or our desynchronized
 * bref will not properly update in the parent.
 *
 * This routine can be called from several places but the most important
 * is from the hammer2_vop_reclaim() function.  We want to try to completely
 * clean out the inode structure to prevent disconnected inodes from
 * building up and blowing out the kmalloc pool.
 *
 * If modify_tid is 0 (usual case), a new modify_tid is allocated and
 * applied to the flush.  The depth-limit handling code is the only
 * code which passes a non-zero modify_tid to hammer2_chain_flush().
 *
 * chain is locked on call and will remain locked on return.
 */
Stand-alone flush.  If the chain is unable to completely flush we have
to be sure that SUBMODIFIED propagates up the parent chain.  We must not
clear the MOVED bit after flushing in this situation or our desynchronized
bref will not properly update in the parent.

This routine can be called from several places but the most important
is from the hammer2_vop_reclaim() function.  We want to try to completely
clean out the inode structure to prevent disconnected inodes from
building up and blowing out the kmalloc pool.

If modify_tid is 0 (usual case), a new modify_tid is allocated and
applied to the flush.  The depth-limit handling code is the only
code which passes a non-zero modify_tid to hammer2_chain_flush().

chain is locked on call and will remain locked on return.
Stand - alone flush . If the chain is unable to completely flush we have to be sure that SUBMODIFIED propagates up the parent chain . We must not clear the MOVED bit after flushing in this situation or our desynchronized bref will not properly update in the parent . This routine can be called from several places but the most important is from the hammer2_vop_reclaim () function . We want to try to completely clean out the inode structure to prevent disconnected inodes from building up and blowing out the kmalloc pool . If modify_tid is 0 ( usual case ) a new modify_tid is allocated and applied to the flush . The depth - limit handling code is the only code which passes a non - zero modify_tid to hammer2_chain_flush () . chain is locked on call and will remain locked on return .
void
hammer2_chain_flush(hammer2_mount_t *hmp, hammer2_chain_t *chain,
		    hammer2_tid_t modify_tid)
{
	hammer2_chain_t *parent;
	hammer2_chain_t *scan;
	hammer2_blockref_t *base;
	hammer2_flush_info_t info;
	int count;
	int reflush;
	bzero(&info, sizeof(info));
	TAILQ_INIT(&info.flush_list);
	if (modify_tid == 0) {
		hammer2_voldata_lock(hmp);
		info.modify_tid = hmp->voldata.alloc_tid++;
		atomic_set_int(&hmp->vchain.flags, HAMMER2_CHAIN_MODIFIED_AUX);
		hammer2_voldata_unlock(hmp);
	} else {
		info.modify_tid = modify_tid;
	}
	reflush = 1;
	while (reflush) {
		hammer2_chain_flush_pass1(hmp, chain, &info);
		reflush = 0;
		while ((scan = TAILQ_FIRST(&info.flush_list)) != NULL) {
			KKASSERT(scan->flags & HAMMER2_CHAIN_DEFERRED);
			TAILQ_REMOVE(&info.flush_list, scan, flush_node);
			atomic_clear_int(&scan->flags, HAMMER2_CHAIN_DEFERRED);
			if (hammer2_debug & 0x0040)
				kprintf("defered flush %p\n", scan);
			hammer2_chain_lock(hmp, scan, HAMMER2_RESOLVE_MAYBE);
			hammer2_chain_flush(hmp, scan, info.modify_tid);
			hammer2_chain_unlock(hmp, scan);
			if ((scan->flags & (HAMMER2_CHAIN_SUBMODIFIED |
					    HAMMER2_CHAIN_MODIFIED |
					    HAMMER2_CHAIN_MODIFIED_AUX)) == 0) {
				reflush = 1;
			}
			hammer2_chain_drop(hmp, scan);
		}
		if ((hammer2_debug & 0x0040) && reflush)
			kprintf("reflush %p\n", chain);
	}
	if (chain->flags & (HAMMER2_CHAIN_SUBMODIFIED |
			    HAMMER2_CHAIN_MODIFIED |
			    HAMMER2_CHAIN_MODIFIED_AUX |
			    HAMMER2_CHAIN_MOVED)) {
		hammer2_chain_parent_setsubmod(hmp, chain);
	}
	parent = chain->parent;
	if (parent == NULL) {
		return;
	}
	if (chain->bref.type != HAMMER2_BREF_TYPE_INODE ||
	    (chain->flags & (HAMMER2_CHAIN_SUBMODIFIED |
			     HAMMER2_CHAIN_MODIFIED |
			     HAMMER2_CHAIN_MODIFIED_AUX |
			     HAMMER2_CHAIN_MOVED)) != HAMMER2_CHAIN_MOVED) {
		return;
	}
	if (ccms_thread_lock_nonblock(&parent->cst, CCMS_STATE_EXCLUSIVE))
		return;
	hammer2_chain_lock(hmp, parent, HAMMER2_RESOLVE_MAYBE);
	hammer2_chain_modify(hmp, parent, HAMMER2_MODIFY_NO_MODIFY_TID);
	switch(parent->bref.type) {
	case HAMMER2_BREF_TYPE_INODE:
		base = &parent->data->ipdata.u.blockset.
			blockref[0];
		count = HAMMER2_SET_COUNT;
		break;
	case HAMMER2_BREF_TYPE_INDIRECT:
		base = &parent->data->npdata.blockref[0];
		count = parent->bytes /
			sizeof(hammer2_blockref_t);
		break;
	case HAMMER2_BREF_TYPE_VOLUME:
		base = &hmp->voldata.sroot_blockset.blockref[0];
		count = HAMMER2_SET_COUNT;
		if (chain->flags & HAMMER2_CHAIN_MOVED) {
			if (hmp->voldata.mirror_tid < chain->bref.mirror_tid) {
				hmp->voldata.mirror_tid =
					chain->bref.mirror_tid;
			}
		}
		break;
	default:
		base = NULL;
		panic("hammer2_chain_flush: "
		      "unrecognized blockref type: %d",
		      parent->bref.type);
	}
	KKASSERT(chain->index >= 0 &&
		 chain->index < count);
	KKASSERT(chain->parent == parent);
	if (chain->flags & HAMMER2_CHAIN_MOVED) {
		base[chain->index] = chain->bref_flush;
		if (parent->bref.mirror_tid < chain->bref_flush.mirror_tid)
			parent->bref.mirror_tid = chain->bref_flush.mirror_tid;
		atomic_clear_int(&chain->flags, HAMMER2_CHAIN_MOVED);
		hammer2_chain_drop(hmp, chain);
	} else if (bcmp(&base[chain->index], &chain->bref_flush,
		   sizeof(chain->bref)) != 0) {
		panic("hammer2: unflagged bref update(2)");
	}
	ccms_thread_unlock(&parent->cst);		
	hammer2_chain_unlock(hmp, parent);
}
void hammer2_chain_flush ( hammer2_mount_t * hmp , hammer2_chain_t * chain , hammer2_tid_t modify_tid ) { hammer2_chain_t * parent ; hammer2_chain_t * scan ; hammer2_blockref_t * base ; hammer2_flush_info_t info ; int count ; int reflush ; bzero ( & info , sizeof ( info ) ) ; TAILQ_INIT ( & info . flush_list ) ; if ( modify_tid == 0 ) { hammer2_voldata_lock ( hmp ) ; info . modify_tid = hmp -> voldata . alloc_tid ++ ; atomic_set_int ( & hmp -> vchain . flags , HAMMER2_CHAIN_MODIFIED_AUX ) ; hammer2_voldata_unlock ( hmp ) ; } else { info . modify_tid = modify_tid ; } reflush = 1 ; while ( reflush ) { hammer2_chain_flush_pass1 ( hmp , chain , & info ) ; reflush = 0 ; while ( ( scan = TAILQ_FIRST ( & info . flush_list ) ) != NULL ) { KKASSERT ( scan -> flags & HAMMER2_CHAIN_DEFERRED ) ; TAILQ_REMOVE ( & info . flush_list , scan , flush_node ) ; atomic_clear_int ( & scan -> flags , HAMMER2_CHAIN_DEFERRED ) ; if ( hammer2_debug & 0x0040 ) kprintf ( " \n " , scan ) ; hammer2_chain_lock ( hmp , scan , HAMMER2_RESOLVE_MAYBE ) ; hammer2_chain_flush ( hmp , scan , info . modify_tid ) ; hammer2_chain_unlock ( hmp , scan ) ; if ( ( scan -> flags & ( HAMMER2_CHAIN_SUBMODIFIED | HAMMER2_CHAIN_MODIFIED | HAMMER2_CHAIN_MODIFIED_AUX ) ) == 0 ) { reflush = 1 ; } hammer2_chain_drop ( hmp , scan ) ; } if ( ( hammer2_debug & 0x0040 ) && reflush ) kprintf ( " \n " , chain ) ; } if ( chain -> flags & ( HAMMER2_CHAIN_SUBMODIFIED | HAMMER2_CHAIN_MODIFIED | HAMMER2_CHAIN_MODIFIED_AUX | HAMMER2_CHAIN_MOVED ) ) { hammer2_chain_parent_setsubmod ( hmp , chain ) ; } parent = chain -> parent ; if ( parent == NULL ) { return ; } if ( chain -> bref . type != HAMMER2_BREF_TYPE_INODE || ( chain -> flags & ( HAMMER2_CHAIN_SUBMODIFIED | HAMMER2_CHAIN_MODIFIED | HAMMER2_CHAIN_MODIFIED_AUX | HAMMER2_CHAIN_MOVED ) ) != HAMMER2_CHAIN_MOVED ) { return ; } if ( ccms_thread_lock_nonblock ( & parent -> cst , CCMS_STATE_EXCLUSIVE ) ) return ; hammer2_chain_lock ( hmp , parent , HAMMER2_RESOLVE_MAYBE ) ; hammer2_chain_modify ( hmp , parent , HAMMER2_MODIFY_NO_MODIFY_TID ) ; switch ( parent -> bref . type ) { case HAMMER2_BREF_TYPE_INODE : base = & parent -> data -> ipdata . u . blockset . blockref [ 0 ] ; count = HAMMER2_SET_COUNT ; break ; case HAMMER2_BREF_TYPE_INDIRECT : base = & parent -> data -> npdata . blockref [ 0 ] ; count = parent -> bytes / sizeof ( hammer2_blockref_t ) ; break ; case HAMMER2_BREF_TYPE_VOLUME : base = & hmp -> voldata . sroot_blockset . blockref [ 0 ] ; count = HAMMER2_SET_COUNT ; if ( chain -> flags & HAMMER2_CHAIN_MOVED ) { if ( hmp -> voldata . mirror_tid < chain -> bref . mirror_tid ) { hmp -> voldata . mirror_tid = chain -> bref . mirror_tid ; } } break ; default : base = NULL ; panic ( " " " " , parent -> bref . type ) ; } KKASSERT ( chain -> index >= 0 && chain -> index < count ) ; KKASSERT ( chain -> parent == parent ) ; if ( chain -> flags & HAMMER2_CHAIN_MOVED ) { base [ chain -> index ] = chain -> bref_flush ; if ( parent -> bref . mirror_tid < chain -> bref_flush . mirror_tid ) parent -> bref . mirror_tid = chain -> bref_flush . mirror_tid ; atomic_clear_int ( & chain -> flags , HAMMER2_CHAIN_MOVED ) ; hammer2_chain_drop ( hmp , chain ) ; } else if ( bcmp ( & base [ chain -> index ] , & chain -> bref_flush , sizeof ( chain -> bref ) ) != 0 ) { panic ( " " ) ; } ccms_thread_unlock ( & parent -> cst ) ; hammer2_chain_unlock ( hmp , parent ) ; }
Stand-alone flush.
Stand - alone flush .
/*
	 * Execute the recursive flush and handle deferrals.
	 *
	 * Chains can be ridiculously long (thousands deep), so to
	 * avoid blowing out the kernel stack the recursive flush has a
	 * depth limit.  Elements at the limit are placed on a list
	 * for re-execution after the stack has been popped.
	 */ /*
		 * Primary recursion
		 */ /*
			 * Secondary recursion.  Note that a reference is
			 * retained from the element's presence on the
			 * deferral list.
			 */ /*
			 * Now that we've popped back up we can do a secondary
			 * recursion on the deferred elements.
			 */ /*
			 * Only flag a reflush if SUBMODIFIED is no longer
			 * set.  If SUBMODIFIED is set the element will just
			 * wind up on our flush_list again.
			 */ /*
	 * The SUBMODIFIED bit must propagate upward if the chain could not
	 * be completely flushed.
	 */ /*
	 * If the only thing left is a simple bref update try to
	 * pro-actively update the parent, otherwise return early.
	 */ /*
	 * We are locking backwards so allow the lock to fail.
	 */ /*
	 * We are updating brefs but we have to call chain_modify()
	 * because our caller is not being run from a recursive flush.
	 *
	 * This will also chain up the parent list and set the SUBMODIFIED
	 * flag.
	 *
	 * We do not want to set HAMMER2_CHAIN_MODIFY_TID here because the
	 * modification is only related to updating a bref in the parent.
	 *
	 * When updating the blockset embedded in the volume header we must
	 * also update voldata.mirror_tid.
	 */ /*
	 * Update the blockref in the parent.  We do not have to set
	 * MOVED in the parent because the parent has been marked modified,
	 * so the flush sequence will pick up the bref change.
	 *
	 * We do have to propagate mirror_tid upward.
	 */ /* release manual op */