xref: /dflybsd-src/sys/vfs/hammer/hammer_mirror.c (revision 842e7a706043e01c8dca9c0d4b6f54b46800c88e)
1dd94f1b1SMatthew Dillon /*
2dd94f1b1SMatthew Dillon  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3dd94f1b1SMatthew Dillon  *
4dd94f1b1SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
5dd94f1b1SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
6dd94f1b1SMatthew Dillon  *
7dd94f1b1SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
8dd94f1b1SMatthew Dillon  * modification, are permitted provided that the following conditions
9dd94f1b1SMatthew Dillon  * are met:
10dd94f1b1SMatthew Dillon  *
11dd94f1b1SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
12dd94f1b1SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
13dd94f1b1SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
14dd94f1b1SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
15dd94f1b1SMatthew Dillon  *    the documentation and/or other materials provided with the
16dd94f1b1SMatthew Dillon  *    distribution.
17dd94f1b1SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
18dd94f1b1SMatthew Dillon  *    contributors may be used to endorse or promote products derived
19dd94f1b1SMatthew Dillon  *    from this software without specific, prior written permission.
20dd94f1b1SMatthew Dillon  *
21dd94f1b1SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22dd94f1b1SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23dd94f1b1SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24dd94f1b1SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25dd94f1b1SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26dd94f1b1SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27dd94f1b1SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28dd94f1b1SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29dd94f1b1SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30dd94f1b1SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31dd94f1b1SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32dd94f1b1SMatthew Dillon  * SUCH DAMAGE.
33dd94f1b1SMatthew Dillon  *
34*842e7a70SMatthew Dillon  * $DragonFly: src/sys/vfs/hammer/hammer_mirror.c,v 1.13 2008/07/12 02:47:39 dillon Exp $
35dd94f1b1SMatthew Dillon  */
36dd94f1b1SMatthew Dillon /*
37dd94f1b1SMatthew Dillon  * HAMMER mirroring ioctls - serialize and deserialize modifications made
38dd94f1b1SMatthew Dillon  *			     to a filesystem.
39dd94f1b1SMatthew Dillon  */
40dd94f1b1SMatthew Dillon 
41dd94f1b1SMatthew Dillon #include "hammer.h"
42dd94f1b1SMatthew Dillon 
43c82af904SMatthew Dillon static int hammer_mirror_check(hammer_cursor_t cursor,
444c038e17SMatthew Dillon 				struct hammer_ioc_mrecord_rec *mrec);
45c82af904SMatthew Dillon static int hammer_mirror_update(hammer_cursor_t cursor,
464c038e17SMatthew Dillon 				struct hammer_ioc_mrecord_rec *mrec);
47c82af904SMatthew Dillon static int hammer_mirror_write(hammer_cursor_t cursor,
484c038e17SMatthew Dillon 				struct hammer_ioc_mrecord_rec *mrec,
494c038e17SMatthew Dillon 				char *udata);
504c038e17SMatthew Dillon static int hammer_ioc_mirror_write_rec(hammer_cursor_t cursor,
514c038e17SMatthew Dillon 				struct hammer_ioc_mrecord_rec *mrec,
524c038e17SMatthew Dillon 				struct hammer_ioc_mirror_rw *mirror,
534c038e17SMatthew Dillon 				u_int32_t localization,
544c038e17SMatthew Dillon 				char *uptr);
554c038e17SMatthew Dillon static int hammer_ioc_mirror_write_pass(hammer_cursor_t cursor,
564c038e17SMatthew Dillon 				struct hammer_ioc_mrecord_rec *mrec,
574c038e17SMatthew Dillon 				struct hammer_ioc_mirror_rw *mirror,
584c038e17SMatthew Dillon 				u_int32_t localization);
594c038e17SMatthew Dillon static int hammer_ioc_mirror_write_skip(hammer_cursor_t cursor,
604c038e17SMatthew Dillon 				struct hammer_ioc_mrecord_skip *mrec,
614c038e17SMatthew Dillon 				struct hammer_ioc_mirror_rw *mirror,
624c038e17SMatthew Dillon 				u_int32_t localization);
63*842e7a70SMatthew Dillon static int hammer_mirror_delete_to(hammer_cursor_t cursor,
644c038e17SMatthew Dillon 			        struct hammer_ioc_mirror_rw *mirror);
65c82af904SMatthew Dillon static int hammer_mirror_localize_data(hammer_data_ondisk_t data,
66c82af904SMatthew Dillon 				hammer_btree_leaf_elm_t leaf);
67c82af904SMatthew Dillon 
68c82af904SMatthew Dillon /*
69c82af904SMatthew Dillon  * All B-Tree records within the specified key range which also conform
70c82af904SMatthew Dillon  * to the transaction id range are returned.  Mirroring code keeps track
71c82af904SMatthew Dillon  * of the last transaction id fully scanned and can efficiently pick up
72c82af904SMatthew Dillon  * where it left off if interrupted.
73ea434b6fSMatthew Dillon  *
74ea434b6fSMatthew Dillon  * The PFS is identified in the mirror structure.  The passed ip is just
75ea434b6fSMatthew Dillon  * some directory in the overall HAMMER filesystem and has nothing to
76ea434b6fSMatthew Dillon  * do with the PFS.
77c82af904SMatthew Dillon  */
78dd94f1b1SMatthew Dillon int
79dd94f1b1SMatthew Dillon hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip,
80dd94f1b1SMatthew Dillon 		       struct hammer_ioc_mirror_rw *mirror)
81dd94f1b1SMatthew Dillon {
824c038e17SMatthew Dillon 	struct hammer_cmirror cmirror;
83dd94f1b1SMatthew Dillon 	struct hammer_cursor cursor;
844c038e17SMatthew Dillon 	union hammer_ioc_mrecord_any mrec;
85c82af904SMatthew Dillon 	hammer_btree_leaf_elm_t elm;
86c82af904SMatthew Dillon 	const int crc_start = HAMMER_MREC_CRCOFF;
87c82af904SMatthew Dillon 	char *uptr;
88dd94f1b1SMatthew Dillon 	int error;
89c82af904SMatthew Dillon 	int data_len;
90c82af904SMatthew Dillon 	int bytes;
914c038e17SMatthew Dillon 	int eatdisk;
92ea434b6fSMatthew Dillon 	u_int32_t localization;
934c038e17SMatthew Dillon 	u_int32_t rec_crc;
94ea434b6fSMatthew Dillon 
95ea434b6fSMatthew Dillon 	localization = (u_int32_t)mirror->pfs_id << 16;
96dd94f1b1SMatthew Dillon 
97dd94f1b1SMatthew Dillon 	if ((mirror->key_beg.localization | mirror->key_end.localization) &
98dd94f1b1SMatthew Dillon 	    HAMMER_LOCALIZE_PSEUDOFS_MASK) {
99dd94f1b1SMatthew Dillon 		return(EINVAL);
100dd94f1b1SMatthew Dillon 	}
101dd94f1b1SMatthew Dillon 	if (hammer_btree_cmp(&mirror->key_beg, &mirror->key_end) > 0)
102dd94f1b1SMatthew Dillon 		return(EINVAL);
103dd94f1b1SMatthew Dillon 
104dd94f1b1SMatthew Dillon 	mirror->key_cur = mirror->key_beg;
1054c038e17SMatthew Dillon 	mirror->key_cur.localization &= HAMMER_LOCALIZE_MASK;
106ea434b6fSMatthew Dillon 	mirror->key_cur.localization += localization;
107c82af904SMatthew Dillon 	bzero(&mrec, sizeof(mrec));
1084c038e17SMatthew Dillon 	bzero(&cmirror, sizeof(cmirror));
109dd94f1b1SMatthew Dillon 
110dd94f1b1SMatthew Dillon retry:
111dd94f1b1SMatthew Dillon 	error = hammer_init_cursor(trans, &cursor, NULL, NULL);
112dd94f1b1SMatthew Dillon 	if (error) {
113dd94f1b1SMatthew Dillon 		hammer_done_cursor(&cursor);
114dd94f1b1SMatthew Dillon 		goto failed;
115dd94f1b1SMatthew Dillon 	}
116dd94f1b1SMatthew Dillon 	cursor.key_beg = mirror->key_cur;
117dd94f1b1SMatthew Dillon 	cursor.key_end = mirror->key_end;
1184c038e17SMatthew Dillon 	cursor.key_end.localization &= HAMMER_LOCALIZE_MASK;
119ea434b6fSMatthew Dillon 	cursor.key_end.localization += localization;
120dd94f1b1SMatthew Dillon 
121dd94f1b1SMatthew Dillon 	cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
122dd94f1b1SMatthew Dillon 	cursor.flags |= HAMMER_CURSOR_BACKEND;
123dd94f1b1SMatthew Dillon 
124dd94f1b1SMatthew Dillon 	/*
125c82af904SMatthew Dillon 	 * This flag filters the search to only return elements whos create
126c82af904SMatthew Dillon 	 * or delete TID is >= mirror_tid.  The B-Tree uses the mirror_tid
127c82af904SMatthew Dillon 	 * field stored with internal and leaf nodes to shortcut the scan.
128dd94f1b1SMatthew Dillon 	 */
129c82af904SMatthew Dillon 	cursor.flags |= HAMMER_CURSOR_MIRROR_FILTERED;
1304c038e17SMatthew Dillon 	cursor.cmirror = &cmirror;
1314c038e17SMatthew Dillon 	cmirror.mirror_tid = mirror->tid_beg;
132dd94f1b1SMatthew Dillon 
133dd94f1b1SMatthew Dillon 	error = hammer_btree_first(&cursor);
134dd94f1b1SMatthew Dillon 	while (error == 0) {
135dd94f1b1SMatthew Dillon 		/*
13693291532SMatthew Dillon 		 * Yield to more important tasks
13793291532SMatthew Dillon 		 */
13893291532SMatthew Dillon 		if (error == 0) {
13993291532SMatthew Dillon 			error = hammer_signal_check(trans->hmp);
14093291532SMatthew Dillon 			if (error)
14193291532SMatthew Dillon 				break;
14293291532SMatthew Dillon 		}
14393291532SMatthew Dillon 
14493291532SMatthew Dillon 		/*
1454c038e17SMatthew Dillon 		 * An internal node can be returned in mirror-filtered
1464c038e17SMatthew Dillon 		 * mode and indicates that the scan is returning a skip
1474c038e17SMatthew Dillon 		 * range in the cursor->cmirror structure.
1484c038e17SMatthew Dillon 		 */
1494c038e17SMatthew Dillon 		uptr = (char *)mirror->ubuf + mirror->count;
1504c038e17SMatthew Dillon 		if (cursor.node->ondisk->type == HAMMER_BTREE_TYPE_INTERNAL) {
1514c038e17SMatthew Dillon 			/*
1524c038e17SMatthew Dillon 			 * Check space
1534c038e17SMatthew Dillon 			 */
1544c038e17SMatthew Dillon 			mirror->key_cur = cmirror.skip_beg;
1554c038e17SMatthew Dillon 			bytes = sizeof(mrec.skip);
1564c038e17SMatthew Dillon 			if (mirror->count + HAMMER_HEAD_DOALIGN(bytes) >
1574c038e17SMatthew Dillon 			    mirror->size) {
1584c038e17SMatthew Dillon 				break;
1594c038e17SMatthew Dillon 			}
1604c038e17SMatthew Dillon 
1614c038e17SMatthew Dillon 			/*
1624c038e17SMatthew Dillon 			 * Fill mrec
1634c038e17SMatthew Dillon 			 */
1644c038e17SMatthew Dillon 			mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
1654c038e17SMatthew Dillon 			mrec.head.type = HAMMER_MREC_TYPE_SKIP;
1664c038e17SMatthew Dillon 			mrec.head.rec_size = bytes;
1674c038e17SMatthew Dillon 			mrec.skip.skip_beg = cmirror.skip_beg;
1684c038e17SMatthew Dillon 			mrec.skip.skip_end = cmirror.skip_end;
1694c038e17SMatthew Dillon 			mrec.head.rec_crc = crc32(&mrec.head.rec_size,
1704c038e17SMatthew Dillon 						 bytes - crc_start);
1714c038e17SMatthew Dillon 			error = copyout(&mrec, uptr, bytes);
1724c038e17SMatthew Dillon 			eatdisk = 0;
1734c038e17SMatthew Dillon 			goto didwrite;
1744c038e17SMatthew Dillon 		}
1754c038e17SMatthew Dillon 
1764c038e17SMatthew Dillon 		/*
1774c038e17SMatthew Dillon 		 * Leaf node.  In full-history mode we could filter out
1784c038e17SMatthew Dillon 		 * elements modified outside the user-requested TID range.
1794c038e17SMatthew Dillon 		 *
1804c038e17SMatthew Dillon 		 * However, such elements must be returned so the writer
1814c038e17SMatthew Dillon 		 * can compare them against the target to detemrine what
1824c038e17SMatthew Dillon 		 * needs to be deleted on the target, particular for
1834c038e17SMatthew Dillon 		 * no-history mirrors.
184dd94f1b1SMatthew Dillon 		 */
185c82af904SMatthew Dillon 		KKASSERT(cursor.node->ondisk->type == HAMMER_BTREE_TYPE_LEAF);
186c82af904SMatthew Dillon 		elm = &cursor.node->ondisk->elms[cursor.index].leaf;
187c82af904SMatthew Dillon 		mirror->key_cur = elm->base;
188dd94f1b1SMatthew Dillon 
1894c038e17SMatthew Dillon 		if ((elm->base.create_tid < mirror->tid_beg ||
1904c038e17SMatthew Dillon 		    elm->base.create_tid > mirror->tid_end) &&
1914c038e17SMatthew Dillon 		    (elm->base.delete_tid < mirror->tid_beg ||
1924c038e17SMatthew Dillon 		    elm->base.delete_tid > mirror->tid_end)) {
1934c038e17SMatthew Dillon 			bytes = sizeof(mrec.rec);
1944c038e17SMatthew Dillon 			if (mirror->count + HAMMER_HEAD_DOALIGN(bytes) >
1954c038e17SMatthew Dillon 			    mirror->size) {
1964c038e17SMatthew Dillon 				break;
1974c038e17SMatthew Dillon 			}
1984c038e17SMatthew Dillon 
1994c038e17SMatthew Dillon 			/*
2004c038e17SMatthew Dillon 			 * Fill mrec.  PASS records are records which are
2014c038e17SMatthew Dillon 			 * outside the TID range needed for the mirror
2024c038e17SMatthew Dillon 			 * update.  They are sent without any data payload
2034c038e17SMatthew Dillon 			 * because the mirroring target must still compare
2044c038e17SMatthew Dillon 			 * records that fall outside the SKIP ranges to
2054c038e17SMatthew Dillon 			 * determine what might need to be deleted.  Such
2064c038e17SMatthew Dillon 			 * deletions are needed if the master or files on
2074c038e17SMatthew Dillon 			 * the master are no-history, or if the slave is
2084c038e17SMatthew Dillon 			 * so far behind the master has already been pruned.
2094c038e17SMatthew Dillon 			 */
2104c038e17SMatthew Dillon 			mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
2114c038e17SMatthew Dillon 			mrec.head.type = HAMMER_MREC_TYPE_PASS;
2124c038e17SMatthew Dillon 			mrec.head.rec_size = bytes;
2134c038e17SMatthew Dillon 			mrec.rec.leaf = *elm;
2144c038e17SMatthew Dillon 			mrec.head.rec_crc = crc32(&mrec.head.rec_size,
2154c038e17SMatthew Dillon 						 bytes - crc_start);
2164c038e17SMatthew Dillon 			error = copyout(&mrec, uptr, bytes);
2174c038e17SMatthew Dillon 			eatdisk = 1;
2184c038e17SMatthew Dillon 			goto didwrite;
2194c038e17SMatthew Dillon 
2204c038e17SMatthew Dillon 		}
2214c038e17SMatthew Dillon 
222dd94f1b1SMatthew Dillon 		/*
223c82af904SMatthew Dillon 		 * The core code exports the data to userland.
224dd94f1b1SMatthew Dillon 		 */
225c82af904SMatthew Dillon 		data_len = (elm->data_offset) ? elm->data_len : 0;
226c82af904SMatthew Dillon 		if (data_len) {
227c82af904SMatthew Dillon 			error = hammer_btree_extract(&cursor,
228c82af904SMatthew Dillon 						     HAMMER_CURSOR_GET_DATA);
229c82af904SMatthew Dillon 			if (error)
230c82af904SMatthew Dillon 				break;
231c82af904SMatthew Dillon 		}
2324c038e17SMatthew Dillon 
2334c038e17SMatthew Dillon 		bytes = sizeof(mrec.rec) + data_len;
2344c038e17SMatthew Dillon 		if (mirror->count + HAMMER_HEAD_DOALIGN(bytes) > mirror->size)
235c82af904SMatthew Dillon 			break;
236c82af904SMatthew Dillon 
237c82af904SMatthew Dillon 		/*
238c82af904SMatthew Dillon 		 * Construct the record for userland and copyout.
239c82af904SMatthew Dillon 		 *
240c82af904SMatthew Dillon 		 * The user is asking for a snapshot, if the record was
241c82af904SMatthew Dillon 		 * deleted beyond the user-requested ending tid, the record
242c82af904SMatthew Dillon 		 * is not considered deleted from the point of view of
243c82af904SMatthew Dillon 		 * userland and delete_tid is cleared.
244c82af904SMatthew Dillon 		 */
2454c038e17SMatthew Dillon 		mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
2464c038e17SMatthew Dillon 		mrec.head.type = HAMMER_MREC_TYPE_REC;
2474c038e17SMatthew Dillon 		mrec.head.rec_size = bytes;
2484c038e17SMatthew Dillon 		mrec.rec.leaf = *elm;
249c82af904SMatthew Dillon 		if (elm->base.delete_tid >= mirror->tid_end)
2504c038e17SMatthew Dillon 			mrec.rec.leaf.base.delete_tid = 0;
2514c038e17SMatthew Dillon 		rec_crc = crc32(&mrec.head.rec_size,
2524c038e17SMatthew Dillon 				sizeof(mrec.rec) - crc_start);
2534c038e17SMatthew Dillon 		if (data_len)
2544c038e17SMatthew Dillon 			rec_crc = crc32_ext(cursor.data, data_len, rec_crc);
2554c038e17SMatthew Dillon 		mrec.head.rec_crc = rec_crc;
2564c038e17SMatthew Dillon 		error = copyout(&mrec, uptr, sizeof(mrec.rec));
257c82af904SMatthew Dillon 		if (data_len && error == 0) {
2584c038e17SMatthew Dillon 			error = copyout(cursor.data, uptr + sizeof(mrec.rec),
259c82af904SMatthew Dillon 					data_len);
260c82af904SMatthew Dillon 		}
2614c038e17SMatthew Dillon 		eatdisk = 1;
2624c038e17SMatthew Dillon 
2634c038e17SMatthew Dillon 		/*
2644c038e17SMatthew Dillon 		 * eatdisk controls whether we skip the current cursor
2654c038e17SMatthew Dillon 		 * position on the next scan or not.  If doing a SKIP
2664c038e17SMatthew Dillon 		 * the cursor is already positioned properly for the next
2674c038e17SMatthew Dillon 		 * scan and eatdisk will be 0.
2684c038e17SMatthew Dillon 		 */
2694c038e17SMatthew Dillon didwrite:
270dd94f1b1SMatthew Dillon 		if (error == 0) {
2714c038e17SMatthew Dillon 			mirror->count += HAMMER_HEAD_DOALIGN(bytes);
2724c038e17SMatthew Dillon 			if (eatdisk)
273dd94f1b1SMatthew Dillon 				cursor.flags |= HAMMER_CURSOR_ATEDISK;
2744c038e17SMatthew Dillon 			else
2754c038e17SMatthew Dillon 				cursor.flags &= ~HAMMER_CURSOR_ATEDISK;
276dd94f1b1SMatthew Dillon 			error = hammer_btree_iterate(&cursor);
277dd94f1b1SMatthew Dillon 		}
278dd94f1b1SMatthew Dillon 	}
279c82af904SMatthew Dillon 	if (error == ENOENT) {
280c82af904SMatthew Dillon 		mirror->key_cur = mirror->key_end;
281dd94f1b1SMatthew Dillon 		error = 0;
282c82af904SMatthew Dillon 	}
283dd94f1b1SMatthew Dillon 	hammer_done_cursor(&cursor);
284dd94f1b1SMatthew Dillon 	if (error == EDEADLK)
285dd94f1b1SMatthew Dillon 		goto retry;
286dd94f1b1SMatthew Dillon 	if (error == EINTR) {
287c82af904SMatthew Dillon 		mirror->head.flags |= HAMMER_IOC_HEAD_INTR;
288dd94f1b1SMatthew Dillon 		error = 0;
289dd94f1b1SMatthew Dillon 	}
290dd94f1b1SMatthew Dillon failed:
291dd94f1b1SMatthew Dillon 	mirror->key_cur.localization &= HAMMER_LOCALIZE_MASK;
292dd94f1b1SMatthew Dillon 	return(error);
293dd94f1b1SMatthew Dillon }
294dd94f1b1SMatthew Dillon 
295c82af904SMatthew Dillon /*
2964c038e17SMatthew Dillon  * Copy records from userland to the target mirror.
297602c6cb8SMatthew Dillon  *
298ea434b6fSMatthew Dillon  * The PFS is identified in the mirror structure.  The passed ip is just
299ea434b6fSMatthew Dillon  * some directory in the overall HAMMER filesystem and has nothing to
300ea434b6fSMatthew Dillon  * do with the PFS.  In fact, there might not even be a root directory for
301ea434b6fSMatthew Dillon  * the PFS yet!
302c82af904SMatthew Dillon  */
303c82af904SMatthew Dillon int
304c82af904SMatthew Dillon hammer_ioc_mirror_write(hammer_transaction_t trans, hammer_inode_t ip,
305c82af904SMatthew Dillon 		       struct hammer_ioc_mirror_rw *mirror)
306c82af904SMatthew Dillon {
3074c038e17SMatthew Dillon 	union hammer_ioc_mrecord_any mrec;
308c82af904SMatthew Dillon 	struct hammer_cursor cursor;
309ea434b6fSMatthew Dillon 	u_int32_t localization;
31093291532SMatthew Dillon 	int checkspace_count = 0;
3114c038e17SMatthew Dillon 	int error;
3124c038e17SMatthew Dillon 	int bytes;
3134c038e17SMatthew Dillon 	char *uptr;
31493291532SMatthew Dillon 	int seq;
315ea434b6fSMatthew Dillon 
316ea434b6fSMatthew Dillon 	localization = (u_int32_t)mirror->pfs_id << 16;
31793291532SMatthew Dillon 	seq = trans->hmp->flusher.act;
318c82af904SMatthew Dillon 
3194c038e17SMatthew Dillon 	/*
3204c038e17SMatthew Dillon 	 * Validate the mirror structure and relocalize the tracking keys.
3214c038e17SMatthew Dillon 	 */
322c82af904SMatthew Dillon 	if (mirror->size < 0 || mirror->size > 0x70000000)
323c82af904SMatthew Dillon 		return(EINVAL);
3244c038e17SMatthew Dillon 	mirror->key_beg.localization &= HAMMER_LOCALIZE_MASK;
3254c038e17SMatthew Dillon 	mirror->key_beg.localization += localization;
3264c038e17SMatthew Dillon 	mirror->key_end.localization &= HAMMER_LOCALIZE_MASK;
3274c038e17SMatthew Dillon 	mirror->key_end.localization += localization;
3284c038e17SMatthew Dillon 	mirror->key_cur.localization &= HAMMER_LOCALIZE_MASK;
3294c038e17SMatthew Dillon 	mirror->key_cur.localization += localization;
330c82af904SMatthew Dillon 
3314c038e17SMatthew Dillon 	/*
3324c038e17SMatthew Dillon 	 * Set up our tracking cursor for the loop.  The tracking cursor
3334c038e17SMatthew Dillon 	 * is used to delete records that are no longer present on the
3344c038e17SMatthew Dillon 	 * master.  The last handled record at key_cur must be skipped.
3354c038e17SMatthew Dillon 	 */
336c82af904SMatthew Dillon 	error = hammer_init_cursor(trans, &cursor, NULL, NULL);
337c82af904SMatthew Dillon 
3384c038e17SMatthew Dillon 	cursor.key_beg = mirror->key_cur;
3394c038e17SMatthew Dillon 	cursor.key_end = mirror->key_end;
3404c038e17SMatthew Dillon 	cursor.flags |= HAMMER_CURSOR_BACKEND;
3414c038e17SMatthew Dillon 	error = hammer_btree_first(&cursor);
3424c038e17SMatthew Dillon 	if (error == 0)
3434c038e17SMatthew Dillon 		cursor.flags |= HAMMER_CURSOR_ATEDISK;
3444c038e17SMatthew Dillon 	if (error == ENOENT)
3454c038e17SMatthew Dillon 		error = 0;
3464c038e17SMatthew Dillon 
3474c038e17SMatthew Dillon 	/*
3484c038e17SMatthew Dillon 	 * Loop until our input buffer has been exhausted.
3494c038e17SMatthew Dillon 	 */
3504c038e17SMatthew Dillon 	while (error == 0 &&
3514c038e17SMatthew Dillon 		mirror->count + sizeof(mrec.head) <= mirror->size) {
3524c038e17SMatthew Dillon 
353c82af904SMatthew Dillon 	        /*
35493291532SMatthew Dillon 		 * Don't blow out the buffer cache.  Leave room for frontend
35593291532SMatthew Dillon 		 * cache as well.
35693291532SMatthew Dillon 		 */
35793291532SMatthew Dillon 		if (hammer_flusher_meta_halflimit(trans->hmp) ||
35893291532SMatthew Dillon 		    hammer_flusher_undo_exhausted(trans, 1)) {
35993291532SMatthew Dillon 			hammer_unlock_cursor(&cursor, 0);
36093291532SMatthew Dillon 			hammer_flusher_wait(trans->hmp, seq);
36193291532SMatthew Dillon 			hammer_lock_cursor(&cursor, 0);
36293291532SMatthew Dillon 			seq = hammer_flusher_async(trans->hmp);
36393291532SMatthew Dillon 		}
36493291532SMatthew Dillon 
36593291532SMatthew Dillon 		/*
36693291532SMatthew Dillon 		 * If there is insufficient free space it may be due to
36793291532SMatthew Dillon 		 * reserved bigblocks, which flushing might fix.
36893291532SMatthew Dillon 		 */
36993291532SMatthew Dillon 		if (hammer_checkspace(trans->hmp, HAMMER_CHKSPC_MIRROR)) {
37093291532SMatthew Dillon 			if (++checkspace_count == 10) {
37193291532SMatthew Dillon 				error = ENOSPC;
37293291532SMatthew Dillon 				break;
37393291532SMatthew Dillon 			}
37493291532SMatthew Dillon 			hammer_unlock_cursor(&cursor, 0);
37593291532SMatthew Dillon 			hammer_flusher_wait(trans->hmp, seq);
37693291532SMatthew Dillon 			hammer_lock_cursor(&cursor, 0);
37793291532SMatthew Dillon 			seq = hammer_flusher_async(trans->hmp);
37893291532SMatthew Dillon 		}
37993291532SMatthew Dillon 
38093291532SMatthew Dillon 
38193291532SMatthew Dillon 		/*
382c82af904SMatthew Dillon 		 * Acquire and validate header
383c82af904SMatthew Dillon 		 */
3844c038e17SMatthew Dillon 		if ((bytes = mirror->size - mirror->count) > sizeof(mrec))
3854c038e17SMatthew Dillon 			bytes = sizeof(mrec);
386c82af904SMatthew Dillon 		uptr = (char *)mirror->ubuf + mirror->count;
3874c038e17SMatthew Dillon 		error = copyin(uptr, &mrec, bytes);
388c82af904SMatthew Dillon 		if (error)
389c82af904SMatthew Dillon 			break;
3904c038e17SMatthew Dillon 		if (mrec.head.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
391c82af904SMatthew Dillon 			error = EINVAL;
392c82af904SMatthew Dillon 			break;
393c82af904SMatthew Dillon 		}
3944c038e17SMatthew Dillon 		if (mrec.head.rec_size < sizeof(mrec.head) ||
3954c038e17SMatthew Dillon 		    mrec.head.rec_size > sizeof(mrec) + HAMMER_XBUFSIZE ||
3964c038e17SMatthew Dillon 		    mirror->count + mrec.head.rec_size > mirror->size) {
3975fa5c92fSMatthew Dillon 			error = EINVAL;
3985fa5c92fSMatthew Dillon 			break;
3995fa5c92fSMatthew Dillon 		}
4004c038e17SMatthew Dillon 
4014c038e17SMatthew Dillon 		switch(mrec.head.type) {
4024c038e17SMatthew Dillon 		case HAMMER_MREC_TYPE_SKIP:
4034c038e17SMatthew Dillon 			if (mrec.head.rec_size != sizeof(mrec.skip))
4044c038e17SMatthew Dillon 				error = EINVAL;
4054c038e17SMatthew Dillon 			if (error == 0)
4064c038e17SMatthew Dillon 				error = hammer_ioc_mirror_write_skip(&cursor, &mrec.skip, mirror, localization);
4074c038e17SMatthew Dillon 			break;
4084c038e17SMatthew Dillon 		case HAMMER_MREC_TYPE_REC:
4094c038e17SMatthew Dillon 			if (mrec.head.rec_size < sizeof(mrec.rec))
4104c038e17SMatthew Dillon 				error = EINVAL;
4114c038e17SMatthew Dillon 			if (error == 0)
4124c038e17SMatthew Dillon 				error = hammer_ioc_mirror_write_rec(&cursor, &mrec.rec, mirror, localization, uptr + sizeof(mrec.rec));
4134c038e17SMatthew Dillon 			break;
4144c038e17SMatthew Dillon 		case HAMMER_MREC_TYPE_PASS:
4154c038e17SMatthew Dillon 			if (mrec.head.rec_size != sizeof(mrec.rec))
4164c038e17SMatthew Dillon 				error = EINVAL;
4174c038e17SMatthew Dillon 			if (error == 0)
4184c038e17SMatthew Dillon 				error = hammer_ioc_mirror_write_pass(&cursor, &mrec.rec, mirror, localization);
4194c038e17SMatthew Dillon 			break;
4204c038e17SMatthew Dillon 		default:
421c82af904SMatthew Dillon 			error = EINVAL;
422c82af904SMatthew Dillon 			break;
423c82af904SMatthew Dillon 		}
4244c038e17SMatthew Dillon 
4254c038e17SMatthew Dillon 		/*
4264c038e17SMatthew Dillon 		 * Retry the current record on deadlock, otherwise setup
4274c038e17SMatthew Dillon 		 * for the next loop.
4284c038e17SMatthew Dillon 		 */
4294c038e17SMatthew Dillon 		if (error == EDEADLK) {
4304c038e17SMatthew Dillon 			while (error == EDEADLK) {
4314c038e17SMatthew Dillon 				hammer_recover_cursor(&cursor);
4324c038e17SMatthew Dillon 				error = hammer_cursor_upgrade(&cursor);
433c82af904SMatthew Dillon 			}
4344c038e17SMatthew Dillon 		} else {
4354c038e17SMatthew Dillon 			if (error == EALREADY)
4364c038e17SMatthew Dillon 				error = 0;
4374c038e17SMatthew Dillon 			if (error == 0) {
4384c038e17SMatthew Dillon 				mirror->count +=
4394c038e17SMatthew Dillon 					HAMMER_HEAD_DOALIGN(mrec.head.rec_size);
4404c038e17SMatthew Dillon 			}
4414c038e17SMatthew Dillon 		}
4424c038e17SMatthew Dillon 	}
4434c038e17SMatthew Dillon 	hammer_done_cursor(&cursor);
4444c038e17SMatthew Dillon 
4454c038e17SMatthew Dillon 	/*
4464c038e17SMatthew Dillon 	 * cumulative error
4474c038e17SMatthew Dillon 	 */
4484c038e17SMatthew Dillon 	if (error) {
4494c038e17SMatthew Dillon 		mirror->head.flags |= HAMMER_IOC_HEAD_ERROR;
4504c038e17SMatthew Dillon 		mirror->head.error = error;
4514c038e17SMatthew Dillon 	}
4524c038e17SMatthew Dillon 
4534c038e17SMatthew Dillon 	/*
4544c038e17SMatthew Dillon 	 * ioctls don't update the RW data structure if an error is returned,
4554c038e17SMatthew Dillon 	 * always return 0.
4564c038e17SMatthew Dillon 	 */
4574c038e17SMatthew Dillon 	return(0);
4584c038e17SMatthew Dillon }
4594c038e17SMatthew Dillon 
4604c038e17SMatthew Dillon /*
4614c038e17SMatthew Dillon  * Handle skip records.
4624c038e17SMatthew Dillon  *
4634c038e17SMatthew Dillon  * We must iterate from the last resolved record position at mirror->key_cur
4644c038e17SMatthew Dillon  * to skip_beg and delete any records encountered.
4654c038e17SMatthew Dillon  *
4664c038e17SMatthew Dillon  * mirror->key_cur must be carefully set when we succeed in processing
4674c038e17SMatthew Dillon  * this mrec.
4684c038e17SMatthew Dillon  */
4694c038e17SMatthew Dillon static int
4704c038e17SMatthew Dillon hammer_ioc_mirror_write_skip(hammer_cursor_t cursor,
4714c038e17SMatthew Dillon 			     struct hammer_ioc_mrecord_skip *mrec,
4724c038e17SMatthew Dillon 			     struct hammer_ioc_mirror_rw *mirror,
4734c038e17SMatthew Dillon 			     u_int32_t localization)
4744c038e17SMatthew Dillon {
4754c038e17SMatthew Dillon 	int error;
4764c038e17SMatthew Dillon 
4774c038e17SMatthew Dillon 	/*
4784c038e17SMatthew Dillon 	 * Relocalize the skip range
4794c038e17SMatthew Dillon 	 */
4804c038e17SMatthew Dillon 	mrec->skip_beg.localization &= HAMMER_LOCALIZE_MASK;
4814c038e17SMatthew Dillon 	mrec->skip_beg.localization += localization;
4824c038e17SMatthew Dillon 	mrec->skip_end.localization &= HAMMER_LOCALIZE_MASK;
4834c038e17SMatthew Dillon 	mrec->skip_end.localization += localization;
4844c038e17SMatthew Dillon 
4854c038e17SMatthew Dillon 	/*
4864c038e17SMatthew Dillon 	 * Iterate from current position to skip_beg, deleting any records
4874c038e17SMatthew Dillon 	 * we encounter.
4884c038e17SMatthew Dillon 	 */
4894c038e17SMatthew Dillon 	cursor->key_end = mrec->skip_beg;
4904c038e17SMatthew Dillon 	cursor->flags |= HAMMER_CURSOR_BACKEND;
491*842e7a70SMatthew Dillon 	error = hammer_mirror_delete_to(cursor, mirror);
4924c038e17SMatthew Dillon 
4934c038e17SMatthew Dillon 	/*
4944c038e17SMatthew Dillon 	 * Now skip past the skip (which is the whole point point of
4954c038e17SMatthew Dillon 	 * having a skip record).  The sender has not sent us any records
4964c038e17SMatthew Dillon 	 * for the skip area so we wouldn't know what to keep and what
4974c038e17SMatthew Dillon 	 * to delete anyway.
4984c038e17SMatthew Dillon 	 *
4994c038e17SMatthew Dillon 	 * Clear ATEDISK because skip_end is non-inclusive, so we can't
5004c038e17SMatthew Dillon 	 * count an exact match if we happened to get one.
5014c038e17SMatthew Dillon 	 */
5024c038e17SMatthew Dillon 	if (error == 0) {
5034c038e17SMatthew Dillon 		mirror->key_cur = mrec->skip_end;
5044c038e17SMatthew Dillon 		cursor->key_beg = mrec->skip_end;
5054c038e17SMatthew Dillon 		error = hammer_btree_lookup(cursor);
5064c038e17SMatthew Dillon 		cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
5074c038e17SMatthew Dillon 		if (error == ENOENT)
5084c038e17SMatthew Dillon 			error = 0;
5094c038e17SMatthew Dillon 	}
5104c038e17SMatthew Dillon 	return(error);
5114c038e17SMatthew Dillon }
5124c038e17SMatthew Dillon 
5134c038e17SMatthew Dillon /*
5144c038e17SMatthew Dillon  * Handle B-Tree records.
5154c038e17SMatthew Dillon  *
5164c038e17SMatthew Dillon  * We must iterate to mrec->base.key (non-inclusively), and then process
5174c038e17SMatthew Dillon  * the record.  We are allowed to write a new record or delete an existing
5184c038e17SMatthew Dillon  * record, but cannot replace an existing record.
5194c038e17SMatthew Dillon  *
5204c038e17SMatthew Dillon  * mirror->key_cur must be carefully set when we succeed in processing
5214c038e17SMatthew Dillon  * this mrec.
5224c038e17SMatthew Dillon  */
5234c038e17SMatthew Dillon static int
5244c038e17SMatthew Dillon hammer_ioc_mirror_write_rec(hammer_cursor_t cursor,
5254c038e17SMatthew Dillon 			    struct hammer_ioc_mrecord_rec *mrec,
5264c038e17SMatthew Dillon 			    struct hammer_ioc_mirror_rw *mirror,
5274c038e17SMatthew Dillon 			    u_int32_t localization,
5284c038e17SMatthew Dillon 			    char *uptr)
5294c038e17SMatthew Dillon {
5304c038e17SMatthew Dillon 	hammer_transaction_t trans;
5314c038e17SMatthew Dillon 	u_int32_t rec_crc;
5324c038e17SMatthew Dillon 	int error;
5334c038e17SMatthew Dillon 
5344c038e17SMatthew Dillon 	trans = cursor->trans;
5354c038e17SMatthew Dillon 	rec_crc = crc32(mrec, sizeof(*mrec));
5364c038e17SMatthew Dillon 
5374c038e17SMatthew Dillon 	if (mrec->leaf.data_len < 0 ||
5384c038e17SMatthew Dillon 	    mrec->leaf.data_len > HAMMER_XBUFSIZE ||
5394c038e17SMatthew Dillon 	    mrec->leaf.data_len + sizeof(*mrec) > mrec->head.rec_size) {
5404c038e17SMatthew Dillon 		return(EINVAL);
541c82af904SMatthew Dillon 	}
542c82af904SMatthew Dillon 
543c82af904SMatthew Dillon 	/*
544c82af904SMatthew Dillon 	 * Re-localize for target.  relocalization of data is handled
545c82af904SMatthew Dillon 	 * by hammer_mirror_write().
546c82af904SMatthew Dillon 	 */
5474c038e17SMatthew Dillon 	mrec->leaf.base.localization &= HAMMER_LOCALIZE_MASK;
5484c038e17SMatthew Dillon 	mrec->leaf.base.localization += localization;
5494c038e17SMatthew Dillon 
5504c038e17SMatthew Dillon 	/*
5514c038e17SMatthew Dillon 	 * Delete records through until we reach (non-inclusively) the
5524c038e17SMatthew Dillon 	 * target record.
5534c038e17SMatthew Dillon 	 */
5544c038e17SMatthew Dillon 	cursor->key_end = mrec->leaf.base;
5554c038e17SMatthew Dillon 	cursor->flags &= ~HAMMER_CURSOR_END_INCLUSIVE;
5564c038e17SMatthew Dillon 	cursor->flags |= HAMMER_CURSOR_BACKEND;
557*842e7a70SMatthew Dillon 	error = hammer_mirror_delete_to(cursor, mirror);
558c82af904SMatthew Dillon 
559c82af904SMatthew Dillon 	/*
560c82af904SMatthew Dillon 	 * Locate the record.
561c82af904SMatthew Dillon 	 *
562c82af904SMatthew Dillon 	 * If the record exists only the delete_tid may be updated.
563c82af904SMatthew Dillon 	 *
564c82af904SMatthew Dillon 	 * If the record does not exist we create it.  For now we
565c82af904SMatthew Dillon 	 * ignore records with a non-zero delete_tid.  Note that
566c82af904SMatthew Dillon 	 * mirror operations are effective an as-of operation and
567c82af904SMatthew Dillon 	 * delete_tid can be 0 for mirroring purposes even if it is
568c82af904SMatthew Dillon 	 * not actually 0 at the originator.
56998da6d8cSMatthew Dillon 	 *
57098da6d8cSMatthew Dillon 	 * These functions can return EDEADLK
571c82af904SMatthew Dillon 	 */
5724c038e17SMatthew Dillon 	cursor->key_beg = mrec->leaf.base;
5734c038e17SMatthew Dillon 	cursor->flags |= HAMMER_CURSOR_BACKEND;
5744c038e17SMatthew Dillon 	cursor->flags &= ~HAMMER_CURSOR_INSERT;
5754c038e17SMatthew Dillon 	error = hammer_btree_lookup(cursor);
576c82af904SMatthew Dillon 
5774c038e17SMatthew Dillon 	if (error == 0 && hammer_mirror_check(cursor, mrec)) {
5784c038e17SMatthew Dillon 		error = hammer_mirror_update(cursor, mrec);
5794c038e17SMatthew Dillon 	} else if (error == ENOENT && mrec->leaf.base.delete_tid == 0) {
5804c038e17SMatthew Dillon 		error = hammer_mirror_write(cursor, mrec, uptr);
581adf01747SMatthew Dillon 	} else if (error == ENOENT) {
582adf01747SMatthew Dillon 		error = 0;
583c82af904SMatthew Dillon 	}
5844c038e17SMatthew Dillon 	if (error == 0 || error == EALREADY)
5854c038e17SMatthew Dillon 		mirror->key_cur = mrec->leaf.base;
5864c038e17SMatthew Dillon 	return(error);
5874c038e17SMatthew Dillon }
588c82af904SMatthew Dillon 
589c82af904SMatthew Dillon /*
5904c038e17SMatthew Dillon  * This works like write_rec but no write or update is necessary,
5914c038e17SMatthew Dillon  * and no data payload is included so we couldn't do a write even
5924c038e17SMatthew Dillon  * if we wanted to.
5934c038e17SMatthew Dillon  *
5944c038e17SMatthew Dillon  * We must still iterate for deletions, and we can validate the
5954c038e17SMatthew Dillon  * record header which is a good way to test for corrupted mirror
5964c038e17SMatthew Dillon  * targets XXX.
5974c038e17SMatthew Dillon  *
5984c038e17SMatthew Dillon  * mirror->key_cur must be carefully set when we succeed in processing
5994c038e17SMatthew Dillon  * this mrec.
600c82af904SMatthew Dillon  */
6014c038e17SMatthew Dillon static
6024c038e17SMatthew Dillon int
6034c038e17SMatthew Dillon hammer_ioc_mirror_write_pass(hammer_cursor_t cursor,
6044c038e17SMatthew Dillon 			     struct hammer_ioc_mrecord_rec *mrec,
6054c038e17SMatthew Dillon 			     struct hammer_ioc_mirror_rw *mirror,
6064c038e17SMatthew Dillon 			     u_int32_t localization)
6074c038e17SMatthew Dillon {
6084c038e17SMatthew Dillon 	hammer_transaction_t trans;
6094c038e17SMatthew Dillon 	u_int32_t rec_crc;
6104c038e17SMatthew Dillon 	int error;
6114c038e17SMatthew Dillon 
6124c038e17SMatthew Dillon 	trans = cursor->trans;
6134c038e17SMatthew Dillon 	rec_crc = crc32(mrec, sizeof(*mrec));
6144c038e17SMatthew Dillon 
6154c038e17SMatthew Dillon 	/*
6164c038e17SMatthew Dillon 	 * Re-localize for target.  Relocalization of data is handled
6174c038e17SMatthew Dillon 	 * by hammer_mirror_write().
6184c038e17SMatthew Dillon 	 */
6194c038e17SMatthew Dillon 	mrec->leaf.base.localization &= HAMMER_LOCALIZE_MASK;
6204c038e17SMatthew Dillon 	mrec->leaf.base.localization += localization;
6214c038e17SMatthew Dillon 
6224c038e17SMatthew Dillon 	/*
6234c038e17SMatthew Dillon 	 * Delete records through until we reach (non-inclusively) the
6244c038e17SMatthew Dillon 	 * target record.
6254c038e17SMatthew Dillon 	 */
6264c038e17SMatthew Dillon 	cursor->key_end = mrec->leaf.base;
6274c038e17SMatthew Dillon 	cursor->flags &= ~HAMMER_CURSOR_END_INCLUSIVE;
6284c038e17SMatthew Dillon 	cursor->flags |= HAMMER_CURSOR_BACKEND;
6294c038e17SMatthew Dillon 
630*842e7a70SMatthew Dillon 	error = hammer_mirror_delete_to(cursor, mirror);
6314c038e17SMatthew Dillon 
6324c038e17SMatthew Dillon 	/*
6334c038e17SMatthew Dillon 	 * Locate the record and get past it by setting ATEDISK.
6344c038e17SMatthew Dillon 	 */
6354c038e17SMatthew Dillon 	if (error == 0) {
6364c038e17SMatthew Dillon 		mirror->key_cur = mrec->leaf.base;
6374c038e17SMatthew Dillon 		cursor->key_beg = mrec->leaf.base;
6384c038e17SMatthew Dillon 		cursor->flags |= HAMMER_CURSOR_BACKEND;
6394c038e17SMatthew Dillon 		cursor->flags &= ~HAMMER_CURSOR_INSERT;
6404c038e17SMatthew Dillon 		error = hammer_btree_lookup(cursor);
6414c038e17SMatthew Dillon 		if (error == 0)
6424c038e17SMatthew Dillon 			cursor->flags |= HAMMER_CURSOR_ATEDISK;
6434c038e17SMatthew Dillon 		else
6444c038e17SMatthew Dillon 			cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
6454c038e17SMatthew Dillon 		if (error == ENOENT)
6464c038e17SMatthew Dillon 			error = 0;
6474c038e17SMatthew Dillon 	}
6484c038e17SMatthew Dillon 	return(error);
649c82af904SMatthew Dillon }
650adf01747SMatthew Dillon 
6514c038e17SMatthew Dillon /*
6524c038e17SMatthew Dillon  * As part of the mirror write we iterate across swaths of records
6534c038e17SMatthew Dillon  * on the target which no longer exist on the source, and mark them
6544c038e17SMatthew Dillon  * deleted.
655*842e7a70SMatthew Dillon  *
656*842e7a70SMatthew Dillon  * The caller has indexed the cursor and set up key_end.  We iterate
657*842e7a70SMatthew Dillon  * through to key_end.
6584c038e17SMatthew Dillon  */
6594c038e17SMatthew Dillon static
6604c038e17SMatthew Dillon int
661*842e7a70SMatthew Dillon hammer_mirror_delete_to(hammer_cursor_t cursor,
6624c038e17SMatthew Dillon 		       struct hammer_ioc_mirror_rw *mirror)
6634c038e17SMatthew Dillon {
664*842e7a70SMatthew Dillon 	hammer_btree_leaf_elm_t elm;
66598da6d8cSMatthew Dillon 	int error;
66698da6d8cSMatthew Dillon 
667*842e7a70SMatthew Dillon 	error = hammer_btree_iterate(cursor);
668*842e7a70SMatthew Dillon 	while (error == 0) {
669*842e7a70SMatthew Dillon 		elm = &cursor->node->ondisk->elms[cursor->index].leaf;
670*842e7a70SMatthew Dillon 		KKASSERT(elm->base.btype == HAMMER_BTREE_TYPE_RECORD);
671*842e7a70SMatthew Dillon 		if (elm->base.delete_tid == 0) {
672*842e7a70SMatthew Dillon 			error = hammer_delete_at_cursor(cursor,
673*842e7a70SMatthew Dillon 							HAMMER_DELETE_ADJUST,
674*842e7a70SMatthew Dillon 							mirror->tid_end,
675*842e7a70SMatthew Dillon 							time_second,
676*842e7a70SMatthew Dillon 							1, NULL);
677*842e7a70SMatthew Dillon 			if (error == 0)
6784c038e17SMatthew Dillon 				cursor->flags |= HAMMER_CURSOR_ATEDISK;
679*842e7a70SMatthew Dillon 		}
680*842e7a70SMatthew Dillon 		if (error == 0)
681*842e7a70SMatthew Dillon 			error = hammer_btree_iterate(cursor);
682*842e7a70SMatthew Dillon 	}
683*842e7a70SMatthew Dillon 	if (error == ENOENT)
684*842e7a70SMatthew Dillon 		error = 0;
685*842e7a70SMatthew Dillon 	return(error);
686c82af904SMatthew Dillon }
687c82af904SMatthew Dillon 
688c82af904SMatthew Dillon /*
689c82af904SMatthew Dillon  * Check whether an update is needed in the case where a match already
690c82af904SMatthew Dillon  * exists on the target.  The only type of update allowed in this case
691c82af904SMatthew Dillon  * is an update of the delete_tid.
692c82af904SMatthew Dillon  *
693c82af904SMatthew Dillon  * Return non-zero if the update should proceed.
694c82af904SMatthew Dillon  */
695c82af904SMatthew Dillon static
696c82af904SMatthew Dillon int
6974c038e17SMatthew Dillon hammer_mirror_check(hammer_cursor_t cursor, struct hammer_ioc_mrecord_rec *mrec)
698c82af904SMatthew Dillon {
699c82af904SMatthew Dillon 	hammer_btree_leaf_elm_t leaf = cursor->leaf;
700c82af904SMatthew Dillon 
701c82af904SMatthew Dillon 	if (leaf->base.delete_tid != mrec->leaf.base.delete_tid) {
702ea434b6fSMatthew Dillon 		if (mrec->leaf.base.delete_tid != 0)
703c82af904SMatthew Dillon 			return(1);
704c82af904SMatthew Dillon 	}
705c82af904SMatthew Dillon 	return(0);
706c82af904SMatthew Dillon }
707c82af904SMatthew Dillon 
708c82af904SMatthew Dillon /*
709*842e7a70SMatthew Dillon  * Update a record in-place.  Only the delete_tid can change, and
710*842e7a70SMatthew Dillon  * only from zero to non-zero.
711c82af904SMatthew Dillon  */
712c82af904SMatthew Dillon static
713c82af904SMatthew Dillon int
7144c038e17SMatthew Dillon hammer_mirror_update(hammer_cursor_t cursor,
7154c038e17SMatthew Dillon 		     struct hammer_ioc_mrecord_rec *mrec)
716c82af904SMatthew Dillon {
71798da6d8cSMatthew Dillon 	int error;
71898da6d8cSMatthew Dillon 
719*842e7a70SMatthew Dillon 	/*
720*842e7a70SMatthew Dillon 	 * This case shouldn't occur.
721*842e7a70SMatthew Dillon 	 */
722*842e7a70SMatthew Dillon 	if (mrec->leaf.base.delete_tid == 0)
72306ad81ffSMatthew Dillon 		return(0);
724adf01747SMatthew Dillon 
725adf01747SMatthew Dillon 	/*
726*842e7a70SMatthew Dillon 	 * Mark the record deleted on the mirror target.
7274c038e17SMatthew Dillon 	 */
728*842e7a70SMatthew Dillon 	error = hammer_delete_at_cursor(cursor, HAMMER_DELETE_ADJUST,
729*842e7a70SMatthew Dillon 					mrec->leaf.base.delete_tid,
730*842e7a70SMatthew Dillon 					mrec->leaf.delete_ts,
731*842e7a70SMatthew Dillon 					1, NULL);
7324c038e17SMatthew Dillon 	cursor->flags |= HAMMER_CURSOR_ATEDISK;
733*842e7a70SMatthew Dillon 	return(error);
734c82af904SMatthew Dillon }
735c82af904SMatthew Dillon 
736c82af904SMatthew Dillon /*
737c82af904SMatthew Dillon  * Write out a new record.
738c82af904SMatthew Dillon  */
739c82af904SMatthew Dillon static
740c82af904SMatthew Dillon int
7414c038e17SMatthew Dillon hammer_mirror_write(hammer_cursor_t cursor,
7424c038e17SMatthew Dillon 		    struct hammer_ioc_mrecord_rec *mrec,
7434c038e17SMatthew Dillon 		    char *udata)
744c82af904SMatthew Dillon {
745adf01747SMatthew Dillon 	hammer_transaction_t trans;
746adf01747SMatthew Dillon 	hammer_buffer_t data_buffer;
747c82af904SMatthew Dillon 	hammer_off_t ndata_offset;
748a56cb012SMatthew Dillon 	hammer_tid_t high_tid;
749c82af904SMatthew Dillon 	void *ndata;
750c82af904SMatthew Dillon 	int error;
751602c6cb8SMatthew Dillon 	int doprop;
752c82af904SMatthew Dillon 
753adf01747SMatthew Dillon 	trans = cursor->trans;
754adf01747SMatthew Dillon 	data_buffer = NULL;
755adf01747SMatthew Dillon 
756adf01747SMatthew Dillon 	/*
75798da6d8cSMatthew Dillon 	 * Get the sync lock so the whole mess is atomic
75898da6d8cSMatthew Dillon 	 */
75998da6d8cSMatthew Dillon 	hammer_sync_lock_sh(trans);
76098da6d8cSMatthew Dillon 
76198da6d8cSMatthew Dillon 	/*
762adf01747SMatthew Dillon 	 * Allocate and adjust data
763adf01747SMatthew Dillon 	 */
764c82af904SMatthew Dillon 	if (mrec->leaf.data_len && mrec->leaf.data_offset) {
765adf01747SMatthew Dillon 		ndata = hammer_alloc_data(trans, mrec->leaf.data_len,
766c82af904SMatthew Dillon 					  mrec->leaf.base.rec_type,
767c82af904SMatthew Dillon 					  &ndata_offset, &data_buffer, &error);
768c82af904SMatthew Dillon 		if (ndata == NULL)
769c82af904SMatthew Dillon 			return(error);
770c82af904SMatthew Dillon 		mrec->leaf.data_offset = ndata_offset;
771adf01747SMatthew Dillon 		hammer_modify_buffer(trans, data_buffer, NULL, 0);
772c82af904SMatthew Dillon 		error = copyin(udata, ndata, mrec->leaf.data_len);
773c82af904SMatthew Dillon 		if (error == 0) {
774c82af904SMatthew Dillon 			if (hammer_crc_test_leaf(ndata, &mrec->leaf) == 0) {
775c82af904SMatthew Dillon 				kprintf("data crc mismatch on pipe\n");
776c82af904SMatthew Dillon 				error = EINVAL;
777c82af904SMatthew Dillon 			} else {
778c82af904SMatthew Dillon 				error = hammer_mirror_localize_data(
779c82af904SMatthew Dillon 							ndata, &mrec->leaf);
780c82af904SMatthew Dillon 			}
781c82af904SMatthew Dillon 		}
782c82af904SMatthew Dillon 		hammer_modify_buffer_done(data_buffer);
783c82af904SMatthew Dillon 	} else {
784c82af904SMatthew Dillon 		mrec->leaf.data_offset = 0;
785c82af904SMatthew Dillon 		error = 0;
786c82af904SMatthew Dillon 		ndata = NULL;
787c82af904SMatthew Dillon 	}
788c82af904SMatthew Dillon 	if (error)
789c82af904SMatthew Dillon 		goto failed;
790adf01747SMatthew Dillon 
791adf01747SMatthew Dillon 	/*
7924c038e17SMatthew Dillon 	 * Do the insertion.  This can fail with a EDEADLK or EALREADY
793adf01747SMatthew Dillon 	 */
794c82af904SMatthew Dillon 	cursor->flags |= HAMMER_CURSOR_INSERT;
795c82af904SMatthew Dillon 	error = hammer_btree_lookup(cursor);
796c82af904SMatthew Dillon 	if (error != ENOENT) {
797c82af904SMatthew Dillon 		if (error == 0)
798c82af904SMatthew Dillon 			error = EALREADY;
799c82af904SMatthew Dillon 		goto failed;
800c82af904SMatthew Dillon 	}
801c82af904SMatthew Dillon 
802602c6cb8SMatthew Dillon 	error = hammer_btree_insert(cursor, &mrec->leaf, &doprop);
803adf01747SMatthew Dillon 
804adf01747SMatthew Dillon 	/*
8054c038e17SMatthew Dillon 	 * Cursor is left on the current element, we want to skip it now.
8064c038e17SMatthew Dillon 	 */
8074c038e17SMatthew Dillon 	cursor->flags |= HAMMER_CURSOR_ATEDISK;
8084c038e17SMatthew Dillon 	cursor->flags &= ~HAMMER_CURSOR_INSERT;
8094c038e17SMatthew Dillon 
8104c038e17SMatthew Dillon 	/*
811adf01747SMatthew Dillon 	 * Track a count of active inodes.
812adf01747SMatthew Dillon 	 */
813*842e7a70SMatthew Dillon 	if (error == 0 &&
814*842e7a70SMatthew Dillon 	    mrec->leaf.base.rec_type == HAMMER_RECTYPE_INODE &&
815*842e7a70SMatthew Dillon 	    mrec->leaf.base.delete_tid == 0) {
816adf01747SMatthew Dillon 		hammer_modify_volume_field(trans,
817adf01747SMatthew Dillon 					   trans->rootvol,
818adf01747SMatthew Dillon 					   vol0_stat_inodes);
819adf01747SMatthew Dillon 		++trans->hmp->rootvol->ondisk->vol0_stat_inodes;
820adf01747SMatthew Dillon 		hammer_modify_volume_done(trans->rootvol);
821adf01747SMatthew Dillon 	}
822a56cb012SMatthew Dillon 
823a56cb012SMatthew Dillon 	/*
824a56cb012SMatthew Dillon 	 * vol0_next_tid must track the highest TID stored in the filesystem.
825a56cb012SMatthew Dillon 	 * We do not need to generate undo for this update.
826a56cb012SMatthew Dillon 	 */
827a56cb012SMatthew Dillon 	high_tid = mrec->leaf.base.create_tid;
828a56cb012SMatthew Dillon 	if (high_tid < mrec->leaf.base.delete_tid)
829a56cb012SMatthew Dillon 		high_tid = mrec->leaf.base.delete_tid;
830a56cb012SMatthew Dillon 	if (trans->rootvol->ondisk->vol0_next_tid < high_tid) {
831a56cb012SMatthew Dillon 		hammer_modify_volume(trans, trans->rootvol, NULL, 0);
832a56cb012SMatthew Dillon 		trans->rootvol->ondisk->vol0_next_tid = high_tid;
833a56cb012SMatthew Dillon 		hammer_modify_volume_done(trans->rootvol);
834a56cb012SMatthew Dillon 	}
835a56cb012SMatthew Dillon 
836602c6cb8SMatthew Dillon 	if (error == 0 && doprop)
8374c038e17SMatthew Dillon 		hammer_btree_do_propagation(cursor, NULL, &mrec->leaf);
838c82af904SMatthew Dillon 
839c82af904SMatthew Dillon failed:
840c82af904SMatthew Dillon 	/*
841c82af904SMatthew Dillon 	 * Cleanup
842c82af904SMatthew Dillon 	 */
843c82af904SMatthew Dillon 	if (error && mrec->leaf.data_offset) {
844c82af904SMatthew Dillon 		hammer_blockmap_free(cursor->trans,
845c82af904SMatthew Dillon 				     mrec->leaf.data_offset,
846c82af904SMatthew Dillon 				     mrec->leaf.data_len);
847c82af904SMatthew Dillon 	}
84898da6d8cSMatthew Dillon 	hammer_sync_unlock(trans);
849c82af904SMatthew Dillon 	if (data_buffer)
850c82af904SMatthew Dillon 		hammer_rel_buffer(data_buffer, 0);
851c82af904SMatthew Dillon 	return(error);
852c82af904SMatthew Dillon }
853c82af904SMatthew Dillon 
854c82af904SMatthew Dillon /*
855c82af904SMatthew Dillon  * Localize the data payload.  Directory entries may need their
856c82af904SMatthew Dillon  * localization adjusted.
857c82af904SMatthew Dillon  *
858adf01747SMatthew Dillon  * PFS directory entries must be skipped entirely (return EALREADY).
859c82af904SMatthew Dillon  */
860c82af904SMatthew Dillon static
861c82af904SMatthew Dillon int
862c82af904SMatthew Dillon hammer_mirror_localize_data(hammer_data_ondisk_t data,
863c82af904SMatthew Dillon 			    hammer_btree_leaf_elm_t leaf)
864c82af904SMatthew Dillon {
865c82af904SMatthew Dillon 	u_int32_t localization;
866c82af904SMatthew Dillon 
867c82af904SMatthew Dillon 	if (leaf->base.rec_type == HAMMER_RECTYPE_DIRENTRY) {
868adf01747SMatthew Dillon 		if (data->entry.obj_id == HAMMER_OBJID_ROOT)
869adf01747SMatthew Dillon 			return(EALREADY);
870c82af904SMatthew Dillon 		localization = leaf->base.localization &
871c82af904SMatthew Dillon 			       HAMMER_LOCALIZE_PSEUDOFS_MASK;
872c82af904SMatthew Dillon 		if (data->entry.localization != localization) {
873c82af904SMatthew Dillon 			data->entry.localization = localization;
874c82af904SMatthew Dillon 			hammer_crc_set_leaf(data, leaf);
875adf01747SMatthew Dillon 		}
876adf01747SMatthew Dillon 	}
877adf01747SMatthew Dillon 	return(0);
878c82af904SMatthew Dillon }
879c82af904SMatthew Dillon 
880