xref: /dflybsd-src/sys/vfs/hammer2/hammer2_synchro.c (revision 022bb0a9ed6967bc18e421ed074f5727e49314e0)
13f01ebaaSMatthew Dillon /*
268b321c1SMatthew Dillon  * Copyright (c) 2015-2018 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,
77fda30e02SMatthew Dillon 			hammer2_xop_head_t *xop, 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,
84fda30e02SMatthew Dillon 			hammer2_xop_head_t *xop, hammer2_chain_t *focus,
85fda30e02SMatthew Dillon 			int isroot);
863f01ebaaSMatthew Dillon 
873f01ebaaSMatthew Dillon /****************************************************************************
883f01ebaaSMatthew Dillon  *			    HAMMER2 SYNC THREADS 			    *
893f01ebaaSMatthew Dillon  ****************************************************************************/
903f01ebaaSMatthew Dillon /*
913f01ebaaSMatthew Dillon  * Primary management thread for an element of a node.  A thread will exist
923f01ebaaSMatthew Dillon  * for each element requiring management.
933f01ebaaSMatthew Dillon  *
943f01ebaaSMatthew Dillon  * No management threads are needed for the SPMP or for any PMP with only
953f01ebaaSMatthew Dillon  * a single MASTER.
963f01ebaaSMatthew Dillon  *
973f01ebaaSMatthew Dillon  * On the SPMP - handles bulkfree and dedup operations
983f01ebaaSMatthew Dillon  * On a PFS    - handles remastering and synchronization
993f01ebaaSMatthew Dillon  */
1003f01ebaaSMatthew Dillon void
hammer2_primary_sync_thread(void * arg)1013f01ebaaSMatthew Dillon hammer2_primary_sync_thread(void *arg)
1023f01ebaaSMatthew Dillon {
1033f01ebaaSMatthew Dillon 	hammer2_thread_t *thr = arg;
1043f01ebaaSMatthew Dillon 	hammer2_pfs_t *pmp;
1053f01ebaaSMatthew Dillon 	hammer2_deferred_list_t list;
1063f01ebaaSMatthew Dillon 	hammer2_deferred_ip_t *defer;
1073f01ebaaSMatthew Dillon 	int error;
108660d007eSMatthew Dillon 	uint32_t flags;
109660d007eSMatthew Dillon 	uint32_t nflags;
1103f01ebaaSMatthew Dillon 
1113f01ebaaSMatthew Dillon 	pmp = thr->pmp;
1123f01ebaaSMatthew Dillon 	bzero(&list, sizeof(list));
1133f01ebaaSMatthew Dillon 
114660d007eSMatthew Dillon 	for (;;) {
115660d007eSMatthew Dillon 		flags = thr->flags;
116660d007eSMatthew Dillon 		cpu_ccfence();
117660d007eSMatthew Dillon 
118660d007eSMatthew Dillon 		/*
119660d007eSMatthew Dillon 		 * Handle stop request
120660d007eSMatthew Dillon 		 */
121660d007eSMatthew Dillon 		if (flags & HAMMER2_THREAD_STOP)
122660d007eSMatthew Dillon 			break;
123660d007eSMatthew Dillon 
1243f01ebaaSMatthew Dillon 		/*
1253f01ebaaSMatthew Dillon 		 * Handle freeze request
1263f01ebaaSMatthew Dillon 		 */
127660d007eSMatthew Dillon 		if (flags & HAMMER2_THREAD_FREEZE) {
128660d007eSMatthew Dillon 			nflags = (flags & ~(HAMMER2_THREAD_FREEZE |
1299dca9515SMatthew Dillon 					    HAMMER2_THREAD_WAITING)) |
130660d007eSMatthew Dillon 				 HAMMER2_THREAD_FROZEN;
131660d007eSMatthew Dillon 			if (!atomic_cmpset_int(&thr->flags, flags, nflags))
132660d007eSMatthew Dillon 				continue;
1339dca9515SMatthew Dillon 			if (flags & HAMMER2_THREAD_WAITING)
134660d007eSMatthew Dillon 				wakeup(&thr->flags);
13559eb0066SMatthew Dillon 			continue;
136660d007eSMatthew Dillon 		}
137660d007eSMatthew Dillon 
138660d007eSMatthew Dillon 		if (flags & HAMMER2_THREAD_UNFREEZE) {
139660d007eSMatthew Dillon 			nflags = flags & ~(HAMMER2_THREAD_UNFREEZE |
140660d007eSMatthew Dillon 					   HAMMER2_THREAD_FROZEN |
1419dca9515SMatthew Dillon 					   HAMMER2_THREAD_WAITING);
142660d007eSMatthew Dillon 			if (!atomic_cmpset_int(&thr->flags, flags, nflags))
143660d007eSMatthew Dillon 				continue;
1449dca9515SMatthew Dillon 			if (flags & HAMMER2_THREAD_WAITING)
145660d007eSMatthew Dillon 				wakeup(&thr->flags);
14659eb0066SMatthew Dillon 			continue;
1473f01ebaaSMatthew Dillon 		}
1483f01ebaaSMatthew Dillon 
1493f01ebaaSMatthew Dillon 		/*
1503f01ebaaSMatthew Dillon 		 * Force idle if frozen until unfrozen or stopped.
1513f01ebaaSMatthew Dillon 		 */
152660d007eSMatthew Dillon 		if (flags & HAMMER2_THREAD_FROZEN) {
153660d007eSMatthew Dillon 			nflags = flags | HAMMER2_THREAD_WAITING;
15459eb0066SMatthew Dillon 
155660d007eSMatthew Dillon 			tsleep_interlock(&thr->flags, 0);
15659eb0066SMatthew Dillon 			if (atomic_cmpset_int(&thr->flags, flags, nflags))
157660d007eSMatthew Dillon 				tsleep(&thr->flags, PINTERLOCKED, "frozen", 0);
1583f01ebaaSMatthew Dillon 			continue;
1593f01ebaaSMatthew Dillon 		}
1603f01ebaaSMatthew Dillon 
1613f01ebaaSMatthew Dillon 		/*
1623f01ebaaSMatthew Dillon 		 * Reset state on REMASTER request
1633f01ebaaSMatthew Dillon 		 */
1643f01ebaaSMatthew Dillon 		if (thr->flags & HAMMER2_THREAD_REMASTER) {
165660d007eSMatthew Dillon 			nflags = flags & ~HAMMER2_THREAD_REMASTER;
166660d007eSMatthew Dillon 			if (atomic_cmpset_int(&thr->flags, flags, nflags)) {
167660d007eSMatthew Dillon 				/* reset state here */
168660d007eSMatthew Dillon 			}
169660d007eSMatthew Dillon 			continue;
1703f01ebaaSMatthew Dillon 		}
1713f01ebaaSMatthew Dillon 
1723f01ebaaSMatthew Dillon 		/*
1733f01ebaaSMatthew Dillon 		 * Synchronization scan.
1743f01ebaaSMatthew Dillon 		 */
1753cbe226bSMatthew Dillon 		if (hammer2_debug & 0x8000)
1763f01ebaaSMatthew Dillon 			kprintf("sync_slaves pfs %s clindex %d\n",
1773f01ebaaSMatthew Dillon 				pmp->pfs_names[thr->clindex], thr->clindex);
1783f01ebaaSMatthew Dillon 		hammer2_trans_init(pmp, 0);
1793f01ebaaSMatthew Dillon 
1803f01ebaaSMatthew Dillon 		hammer2_inode_ref(pmp->iroot);
1813f01ebaaSMatthew Dillon 
1823f01ebaaSMatthew Dillon 		for (;;) {
1833f01ebaaSMatthew Dillon 			int didbreak = 0;
1843f01ebaaSMatthew Dillon 			/* XXX lock synchronize pmp->modify_tid */
185b02c0ae6SMatthew Dillon 			error = hammer2_sync_slaves(thr, pmp->iroot, &list, 1);
186b02c0ae6SMatthew Dillon 			if (hammer2_debug & 0x8000) {
187b02c0ae6SMatthew Dillon 				kprintf("sync_slaves error %d defer %p\n",
188b02c0ae6SMatthew Dillon 					error, list.base);
189b02c0ae6SMatthew Dillon 			}
19065cacacfSMatthew Dillon 			if (error != HAMMER2_ERROR_EAGAIN)
1913f01ebaaSMatthew Dillon 				break;
1923f01ebaaSMatthew Dillon 			while ((defer = list.base) != NULL) {
1933f01ebaaSMatthew Dillon 				hammer2_inode_t *nip;
1943f01ebaaSMatthew Dillon 
1953f01ebaaSMatthew Dillon 				nip = defer->ip;
1960d66a712SMatthew Dillon 				error = hammer2_sync_slaves(thr, nip, &list,
1970d66a712SMatthew Dillon 							(nip == pmp->iroot));
19865cacacfSMatthew Dillon 				if (error &&
19965cacacfSMatthew Dillon 				    error != HAMMER2_ERROR_EAGAIN &&
20065cacacfSMatthew Dillon 				    error != HAMMER2_ERROR_ENOENT) {
2013f01ebaaSMatthew Dillon 					break;
20265cacacfSMatthew Dillon 				}
2033f01ebaaSMatthew Dillon 				if (hammer2_thr_break(thr)) {
2043f01ebaaSMatthew Dillon 					didbreak = 1;
2053f01ebaaSMatthew Dillon 					break;
2063f01ebaaSMatthew Dillon 				}
2073f01ebaaSMatthew Dillon 
2083f01ebaaSMatthew Dillon 				/*
2093f01ebaaSMatthew Dillon 				 * If no additional defers occurred we can
210b02c0ae6SMatthew Dillon 				 * remove this one, otherwise keep it on
2113f01ebaaSMatthew Dillon 				 * the list and retry once the additional
2123f01ebaaSMatthew Dillon 				 * defers have completed.
2133f01ebaaSMatthew Dillon 				 */
2143f01ebaaSMatthew Dillon 				if (defer == list.base) {
2153f01ebaaSMatthew Dillon 					--list.count;
2163f01ebaaSMatthew Dillon 					list.base = defer->next;
2173f01ebaaSMatthew Dillon 					kfree(defer, M_HAMMER2);
2183f01ebaaSMatthew Dillon 					defer = NULL;	/* safety */
2193f01ebaaSMatthew Dillon 					hammer2_inode_drop(nip);
2203f01ebaaSMatthew Dillon 				}
2213f01ebaaSMatthew Dillon 			}
2223f01ebaaSMatthew Dillon 
2233f01ebaaSMatthew Dillon 			/*
2243f01ebaaSMatthew Dillon 			 * If the thread is being remastered, frozen, or
2253f01ebaaSMatthew Dillon 			 * stopped, clean up any left-over deferals.
2263f01ebaaSMatthew Dillon 			 */
22765cacacfSMatthew Dillon 			if (didbreak ||
22865cacacfSMatthew Dillon 			    (error && error != HAMMER2_ERROR_EAGAIN)) {
2293f01ebaaSMatthew Dillon 				kprintf("didbreak\n");
2303f01ebaaSMatthew Dillon 				while ((defer = list.base) != NULL) {
2313f01ebaaSMatthew Dillon 					--list.count;
2323f01ebaaSMatthew Dillon 					hammer2_inode_drop(defer->ip);
2333f01ebaaSMatthew Dillon 					list.base = defer->next;
2343f01ebaaSMatthew Dillon 					kfree(defer, M_HAMMER2);
2353f01ebaaSMatthew Dillon 				}
23665cacacfSMatthew Dillon 				if (error == 0 || error == HAMMER2_ERROR_EAGAIN)
23765cacacfSMatthew Dillon 					error = HAMMER2_ERROR_EINPROGRESS;
2383f01ebaaSMatthew Dillon 				break;
2393f01ebaaSMatthew Dillon 			}
2403f01ebaaSMatthew Dillon 		}
2413f01ebaaSMatthew Dillon 
2423f01ebaaSMatthew Dillon 		hammer2_inode_drop(pmp->iroot);
243257c2728SMatthew Dillon 		hammer2_trans_done(pmp, 0);
2443f01ebaaSMatthew Dillon 
24565cacacfSMatthew Dillon 		if (error && error != HAMMER2_ERROR_EINPROGRESS)
2463f01ebaaSMatthew Dillon 			kprintf("hammer2_sync_slaves: error %d\n", error);
2473f01ebaaSMatthew Dillon 
2483f01ebaaSMatthew Dillon 		/*
2493f01ebaaSMatthew Dillon 		 * Wait for event, or 5-second poll.
2503f01ebaaSMatthew Dillon 		 */
251660d007eSMatthew Dillon 		nflags = flags | HAMMER2_THREAD_WAITING;
252660d007eSMatthew Dillon 		tsleep_interlock(&thr->flags, 0);
253660d007eSMatthew Dillon 		if (atomic_cmpset_int(&thr->flags, flags, nflags)) {
254660d007eSMatthew Dillon 			tsleep(&thr->flags, 0, "h2idle", hz * 5);
255660d007eSMatthew Dillon 		}
2563f01ebaaSMatthew Dillon 	}
2573f01ebaaSMatthew Dillon 	thr->td = NULL;
2589dca9515SMatthew Dillon 	hammer2_thr_signal(thr, HAMMER2_THREAD_STOPPED);
2593f01ebaaSMatthew Dillon 	/* thr structure can go invalid after this point */
2603f01ebaaSMatthew Dillon }
2613f01ebaaSMatthew Dillon 
2623f01ebaaSMatthew Dillon #if 0
2633f01ebaaSMatthew Dillon /*
2643f01ebaaSMatthew Dillon  * Given a locked cluster created from pmp->iroot, update the PFS's
2653f01ebaaSMatthew Dillon  * reporting status.
2663f01ebaaSMatthew Dillon  */
2673f01ebaaSMatthew Dillon static
2683f01ebaaSMatthew Dillon void
2693f01ebaaSMatthew Dillon hammer2_update_pfs_status(hammer2_thread_t *thr, uint32_t flags)
2703f01ebaaSMatthew Dillon {
2713f01ebaaSMatthew Dillon 	hammer2_pfs_t *pmp = thr->pmp;
2723f01ebaaSMatthew Dillon 
2733f01ebaaSMatthew Dillon 	flags &= HAMMER2_CLUSTER_ZFLAGS;
2743f01ebaaSMatthew Dillon 	if (pmp->cluster_flags == flags)
2753f01ebaaSMatthew Dillon 		return;
2763f01ebaaSMatthew Dillon 	pmp->cluster_flags = flags;
2773f01ebaaSMatthew Dillon 
2783f01ebaaSMatthew Dillon 	kprintf("pfs %p", pmp);
2793f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_MSYNCED)
2803f01ebaaSMatthew Dillon 		kprintf(" masters-all-good");
2813f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_SSYNCED)
2823f01ebaaSMatthew Dillon 		kprintf(" slaves-all-good");
2833f01ebaaSMatthew Dillon 
2843f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_WRHARD)
2853f01ebaaSMatthew Dillon 		kprintf(" quorum/rw");
2863f01ebaaSMatthew Dillon 	else if (flags & HAMMER2_CLUSTER_RDHARD)
2873f01ebaaSMatthew Dillon 		kprintf(" quorum/ro");
2883f01ebaaSMatthew Dillon 
2893f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_UNHARD)
2903f01ebaaSMatthew Dillon 		kprintf(" out-of-sync-masters");
2913f01ebaaSMatthew Dillon 	else if (flags & HAMMER2_CLUSTER_NOHARD)
2923f01ebaaSMatthew Dillon 		kprintf(" no-masters-visible");
2933f01ebaaSMatthew Dillon 
2943f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_WRSOFT)
2953f01ebaaSMatthew Dillon 		kprintf(" soft/rw");
2963f01ebaaSMatthew Dillon 	else if (flags & HAMMER2_CLUSTER_RDSOFT)
2973f01ebaaSMatthew Dillon 		kprintf(" soft/ro");
2983f01ebaaSMatthew Dillon 
2993f01ebaaSMatthew Dillon 	if (flags & HAMMER2_CLUSTER_UNSOFT)
3003f01ebaaSMatthew Dillon 		kprintf(" out-of-sync-slaves");
3013f01ebaaSMatthew Dillon 	else if (flags & HAMMER2_CLUSTER_NOSOFT)
3023f01ebaaSMatthew Dillon 		kprintf(" no-slaves-visible");
3033f01ebaaSMatthew Dillon 	kprintf("\n");
3043f01ebaaSMatthew Dillon }
3053f01ebaaSMatthew Dillon #endif
3063f01ebaaSMatthew Dillon 
3073f01ebaaSMatthew Dillon #if 0
3083f01ebaaSMatthew Dillon static
3093f01ebaaSMatthew Dillon void
3103f01ebaaSMatthew Dillon dumpcluster(const char *label,
3113f01ebaaSMatthew Dillon 	    hammer2_cluster_t *cparent, hammer2_cluster_t *cluster)
3123f01ebaaSMatthew Dillon {
3133f01ebaaSMatthew Dillon 	hammer2_chain_t *chain;
3143f01ebaaSMatthew Dillon 	int i;
3153f01ebaaSMatthew Dillon 
3163f01ebaaSMatthew Dillon 	if ((hammer2_debug & 1) == 0)
3173f01ebaaSMatthew Dillon 		return;
3183f01ebaaSMatthew Dillon 
3193f01ebaaSMatthew Dillon 	kprintf("%s\t", label);
3203f01ebaaSMatthew Dillon 	KKASSERT(cparent->nchains == cluster->nchains);
3213f01ebaaSMatthew Dillon 	for (i = 0; i < cparent->nchains; ++i) {
3223f01ebaaSMatthew Dillon 		if (i)
3233f01ebaaSMatthew Dillon 			kprintf("\t");
3243f01ebaaSMatthew Dillon 		kprintf("%d ", i);
3253f01ebaaSMatthew Dillon 		if ((chain = cparent->array[i].chain) != NULL) {
3263f01ebaaSMatthew Dillon 			kprintf("%016jx%s ",
3273f01ebaaSMatthew Dillon 				chain->bref.key,
3283f01ebaaSMatthew Dillon 				((cparent->array[i].flags &
3293f01ebaaSMatthew Dillon 				  HAMMER2_CITEM_INVALID) ? "(I)" : "   ")
3303f01ebaaSMatthew Dillon 			);
3313f01ebaaSMatthew Dillon 		} else {
3323f01ebaaSMatthew Dillon 			kprintf("      NULL      %s ", "   ");
3333f01ebaaSMatthew Dillon 		}
3343f01ebaaSMatthew Dillon 		if ((chain = cluster->array[i].chain) != NULL) {
3353f01ebaaSMatthew Dillon 			kprintf("%016jx%s ",
3363f01ebaaSMatthew Dillon 				chain->bref.key,
3373f01ebaaSMatthew Dillon 				((cluster->array[i].flags &
3383f01ebaaSMatthew Dillon 				  HAMMER2_CITEM_INVALID) ? "(I)" : "   ")
3393f01ebaaSMatthew Dillon 			);
3403f01ebaaSMatthew Dillon 		} else {
3413f01ebaaSMatthew Dillon 			kprintf("      NULL      %s ", "   ");
3423f01ebaaSMatthew Dillon 		}
3433f01ebaaSMatthew Dillon 		kprintf("\n");
3443f01ebaaSMatthew Dillon 	}
3453f01ebaaSMatthew Dillon }
3463f01ebaaSMatthew Dillon #endif
3473f01ebaaSMatthew Dillon 
3483f01ebaaSMatthew Dillon /*
3493f01ebaaSMatthew Dillon  * Each out of sync node sync-thread must issue an all-nodes XOP scan of
3503f01ebaaSMatthew Dillon  * the inode.  This creates a multiplication effect since the XOP scan itself
3513f01ebaaSMatthew Dillon  * issues to all nodes.  However, this is the only way we can safely
3523f01ebaaSMatthew Dillon  * synchronize nodes which might have disparate I/O bandwidths and the only
3533f01ebaaSMatthew Dillon  * way we can safely deal with stalled nodes.
354c8c0a18aSMatthew Dillon  *
355c8c0a18aSMatthew Dillon  * XXX serror / merror rollup and handling.
3563f01ebaaSMatthew Dillon  */
3573f01ebaaSMatthew Dillon static
3583f01ebaaSMatthew Dillon int
hammer2_sync_slaves(hammer2_thread_t * thr,hammer2_inode_t * ip,hammer2_deferred_list_t * list,int isroot)3593f01ebaaSMatthew Dillon hammer2_sync_slaves(hammer2_thread_t *thr, hammer2_inode_t *ip,
360b02c0ae6SMatthew Dillon 		    hammer2_deferred_list_t *list, int isroot)
3613f01ebaaSMatthew Dillon {
3623f01ebaaSMatthew Dillon 	hammer2_xop_scanall_t *xop;
3633f01ebaaSMatthew Dillon 	hammer2_chain_t *parent;
3643f01ebaaSMatthew Dillon 	hammer2_chain_t *chain;
3653f01ebaaSMatthew Dillon 	hammer2_pfs_t *pmp;
3663f01ebaaSMatthew Dillon 	hammer2_key_t key_next;
3673f01ebaaSMatthew Dillon 	hammer2_tid_t sync_tid;
3683f01ebaaSMatthew Dillon 	int needrescan;
369b02c0ae6SMatthew Dillon 	int want_update;
370c8c0a18aSMatthew Dillon 	int serror;		/* slave error */
371c8c0a18aSMatthew Dillon 	int merror;		/* master error (from xop_collect) */
372c8c0a18aSMatthew Dillon 	int nerror;		/* temporary error */
3733f01ebaaSMatthew Dillon 	int idx;
3743f01ebaaSMatthew Dillon 	int n;
3753f01ebaaSMatthew Dillon 
3763f01ebaaSMatthew Dillon 	pmp = ip->pmp;
3773f01ebaaSMatthew Dillon 	idx = thr->clindex;	/* cluster node we are responsible for */
3783f01ebaaSMatthew Dillon 	needrescan = 0;
379b02c0ae6SMatthew Dillon 	want_update = 0;
380b02c0ae6SMatthew Dillon 	sync_tid = 0;
381b02c0ae6SMatthew Dillon 	chain = NULL;
382b02c0ae6SMatthew Dillon 	parent = NULL;
3833f01ebaaSMatthew Dillon 
3843f01ebaaSMatthew Dillon #if 0
3853f01ebaaSMatthew Dillon 	/*
3863f01ebaaSMatthew Dillon 	 * Nothing to do if all slaves are synchronized.
3873f01ebaaSMatthew Dillon 	 * Nothing to do if cluster not authoritatively readable.
3883f01ebaaSMatthew Dillon 	 */
3893f01ebaaSMatthew Dillon 	if (pmp->cluster_flags & HAMMER2_CLUSTER_SSYNCED)
3903f01ebaaSMatthew Dillon 		return(0);
3913f01ebaaSMatthew Dillon 	if ((pmp->cluster_flags & HAMMER2_CLUSTER_RDHARD) == 0)
3923f01ebaaSMatthew Dillon 		return(HAMMER2_ERROR_INCOMPLETE);
3933f01ebaaSMatthew Dillon #endif
3943f01ebaaSMatthew Dillon 
395c8c0a18aSMatthew Dillon 	merror = 0;
3963f01ebaaSMatthew Dillon 
3973f01ebaaSMatthew Dillon 	/*
398b02c0ae6SMatthew Dillon 	 * Resolve the root inode of the PFS and determine if synchronization
399b02c0ae6SMatthew Dillon 	 * is needed by checking modify_tid.
4000d66a712SMatthew Dillon 	 *
4010d66a712SMatthew Dillon 	 * Retain the synchronization TID from the focus inode and use it
4020d66a712SMatthew Dillon 	 * later to synchronize the focus inode if/when the recursion
4030d66a712SMatthew Dillon 	 * succeeds.
404b02c0ae6SMatthew Dillon 	 */
405b02c0ae6SMatthew Dillon 	{
406b02c0ae6SMatthew Dillon 		hammer2_xop_ipcluster_t *xop2;
407b02c0ae6SMatthew Dillon 		hammer2_chain_t *focus;
408b02c0ae6SMatthew Dillon 
409b02c0ae6SMatthew Dillon 		hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
410b02c0ae6SMatthew Dillon 		xop2 = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
411c4421f07SMatthew Dillon 		hammer2_xop_start_except(&xop2->head, &hammer2_ipcluster_desc,
412b02c0ae6SMatthew Dillon 					 idx);
413b02c0ae6SMatthew Dillon 		hammer2_inode_unlock(ip);
414c8c0a18aSMatthew Dillon 		merror = hammer2_xop_collect(&xop2->head, 0);
415c8c0a18aSMatthew Dillon 		if (merror == 0 && (focus = xop2->head.cluster.focus) != NULL) {
4160d66a712SMatthew Dillon 			sync_tid = focus->bref.modify_tid;
417b02c0ae6SMatthew Dillon 			chain = hammer2_inode_chain_and_parent(ip, idx,
418b02c0ae6SMatthew Dillon 						    &parent,
419b02c0ae6SMatthew Dillon 						    HAMMER2_RESOLVE_ALWAYS |
420b02c0ae6SMatthew Dillon 						    HAMMER2_RESOLVE_SHARED);
421b02c0ae6SMatthew Dillon 			want_update = (chain->bref.modify_tid != sync_tid);
422b02c0ae6SMatthew Dillon 			if (chain) {
423b02c0ae6SMatthew Dillon 				hammer2_chain_unlock(chain);
424b02c0ae6SMatthew Dillon 				hammer2_chain_drop(chain);
425b02c0ae6SMatthew Dillon 				chain = NULL;
426b02c0ae6SMatthew Dillon 			}
427b02c0ae6SMatthew Dillon 			if (parent) {
428b02c0ae6SMatthew Dillon 				hammer2_chain_unlock(parent);
429b02c0ae6SMatthew Dillon 				hammer2_chain_drop(parent);
430b02c0ae6SMatthew Dillon 				parent = NULL;
431b02c0ae6SMatthew Dillon 			}
432b02c0ae6SMatthew Dillon 		}
433b02c0ae6SMatthew Dillon 		hammer2_xop_retire(&xop2->head, HAMMER2_XOPMASK_VOP);
434b02c0ae6SMatthew Dillon 	}
435b02c0ae6SMatthew Dillon 
436b02c0ae6SMatthew Dillon 	if (want_update == 0)
437b02c0ae6SMatthew Dillon 		return(0);
438b02c0ae6SMatthew Dillon 
439b02c0ae6SMatthew Dillon 	/*
4403f01ebaaSMatthew Dillon 	 * The inode is left unlocked during the scan.  Issue a XOP
4413f01ebaaSMatthew Dillon 	 * that does *not* include our cluster index to iterate
4423f01ebaaSMatthew Dillon 	 * properly synchronized elements and resolve our cluster index
4433f01ebaaSMatthew Dillon 	 * against it.
4443f01ebaaSMatthew Dillon 	 */
4453f01ebaaSMatthew Dillon 	hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
4463f01ebaaSMatthew Dillon 	xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
4473f01ebaaSMatthew Dillon 	xop->key_beg = HAMMER2_KEY_MIN;
4483f01ebaaSMatthew Dillon 	xop->key_end = HAMMER2_KEY_MAX;
449b02c0ae6SMatthew Dillon 	xop->resolve_flags = HAMMER2_RESOLVE_SHARED |
450b02c0ae6SMatthew Dillon 			     HAMMER2_RESOLVE_ALWAYS;
451b02c0ae6SMatthew Dillon 	xop->lookup_flags = HAMMER2_LOOKUP_SHARED |
452b02c0ae6SMatthew Dillon 			    HAMMER2_LOOKUP_NODIRECT |
453b02c0ae6SMatthew Dillon 			    HAMMER2_LOOKUP_ALWAYS;
454c4421f07SMatthew Dillon 	hammer2_xop_start_except(&xop->head, &hammer2_scanall_desc, idx);
4553f01ebaaSMatthew Dillon 	parent = hammer2_inode_chain(ip, idx,
4563f01ebaaSMatthew Dillon 				     HAMMER2_RESOLVE_ALWAYS |
4573f01ebaaSMatthew Dillon 				     HAMMER2_RESOLVE_SHARED);
4583f01ebaaSMatthew Dillon 	hammer2_inode_unlock(ip);
4593f01ebaaSMatthew Dillon 
4603f01ebaaSMatthew Dillon 	chain = hammer2_chain_lookup(&parent, &key_next,
4613f01ebaaSMatthew Dillon 				     HAMMER2_KEY_MIN, HAMMER2_KEY_MAX,
462c8c0a18aSMatthew Dillon 				     &serror,
4633f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_SHARED |
4643f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_NODIRECT |
4653f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_NODATA);
466c8c0a18aSMatthew Dillon 	merror = hammer2_xop_collect(&xop->head, 0);
4670d66a712SMatthew Dillon 	if (hammer2_debug & 0x8000) {
468b02c0ae6SMatthew Dillon 		kprintf("START_SCAN IP=%016jx chain=%p (%016jx)\n",
469b02c0ae6SMatthew Dillon 			ip->meta.name_key, chain,
470b02c0ae6SMatthew Dillon 			(chain ? chain->bref.key : -1));
4710d66a712SMatthew Dillon 	}
4723f01ebaaSMatthew Dillon 
4733f01ebaaSMatthew Dillon 	for (;;) {
4743f01ebaaSMatthew Dillon 		/*
4753f01ebaaSMatthew Dillon 		 * We are done if our scan is done and the XOP scan is done.
4763f01ebaaSMatthew Dillon 		 * We are done if the XOP scan failed (that is, we don't
4773f01ebaaSMatthew Dillon 		 * have authoritative data to synchronize with).
4783f01ebaaSMatthew Dillon 		 */
4793f01ebaaSMatthew Dillon 		int advance_local = 0;
4803f01ebaaSMatthew Dillon 		int advance_xop = 0;
4813f01ebaaSMatthew Dillon 		int dodefer = 0;
4823f01ebaaSMatthew Dillon 		hammer2_chain_t *focus;
4833f01ebaaSMatthew Dillon 
48465cacacfSMatthew Dillon 		if (chain == NULL && merror == HAMMER2_ERROR_ENOENT)
4853f01ebaaSMatthew Dillon 			break;
48665cacacfSMatthew Dillon 		if (merror && merror != HAMMER2_ERROR_ENOENT)
4873f01ebaaSMatthew Dillon 			break;
4883f01ebaaSMatthew Dillon 
4893f01ebaaSMatthew Dillon 		/*
4903f01ebaaSMatthew Dillon 		 * Compare
4913f01ebaaSMatthew Dillon 		 */
49265cacacfSMatthew Dillon 		if (chain && merror == HAMMER2_ERROR_ENOENT) {
4933f01ebaaSMatthew Dillon 			/*
4943f01ebaaSMatthew Dillon 			 * If we have local chains but the XOP scan is done,
4953f01ebaaSMatthew Dillon 			 * the chains need to be deleted.
4963f01ebaaSMatthew Dillon 			 */
4973f01ebaaSMatthew Dillon 			n = -1;
4983f01ebaaSMatthew Dillon 			focus = NULL;
4993f01ebaaSMatthew Dillon 		} else if (chain == NULL) {
5003f01ebaaSMatthew Dillon 			/*
5013f01ebaaSMatthew Dillon 			 * If our local scan is done but the XOP scan is not,
5023f01ebaaSMatthew Dillon 			 * we need to create the missing chain(s).
5033f01ebaaSMatthew Dillon 			 */
5043f01ebaaSMatthew Dillon 			n = 1;
5053f01ebaaSMatthew Dillon 			focus = xop->head.cluster.focus;
5063f01ebaaSMatthew Dillon 		} else {
5073f01ebaaSMatthew Dillon 			/*
5083f01ebaaSMatthew Dillon 			 * Otherwise compare to determine the action
5093f01ebaaSMatthew Dillon 			 * needed.
5103f01ebaaSMatthew Dillon 			 */
5113f01ebaaSMatthew Dillon 			focus = xop->head.cluster.focus;
5123f01ebaaSMatthew Dillon 			n = hammer2_chain_cmp(chain, focus);
5133f01ebaaSMatthew Dillon 		}
5143f01ebaaSMatthew Dillon 
5153f01ebaaSMatthew Dillon 		/*
5163f01ebaaSMatthew Dillon 		 * Take action based on comparison results.
5173f01ebaaSMatthew Dillon 		 */
5183f01ebaaSMatthew Dillon 		if (n < 0) {
5193f01ebaaSMatthew Dillon 			/*
5203f01ebaaSMatthew Dillon 			 * Delete extranious local data.  This will
5213f01ebaaSMatthew Dillon 			 * automatically advance the chain.
5223f01ebaaSMatthew Dillon 			 */
5233f01ebaaSMatthew Dillon 			nerror = hammer2_sync_destroy(thr, &parent, &chain,
5243f01ebaaSMatthew Dillon 						      0, idx);
5253f01ebaaSMatthew Dillon 		} else if (n == 0 && chain->bref.modify_tid !=
5263f01ebaaSMatthew Dillon 				     focus->bref.modify_tid) {
5273f01ebaaSMatthew Dillon 			/*
5283f01ebaaSMatthew Dillon 			 * Matching key but local data or meta-data requires
5293f01ebaaSMatthew Dillon 			 * updating.  If we will recurse, we still need to
5303f01ebaaSMatthew Dillon 			 * update to compatible content first but we do not
5313f01ebaaSMatthew Dillon 			 * synchronize modify_tid until the entire recursion
5323f01ebaaSMatthew Dillon 			 * has completed successfully.
5333f01ebaaSMatthew Dillon 			 */
534da0cdd33SMatthew Dillon 			if (focus->bref.type == HAMMER2_BREF_TYPE_INODE) {
5353f01ebaaSMatthew Dillon 				nerror = hammer2_sync_replace(
5363f01ebaaSMatthew Dillon 						thr, parent, chain,
5373f01ebaaSMatthew Dillon 						0,
538fda30e02SMatthew Dillon 						idx, &xop->head, focus, 0);
5393f01ebaaSMatthew Dillon 				dodefer = 1;
5403f01ebaaSMatthew Dillon 			} else {
5413f01ebaaSMatthew Dillon 				nerror = hammer2_sync_replace(
5423f01ebaaSMatthew Dillon 						thr, parent, chain,
5433f01ebaaSMatthew Dillon 						focus->bref.modify_tid,
544fda30e02SMatthew Dillon 						idx, &xop->head, focus, 0);
5453f01ebaaSMatthew Dillon 			}
546b02c0ae6SMatthew Dillon 			advance_local = 1;
547b02c0ae6SMatthew Dillon 			advance_xop = 1;
5483f01ebaaSMatthew Dillon 		} else if (n == 0) {
5493f01ebaaSMatthew Dillon 			/*
5503f01ebaaSMatthew Dillon 			 * 100% match, advance both
5513f01ebaaSMatthew Dillon 			 */
5523f01ebaaSMatthew Dillon 			advance_local = 1;
5533f01ebaaSMatthew Dillon 			advance_xop = 1;
5543f01ebaaSMatthew Dillon 			nerror = 0;
5553f01ebaaSMatthew Dillon 		} else if (n > 0) {
5563f01ebaaSMatthew Dillon 			/*
5573f01ebaaSMatthew Dillon 			 * Insert missing local data.
5583f01ebaaSMatthew Dillon 			 *
5593f01ebaaSMatthew Dillon 			 * If we will recurse, we still need to update to
5603f01ebaaSMatthew Dillon 			 * compatible content first but we do not synchronize
5613f01ebaaSMatthew Dillon 			 * modify_tid until the entire recursion has
5623f01ebaaSMatthew Dillon 			 * completed successfully.
5633f01ebaaSMatthew Dillon 			 */
564da0cdd33SMatthew Dillon 			if (focus->bref.type == HAMMER2_BREF_TYPE_INODE) {
5653f01ebaaSMatthew Dillon 				nerror = hammer2_sync_insert(
5663f01ebaaSMatthew Dillon 						thr, &parent, &chain,
5673f01ebaaSMatthew Dillon 						0,
568fda30e02SMatthew Dillon 						idx, &xop->head, focus);
5693f01ebaaSMatthew Dillon 				dodefer = 2;
5703f01ebaaSMatthew Dillon 			} else {
5713f01ebaaSMatthew Dillon 				nerror = hammer2_sync_insert(
5723f01ebaaSMatthew Dillon 						thr, &parent, &chain,
5733f01ebaaSMatthew Dillon 						focus->bref.modify_tid,
574fda30e02SMatthew Dillon 						idx, &xop->head, focus);
5753f01ebaaSMatthew Dillon 			}
5763f01ebaaSMatthew Dillon 			advance_local = 1;
5773f01ebaaSMatthew Dillon 			advance_xop = 1;
5783f01ebaaSMatthew Dillon 		}
5793f01ebaaSMatthew Dillon 
5803f01ebaaSMatthew Dillon 		/*
5813f01ebaaSMatthew Dillon 		 * We cannot recurse depth-first because the XOP is still
5823f01ebaaSMatthew Dillon 		 * running in node threads for this scan.  Create a placemarker
5833f01ebaaSMatthew Dillon 		 * by obtaining and record the hammer2_inode.
5843f01ebaaSMatthew Dillon 		 *
5853f01ebaaSMatthew Dillon 		 * We excluded our node from the XOP so we must temporarily
5863f01ebaaSMatthew Dillon 		 * add it to xop->head.cluster so it is properly incorporated
5873f01ebaaSMatthew Dillon 		 * into the inode.
5883f01ebaaSMatthew Dillon 		 *
5893f01ebaaSMatthew Dillon 		 * The deferral is pushed onto a LIFO list for bottom-up
5903f01ebaaSMatthew Dillon 		 * synchronization.
5913f01ebaaSMatthew Dillon 		 */
592c8c0a18aSMatthew Dillon 		if (merror == 0 && dodefer) {
5933f01ebaaSMatthew Dillon 			hammer2_inode_t *nip;
5943f01ebaaSMatthew Dillon 			hammer2_deferred_ip_t *defer;
5953f01ebaaSMatthew Dillon 
5963f01ebaaSMatthew Dillon 			KKASSERT(focus->bref.type == HAMMER2_BREF_TYPE_INODE);
5973f01ebaaSMatthew Dillon 
5983f01ebaaSMatthew Dillon 			defer = kmalloc(sizeof(*defer), M_HAMMER2,
5993f01ebaaSMatthew Dillon 					M_WAITOK | M_ZERO);
6003f01ebaaSMatthew Dillon 			KKASSERT(xop->head.cluster.array[idx].chain == NULL);
6013f01ebaaSMatthew Dillon 			xop->head.cluster.array[idx].flags =
6023f01ebaaSMatthew Dillon 							HAMMER2_CITEM_INVALID;
6033f01ebaaSMatthew Dillon 			xop->head.cluster.array[idx].chain = chain;
6045afbe9d8SMatthew Dillon 			nip = hammer2_inode_get(pmp, &xop->head, -1, idx);
6053f01ebaaSMatthew Dillon 			xop->head.cluster.array[idx].chain = NULL;
6063f01ebaaSMatthew Dillon 
6073f01ebaaSMatthew Dillon 			hammer2_inode_ref(nip);
6083f01ebaaSMatthew Dillon 			hammer2_inode_unlock(nip);
6093f01ebaaSMatthew Dillon 
6103f01ebaaSMatthew Dillon 			defer->next = list->base;
6113f01ebaaSMatthew Dillon 			defer->ip = nip;
6123f01ebaaSMatthew Dillon 			list->base = defer;
6133f01ebaaSMatthew Dillon 			++list->count;
6143f01ebaaSMatthew Dillon 			needrescan = 1;
6153f01ebaaSMatthew Dillon 		}
6163f01ebaaSMatthew Dillon 
6173f01ebaaSMatthew Dillon 		/*
6183f01ebaaSMatthew Dillon 		 * If at least one deferral was added and the deferral
6193f01ebaaSMatthew Dillon 		 * list has grown too large, stop adding more.  This
62065cacacfSMatthew Dillon 		 * will trigger an HAMMER2_ERROR_EAGAIN return.
6213f01ebaaSMatthew Dillon 		 */
6223f01ebaaSMatthew Dillon 		if (needrescan && list->count > 1000)
6233f01ebaaSMatthew Dillon 			break;
6243f01ebaaSMatthew Dillon 
6253f01ebaaSMatthew Dillon 		/*
6263f01ebaaSMatthew Dillon 		 * Advancements for iteration.
6273f01ebaaSMatthew Dillon 		 */
6283f01ebaaSMatthew Dillon 		if (advance_xop) {
629c8c0a18aSMatthew Dillon 			merror = hammer2_xop_collect(&xop->head, 0);
6303f01ebaaSMatthew Dillon 		}
6313f01ebaaSMatthew Dillon 		if (advance_local) {
6323f01ebaaSMatthew Dillon 			chain = hammer2_chain_next(&parent, chain, &key_next,
6333f01ebaaSMatthew Dillon 						   key_next, HAMMER2_KEY_MAX,
634c8c0a18aSMatthew Dillon 						   &serror,
6353f01ebaaSMatthew Dillon 						   HAMMER2_LOOKUP_SHARED |
6363f01ebaaSMatthew Dillon 						   HAMMER2_LOOKUP_NODIRECT |
6373f01ebaaSMatthew Dillon 						   HAMMER2_LOOKUP_NODATA);
6383f01ebaaSMatthew Dillon 		}
6393f01ebaaSMatthew Dillon 	}
6403f01ebaaSMatthew Dillon 	hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
6413f01ebaaSMatthew Dillon 	if (chain) {
6423f01ebaaSMatthew Dillon 		hammer2_chain_unlock(chain);
6433f01ebaaSMatthew Dillon 		hammer2_chain_drop(chain);
6443f01ebaaSMatthew Dillon 	}
6453f01ebaaSMatthew Dillon 	if (parent) {
6463f01ebaaSMatthew Dillon 		hammer2_chain_unlock(parent);
6473f01ebaaSMatthew Dillon 		hammer2_chain_drop(parent);
6483f01ebaaSMatthew Dillon 	}
6493f01ebaaSMatthew Dillon 
6503f01ebaaSMatthew Dillon 	/*
6513f01ebaaSMatthew Dillon 	 * If we added deferrals we want the caller to synchronize them
6523f01ebaaSMatthew Dillon 	 * and then call us again.
6533f01ebaaSMatthew Dillon 	 *
6543f01ebaaSMatthew Dillon 	 * NOTE: In this situation we do not yet want to synchronize our
6553f01ebaaSMatthew Dillon 	 *	 inode, setting the error code also has that effect.
6563f01ebaaSMatthew Dillon 	 */
65765cacacfSMatthew Dillon 	if ((merror == 0 || merror == HAMMER2_ERROR_ENOENT) && needrescan)
65865cacacfSMatthew Dillon 		merror = HAMMER2_ERROR_EAGAIN;
6593f01ebaaSMatthew Dillon 
6603f01ebaaSMatthew Dillon 	/*
661b02c0ae6SMatthew Dillon 	 * If no error occurred we can synchronize the inode meta-data
662b02c0ae6SMatthew Dillon 	 * and modify_tid.  Only limited changes are made to PFSROOTs.
6633f01ebaaSMatthew Dillon 	 *
6643f01ebaaSMatthew Dillon 	 * XXX inode lock was lost
6653f01ebaaSMatthew Dillon 	 */
66665cacacfSMatthew Dillon 	if (merror == 0 || merror == HAMMER2_ERROR_ENOENT) {
6673f01ebaaSMatthew Dillon 		hammer2_xop_ipcluster_t *xop2;
6683f01ebaaSMatthew Dillon 		hammer2_chain_t *focus;
6693f01ebaaSMatthew Dillon 
670b02c0ae6SMatthew Dillon 		hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
6713f01ebaaSMatthew Dillon 		xop2 = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
672c4421f07SMatthew Dillon 		hammer2_xop_start_except(&xop2->head, &hammer2_ipcluster_desc,
6733f01ebaaSMatthew Dillon 					 idx);
674b02c0ae6SMatthew Dillon 		hammer2_inode_unlock(ip);
675c8c0a18aSMatthew Dillon 		merror = hammer2_xop_collect(&xop2->head, 0);
676c8c0a18aSMatthew Dillon 		if (merror == 0) {
6773f01ebaaSMatthew Dillon 			focus = xop2->head.cluster.focus;
678fda30e02SMatthew Dillon 			if ((hammer2_debug & 0x8000) && focus) {
679fda30e02SMatthew Dillon 				const char *filename;
680fda30e02SMatthew Dillon 
681fda30e02SMatthew Dillon 				filename = hammer2_xop_gdata(&xop2->head)->
682fda30e02SMatthew Dillon 						ipdata.filename;
6833f01ebaaSMatthew Dillon 				kprintf("syncthr: update inode %p (%s)\n",
684fda30e02SMatthew Dillon 					focus, filename);
685fda30e02SMatthew Dillon 				hammer2_xop_pdata(&xop2->head);
6860d66a712SMatthew Dillon 			}
6873f01ebaaSMatthew Dillon 			chain = hammer2_inode_chain_and_parent(ip, idx,
6883f01ebaaSMatthew Dillon 						    &parent,
6893f01ebaaSMatthew Dillon 						    HAMMER2_RESOLVE_ALWAYS |
6903f01ebaaSMatthew Dillon 						    HAMMER2_RESOLVE_SHARED);
6913f01ebaaSMatthew Dillon 
6923f01ebaaSMatthew Dillon 			KKASSERT(parent != NULL);
6933f01ebaaSMatthew Dillon 			nerror = hammer2_sync_replace(
6943f01ebaaSMatthew Dillon 					thr, parent, chain,
6953f01ebaaSMatthew Dillon 					sync_tid,
696fda30e02SMatthew Dillon 					idx, &xop2->head, focus, isroot);
6973f01ebaaSMatthew Dillon 			hammer2_chain_unlock(chain);
6983f01ebaaSMatthew Dillon 			hammer2_chain_drop(chain);
6993f01ebaaSMatthew Dillon 			hammer2_chain_unlock(parent);
7003f01ebaaSMatthew Dillon 			hammer2_chain_drop(parent);
7013f01ebaaSMatthew Dillon 			/* XXX */
7023f01ebaaSMatthew Dillon 		}
7033f01ebaaSMatthew Dillon 		hammer2_xop_retire(&xop2->head, HAMMER2_XOPMASK_VOP);
7043f01ebaaSMatthew Dillon 	}
7053f01ebaaSMatthew Dillon 
706c8c0a18aSMatthew Dillon 	return merror;
7073f01ebaaSMatthew Dillon }
7083f01ebaaSMatthew Dillon 
7093f01ebaaSMatthew Dillon /*
7103f01ebaaSMatthew Dillon  * Create a missing chain by copying the focus from another device.
7113f01ebaaSMatthew Dillon  *
7123f01ebaaSMatthew Dillon  * On entry *parentp and focus are both locked shared.  The chain will be
7133f01ebaaSMatthew Dillon  * created and returned in *chainp also locked shared.
7143f01ebaaSMatthew Dillon  */
7153f01ebaaSMatthew Dillon static
7163f01ebaaSMatthew Dillon int
hammer2_sync_insert(hammer2_thread_t * thr,hammer2_chain_t ** parentp,hammer2_chain_t ** chainp,hammer2_tid_t mtid,int idx,hammer2_xop_head_t * xop,hammer2_chain_t * focus)7173f01ebaaSMatthew Dillon hammer2_sync_insert(hammer2_thread_t *thr,
7183f01ebaaSMatthew Dillon 		    hammer2_chain_t **parentp, hammer2_chain_t **chainp,
719fda30e02SMatthew Dillon 		    hammer2_tid_t mtid, int idx, hammer2_xop_head_t *xop,
720fda30e02SMatthew Dillon 		    hammer2_chain_t *focus)
7213f01ebaaSMatthew Dillon {
7223f01ebaaSMatthew Dillon 	hammer2_chain_t *chain;
723b02c0ae6SMatthew Dillon 	hammer2_key_t dummy;
724c8c0a18aSMatthew Dillon 	int error;
7253f01ebaaSMatthew Dillon 
7263f01ebaaSMatthew Dillon #if HAMMER2_SYNCHRO_DEBUG
7273f01ebaaSMatthew Dillon 	if (hammer2_debug & 1)
7283f01ebaaSMatthew Dillon 	kprintf("insert rec par=%p/%d.%016jx slave %d %d.%016jx mod=%016jx\n",
7293f01ebaaSMatthew Dillon 		*parentp,
7303f01ebaaSMatthew Dillon 		(*parentp)->bref.type,
7313f01ebaaSMatthew Dillon 		(*parentp)->bref.key,
7323f01ebaaSMatthew Dillon 		idx,
7333f01ebaaSMatthew Dillon 		focus->bref.type, focus->bref.key, mtid);
7343f01ebaaSMatthew Dillon #endif
7353f01ebaaSMatthew Dillon 
7363f01ebaaSMatthew Dillon 	/*
737b02c0ae6SMatthew Dillon 	 * Parent requires an exclusive lock for the insertion.
738b02c0ae6SMatthew Dillon 	 * We must unlock the child to avoid deadlocks while
739b02c0ae6SMatthew Dillon 	 * relocking the parent.
7403f01ebaaSMatthew Dillon 	 */
741b02c0ae6SMatthew Dillon 	if (*chainp) {
7423f01ebaaSMatthew Dillon 		hammer2_chain_unlock(*chainp);
743b02c0ae6SMatthew Dillon 		hammer2_chain_drop(*chainp);
744b02c0ae6SMatthew Dillon 		*chainp = NULL;
745b02c0ae6SMatthew Dillon 	}
7463f01ebaaSMatthew Dillon 	hammer2_chain_unlock(*parentp);
7473f01ebaaSMatthew Dillon 	hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_ALWAYS);
748b02c0ae6SMatthew Dillon 
749b02c0ae6SMatthew Dillon 	/*
750b02c0ae6SMatthew Dillon 	 * We must reissue the lookup to properly position (*parentp)
751b02c0ae6SMatthew Dillon 	 * for the insertion.
752b02c0ae6SMatthew Dillon 	 */
753b02c0ae6SMatthew Dillon 	chain = hammer2_chain_lookup(parentp, &dummy,
754b02c0ae6SMatthew Dillon 				     focus->bref.key, focus->bref.key,
755c8c0a18aSMatthew Dillon 				     &error,
756b02c0ae6SMatthew Dillon 				     HAMMER2_LOOKUP_NODIRECT |
757b02c0ae6SMatthew Dillon 				     HAMMER2_LOOKUP_ALWAYS);
758b02c0ae6SMatthew Dillon 	KKASSERT(chain == NULL);
7593f01ebaaSMatthew Dillon 
7603f01ebaaSMatthew Dillon 	chain = NULL;
761*ecfe89b8SMatthew Dillon 	error = hammer2_chain_create(parentp, &chain, NULL, thr->pmp,
762*ecfe89b8SMatthew Dillon 				     focus->bref.methods,
7633f01ebaaSMatthew Dillon 				     focus->bref.key, focus->bref.keybits,
7643f01ebaaSMatthew Dillon 				     focus->bref.type, focus->bytes,
7653f01ebaaSMatthew Dillon 				     mtid, 0, 0);
766c8c0a18aSMatthew Dillon 	if (error == 0) {
767fda30e02SMatthew Dillon 		const hammer2_media_data_t *data;
768fda30e02SMatthew Dillon 
76965cacacfSMatthew Dillon 		error = hammer2_chain_modify(chain, mtid, 0, 0);
77065cacacfSMatthew Dillon 		if (error)
77165cacacfSMatthew Dillon 			goto failed;
7723f01ebaaSMatthew Dillon 
7733f01ebaaSMatthew Dillon 		/*
7743f01ebaaSMatthew Dillon 		 * Copy focus to new chain
7753f01ebaaSMatthew Dillon 		 */
7763f01ebaaSMatthew Dillon 
7773f01ebaaSMatthew Dillon 		/* type already set */
7783f01ebaaSMatthew Dillon 		chain->bref.methods = focus->bref.methods;
7793f01ebaaSMatthew Dillon 		/* keybits already set */
7803f01ebaaSMatthew Dillon 		chain->bref.vradix = focus->bref.vradix;
7813f01ebaaSMatthew Dillon 		/* mirror_tid set by flush */
7823f01ebaaSMatthew Dillon 		KKASSERT(chain->bref.modify_tid == mtid);
7833f01ebaaSMatthew Dillon 		chain->bref.flags = focus->bref.flags;
7843f01ebaaSMatthew Dillon 		/* key already present */
7853f01ebaaSMatthew Dillon 		/* check code will be recalculated */
7863f01ebaaSMatthew Dillon 
7873f01ebaaSMatthew Dillon 		/*
7883f01ebaaSMatthew Dillon 		 * Copy data body.
7893f01ebaaSMatthew Dillon 		 */
7903f01ebaaSMatthew Dillon 		switch(chain->bref.type) {
7913f01ebaaSMatthew Dillon 		case HAMMER2_BREF_TYPE_INODE:
792fda30e02SMatthew Dillon 			data = hammer2_xop_gdata(xop);
793fda30e02SMatthew Dillon 
794fda30e02SMatthew Dillon 			if ((data->ipdata.meta.op_flags &
7953f01ebaaSMatthew Dillon 			     HAMMER2_OPFLAG_DIRECTDATA) == 0) {
796b02c0ae6SMatthew Dillon 				/* do not copy block table */
797fda30e02SMatthew Dillon 				bcopy(data, chain->data,
7983f01ebaaSMatthew Dillon 				      offsetof(hammer2_inode_data_t, u));
799fda30e02SMatthew Dillon 				hammer2_xop_pdata(xop);
8003f01ebaaSMatthew Dillon 				break;
8013f01ebaaSMatthew Dillon 			}
802fda30e02SMatthew Dillon 			hammer2_xop_pdata(xop);
803b02c0ae6SMatthew Dillon 			/* fall through copy whole thing */
8043f01ebaaSMatthew Dillon 		case HAMMER2_BREF_TYPE_DATA:
805fda30e02SMatthew Dillon 			data = hammer2_xop_gdata(xop);
806fda30e02SMatthew Dillon 			bcopy(data, chain->data, chain->bytes);
8073f01ebaaSMatthew Dillon 			hammer2_chain_setcheck(chain, chain->data);
808fda30e02SMatthew Dillon 			hammer2_xop_pdata(xop);
8093f01ebaaSMatthew Dillon 			break;
810da0cdd33SMatthew Dillon 		case HAMMER2_BREF_TYPE_DIRENT:
811da0cdd33SMatthew Dillon 			/*
812da0cdd33SMatthew Dillon 			 * Directory entries embed data in the blockref.
813da0cdd33SMatthew Dillon 			 */
814da0cdd33SMatthew Dillon 			if (chain->bytes) {
815fda30e02SMatthew Dillon 				data = hammer2_xop_gdata(xop);
816fda30e02SMatthew Dillon 				bcopy(data, chain->data, chain->bytes);
817da0cdd33SMatthew Dillon 				hammer2_chain_setcheck(chain, chain->data);
818fda30e02SMatthew Dillon 				hammer2_xop_pdata(xop);
819da0cdd33SMatthew Dillon 			} else {
820da0cdd33SMatthew Dillon 				chain->bref.check = focus->bref.check;
821da0cdd33SMatthew Dillon 			}
822da0cdd33SMatthew Dillon 			chain->bref.embed = focus->bref.embed;
823da0cdd33SMatthew Dillon 			break;
8243f01ebaaSMatthew Dillon 		default:
8253f01ebaaSMatthew Dillon 			KKASSERT(0);
8263f01ebaaSMatthew Dillon 			break;
8273f01ebaaSMatthew Dillon 		}
828c8c0a18aSMatthew Dillon 	}
8293f01ebaaSMatthew Dillon 
83065cacacfSMatthew Dillon failed:
831c8c0a18aSMatthew Dillon 	if (chain)
8323f01ebaaSMatthew Dillon 		hammer2_chain_unlock(chain);	/* unlock, leave ref */
8333f01ebaaSMatthew Dillon 	*chainp = chain;			/* will be returned locked */
8343f01ebaaSMatthew Dillon 
8353f01ebaaSMatthew Dillon 	/*
836da0cdd33SMatthew Dillon 	 * Avoid an ordering deadlock when relocking shared.
8373f01ebaaSMatthew Dillon 	 */
8383f01ebaaSMatthew Dillon 	hammer2_chain_unlock(*parentp);
8393f01ebaaSMatthew Dillon 	hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_SHARED |
8403f01ebaaSMatthew Dillon 				     HAMMER2_RESOLVE_ALWAYS);
841c8c0a18aSMatthew Dillon 	if (chain) {
8423f01ebaaSMatthew Dillon 		hammer2_chain_lock(chain, HAMMER2_RESOLVE_SHARED |
8433f01ebaaSMatthew Dillon 					  HAMMER2_RESOLVE_ALWAYS);
844c8c0a18aSMatthew Dillon 		error = chain->error;
845c8c0a18aSMatthew Dillon 	}
8463f01ebaaSMatthew Dillon 
847c8c0a18aSMatthew Dillon 	return error;
8483f01ebaaSMatthew Dillon }
8493f01ebaaSMatthew Dillon 
8503f01ebaaSMatthew Dillon /*
8513f01ebaaSMatthew Dillon  * Destroy an extranious chain.
8523f01ebaaSMatthew Dillon  *
8533f01ebaaSMatthew Dillon  * Both *parentp and *chainp are locked shared.
8543f01ebaaSMatthew Dillon  *
8553f01ebaaSMatthew Dillon  * On return, *chainp will be adjusted to point to the next element in the
8563f01ebaaSMatthew Dillon  * iteration and locked shared.
8573f01ebaaSMatthew Dillon  */
8583f01ebaaSMatthew Dillon static
8593f01ebaaSMatthew Dillon int
hammer2_sync_destroy(hammer2_thread_t * thr,hammer2_chain_t ** parentp,hammer2_chain_t ** chainp,hammer2_tid_t mtid,int idx)8603f01ebaaSMatthew Dillon hammer2_sync_destroy(hammer2_thread_t *thr,
8613f01ebaaSMatthew Dillon 		     hammer2_chain_t **parentp, hammer2_chain_t **chainp,
8623f01ebaaSMatthew Dillon 		     hammer2_tid_t mtid, int idx)
8633f01ebaaSMatthew Dillon {
8643f01ebaaSMatthew Dillon 	hammer2_chain_t *chain;
8653f01ebaaSMatthew Dillon 	hammer2_key_t key_next;
8663f01ebaaSMatthew Dillon 	hammer2_key_t save_key;
867c8c0a18aSMatthew Dillon 	int error;
8683f01ebaaSMatthew Dillon 
8693f01ebaaSMatthew Dillon 	chain = *chainp;
8703f01ebaaSMatthew Dillon 
8713f01ebaaSMatthew Dillon #if HAMMER2_SYNCHRO_DEBUG
8723f01ebaaSMatthew Dillon 	if (hammer2_debug & 1)
8733f01ebaaSMatthew Dillon 	kprintf("destroy rec %p/%p slave %d %d.%016jx\n",
8743f01ebaaSMatthew Dillon 		*parentp, chain,
8753f01ebaaSMatthew Dillon 		idx, chain->bref.type, chain->bref.key);
8763f01ebaaSMatthew Dillon #endif
8773f01ebaaSMatthew Dillon 
8783f01ebaaSMatthew Dillon 	save_key = chain->bref.key;
8793f01ebaaSMatthew Dillon 	if (save_key != HAMMER2_KEY_MAX)
8803f01ebaaSMatthew Dillon 		++save_key;
8813f01ebaaSMatthew Dillon 
8823f01ebaaSMatthew Dillon 	/*
8833f01ebaaSMatthew Dillon 	 * Try to avoid unnecessary I/O.
8843f01ebaaSMatthew Dillon 	 *
8853f01ebaaSMatthew Dillon 	 * XXX accounting not propagated up properly.  We might have to do
8863f01ebaaSMatthew Dillon 	 *     a RESOLVE_MAYBE here and pass 0 for the flags.
8873f01ebaaSMatthew Dillon 	 */
8883f01ebaaSMatthew Dillon 	hammer2_chain_unlock(chain);	/* relock exclusive */
8893f01ebaaSMatthew Dillon 	hammer2_chain_unlock(*parentp);
8903f01ebaaSMatthew Dillon 	hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_ALWAYS);
8913f01ebaaSMatthew Dillon 	hammer2_chain_lock(chain, HAMMER2_RESOLVE_NEVER);
8923f01ebaaSMatthew Dillon 
8933f01ebaaSMatthew Dillon 	hammer2_chain_delete(*parentp, chain, mtid, HAMMER2_DELETE_PERMANENT);
8943f01ebaaSMatthew Dillon 	hammer2_chain_unlock(chain);
8953f01ebaaSMatthew Dillon 	hammer2_chain_drop(chain);
8963f01ebaaSMatthew Dillon 	chain = NULL;			/* safety */
8973f01ebaaSMatthew Dillon 
8983f01ebaaSMatthew Dillon 	hammer2_chain_unlock(*parentp);	/* relock shared */
8993f01ebaaSMatthew Dillon 	hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_SHARED |
9003f01ebaaSMatthew Dillon 				     HAMMER2_RESOLVE_ALWAYS);
9010d66a712SMatthew Dillon 	*chainp = hammer2_chain_lookup(parentp, &key_next,
9023f01ebaaSMatthew Dillon 				     save_key, HAMMER2_KEY_MAX,
903c8c0a18aSMatthew Dillon 				     &error,
9043f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_SHARED |
9053f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_NODIRECT |
9063f01ebaaSMatthew Dillon 				     HAMMER2_LOOKUP_NODATA);
907c8c0a18aSMatthew Dillon 	return error;
9083f01ebaaSMatthew Dillon }
9093f01ebaaSMatthew Dillon 
9103f01ebaaSMatthew Dillon /*
9113f01ebaaSMatthew Dillon  * cparent is locked exclusively, with an extra ref, cluster is not locked.
9123f01ebaaSMatthew Dillon  * Replace element [i] in the cluster.
9133f01ebaaSMatthew Dillon  */
9143f01ebaaSMatthew Dillon static
9153f01ebaaSMatthew Dillon int
hammer2_sync_replace(hammer2_thread_t * thr,hammer2_chain_t * parent,hammer2_chain_t * chain,hammer2_tid_t mtid,int idx,hammer2_xop_head_t * xop,hammer2_chain_t * focus,int isroot)9163f01ebaaSMatthew Dillon hammer2_sync_replace(hammer2_thread_t *thr,
9173f01ebaaSMatthew Dillon 		     hammer2_chain_t *parent, hammer2_chain_t *chain,
9183f01ebaaSMatthew Dillon 		     hammer2_tid_t mtid, int idx,
919fda30e02SMatthew Dillon 		     hammer2_xop_head_t *xop, hammer2_chain_t *focus,
920fda30e02SMatthew Dillon 		     int isroot)
9213f01ebaaSMatthew Dillon {
9223f01ebaaSMatthew Dillon 	uint8_t otype;
923c8c0a18aSMatthew Dillon 	int nradix;
92465cacacfSMatthew Dillon 	int error;
9253f01ebaaSMatthew Dillon 
9263f01ebaaSMatthew Dillon #if HAMMER2_SYNCHRO_DEBUG
9273f01ebaaSMatthew Dillon 	if (hammer2_debug & 1)
9283f01ebaaSMatthew Dillon 	kprintf("replace rec %p slave %d %d.%016jx mod=%016jx\n",
9293f01ebaaSMatthew Dillon 		chain,
9303f01ebaaSMatthew Dillon 		idx,
9313f01ebaaSMatthew Dillon 		focus->bref.type, focus->bref.key, mtid);
9323f01ebaaSMatthew Dillon #endif
9333f01ebaaSMatthew Dillon 	hammer2_chain_unlock(chain);
9343f01ebaaSMatthew Dillon 	hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS);
93565cacacfSMatthew Dillon 	error = chain->error;
93665cacacfSMatthew Dillon 	if (error == 0) {
937fda30e02SMatthew Dillon 		const hammer2_media_data_t *data;
938fda30e02SMatthew Dillon 
9393f01ebaaSMatthew Dillon 		if (chain->bytes != focus->bytes) {
9403f01ebaaSMatthew Dillon 			/* XXX what if compressed? */
9413f01ebaaSMatthew Dillon 			nradix = hammer2_getradix(chain->bytes);
94265cacacfSMatthew Dillon 			error = hammer2_chain_resize(chain, mtid, 0, nradix, 0);
94365cacacfSMatthew Dillon 			if (error)
94465cacacfSMatthew Dillon 				goto failed;
9453f01ebaaSMatthew Dillon 		}
94665cacacfSMatthew Dillon 		error = hammer2_chain_modify(chain, mtid, 0, 0);
94765cacacfSMatthew Dillon 		if (error)
94865cacacfSMatthew Dillon 			goto failed;
9493f01ebaaSMatthew Dillon 		otype = chain->bref.type;
950fda30e02SMatthew Dillon 		data = hammer2_xop_gdata(xop);
9513f01ebaaSMatthew Dillon 		chain->bref.type = focus->bref.type;
9523f01ebaaSMatthew Dillon 		chain->bref.methods = focus->bref.methods;
9533f01ebaaSMatthew Dillon 		chain->bref.keybits = focus->bref.keybits;
9543f01ebaaSMatthew Dillon 		chain->bref.vradix = focus->bref.vradix;
9553f01ebaaSMatthew Dillon 		/* mirror_tid updated by flush */
956b02c0ae6SMatthew Dillon 		KKASSERT(mtid == 0 || chain->bref.modify_tid == mtid);
9573f01ebaaSMatthew Dillon 		chain->bref.flags = focus->bref.flags;
9583f01ebaaSMatthew Dillon 		/* key already present */
9593f01ebaaSMatthew Dillon 		/* check code will be recalculated */
9603f01ebaaSMatthew Dillon 
9613f01ebaaSMatthew Dillon 		/*
9623f01ebaaSMatthew Dillon 		 * Copy data body.
9633f01ebaaSMatthew Dillon 		 */
9643f01ebaaSMatthew Dillon 		switch(chain->bref.type) {
9653f01ebaaSMatthew Dillon 		case HAMMER2_BREF_TYPE_INODE:
966b02c0ae6SMatthew Dillon 			/*
967c8c0a18aSMatthew Dillon 			 * Special case PFSROOTs, only limited changes can
968c8c0a18aSMatthew Dillon 			 * be made since the meta-data contains miscellanious
969c8c0a18aSMatthew Dillon 			 * distinguishing fields.
970b02c0ae6SMatthew Dillon 			 */
971b02c0ae6SMatthew Dillon 			if (isroot) {
972b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.uflags =
973fda30e02SMatthew Dillon 					data->ipdata.meta.uflags;
974b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.rmajor =
975fda30e02SMatthew Dillon 					data->ipdata.meta.rmajor;
976b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.rminor =
977fda30e02SMatthew Dillon 					data->ipdata.meta.rminor;
978b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.ctime =
979fda30e02SMatthew Dillon 					data->ipdata.meta.ctime;
980b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.mtime =
981fda30e02SMatthew Dillon 					data->ipdata.meta.mtime;
982b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.atime =
983fda30e02SMatthew Dillon 					data->ipdata.meta.atime;
984b02c0ae6SMatthew Dillon 				/* not btime */
985b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.uid =
986fda30e02SMatthew Dillon 					data->ipdata.meta.uid;
987b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.gid =
988fda30e02SMatthew Dillon 					data->ipdata.meta.gid;
989b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.mode =
990fda30e02SMatthew Dillon 					data->ipdata.meta.mode;
991b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.ncopies =
992fda30e02SMatthew Dillon 					data->ipdata.meta.ncopies;
993b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.comp_algo =
994fda30e02SMatthew Dillon 					data->ipdata.meta.comp_algo;
995b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.check_algo =
996fda30e02SMatthew Dillon 					data->ipdata.meta.check_algo;
997b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.data_quota =
998fda30e02SMatthew Dillon 					data->ipdata.meta.data_quota;
999b02c0ae6SMatthew Dillon 				chain->data->ipdata.meta.inode_quota =
1000fda30e02SMatthew Dillon 					data->ipdata.meta.inode_quota;
10017fece146SMatthew Dillon 
10027fece146SMatthew Dillon 				/*
10037fece146SMatthew Dillon 				 * last snapshot tid controls overwrite
10047fece146SMatthew Dillon 				 */
10057fece146SMatthew Dillon 				if (chain->data->ipdata.meta.pfs_lsnap_tid <
1006fda30e02SMatthew Dillon 				    data->ipdata.meta.pfs_lsnap_tid) {
10077fece146SMatthew Dillon 					chain->data->ipdata.meta.pfs_lsnap_tid =
1008fda30e02SMatthew Dillon 					data->ipdata.meta.pfs_lsnap_tid;
10097fece146SMatthew Dillon 				}
10107fece146SMatthew Dillon 
1011b02c0ae6SMatthew Dillon 				hammer2_chain_setcheck(chain, chain->data);
1012b02c0ae6SMatthew Dillon 				break;
1013b02c0ae6SMatthew Dillon 			}
1014b02c0ae6SMatthew Dillon 
1015b02c0ae6SMatthew Dillon 			/*
1016b02c0ae6SMatthew Dillon 			 * Normal replacement.
1017b02c0ae6SMatthew Dillon 			 */
1018fda30e02SMatthew Dillon 			if ((data->ipdata.meta.op_flags &
10193f01ebaaSMatthew Dillon 			     HAMMER2_OPFLAG_DIRECTDATA) == 0) {
10203f01ebaaSMatthew Dillon 				/*
1021c8c0a18aSMatthew Dillon 				 * If DIRECTDATA is transitioning to 0 or the
1022c8c0a18aSMatthew Dillon 				 * old chain is not an inode we have to
1023c8c0a18aSMatthew Dillon 				 * initialize the block table.
10243f01ebaaSMatthew Dillon 				 */
10253f01ebaaSMatthew Dillon 				if (otype != HAMMER2_BREF_TYPE_INODE ||
10263f01ebaaSMatthew Dillon 				    (chain->data->ipdata.meta.op_flags &
10273f01ebaaSMatthew Dillon 				     HAMMER2_OPFLAG_DIRECTDATA)) {
1028c8c0a18aSMatthew Dillon 					kprintf("chain inode trans "
1029c8c0a18aSMatthew Dillon 						"away from dd\n");
10303f01ebaaSMatthew Dillon 					bzero(&chain->data->ipdata.u,
10313f01ebaaSMatthew Dillon 					      sizeof(chain->data->ipdata.u));
10323f01ebaaSMatthew Dillon 				}
1033fda30e02SMatthew Dillon 				bcopy(data, chain->data,
10343f01ebaaSMatthew Dillon 				      offsetof(hammer2_inode_data_t, u));
10353f01ebaaSMatthew Dillon 				/* XXX setcheck on inode should not be needed */
10363f01ebaaSMatthew Dillon 				hammer2_chain_setcheck(chain, chain->data);
10373f01ebaaSMatthew Dillon 				break;
10383f01ebaaSMatthew Dillon 			}
10393f01ebaaSMatthew Dillon 			/* fall through */
10403f01ebaaSMatthew Dillon 		case HAMMER2_BREF_TYPE_DATA:
1041fda30e02SMatthew Dillon 			bcopy(data, chain->data, chain->bytes);
10423f01ebaaSMatthew Dillon 			hammer2_chain_setcheck(chain, chain->data);
10433f01ebaaSMatthew Dillon 			break;
1044da0cdd33SMatthew Dillon 		case HAMMER2_BREF_TYPE_DIRENT:
1045da0cdd33SMatthew Dillon 			/*
1046da0cdd33SMatthew Dillon 			 * Directory entries embed data in the blockref.
1047da0cdd33SMatthew Dillon 			 */
1048da0cdd33SMatthew Dillon 			if (chain->bytes) {
1049fda30e02SMatthew Dillon 				bcopy(data, chain->data, chain->bytes);
1050da0cdd33SMatthew Dillon 				hammer2_chain_setcheck(chain, chain->data);
1051da0cdd33SMatthew Dillon 			} else {
1052da0cdd33SMatthew Dillon 				chain->bref.check = focus->bref.check;
1053da0cdd33SMatthew Dillon 			}
1054da0cdd33SMatthew Dillon 			chain->bref.embed = focus->bref.embed;
1055da0cdd33SMatthew Dillon 			break;
10563f01ebaaSMatthew Dillon 		default:
10573f01ebaaSMatthew Dillon 			KKASSERT(0);
10583f01ebaaSMatthew Dillon 			break;
10593f01ebaaSMatthew Dillon 		}
1060fda30e02SMatthew Dillon 		hammer2_xop_pdata(xop);
1061c8c0a18aSMatthew Dillon 	}
10623f01ebaaSMatthew Dillon 
106365cacacfSMatthew Dillon failed:
10643f01ebaaSMatthew Dillon 	hammer2_chain_unlock(chain);
10653f01ebaaSMatthew Dillon 	hammer2_chain_lock(chain, HAMMER2_RESOLVE_SHARED |
10663f01ebaaSMatthew Dillon 				  HAMMER2_RESOLVE_MAYBE);
10673f01ebaaSMatthew Dillon 
106865cacacfSMatthew Dillon 	return error;
10693f01ebaaSMatthew Dillon }
1070