xref: /dflybsd-src/sys/vfs/hammer2/hammer2_synchro.c (revision c8c0a18a66946d3ec5fd24de73f593da9c4af6ba)
13f01ebaaSMatthew Dillon /*
20d66a712SMatthew Dillon  * Copyright (c) 2015-2017 The DragonFly Project.  All rights reserved.
33f01ebaaSMatthew Dillon  *
43f01ebaaSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
53f01ebaaSMatthew Dillon  * by Matthew Dillon <dillon@dragonflybsd.org>
63f01ebaaSMatthew Dillon  *
73f01ebaaSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
83f01ebaaSMatthew Dillon  * modification, are permitted provided that the following conditions
93f01ebaaSMatthew Dillon  * are met:
103f01ebaaSMatthew Dillon  *
113f01ebaaSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
123f01ebaaSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
133f01ebaaSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
143f01ebaaSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
153f01ebaaSMatthew Dillon  *    the documentation and/or other materials provided with the
163f01ebaaSMatthew Dillon  *    distribution.
173f01ebaaSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
183f01ebaaSMatthew Dillon  *    contributors may be used to endorse or promote products derived
193f01ebaaSMatthew Dillon  *    from this software without specific, prior written permission.
203f01ebaaSMatthew Dillon  *
213f01ebaaSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
223f01ebaaSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
233f01ebaaSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
243f01ebaaSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
253f01ebaaSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
263f01ebaaSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
273f01ebaaSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
283f01ebaaSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
293f01ebaaSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
303f01ebaaSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
313f01ebaaSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323f01ebaaSMatthew Dillon  * SUCH DAMAGE.
333f01ebaaSMatthew Dillon  */
343f01ebaaSMatthew Dillon /*
353f01ebaaSMatthew Dillon  * This module implements the cluster synchronizer.  Basically the way
363f01ebaaSMatthew Dillon  * it works is that a thread is created for each cluster node in a PFS.
373f01ebaaSMatthew Dillon  * This thread is responsible for synchronizing the current node using
383f01ebaaSMatthew Dillon  * data from other nodes.
393f01ebaaSMatthew Dillon  *
403f01ebaaSMatthew Dillon  * Any out of sync master or slave can get back into synchronization as
413f01ebaaSMatthew Dillon  * long as a quorum of masters agree on the update_tid.  If a quorum is
423f01ebaaSMatthew Dillon  * not available it may still be possible to synchronize to the highest
433f01ebaaSMatthew Dillon  * available update_tid as a way of trying to catch up as much as possible
443f01ebaaSMatthew Dillon  * until a quorum is available.
453f01ebaaSMatthew Dillon  *
463f01ebaaSMatthew Dillon  * If no quorum is possible (which can happen even if all masters are
473f01ebaaSMatthew Dillon  * available, if the update_tid does not match), then manual intervention
483f01ebaaSMatthew Dillon  * may be required to resolve discrepancies.
493f01ebaaSMatthew Dillon  */
503f01ebaaSMatthew Dillon #include "hammer2.h"
513f01ebaaSMatthew Dillon 
523f01ebaaSMatthew Dillon typedef struct hammer2_deferred_ip {
533f01ebaaSMatthew Dillon 	struct hammer2_deferred_ip *next;
543f01ebaaSMatthew Dillon 	hammer2_inode_t	*ip;
553f01ebaaSMatthew Dillon } hammer2_deferred_ip_t;
563f01ebaaSMatthew Dillon 
573f01ebaaSMatthew Dillon typedef struct hammer2_deferred_list {
583f01ebaaSMatthew Dillon 	hammer2_deferred_ip_t	*base;
593f01ebaaSMatthew Dillon 	int			count;
603f01ebaaSMatthew Dillon } hammer2_deferred_list_t;
613f01ebaaSMatthew Dillon 
623f01ebaaSMatthew Dillon 
633f01ebaaSMatthew Dillon #define HAMMER2_SYNCHRO_DEBUG 1
643f01ebaaSMatthew Dillon 
653f01ebaaSMatthew Dillon static int hammer2_sync_slaves(hammer2_thread_t *thr, hammer2_inode_t *ip,
66b02c0ae6SMatthew Dillon 				hammer2_deferred_list_t *list, int isroot);
673f01ebaaSMatthew Dillon #if 0
683f01ebaaSMatthew Dillon static void hammer2_update_pfs_status(hammer2_thread_t *thr, uint32_t flags);
693f01ebaaSMatthew Dillon 				nerror = hammer2_sync_insert(
703f01ebaaSMatthew Dillon 						thr, &parent, &chain,
713f01ebaaSMatthew Dillon 						focus->bref.modify_tid,
723f01ebaaSMatthew Dillon 						idx, focus);
733f01ebaaSMatthew Dillon #endif
743f01ebaaSMatthew Dillon static int hammer2_sync_insert(hammer2_thread_t *thr,
753f01ebaaSMatthew Dillon 			hammer2_chain_t **parentp, hammer2_chain_t **chainp,
763f01ebaaSMatthew Dillon 			hammer2_tid_t modify_tid, int idx,
773f01ebaaSMatthew Dillon 			hammer2_chain_t *focus);
783f01ebaaSMatthew Dillon static int hammer2_sync_destroy(hammer2_thread_t *thr,
793f01ebaaSMatthew Dillon 			hammer2_chain_t **parentp, hammer2_chain_t **chainp,
803f01ebaaSMatthew Dillon 			hammer2_tid_t mtid, int idx);
813f01ebaaSMatthew Dillon static int hammer2_sync_replace(hammer2_thread_t *thr,
823f01ebaaSMatthew Dillon 			hammer2_chain_t *parent, hammer2_chain_t *chain,
833f01ebaaSMatthew Dillon 			hammer2_tid_t mtid, int idx,
84b02c0ae6SMatthew Dillon 			hammer2_chain_t *focus, int isroot);
853f01ebaaSMatthew Dillon 
863f01ebaaSMatthew Dillon /****************************************************************************
873f01ebaaSMatthew Dillon  *			    HAMMER2 SYNC THREADS 			    *
883f01ebaaSMatthew Dillon  ****************************************************************************/
893f01ebaaSMatthew Dillon /*
903f01ebaaSMatthew Dillon  * Primary management thread for an element of a node.  A thread will exist
913f01ebaaSMatthew Dillon  * for each element requiring management.
923f01ebaaSMatthew Dillon  *
933f01ebaaSMatthew Dillon  * No management threads are needed for the SPMP or for any PMP with only
943f01ebaaSMatthew Dillon  * a single MASTER.
953f01ebaaSMatthew Dillon  *
963f01ebaaSMatthew Dillon  * On the SPMP - handles bulkfree and dedup operations
973f01ebaaSMatthew Dillon  * On a PFS    - handles remastering and synchronization
983f01ebaaSMatthew Dillon  */
993f01ebaaSMatthew Dillon void
1003f01ebaaSMatthew Dillon hammer2_primary_sync_thread(void *arg)
1013f01ebaaSMatthew Dillon {
1023f01ebaaSMatthew Dillon 	hammer2_thread_t *thr = arg;
1033f01ebaaSMatthew Dillon 	hammer2_pfs_t *pmp;
1043f01ebaaSMatthew Dillon 	hammer2_deferred_list_t list;
1053f01ebaaSMatthew Dillon 	hammer2_deferred_ip_t *defer;
1063f01ebaaSMatthew Dillon 	int error;
107660d007eSMatthew Dillon 	uint32_t flags;
108660d007eSMatthew Dillon 	uint32_t nflags;
1093f01ebaaSMatthew Dillon 
1103f01ebaaSMatthew Dillon 	pmp = thr->pmp;
1113f01ebaaSMatthew Dillon 	bzero(&list, sizeof(list));
1123f01ebaaSMatthew Dillon 
113660d007eSMatthew Dillon 	for (;;) {
114660d007eSMatthew Dillon 		flags = thr->flags;
115660d007eSMatthew Dillon 		cpu_ccfence();
116660d007eSMatthew Dillon 
117660d007eSMatthew Dillon 		/*
118660d007eSMatthew Dillon 		 * Handle stop request
119660d007eSMatthew Dillon 		 */
120660d007eSMatthew Dillon 		if (flags & HAMMER2_THREAD_STOP)
121660d007eSMatthew Dillon 			break;
122660d007eSMatthew Dillon 
1233f01ebaaSMatthew Dillon 		/*
1243f01ebaaSMatthew Dillon 		 * Handle freeze request
1253f01ebaaSMatthew Dillon 		 */
126660d007eSMatthew Dillon 		if (flags & HAMMER2_THREAD_FREEZE) {
127660d007eSMatthew Dillon 			nflags = (flags & ~(HAMMER2_THREAD_FREEZE |
1289dca9515SMatthew Dillon 					    HAMMER2_THREAD_WAITING)) |
129660d007eSMatthew Dillon 				 HAMMER2_THREAD_FROZEN;
130660d007eSMatthew Dillon 			if (!atomic_cmpset_int(&thr->flags, flags, nflags))
131660d007eSMatthew Dillon 				continue;
1329dca9515SMatthew Dillon 			if (flags & HAMMER2_THREAD_WAITING)
133660d007eSMatthew Dillon 				wakeup(&thr->flags);
13459eb0066SMatthew Dillon 			continue;
135660d007eSMatthew Dillon 		}
136660d007eSMatthew Dillon 
137660d007eSMatthew Dillon 		if (flags & HAMMER2_THREAD_UNFREEZE) {
138660d007eSMatthew Dillon 			nflags = flags & ~(HAMMER2_THREAD_UNFREEZE |
139660d007eSMatthew Dillon 					   HAMMER2_THREAD_FROZEN |
1409dca9515SMatthew Dillon 					   HAMMER2_THREAD_WAITING);
141660d007eSMatthew Dillon 			if (!atomic_cmpset_int(&thr->flags, flags, nflags))
142660d007eSMatthew Dillon 				continue;
1439dca9515SMatthew Dillon 			if (flags & HAMMER2_THREAD_WAITING)
144660d007eSMatthew Dillon 				wakeup(&thr->flags);
14559eb0066SMatthew Dillon 			continue;
1463f01ebaaSMatthew Dillon 		}
1473f01ebaaSMatthew Dillon 
1483f01ebaaSMatthew Dillon 		/*
1493f01ebaaSMatthew Dillon 		 * Force idle if frozen until unfrozen or stopped.
1503f01ebaaSMatthew Dillon 		 */
151660d007eSMatthew Dillon 		if (flags & HAMMER2_THREAD_FROZEN) {
152660d007eSMatthew Dillon 			nflags = flags | HAMMER2_THREAD_WAITING;
15359eb0066SMatthew Dillon 
154660d007eSMatthew Dillon 			tsleep_interlock(&thr->flags, 0);
15559eb0066SMatthew Dillon 			if (atomic_cmpset_int(&thr->flags, flags, nflags))
156660d007eSMatthew Dillon 				tsleep(&thr->flags, PINTERLOCKED, "frozen", 0);
1573f01ebaaSMatthew Dillon 			continue;
1583f01ebaaSMatthew Dillon 		}
1593f01ebaaSMatthew Dillon 
1603f01ebaaSMatthew Dillon 		/*
1613f01ebaaSMatthew Dillon 		 * Reset state on REMASTER request
1623f01ebaaSMatthew Dillon 		 */
1633f01ebaaSMatthew Dillon 		if (thr->flags & HAMMER2_THREAD_REMASTER) {
164660d007eSMatthew Dillon 			nflags = flags & ~HAMMER2_THREAD_REMASTER;
165660d007eSMatthew Dillon 			if (atomic_cmpset_int(&thr->flags, flags, nflags)) {
166660d007eSMatthew Dillon 				/* reset state here */
167660d007eSMatthew Dillon 			}
168660d007eSMatthew Dillon 			continue;
1693f01ebaaSMatthew Dillon 		}
1703f01ebaaSMatthew Dillon 
1713f01ebaaSMatthew Dillon 		/*
1723f01ebaaSMatthew Dillon 		 * Synchronization scan.
1733f01ebaaSMatthew Dillon 		 */
1743cbe226bSMatthew Dillon 		if (hammer2_debug & 0x8000)
1753f01ebaaSMatthew Dillon 			kprintf("sync_slaves pfs %s clindex %d\n",
1763f01ebaaSMatthew Dillon 				pmp->pfs_names[thr->clindex], thr->clindex);
1773f01ebaaSMatthew Dillon 		hammer2_trans_init(pmp, 0);
1783f01ebaaSMatthew Dillon 
1793f01ebaaSMatthew Dillon 		hammer2_inode_ref(pmp->iroot);
1803f01ebaaSMatthew Dillon 
1813f01ebaaSMatthew Dillon 		for (;;) {
1823f01ebaaSMatthew Dillon 			int didbreak = 0;
1833f01ebaaSMatthew Dillon 			/* XXX lock synchronize pmp->modify_tid */
184b02c0ae6SMatthew Dillon 			error = hammer2_sync_slaves(thr, pmp->iroot, &list, 1);
185b02c0ae6SMatthew Dillon 			if (hammer2_debug & 0x8000) {
186b02c0ae6SMatthew Dillon 				kprintf("sync_slaves error %d defer %p\n",
187b02c0ae6SMatthew Dillon 					error, list.base);
188b02c0ae6SMatthew Dillon 			}
1893f01ebaaSMatthew Dillon 			if (error != EAGAIN)
1903f01ebaaSMatthew Dillon 				break;
1913f01ebaaSMatthew Dillon 			while ((defer = list.base) != NULL) {
1923f01ebaaSMatthew Dillon 				hammer2_inode_t *nip;
1933f01ebaaSMatthew Dillon 
1943f01ebaaSMatthew Dillon 				nip = defer->ip;
1950d66a712SMatthew Dillon 				error = hammer2_sync_slaves(thr, nip, &list,
1960d66a712SMatthew Dillon 							(nip == pmp->iroot));
197b02c0ae6SMatthew Dillon 				if (error && error != EAGAIN && error != ENOENT)
1983f01ebaaSMatthew Dillon 					break;
1993f01ebaaSMatthew Dillon 				if (hammer2_thr_break(thr)) {
2003f01ebaaSMatthew Dillon 					didbreak = 1;
2013f01ebaaSMatthew Dillon 					break;
2023f01ebaaSMatthew Dillon 				}
2033f01ebaaSMatthew Dillon 
2043f01ebaaSMatthew Dillon 				/*
2053f01ebaaSMatthew Dillon 				 * If no additional defers occurred we can
206b02c0ae6SMatthew Dillon 				 * remove this one, otherwise keep it on
2073f01ebaaSMatthew Dillon 				 * the list and retry once the additional
2083f01ebaaSMatthew Dillon 				 * defers have completed.
2093f01ebaaSMatthew Dillon 				 */
2103f01ebaaSMatthew Dillon 				if (defer == list.base) {
2113f01ebaaSMatthew Dillon 					--list.count;
2123f01ebaaSMatthew Dillon 					list.base = defer->next;
2133f01ebaaSMatthew Dillon 					kfree(defer, M_HAMMER2);
2143f01ebaaSMatthew Dillon 					defer = NULL;	/* safety */
2153f01ebaaSMatthew Dillon 					hammer2_inode_drop(nip);
2163f01ebaaSMatthew Dillon 				}
2173f01ebaaSMatthew Dillon 			}
2183f01ebaaSMatthew Dillon 
2193f01ebaaSMatthew Dillon 			/*
2203f01ebaaSMatthew Dillon 			 * If the thread is being remastered, frozen, or
2213f01ebaaSMatthew Dillon 			 * stopped, clean up any left-over deferals.
2223f01ebaaSMatthew Dillon 			 */
2233f01ebaaSMatthew Dillon 			if (didbreak || (error && error != EAGAIN)) {
2243f01ebaaSMatthew Dillon 				kprintf("didbreak\n");
2253f01ebaaSMatthew Dillon 				while ((defer = list.base) != NULL) {
2263f01ebaaSMatthew Dillon 					--list.count;
2273f01ebaaSMatthew Dillon 					hammer2_inode_drop(defer->ip);
2283f01ebaaSMatthew Dillon 					list.base = defer->next;
2293f01ebaaSMatthew Dillon 					kfree(defer, M_HAMMER2);
2303f01ebaaSMatthew Dillon 				}
2313f01ebaaSMatthew Dillon 				if (error == 0 || error == EAGAIN)
2323f01ebaaSMatthew Dillon 					error = EINPROGRESS;
2333f01ebaaSMatthew Dillon 				break;
2343f01ebaaSMatthew Dillon 			}
2353f01ebaaSMatthew Dillon 		}
2363f01ebaaSMatthew Dillon 
2373f01ebaaSMatthew Dillon 		hammer2_inode_drop(pmp->iroot);
2383f01ebaaSMatthew Dillon 		hammer2_trans_done(pmp);
2393f01ebaaSMatthew Dillon 
2403cbe226bSMatthew Dillon 		if (error && error != EINPROGRESS)
2413f01ebaaSMatthew Dillon 			kprintf("hammer2_sync_slaves: error %d\n", error);
2423f01ebaaSMatthew Dillon 
2433f01ebaaSMatthew Dillon 		/*
2443f01ebaaSMatthew Dillon 		 * Wait for event, or 5-second poll.
2453f01ebaaSMatthew Dillon 		 */
246660d007eSMatthew Dillon 		nflags = flags | HAMMER2_THREAD_WAITING;
247660d007eSMatthew Dillon 		tsleep_interlock(&thr->flags, 0);
248660d007eSMatthew Dillon 		if (atomic_cmpset_int(&thr->flags, flags, nflags)) {
249660d007eSMatthew Dillon 			tsleep(&thr->flags, 0, "h2idle", hz * 5);
250660d007eSMatthew Dillon 		}
2513f01ebaaSMatthew Dillon 	}
2523f01ebaaSMatthew Dillon 	thr->td = NULL;
2539dca9515SMatthew Dillon 	hammer2_thr_signal(thr, HAMMER2_THREAD_STOPPED);
2543f01ebaaSMatthew Dillon 	/* thr structure can go invalid after this point */
2553f01ebaaSMatthew Dillon }
2563f01ebaaSMatthew Dillon 
2573f01ebaaSMatthew Dillon #if 0
2583f01ebaaSMatthew Dillon /*
2593f01ebaaSMatthew Dillon  * Given a locked cluster created from pmp->iroot, update the PFS's
2603f01ebaaSMatthew Dillon  * reporting status.
2613f01ebaaSMatthew Dillon  */
2623f01ebaaSMatthew Dillon static
2633f01ebaaSMatthew Dillon void
2643f01ebaaSMatthew Dillon hammer2_update_pfs_status(hammer2_thread_t *thr, uint32_t flags)
2653f01ebaaSMatthew Dillon {
2663f01ebaaSMatthew Dillon 	hammer2_pfs_t *pmp = thr->pmp;
2673f01ebaaSMatthew Dillon 
2683f01ebaaSMatthew Dillon 	flags &= HAMMER2_CLUSTER_ZFLAGS;
2693f01ebaaSMatthew Dillon 	if (pmp->cluster_flags == flags)
2703f01ebaaSMatthew Dillon 		return;
2713f01ebaaSMatthew Dillon 	pmp->cluster_flags = flags;
2723f01ebaaSMatthew Dillon 
2733f01ebaaSMatthew Dillon 	kprintf("pfs %p", pmp);
2743f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_MSYNCED)
2753f01ebaaSMatthew Dillon 		kprintf(" masters-all-good");
2763f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_SSYNCED)
2773f01ebaaSMatthew Dillon 		kprintf(" slaves-all-good");
2783f01ebaaSMatthew Dillon 
2793f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_WRHARD)
2803f01ebaaSMatthew Dillon 		kprintf(" quorum/rw");
2813f01ebaaSMatthew Dillon 	else if (flags & HAMMER2_CLUSTER_RDHARD)
2823f01ebaaSMatthew Dillon 		kprintf(" quorum/ro");
2833f01ebaaSMatthew Dillon 
2843f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_UNHARD)
2853f01ebaaSMatthew Dillon 		kprintf(" out-of-sync-masters");
2863f01ebaaSMatthew Dillon 	else if (flags & HAMMER2_CLUSTER_NOHARD)
2873f01ebaaSMatthew Dillon 		kprintf(" no-masters-visible");
2883f01ebaaSMatthew Dillon 
2893f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_WRSOFT)
2903f01ebaaSMatthew Dillon 		kprintf(" soft/rw");
2913f01ebaaSMatthew Dillon 	else if (flags & HAMMER2_CLUSTER_RDSOFT)
2923f01ebaaSMatthew Dillon 		kprintf(" soft/ro");
2933f01ebaaSMatthew Dillon 
2943f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_UNSOFT)
2953f01ebaaSMatthew Dillon 		kprintf(" out-of-sync-slaves");
2963f01ebaaSMatthew Dillon 	else if (flags & HAMMER2_CLUSTER_NOSOFT)
2973f01ebaaSMatthew Dillon 		kprintf(" no-slaves-visible");
2983f01ebaaSMatthew Dillon 	kprintf("\n");
2993f01ebaaSMatthew Dillon }
3003f01ebaaSMatthew Dillon #endif
3013f01ebaaSMatthew Dillon 
3023f01ebaaSMatthew Dillon #if 0
3033f01ebaaSMatthew Dillon static
3043f01ebaaSMatthew Dillon void
3053f01ebaaSMatthew Dillon dumpcluster(const char *label,
3063f01ebaaSMatthew Dillon 	    hammer2_cluster_t *cparent, hammer2_cluster_t *cluster)
3073f01ebaaSMatthew Dillon {
3083f01ebaaSMatthew Dillon 	hammer2_chain_t *chain;
3093f01ebaaSMatthew Dillon 	int i;
3103f01ebaaSMatthew Dillon 
3113f01ebaaSMatthew Dillon 	if ((hammer2_debug & 1) == 0)
3123f01ebaaSMatthew Dillon 		return;
3133f01ebaaSMatthew Dillon 
3143f01ebaaSMatthew Dillon 	kprintf("%s\t", label);
3153f01ebaaSMatthew Dillon 	KKASSERT(cparent->nchains == cluster->nchains);
3163f01ebaaSMatthew Dillon 	for (i = 0; i < cparent->nchains; ++i) {
3173f01ebaaSMatthew Dillon 		if (i)
3183f01ebaaSMatthew Dillon 			kprintf("\t");
3193f01ebaaSMatthew Dillon 		kprintf("%d ", i);
3203f01ebaaSMatthew Dillon 		if ((chain = cparent->array[i].chain) != NULL) {
3213f01ebaaSMatthew Dillon 			kprintf("%016jx%s ",
3223f01ebaaSMatthew Dillon 				chain->bref.key,
3233f01ebaaSMatthew Dillon 				((cparent->array[i].flags &
3243f01ebaaSMatthew Dillon 				  HAMMER2_CITEM_INVALID) ? "(I)" : "   ")
3253f01ebaaSMatthew Dillon 			);
3263f01ebaaSMatthew Dillon 		} else {
3273f01ebaaSMatthew Dillon 			kprintf("      NULL      %s ", "   ");
3283f01ebaaSMatthew Dillon 		}
3293f01ebaaSMatthew Dillon 		if ((chain = cluster->array[i].chain) != NULL) {
3303f01ebaaSMatthew Dillon 			kprintf("%016jx%s ",
3313f01ebaaSMatthew Dillon 				chain->bref.key,
3323f01ebaaSMatthew Dillon 				((cluster->array[i].flags &
3333f01ebaaSMatthew Dillon 				  HAMMER2_CITEM_INVALID) ? "(I)" : "   ")
3343f01ebaaSMatthew Dillon 			);
3353f01ebaaSMatthew Dillon 		} else {
3363f01ebaaSMatthew Dillon 			kprintf("      NULL      %s ", "   ");
3373f01ebaaSMatthew Dillon 		}
3383f01ebaaSMatthew Dillon 		kprintf("\n");
3393f01ebaaSMatthew Dillon 	}
3403f01ebaaSMatthew Dillon }
3413f01ebaaSMatthew Dillon #endif
3423f01ebaaSMatthew Dillon 
3433f01ebaaSMatthew Dillon /*
3443f01ebaaSMatthew Dillon  * Each out of sync node sync-thread must issue an all-nodes XOP scan of
3453f01ebaaSMatthew Dillon  * the inode.  This creates a multiplication effect since the XOP scan itself
3463f01ebaaSMatthew Dillon  * issues to all nodes.  However, this is the only way we can safely
3473f01ebaaSMatthew Dillon  * synchronize nodes which might have disparate I/O bandwidths and the only
3483f01ebaaSMatthew Dillon  * way we can safely deal with stalled nodes.
349*c8c0a18aSMatthew Dillon  *
350*c8c0a18aSMatthew Dillon  * XXX serror / merror rollup and handling.
3513f01ebaaSMatthew Dillon  */
3523f01ebaaSMatthew Dillon static
3533f01ebaaSMatthew Dillon int
3543f01ebaaSMatthew Dillon hammer2_sync_slaves(hammer2_thread_t *thr, hammer2_inode_t *ip,
355b02c0ae6SMatthew Dillon 		    hammer2_deferred_list_t *list, int isroot)
3563f01ebaaSMatthew Dillon {
3573f01ebaaSMatthew Dillon 	hammer2_xop_scanall_t *xop;
3583f01ebaaSMatthew Dillon 	hammer2_chain_t *parent;
3593f01ebaaSMatthew Dillon 	hammer2_chain_t *chain;
3603f01ebaaSMatthew Dillon 	hammer2_pfs_t *pmp;
3613f01ebaaSMatthew Dillon 	hammer2_key_t key_next;
3623f01ebaaSMatthew Dillon 	hammer2_tid_t sync_tid;
3633f01ebaaSMatthew Dillon 	int needrescan;
364b02c0ae6SMatthew Dillon 	int want_update;
365*c8c0a18aSMatthew Dillon 	int serror;		/* slave error */
366*c8c0a18aSMatthew Dillon 	int merror;		/* master error (from xop_collect) */
367*c8c0a18aSMatthew Dillon 	int nerror;		/* temporary error */
3683f01ebaaSMatthew Dillon 	int idx;
3693f01ebaaSMatthew Dillon 	int n;
3703f01ebaaSMatthew Dillon 
3713f01ebaaSMatthew Dillon 	pmp = ip->pmp;
3723f01ebaaSMatthew Dillon 	idx = thr->clindex;	/* cluster node we are responsible for */
3733f01ebaaSMatthew Dillon 	needrescan = 0;
374b02c0ae6SMatthew Dillon 	want_update = 0;
375b02c0ae6SMatthew Dillon 	sync_tid = 0;
376b02c0ae6SMatthew Dillon 	chain = NULL;
377b02c0ae6SMatthew Dillon 	parent = NULL;
3783f01ebaaSMatthew Dillon 
3793f01ebaaSMatthew Dillon #if 0
3803f01ebaaSMatthew Dillon 	/*
3813f01ebaaSMatthew Dillon 	 * Nothing to do if all slaves are synchronized.
3823f01ebaaSMatthew Dillon 	 * Nothing to do if cluster not authoritatively readable.
3833f01ebaaSMatthew Dillon 	 */
3843f01ebaaSMatthew Dillon 	if (pmp->cluster_flags & HAMMER2_CLUSTER_SSYNCED)
3853f01ebaaSMatthew Dillon 		return(0);
3863f01ebaaSMatthew Dillon 	if ((pmp->cluster_flags & HAMMER2_CLUSTER_RDHARD) == 0)
3873f01ebaaSMatthew Dillon 		return(HAMMER2_ERROR_INCOMPLETE);
3883f01ebaaSMatthew Dillon #endif
3893f01ebaaSMatthew Dillon 
390*c8c0a18aSMatthew Dillon 	merror = 0;
3913f01ebaaSMatthew Dillon 
3923f01ebaaSMatthew Dillon 	/*
393b02c0ae6SMatthew Dillon 	 * Resolve the root inode of the PFS and determine if synchronization
394b02c0ae6SMatthew Dillon 	 * is needed by checking modify_tid.
3950d66a712SMatthew Dillon 	 *
3960d66a712SMatthew Dillon 	 * Retain the synchronization TID from the focus inode and use it
3970d66a712SMatthew Dillon 	 * later to synchronize the focus inode if/when the recursion
3980d66a712SMatthew Dillon 	 * succeeds.
399b02c0ae6SMatthew Dillon 	 */
400b02c0ae6SMatthew Dillon 	{
401b02c0ae6SMatthew Dillon 		hammer2_xop_ipcluster_t *xop2;
402b02c0ae6SMatthew Dillon 		hammer2_chain_t *focus;
403b02c0ae6SMatthew Dillon 
404b02c0ae6SMatthew Dillon 		hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
405b02c0ae6SMatthew Dillon 		xop2 = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
406b02c0ae6SMatthew Dillon 		hammer2_xop_start_except(&xop2->head, hammer2_xop_ipcluster,
407b02c0ae6SMatthew Dillon 					 idx);
408b02c0ae6SMatthew Dillon 		hammer2_inode_unlock(ip);
409*c8c0a18aSMatthew Dillon 		merror = hammer2_xop_collect(&xop2->head, 0);
410*c8c0a18aSMatthew Dillon 		if (merror == 0 && (focus = xop2->head.cluster.focus) != NULL) {
4110d66a712SMatthew Dillon 			sync_tid = focus->bref.modify_tid;
412b02c0ae6SMatthew Dillon 			chain = hammer2_inode_chain_and_parent(ip, idx,
413b02c0ae6SMatthew Dillon 						    &parent,
414b02c0ae6SMatthew Dillon 						    HAMMER2_RESOLVE_ALWAYS |
415b02c0ae6SMatthew Dillon 						    HAMMER2_RESOLVE_SHARED);
416b02c0ae6SMatthew Dillon 			want_update = (chain->bref.modify_tid != sync_tid);
417b02c0ae6SMatthew Dillon 			if (chain) {
418b02c0ae6SMatthew Dillon 				hammer2_chain_unlock(chain);
419b02c0ae6SMatthew Dillon 				hammer2_chain_drop(chain);
420b02c0ae6SMatthew Dillon 				chain = NULL;
421b02c0ae6SMatthew Dillon 			}
422b02c0ae6SMatthew Dillon 			if (parent) {
423b02c0ae6SMatthew Dillon 				hammer2_chain_unlock(parent);
424b02c0ae6SMatthew Dillon 				hammer2_chain_drop(parent);
425b02c0ae6SMatthew Dillon 				parent = NULL;
426b02c0ae6SMatthew Dillon 			}
427b02c0ae6SMatthew Dillon 		}
428b02c0ae6SMatthew Dillon 		hammer2_xop_retire(&xop2->head, HAMMER2_XOPMASK_VOP);
429b02c0ae6SMatthew Dillon 	}
430b02c0ae6SMatthew Dillon 
431b02c0ae6SMatthew Dillon 	if (want_update == 0)
432b02c0ae6SMatthew Dillon 		return(0);
433b02c0ae6SMatthew Dillon 
434b02c0ae6SMatthew Dillon 	/*
4353f01ebaaSMatthew Dillon 	 * The inode is left unlocked during the scan.  Issue a XOP
4363f01ebaaSMatthew Dillon 	 * that does *not* include our cluster index to iterate
4373f01ebaaSMatthew Dillon 	 * properly synchronized elements and resolve our cluster index
4383f01ebaaSMatthew Dillon 	 * against it.
4393f01ebaaSMatthew Dillon 	 */
4403f01ebaaSMatthew Dillon 	hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
4413f01ebaaSMatthew Dillon 	xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
4423f01ebaaSMatthew Dillon 	xop->key_beg = HAMMER2_KEY_MIN;
4433f01ebaaSMatthew Dillon 	xop->key_end = HAMMER2_KEY_MAX;
444b02c0ae6SMatthew Dillon 	xop->resolve_flags = HAMMER2_RESOLVE_SHARED |
445b02c0ae6SMatthew Dillon 			     HAMMER2_RESOLVE_ALWAYS;
446b02c0ae6SMatthew Dillon 	xop->lookup_flags = HAMMER2_LOOKUP_SHARED |
447b02c0ae6SMatthew Dillon 			    HAMMER2_LOOKUP_NODIRECT |
448b02c0ae6SMatthew Dillon 			    HAMMER2_LOOKUP_ALWAYS;
4493f01ebaaSMatthew Dillon 	hammer2_xop_start_except(&xop->head, hammer2_xop_scanall, idx);
4503f01ebaaSMatthew Dillon 	parent = hammer2_inode_chain(ip, idx,
4513f01ebaaSMatthew Dillon 				     HAMMER2_RESOLVE_ALWAYS |
4523f01ebaaSMatthew Dillon 				     HAMMER2_RESOLVE_SHARED);
4533f01ebaaSMatthew Dillon 	hammer2_inode_unlock(ip);
4543f01ebaaSMatthew Dillon 
4553f01ebaaSMatthew Dillon 	chain = hammer2_chain_lookup(&parent, &key_next,
4563f01ebaaSMatthew Dillon 				     HAMMER2_KEY_MIN, HAMMER2_KEY_MAX,
457*c8c0a18aSMatthew Dillon 				     &serror,
4583f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_SHARED |
4593f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_NODIRECT |
4603f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_NODATA);
461*c8c0a18aSMatthew Dillon 	serror = hammer2_error_to_errno(serror);
462*c8c0a18aSMatthew Dillon 	merror = hammer2_xop_collect(&xop->head, 0);
4630d66a712SMatthew Dillon 	if (hammer2_debug & 0x8000) {
464b02c0ae6SMatthew Dillon 		kprintf("START_SCAN IP=%016jx chain=%p (%016jx)\n",
465b02c0ae6SMatthew Dillon 			ip->meta.name_key, chain,
466b02c0ae6SMatthew Dillon 			(chain ? chain->bref.key : -1));
4670d66a712SMatthew Dillon 	}
4683f01ebaaSMatthew Dillon 
4693f01ebaaSMatthew Dillon 	for (;;) {
4703f01ebaaSMatthew Dillon 		/*
4713f01ebaaSMatthew Dillon 		 * We are done if our scan is done and the XOP scan is done.
4723f01ebaaSMatthew Dillon 		 * We are done if the XOP scan failed (that is, we don't
4733f01ebaaSMatthew Dillon 		 * have authoritative data to synchronize with).
4743f01ebaaSMatthew Dillon 		 */
4753f01ebaaSMatthew Dillon 		int advance_local = 0;
4763f01ebaaSMatthew Dillon 		int advance_xop = 0;
4773f01ebaaSMatthew Dillon 		int dodefer = 0;
4783f01ebaaSMatthew Dillon 		hammer2_chain_t *focus;
4793f01ebaaSMatthew Dillon 
480*c8c0a18aSMatthew Dillon 		if (chain == NULL && merror == ENOENT)
4813f01ebaaSMatthew Dillon 			break;
482*c8c0a18aSMatthew Dillon 		if (merror && merror != ENOENT)
4833f01ebaaSMatthew Dillon 			break;
4843f01ebaaSMatthew Dillon 
4853f01ebaaSMatthew Dillon 		/*
4863f01ebaaSMatthew Dillon 		 * Compare
4873f01ebaaSMatthew Dillon 		 */
488*c8c0a18aSMatthew Dillon 		if (chain && merror == ENOENT) {
4893f01ebaaSMatthew Dillon 			/*
4903f01ebaaSMatthew Dillon 			 * If we have local chains but the XOP scan is done,
4913f01ebaaSMatthew Dillon 			 * the chains need to be deleted.
4923f01ebaaSMatthew Dillon 			 */
4933f01ebaaSMatthew Dillon 			n = -1;
4943f01ebaaSMatthew Dillon 			focus = NULL;
4953f01ebaaSMatthew Dillon 		} else if (chain == NULL) {
4963f01ebaaSMatthew Dillon 			/*
4973f01ebaaSMatthew Dillon 			 * If our local scan is done but the XOP scan is not,
4983f01ebaaSMatthew Dillon 			 * we need to create the missing chain(s).
4993f01ebaaSMatthew Dillon 			 */
5003f01ebaaSMatthew Dillon 			n = 1;
5013f01ebaaSMatthew Dillon 			focus = xop->head.cluster.focus;
5023f01ebaaSMatthew Dillon 		} else {
5033f01ebaaSMatthew Dillon 			/*
5043f01ebaaSMatthew Dillon 			 * Otherwise compare to determine the action
5053f01ebaaSMatthew Dillon 			 * needed.
5063f01ebaaSMatthew Dillon 			 */
5073f01ebaaSMatthew Dillon 			focus = xop->head.cluster.focus;
5083f01ebaaSMatthew Dillon 			n = hammer2_chain_cmp(chain, focus);
5093f01ebaaSMatthew Dillon 		}
5103f01ebaaSMatthew Dillon 
5113f01ebaaSMatthew Dillon 		/*
5123f01ebaaSMatthew Dillon 		 * Take action based on comparison results.
5133f01ebaaSMatthew Dillon 		 */
5143f01ebaaSMatthew Dillon 		if (n < 0) {
5153f01ebaaSMatthew Dillon 			/*
5163f01ebaaSMatthew Dillon 			 * Delete extranious local data.  This will
5173f01ebaaSMatthew Dillon 			 * automatically advance the chain.
5183f01ebaaSMatthew Dillon 			 */
5193f01ebaaSMatthew Dillon 			nerror = hammer2_sync_destroy(thr, &parent, &chain,
5203f01ebaaSMatthew Dillon 						      0, idx);
5213f01ebaaSMatthew Dillon 		} else if (n == 0 && chain->bref.modify_tid !=
5223f01ebaaSMatthew Dillon 				     focus->bref.modify_tid) {
5233f01ebaaSMatthew Dillon 			/*
5243f01ebaaSMatthew Dillon 			 * Matching key but local data or meta-data requires
5253f01ebaaSMatthew Dillon 			 * updating.  If we will recurse, we still need to
5263f01ebaaSMatthew Dillon 			 * update to compatible content first but we do not
5273f01ebaaSMatthew Dillon 			 * synchronize modify_tid until the entire recursion
5283f01ebaaSMatthew Dillon 			 * has completed successfully.
5293f01ebaaSMatthew Dillon 			 */
530da0cdd33SMatthew Dillon 			if (focus->bref.type == HAMMER2_BREF_TYPE_INODE) {
5313f01ebaaSMatthew Dillon 				nerror = hammer2_sync_replace(
5323f01ebaaSMatthew Dillon 						thr, parent, chain,
5333f01ebaaSMatthew Dillon 						0,
534b02c0ae6SMatthew Dillon 						idx, focus, 0);
5353f01ebaaSMatthew Dillon 				dodefer = 1;
5363f01ebaaSMatthew Dillon 			} else {
5373f01ebaaSMatthew Dillon 				nerror = hammer2_sync_replace(
5383f01ebaaSMatthew Dillon 						thr, parent, chain,
5393f01ebaaSMatthew Dillon 						focus->bref.modify_tid,
540b02c0ae6SMatthew Dillon 						idx, focus, 0);
5413f01ebaaSMatthew Dillon 			}
542b02c0ae6SMatthew Dillon 			advance_local = 1;
543b02c0ae6SMatthew Dillon 			advance_xop = 1;
5443f01ebaaSMatthew Dillon 		} else if (n == 0) {
5453f01ebaaSMatthew Dillon 			/*
5463f01ebaaSMatthew Dillon 			 * 100% match, advance both
5473f01ebaaSMatthew Dillon 			 */
5483f01ebaaSMatthew Dillon 			advance_local = 1;
5493f01ebaaSMatthew Dillon 			advance_xop = 1;
5503f01ebaaSMatthew Dillon 			nerror = 0;
5513f01ebaaSMatthew Dillon 		} else if (n > 0) {
5523f01ebaaSMatthew Dillon 			/*
5533f01ebaaSMatthew Dillon 			 * Insert missing local data.
5543f01ebaaSMatthew Dillon 			 *
5553f01ebaaSMatthew Dillon 			 * If we will recurse, we still need to update to
5563f01ebaaSMatthew Dillon 			 * compatible content first but we do not synchronize
5573f01ebaaSMatthew Dillon 			 * modify_tid until the entire recursion has
5583f01ebaaSMatthew Dillon 			 * completed successfully.
5593f01ebaaSMatthew Dillon 			 */
560da0cdd33SMatthew Dillon 			if (focus->bref.type == HAMMER2_BREF_TYPE_INODE) {
5613f01ebaaSMatthew Dillon 				nerror = hammer2_sync_insert(
5623f01ebaaSMatthew Dillon 						thr, &parent, &chain,
5633f01ebaaSMatthew Dillon 						0,
5643f01ebaaSMatthew Dillon 						idx, focus);
5653f01ebaaSMatthew Dillon 				dodefer = 2;
5663f01ebaaSMatthew Dillon 			} else {
5673f01ebaaSMatthew Dillon 				nerror = hammer2_sync_insert(
5683f01ebaaSMatthew Dillon 						thr, &parent, &chain,
5693f01ebaaSMatthew Dillon 						focus->bref.modify_tid,
5703f01ebaaSMatthew Dillon 						idx, focus);
5713f01ebaaSMatthew Dillon 			}
5723f01ebaaSMatthew Dillon 			advance_local = 1;
5733f01ebaaSMatthew Dillon 			advance_xop = 1;
5743f01ebaaSMatthew Dillon 		}
5753f01ebaaSMatthew Dillon 
5763f01ebaaSMatthew Dillon 		/*
5773f01ebaaSMatthew Dillon 		 * We cannot recurse depth-first because the XOP is still
5783f01ebaaSMatthew Dillon 		 * running in node threads for this scan.  Create a placemarker
5793f01ebaaSMatthew Dillon 		 * by obtaining and record the hammer2_inode.
5803f01ebaaSMatthew Dillon 		 *
5813f01ebaaSMatthew Dillon 		 * We excluded our node from the XOP so we must temporarily
5823f01ebaaSMatthew Dillon 		 * add it to xop->head.cluster so it is properly incorporated
5833f01ebaaSMatthew Dillon 		 * into the inode.
5843f01ebaaSMatthew Dillon 		 *
5853f01ebaaSMatthew Dillon 		 * The deferral is pushed onto a LIFO list for bottom-up
5863f01ebaaSMatthew Dillon 		 * synchronization.
5873f01ebaaSMatthew Dillon 		 */
588*c8c0a18aSMatthew Dillon 		if (merror == 0 && dodefer) {
5893f01ebaaSMatthew Dillon 			hammer2_inode_t *nip;
5903f01ebaaSMatthew Dillon 			hammer2_deferred_ip_t *defer;
5913f01ebaaSMatthew Dillon 
5923f01ebaaSMatthew Dillon 			KKASSERT(focus->bref.type == HAMMER2_BREF_TYPE_INODE);
5933f01ebaaSMatthew Dillon 
5943f01ebaaSMatthew Dillon 			defer = kmalloc(sizeof(*defer), M_HAMMER2,
5953f01ebaaSMatthew Dillon 					M_WAITOK | M_ZERO);
5963f01ebaaSMatthew Dillon 			KKASSERT(xop->head.cluster.array[idx].chain == NULL);
5973f01ebaaSMatthew Dillon 			xop->head.cluster.array[idx].flags =
5983f01ebaaSMatthew Dillon 							HAMMER2_CITEM_INVALID;
5993f01ebaaSMatthew Dillon 			xop->head.cluster.array[idx].chain = chain;
6003f01ebaaSMatthew Dillon 			nip = hammer2_inode_get(pmp, ip,
6013f01ebaaSMatthew Dillon 						&xop->head.cluster, idx);
6023f01ebaaSMatthew Dillon 			xop->head.cluster.array[idx].chain = NULL;
6033f01ebaaSMatthew Dillon 
6043f01ebaaSMatthew Dillon 			hammer2_inode_ref(nip);
6053f01ebaaSMatthew Dillon 			hammer2_inode_unlock(nip);
6063f01ebaaSMatthew Dillon 
6073f01ebaaSMatthew Dillon 			defer->next = list->base;
6083f01ebaaSMatthew Dillon 			defer->ip = nip;
6093f01ebaaSMatthew Dillon 			list->base = defer;
6103f01ebaaSMatthew Dillon 			++list->count;
6113f01ebaaSMatthew Dillon 			needrescan = 1;
6123f01ebaaSMatthew Dillon 		}
6133f01ebaaSMatthew Dillon 
6143f01ebaaSMatthew Dillon 		/*
6153f01ebaaSMatthew Dillon 		 * If at least one deferral was added and the deferral
6163f01ebaaSMatthew Dillon 		 * list has grown too large, stop adding more.  This
6173f01ebaaSMatthew Dillon 		 * will trigger an EAGAIN return.
6183f01ebaaSMatthew Dillon 		 */
6193f01ebaaSMatthew Dillon 		if (needrescan && list->count > 1000)
6203f01ebaaSMatthew Dillon 			break;
6213f01ebaaSMatthew Dillon 
6223f01ebaaSMatthew Dillon 		/*
6233f01ebaaSMatthew Dillon 		 * Advancements for iteration.
6243f01ebaaSMatthew Dillon 		 */
6253f01ebaaSMatthew Dillon 		if (advance_xop) {
626*c8c0a18aSMatthew Dillon 			merror = hammer2_xop_collect(&xop->head, 0);
6273f01ebaaSMatthew Dillon 		}
6283f01ebaaSMatthew Dillon 		if (advance_local) {
6293f01ebaaSMatthew Dillon 			chain = hammer2_chain_next(&parent, chain, &key_next,
6303f01ebaaSMatthew Dillon 						   key_next, HAMMER2_KEY_MAX,
631*c8c0a18aSMatthew Dillon 						   &serror,
6323f01ebaaSMatthew Dillon 						   HAMMER2_LOOKUP_SHARED |
6333f01ebaaSMatthew Dillon 						   HAMMER2_LOOKUP_NODIRECT |
6343f01ebaaSMatthew Dillon 						   HAMMER2_LOOKUP_NODATA);
635*c8c0a18aSMatthew Dillon 			serror = hammer2_error_to_errno(serror);
6363f01ebaaSMatthew Dillon 		}
6373f01ebaaSMatthew Dillon 	}
6383f01ebaaSMatthew Dillon 	hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
6393f01ebaaSMatthew Dillon 	if (chain) {
6403f01ebaaSMatthew Dillon 		hammer2_chain_unlock(chain);
6413f01ebaaSMatthew Dillon 		hammer2_chain_drop(chain);
6423f01ebaaSMatthew Dillon 	}
6433f01ebaaSMatthew Dillon 	if (parent) {
6443f01ebaaSMatthew Dillon 		hammer2_chain_unlock(parent);
6453f01ebaaSMatthew Dillon 		hammer2_chain_drop(parent);
6463f01ebaaSMatthew Dillon 	}
6473f01ebaaSMatthew Dillon 
6483f01ebaaSMatthew Dillon 	/*
6493f01ebaaSMatthew Dillon 	 * If we added deferrals we want the caller to synchronize them
6503f01ebaaSMatthew Dillon 	 * and then call us again.
6513f01ebaaSMatthew Dillon 	 *
6523f01ebaaSMatthew Dillon 	 * NOTE: In this situation we do not yet want to synchronize our
6533f01ebaaSMatthew Dillon 	 *	 inode, setting the error code also has that effect.
6543f01ebaaSMatthew Dillon 	 */
655*c8c0a18aSMatthew Dillon 	if ((merror == 0 || merror == ENOENT) && needrescan)
656*c8c0a18aSMatthew Dillon 		merror = EAGAIN;
6573f01ebaaSMatthew Dillon 
6583f01ebaaSMatthew Dillon 	/*
659b02c0ae6SMatthew Dillon 	 * If no error occurred we can synchronize the inode meta-data
660b02c0ae6SMatthew Dillon 	 * and modify_tid.  Only limited changes are made to PFSROOTs.
6613f01ebaaSMatthew Dillon 	 *
6623f01ebaaSMatthew Dillon 	 * XXX inode lock was lost
6633f01ebaaSMatthew Dillon 	 */
664*c8c0a18aSMatthew Dillon 	if (merror == 0 || merror == ENOENT) {
6653f01ebaaSMatthew Dillon 		hammer2_xop_ipcluster_t *xop2;
6663f01ebaaSMatthew Dillon 		hammer2_chain_t *focus;
6673f01ebaaSMatthew Dillon 
668b02c0ae6SMatthew Dillon 		hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
6693f01ebaaSMatthew Dillon 		xop2 = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
6703f01ebaaSMatthew Dillon 		hammer2_xop_start_except(&xop2->head, hammer2_xop_ipcluster,
6713f01ebaaSMatthew Dillon 					 idx);
672b02c0ae6SMatthew Dillon 		hammer2_inode_unlock(ip);
673*c8c0a18aSMatthew Dillon 		merror = hammer2_xop_collect(&xop2->head, 0);
674*c8c0a18aSMatthew Dillon 		if (merror == 0) {
6753f01ebaaSMatthew Dillon 			focus = xop2->head.cluster.focus;
6760d66a712SMatthew Dillon 			if (hammer2_debug & 0x8000) {
6773f01ebaaSMatthew Dillon 				kprintf("syncthr: update inode %p (%s)\n",
6783f01ebaaSMatthew Dillon 					focus,
6790d66a712SMatthew Dillon 					(focus ? (char *)focus->data->
6800d66a712SMatthew Dillon 							 ipdata.filename :
6810d66a712SMatthew Dillon 						 "?"));
6820d66a712SMatthew Dillon 			}
6833f01ebaaSMatthew Dillon 			chain = hammer2_inode_chain_and_parent(ip, idx,
6843f01ebaaSMatthew Dillon 						    &parent,
6853f01ebaaSMatthew Dillon 						    HAMMER2_RESOLVE_ALWAYS |
6863f01ebaaSMatthew Dillon 						    HAMMER2_RESOLVE_SHARED);
6873f01ebaaSMatthew Dillon 
6883f01ebaaSMatthew Dillon 			KKASSERT(parent != NULL);
6893f01ebaaSMatthew Dillon 			nerror = hammer2_sync_replace(
6903f01ebaaSMatthew Dillon 					thr, parent, chain,
6913f01ebaaSMatthew Dillon 					sync_tid,
692b02c0ae6SMatthew Dillon 					idx, focus, isroot);
6933f01ebaaSMatthew Dillon 			hammer2_chain_unlock(chain);
6943f01ebaaSMatthew Dillon 			hammer2_chain_drop(chain);
6953f01ebaaSMatthew Dillon 			hammer2_chain_unlock(parent);
6963f01ebaaSMatthew Dillon 			hammer2_chain_drop(parent);
6973f01ebaaSMatthew Dillon 			/* XXX */
6983f01ebaaSMatthew Dillon 		}
6993f01ebaaSMatthew Dillon 		hammer2_xop_retire(&xop2->head, HAMMER2_XOPMASK_VOP);
7003f01ebaaSMatthew Dillon 	}
7013f01ebaaSMatthew Dillon 
702*c8c0a18aSMatthew Dillon 	return merror;
7033f01ebaaSMatthew Dillon }
7043f01ebaaSMatthew Dillon 
7053f01ebaaSMatthew Dillon /*
7063f01ebaaSMatthew Dillon  * Create a missing chain by copying the focus from another device.
7073f01ebaaSMatthew Dillon  *
7083f01ebaaSMatthew Dillon  * On entry *parentp and focus are both locked shared.  The chain will be
7093f01ebaaSMatthew Dillon  * created and returned in *chainp also locked shared.
7103f01ebaaSMatthew Dillon  */
7113f01ebaaSMatthew Dillon static
7123f01ebaaSMatthew Dillon int
7133f01ebaaSMatthew Dillon hammer2_sync_insert(hammer2_thread_t *thr,
7143f01ebaaSMatthew Dillon 		    hammer2_chain_t **parentp, hammer2_chain_t **chainp,
7153f01ebaaSMatthew Dillon 		    hammer2_tid_t mtid, int idx, hammer2_chain_t *focus)
7163f01ebaaSMatthew Dillon {
7173f01ebaaSMatthew Dillon 	hammer2_chain_t *chain;
718b02c0ae6SMatthew Dillon 	hammer2_key_t dummy;
719*c8c0a18aSMatthew Dillon 	int error;
7203f01ebaaSMatthew Dillon 
7213f01ebaaSMatthew Dillon #if HAMMER2_SYNCHRO_DEBUG
7223f01ebaaSMatthew Dillon 	if (hammer2_debug & 1)
7233f01ebaaSMatthew Dillon 	kprintf("insert rec par=%p/%d.%016jx slave %d %d.%016jx mod=%016jx\n",
7243f01ebaaSMatthew Dillon 		*parentp,
7253f01ebaaSMatthew Dillon 		(*parentp)->bref.type,
7263f01ebaaSMatthew Dillon 		(*parentp)->bref.key,
7273f01ebaaSMatthew Dillon 		idx,
7283f01ebaaSMatthew Dillon 		focus->bref.type, focus->bref.key, mtid);
7293f01ebaaSMatthew Dillon #endif
7303f01ebaaSMatthew Dillon 
7313f01ebaaSMatthew Dillon 	/*
732b02c0ae6SMatthew Dillon 	 * Parent requires an exclusive lock for the insertion.
733b02c0ae6SMatthew Dillon 	 * We must unlock the child to avoid deadlocks while
734b02c0ae6SMatthew Dillon 	 * relocking the parent.
7353f01ebaaSMatthew Dillon 	 */
736b02c0ae6SMatthew Dillon 	if (*chainp) {
7373f01ebaaSMatthew Dillon 		hammer2_chain_unlock(*chainp);
738b02c0ae6SMatthew Dillon 		hammer2_chain_drop(*chainp);
739b02c0ae6SMatthew Dillon 		*chainp = NULL;
740b02c0ae6SMatthew Dillon 	}
7413f01ebaaSMatthew Dillon 	hammer2_chain_unlock(*parentp);
7423f01ebaaSMatthew Dillon 	hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_ALWAYS);
743b02c0ae6SMatthew Dillon 
744b02c0ae6SMatthew Dillon 	/*
745b02c0ae6SMatthew Dillon 	 * We must reissue the lookup to properly position (*parentp)
746b02c0ae6SMatthew Dillon 	 * for the insertion.
747b02c0ae6SMatthew Dillon 	 */
748b02c0ae6SMatthew Dillon 	chain = hammer2_chain_lookup(parentp, &dummy,
749b02c0ae6SMatthew Dillon 				     focus->bref.key, focus->bref.key,
750*c8c0a18aSMatthew Dillon 				     &error,
751b02c0ae6SMatthew Dillon 				     HAMMER2_LOOKUP_NODIRECT |
752b02c0ae6SMatthew Dillon 				     HAMMER2_LOOKUP_ALWAYS);
753b02c0ae6SMatthew Dillon 	KKASSERT(chain == NULL);
7543f01ebaaSMatthew Dillon 
7553f01ebaaSMatthew Dillon 	chain = NULL;
756*c8c0a18aSMatthew Dillon 	error = hammer2_chain_create(parentp, &chain,
7577fece146SMatthew Dillon 				     thr->pmp, focus->bref.methods,
7583f01ebaaSMatthew Dillon 				     focus->bref.key, focus->bref.keybits,
7593f01ebaaSMatthew Dillon 				     focus->bref.type, focus->bytes,
7603f01ebaaSMatthew Dillon 				     mtid, 0, 0);
761*c8c0a18aSMatthew Dillon 	if (error == 0) {
7623f01ebaaSMatthew Dillon 		hammer2_chain_modify(chain, mtid, 0, 0);
7633f01ebaaSMatthew Dillon 
7643f01ebaaSMatthew Dillon 		/*
7653f01ebaaSMatthew Dillon 		 * Copy focus to new chain
7663f01ebaaSMatthew Dillon 		 */
7673f01ebaaSMatthew Dillon 
7683f01ebaaSMatthew Dillon 		/* type already set */
7693f01ebaaSMatthew Dillon 		chain->bref.methods = focus->bref.methods;
7703f01ebaaSMatthew Dillon 		/* keybits already set */
7713f01ebaaSMatthew Dillon 		chain->bref.vradix = focus->bref.vradix;
7723f01ebaaSMatthew Dillon 		/* mirror_tid set by flush */
7733f01ebaaSMatthew Dillon 		KKASSERT(chain->bref.modify_tid == mtid);
7743f01ebaaSMatthew Dillon 		chain->bref.flags = focus->bref.flags;
7753f01ebaaSMatthew Dillon 		/* key already present */
7763f01ebaaSMatthew Dillon 		/* check code will be recalculated */
7773f01ebaaSMatthew Dillon 
7783f01ebaaSMatthew Dillon 		/*
7793f01ebaaSMatthew Dillon 		 * Copy data body.
7803f01ebaaSMatthew Dillon 		 */
7813f01ebaaSMatthew Dillon 		switch(chain->bref.type) {
7823f01ebaaSMatthew Dillon 		case HAMMER2_BREF_TYPE_INODE:
7833f01ebaaSMatthew Dillon 			if ((focus->data->ipdata.meta.op_flags &
7843f01ebaaSMatthew Dillon 			     HAMMER2_OPFLAG_DIRECTDATA) == 0) {
785b02c0ae6SMatthew Dillon 				/* do not copy block table */
7863f01ebaaSMatthew Dillon 				bcopy(focus->data, chain->data,
7873f01ebaaSMatthew Dillon 				      offsetof(hammer2_inode_data_t, u));
7883f01ebaaSMatthew Dillon 				break;
7893f01ebaaSMatthew Dillon 			}
790b02c0ae6SMatthew Dillon 			/* fall through copy whole thing */
7913f01ebaaSMatthew Dillon 		case HAMMER2_BREF_TYPE_DATA:
7923f01ebaaSMatthew Dillon 			bcopy(focus->data, chain->data, chain->bytes);
7933f01ebaaSMatthew Dillon 			hammer2_chain_setcheck(chain, chain->data);
7943f01ebaaSMatthew Dillon 			break;
795da0cdd33SMatthew Dillon 		case HAMMER2_BREF_TYPE_DIRENT:
796da0cdd33SMatthew Dillon 			/*
797da0cdd33SMatthew Dillon 			 * Directory entries embed data in the blockref.
798da0cdd33SMatthew Dillon 			 */
799da0cdd33SMatthew Dillon 			if (chain->bytes) {
800da0cdd33SMatthew Dillon 				bcopy(focus->data, chain->data, chain->bytes);
801da0cdd33SMatthew Dillon 				hammer2_chain_setcheck(chain, chain->data);
802da0cdd33SMatthew Dillon 			} else {
803da0cdd33SMatthew Dillon 				chain->bref.check = focus->bref.check;
804da0cdd33SMatthew Dillon 			}
805da0cdd33SMatthew Dillon 			chain->bref.embed = focus->bref.embed;
806da0cdd33SMatthew Dillon 			break;
8073f01ebaaSMatthew Dillon 		default:
8083f01ebaaSMatthew Dillon 			KKASSERT(0);
8093f01ebaaSMatthew Dillon 			break;
8103f01ebaaSMatthew Dillon 		}
811*c8c0a18aSMatthew Dillon 	}
8123f01ebaaSMatthew Dillon 
813*c8c0a18aSMatthew Dillon 	if (chain)
8143f01ebaaSMatthew Dillon 		hammer2_chain_unlock(chain);	/* unlock, leave ref */
8153f01ebaaSMatthew Dillon 	*chainp = chain;			/* will be returned locked */
8163f01ebaaSMatthew Dillon 
8173f01ebaaSMatthew Dillon 	/*
818da0cdd33SMatthew Dillon 	 * Avoid an ordering deadlock when relocking shared.
8193f01ebaaSMatthew Dillon 	 */
8203f01ebaaSMatthew Dillon 	hammer2_chain_unlock(*parentp);
8213f01ebaaSMatthew Dillon 	hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_SHARED |
8223f01ebaaSMatthew Dillon 				     HAMMER2_RESOLVE_ALWAYS);
823*c8c0a18aSMatthew Dillon 	if (chain) {
8243f01ebaaSMatthew Dillon 		hammer2_chain_lock(chain, HAMMER2_RESOLVE_SHARED |
8253f01ebaaSMatthew Dillon 					  HAMMER2_RESOLVE_ALWAYS);
826*c8c0a18aSMatthew Dillon 		error = chain->error;
827*c8c0a18aSMatthew Dillon 	}
8283f01ebaaSMatthew Dillon 
829*c8c0a18aSMatthew Dillon 	return error;
8303f01ebaaSMatthew Dillon }
8313f01ebaaSMatthew Dillon 
8323f01ebaaSMatthew Dillon /*
8333f01ebaaSMatthew Dillon  * Destroy an extranious chain.
8343f01ebaaSMatthew Dillon  *
8353f01ebaaSMatthew Dillon  * Both *parentp and *chainp are locked shared.
8363f01ebaaSMatthew Dillon  *
8373f01ebaaSMatthew Dillon  * On return, *chainp will be adjusted to point to the next element in the
8383f01ebaaSMatthew Dillon  * iteration and locked shared.
8393f01ebaaSMatthew Dillon  */
8403f01ebaaSMatthew Dillon static
8413f01ebaaSMatthew Dillon int
8423f01ebaaSMatthew Dillon hammer2_sync_destroy(hammer2_thread_t *thr,
8433f01ebaaSMatthew Dillon 		     hammer2_chain_t **parentp, hammer2_chain_t **chainp,
8443f01ebaaSMatthew Dillon 		     hammer2_tid_t mtid, int idx)
8453f01ebaaSMatthew Dillon {
8463f01ebaaSMatthew Dillon 	hammer2_chain_t *chain;
8473f01ebaaSMatthew Dillon 	hammer2_key_t key_next;
8483f01ebaaSMatthew Dillon 	hammer2_key_t save_key;
849*c8c0a18aSMatthew Dillon 	int error;
8503f01ebaaSMatthew Dillon 
8513f01ebaaSMatthew Dillon 	chain = *chainp;
8523f01ebaaSMatthew Dillon 
8533f01ebaaSMatthew Dillon #if HAMMER2_SYNCHRO_DEBUG
8543f01ebaaSMatthew Dillon 	if (hammer2_debug & 1)
8553f01ebaaSMatthew Dillon 	kprintf("destroy rec %p/%p slave %d %d.%016jx\n",
8563f01ebaaSMatthew Dillon 		*parentp, chain,
8573f01ebaaSMatthew Dillon 		idx, chain->bref.type, chain->bref.key);
8583f01ebaaSMatthew Dillon #endif
8593f01ebaaSMatthew Dillon 
8603f01ebaaSMatthew Dillon 	save_key = chain->bref.key;
8613f01ebaaSMatthew Dillon 	if (save_key != HAMMER2_KEY_MAX)
8623f01ebaaSMatthew Dillon 		++save_key;
8633f01ebaaSMatthew Dillon 
8643f01ebaaSMatthew Dillon 	/*
8653f01ebaaSMatthew Dillon 	 * Try to avoid unnecessary I/O.
8663f01ebaaSMatthew Dillon 	 *
8673f01ebaaSMatthew Dillon 	 * XXX accounting not propagated up properly.  We might have to do
8683f01ebaaSMatthew Dillon 	 *     a RESOLVE_MAYBE here and pass 0 for the flags.
8693f01ebaaSMatthew Dillon 	 */
8703f01ebaaSMatthew Dillon 	hammer2_chain_unlock(chain);	/* relock exclusive */
8713f01ebaaSMatthew Dillon 	hammer2_chain_unlock(*parentp);
8723f01ebaaSMatthew Dillon 	hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_ALWAYS);
8733f01ebaaSMatthew Dillon 	hammer2_chain_lock(chain, HAMMER2_RESOLVE_NEVER);
8743f01ebaaSMatthew Dillon 
8753f01ebaaSMatthew Dillon 	hammer2_chain_delete(*parentp, chain, mtid, HAMMER2_DELETE_PERMANENT);
8763f01ebaaSMatthew Dillon 	hammer2_chain_unlock(chain);
8773f01ebaaSMatthew Dillon 	hammer2_chain_drop(chain);
8783f01ebaaSMatthew Dillon 	chain = NULL;			/* safety */
8793f01ebaaSMatthew Dillon 
8803f01ebaaSMatthew Dillon 	hammer2_chain_unlock(*parentp);	/* relock shared */
8813f01ebaaSMatthew Dillon 	hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_SHARED |
8823f01ebaaSMatthew Dillon 				     HAMMER2_RESOLVE_ALWAYS);
8830d66a712SMatthew Dillon 	*chainp = hammer2_chain_lookup(parentp, &key_next,
8843f01ebaaSMatthew Dillon 				     save_key, HAMMER2_KEY_MAX,
885*c8c0a18aSMatthew Dillon 				     &error,
8863f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_SHARED |
8873f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_NODIRECT |
8883f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_NODATA);
889*c8c0a18aSMatthew Dillon 	return error;
8903f01ebaaSMatthew Dillon }
8913f01ebaaSMatthew Dillon 
8923f01ebaaSMatthew Dillon /*
8933f01ebaaSMatthew Dillon  * cparent is locked exclusively, with an extra ref, cluster is not locked.
8943f01ebaaSMatthew Dillon  * Replace element [i] in the cluster.
8953f01ebaaSMatthew Dillon  */
8963f01ebaaSMatthew Dillon static
8973f01ebaaSMatthew Dillon int
8983f01ebaaSMatthew Dillon hammer2_sync_replace(hammer2_thread_t *thr,
8993f01ebaaSMatthew Dillon 		     hammer2_chain_t *parent, hammer2_chain_t *chain,
9003f01ebaaSMatthew Dillon 		     hammer2_tid_t mtid, int idx,
901b02c0ae6SMatthew Dillon 		     hammer2_chain_t *focus, int isroot)
9023f01ebaaSMatthew Dillon {
9033f01ebaaSMatthew Dillon 	uint8_t otype;
904*c8c0a18aSMatthew Dillon 	int nradix;
9053f01ebaaSMatthew Dillon 
9063f01ebaaSMatthew Dillon #if HAMMER2_SYNCHRO_DEBUG
9073f01ebaaSMatthew Dillon 	if (hammer2_debug & 1)
9083f01ebaaSMatthew Dillon 	kprintf("replace rec %p slave %d %d.%016jx mod=%016jx\n",
9093f01ebaaSMatthew Dillon 		chain,
9103f01ebaaSMatthew Dillon 		idx,
9113f01ebaaSMatthew Dillon 		focus->bref.type, focus->bref.key, mtid);
9123f01ebaaSMatthew Dillon #endif
9133f01ebaaSMatthew Dillon 	hammer2_chain_unlock(chain);
9143f01ebaaSMatthew Dillon 	hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS);
915*c8c0a18aSMatthew Dillon 	if (chain->error == 0) {
9163f01ebaaSMatthew Dillon 		if (chain->bytes != focus->bytes) {
9173f01ebaaSMatthew Dillon 			/* XXX what if compressed? */
9183f01ebaaSMatthew Dillon 			nradix = hammer2_getradix(chain->bytes);
919da0cdd33SMatthew Dillon 			hammer2_chain_resize(chain, mtid, 0, nradix, 0);
9203f01ebaaSMatthew Dillon 		}
9213f01ebaaSMatthew Dillon 		hammer2_chain_modify(chain, mtid, 0, 0);
9223f01ebaaSMatthew Dillon 		otype = chain->bref.type;
9233f01ebaaSMatthew Dillon 		chain->bref.type = focus->bref.type;
9243f01ebaaSMatthew Dillon 		chain->bref.methods = focus->bref.methods;
9253f01ebaaSMatthew Dillon 		chain->bref.keybits = focus->bref.keybits;
9263f01ebaaSMatthew Dillon 		chain->bref.vradix = focus->bref.vradix;
9273f01ebaaSMatthew Dillon 		/* mirror_tid updated by flush */
928b02c0ae6SMatthew Dillon 		KKASSERT(mtid == 0 || chain->bref.modify_tid == mtid);
9293f01ebaaSMatthew Dillon 		chain->bref.flags = focus->bref.flags;
9303f01ebaaSMatthew Dillon 		/* key already present */
9313f01ebaaSMatthew Dillon 		/* check code will be recalculated */
9323f01ebaaSMatthew Dillon 
9333f01ebaaSMatthew Dillon 		/*
9343f01ebaaSMatthew Dillon 		 * Copy data body.
9353f01ebaaSMatthew Dillon 		 */
9363f01ebaaSMatthew Dillon 		switch(chain->bref.type) {
9373f01ebaaSMatthew Dillon 		case HAMMER2_BREF_TYPE_INODE:
938b02c0ae6SMatthew Dillon 			/*
939*c8c0a18aSMatthew Dillon 			 * Special case PFSROOTs, only limited changes can
940*c8c0a18aSMatthew Dillon 			 * be made since the meta-data contains miscellanious
941*c8c0a18aSMatthew Dillon 			 * distinguishing fields.
942b02c0ae6SMatthew Dillon 			 */
943b02c0ae6SMatthew Dillon 			if (isroot) {
944b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.uflags =
945b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.uflags;
946b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.rmajor =
947b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.rmajor;
948b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.rminor =
949b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.rminor;
950b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.ctime =
951b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.ctime;
952b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.mtime =
953b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.mtime;
954b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.atime =
955b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.atime;
956b02c0ae6SMatthew Dillon 				/* not btime */
957b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.uid =
958b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.uid;
959b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.gid =
960b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.gid;
961b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.mode =
962b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.mode;
963b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.ncopies =
964b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.ncopies;
965b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.comp_algo =
966b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.comp_algo;
967b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.check_algo =
968b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.check_algo;
969b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.data_quota =
970b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.data_quota;
971b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.inode_quota =
972b02c0ae6SMatthew Dillon 					focus->data->ipdata.meta.inode_quota;
9737fece146SMatthew Dillon 
9747fece146SMatthew Dillon 				/*
9757fece146SMatthew Dillon 				 * last snapshot tid controls overwrite
9767fece146SMatthew Dillon 				 */
9777fece146SMatthew Dillon 				if (chain->data->ipdata.meta.pfs_lsnap_tid <
9787fece146SMatthew Dillon 				    focus->data->ipdata.meta.pfs_lsnap_tid) {
9797fece146SMatthew Dillon 					chain->data->ipdata.meta.pfs_lsnap_tid =
9807fece146SMatthew Dillon 					focus->data->ipdata.meta.pfs_lsnap_tid;
9817fece146SMatthew Dillon 				}
9827fece146SMatthew Dillon 
983b02c0ae6SMatthew Dillon 				hammer2_chain_setcheck(chain, chain->data);
984b02c0ae6SMatthew Dillon 				break;
985b02c0ae6SMatthew Dillon 			}
986b02c0ae6SMatthew Dillon 
987b02c0ae6SMatthew Dillon 			/*
988b02c0ae6SMatthew Dillon 			 * Normal replacement.
989b02c0ae6SMatthew Dillon 			 */
9903f01ebaaSMatthew Dillon 			if ((focus->data->ipdata.meta.op_flags &
9913f01ebaaSMatthew Dillon 			     HAMMER2_OPFLAG_DIRECTDATA) == 0) {
9923f01ebaaSMatthew Dillon 				/*
993*c8c0a18aSMatthew Dillon 				 * If DIRECTDATA is transitioning to 0 or the
994*c8c0a18aSMatthew Dillon 				 * old chain is not an inode we have to
995*c8c0a18aSMatthew Dillon 				 * initialize the block table.
9963f01ebaaSMatthew Dillon 				 */
9973f01ebaaSMatthew Dillon 				if (otype != HAMMER2_BREF_TYPE_INODE ||
9983f01ebaaSMatthew Dillon 				    (chain->data->ipdata.meta.op_flags &
9993f01ebaaSMatthew Dillon 				     HAMMER2_OPFLAG_DIRECTDATA)) {
1000*c8c0a18aSMatthew Dillon 					kprintf("chain inode trans "
1001*c8c0a18aSMatthew Dillon 						"away from dd\n");
10023f01ebaaSMatthew Dillon 					bzero(&chain->data->ipdata.u,
10033f01ebaaSMatthew Dillon 					      sizeof(chain->data->ipdata.u));
10043f01ebaaSMatthew Dillon 				}
10053f01ebaaSMatthew Dillon 				bcopy(focus->data, chain->data,
10063f01ebaaSMatthew Dillon 				      offsetof(hammer2_inode_data_t, u));
10073f01ebaaSMatthew Dillon 				/* XXX setcheck on inode should not be needed */
10083f01ebaaSMatthew Dillon 				hammer2_chain_setcheck(chain, chain->data);
10093f01ebaaSMatthew Dillon 				break;
10103f01ebaaSMatthew Dillon 			}
10113f01ebaaSMatthew Dillon 			/* fall through */
10123f01ebaaSMatthew Dillon 		case HAMMER2_BREF_TYPE_DATA:
10133f01ebaaSMatthew Dillon 			bcopy(focus->data, chain->data, chain->bytes);
10143f01ebaaSMatthew Dillon 			hammer2_chain_setcheck(chain, chain->data);
10153f01ebaaSMatthew Dillon 			break;
1016da0cdd33SMatthew Dillon 		case HAMMER2_BREF_TYPE_DIRENT:
1017da0cdd33SMatthew Dillon 			/*
1018da0cdd33SMatthew Dillon 			 * Directory entries embed data in the blockref.
1019da0cdd33SMatthew Dillon 			 */
1020da0cdd33SMatthew Dillon 			if (chain->bytes) {
1021da0cdd33SMatthew Dillon 				bcopy(focus->data, chain->data, chain->bytes);
1022da0cdd33SMatthew Dillon 				hammer2_chain_setcheck(chain, chain->data);
1023da0cdd33SMatthew Dillon 			} else {
1024da0cdd33SMatthew Dillon 				chain->bref.check = focus->bref.check;
1025da0cdd33SMatthew Dillon 			}
1026da0cdd33SMatthew Dillon 			chain->bref.embed = focus->bref.embed;
1027da0cdd33SMatthew Dillon 			break;
10283f01ebaaSMatthew Dillon 		default:
10293f01ebaaSMatthew Dillon 			KKASSERT(0);
10303f01ebaaSMatthew Dillon 			break;
10313f01ebaaSMatthew Dillon 		}
1032*c8c0a18aSMatthew Dillon 	}
10333f01ebaaSMatthew Dillon 
10343f01ebaaSMatthew Dillon 	hammer2_chain_unlock(chain);
10353f01ebaaSMatthew Dillon 	hammer2_chain_lock(chain, HAMMER2_RESOLVE_SHARED |
10363f01ebaaSMatthew Dillon 				  HAMMER2_RESOLVE_MAYBE);
10373f01ebaaSMatthew Dillon 
10383f01ebaaSMatthew Dillon 	return 0;
10393f01ebaaSMatthew Dillon }
1040