xref: /dflybsd-src/sys/vfs/hammer/hammer_dedup.c (revision 43bc39fa7056d3f13bf3f405d0d34687907425db)
1bb29b5d8SMatthew Dillon /*
2bb29b5d8SMatthew Dillon  * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
3bb29b5d8SMatthew Dillon  *
4bb29b5d8SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
5bb29b5d8SMatthew Dillon  * by Ilya Dryomov <idryomov@gmail.com>
6bb29b5d8SMatthew Dillon  *
7bb29b5d8SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
8bb29b5d8SMatthew Dillon  * modification, are permitted provided that the following conditions
9bb29b5d8SMatthew Dillon  * are met:
10bb29b5d8SMatthew Dillon  *
11bb29b5d8SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
12bb29b5d8SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
13bb29b5d8SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
14bb29b5d8SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
15bb29b5d8SMatthew Dillon  *    the documentation and/or other materials provided with the
16bb29b5d8SMatthew Dillon  *    distribution.
17bb29b5d8SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
18bb29b5d8SMatthew Dillon  *    contributors may be used to endorse or promote products derived
19bb29b5d8SMatthew Dillon  *    from this software without specific, prior written permission.
20bb29b5d8SMatthew Dillon  *
21bb29b5d8SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22bb29b5d8SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23bb29b5d8SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24bb29b5d8SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25bb29b5d8SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26bb29b5d8SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27bb29b5d8SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28bb29b5d8SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29bb29b5d8SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30bb29b5d8SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31bb29b5d8SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32bb29b5d8SMatthew Dillon  * SUCH DAMAGE.
33bb29b5d8SMatthew Dillon  */
34bb29b5d8SMatthew Dillon 
35bb29b5d8SMatthew Dillon #include "hammer.h"
36bb29b5d8SMatthew Dillon 
37bb29b5d8SMatthew Dillon int
hammer_ioc_dedup(hammer_transaction_t trans,hammer_inode_t ip,struct hammer_ioc_dedup * dedup)38bb29b5d8SMatthew Dillon hammer_ioc_dedup(hammer_transaction_t trans, hammer_inode_t ip,
39bb29b5d8SMatthew Dillon 		 struct hammer_ioc_dedup *dedup)
40bb29b5d8SMatthew Dillon {
41bb29b5d8SMatthew Dillon 	struct hammer_cursor cursor1, cursor2;
42bb29b5d8SMatthew Dillon 	int error;
4355b50bd5SMatthew Dillon 	int seq;
44bb29b5d8SMatthew Dillon 
45bb29b5d8SMatthew Dillon 	/*
463045a179SMatthew Dillon 	 * Enforce hammer filesystem version requirements
473045a179SMatthew Dillon 	 */
483045a179SMatthew Dillon 	if (trans->hmp->version < HAMMER_VOL_VERSION_FIVE) {
49d053aa8aSTomohiro Kusumi 		hkprintf("Filesystem must be upgraded to v5 "
503045a179SMatthew Dillon 			"before you can run dedup\n");
513045a179SMatthew Dillon 		return (EOPNOTSUPP);
523045a179SMatthew Dillon 	}
533045a179SMatthew Dillon 
543045a179SMatthew Dillon 	/*
55bb29b5d8SMatthew Dillon 	 * Cursor1, return an error -> candidate goes to pass2 list
56bb29b5d8SMatthew Dillon 	 */
57bb29b5d8SMatthew Dillon 	error = hammer_init_cursor(trans, &cursor1, NULL, NULL);
58bb29b5d8SMatthew Dillon 	if (error)
59bb29b5d8SMatthew Dillon 		goto done_cursor;
60bb29b5d8SMatthew Dillon 	cursor1.key_beg = dedup->elm1;
61bb29b5d8SMatthew Dillon 	cursor1.flags |= HAMMER_CURSOR_BACKEND;
62bb29b5d8SMatthew Dillon 
63bb29b5d8SMatthew Dillon 	error = hammer_btree_lookup(&cursor1);
64bb29b5d8SMatthew Dillon 	if (error)
65bb29b5d8SMatthew Dillon 		goto done_cursor;
660a6fabdbSTomohiro Kusumi 	error = hammer_btree_extract_data(&cursor1);
67bb29b5d8SMatthew Dillon 	if (error)
68bb29b5d8SMatthew Dillon 		goto done_cursor;
69bb29b5d8SMatthew Dillon 
70bb29b5d8SMatthew Dillon 	/*
71bb29b5d8SMatthew Dillon 	 * Cursor2, return an error -> candidate goes to pass2 list
72bb29b5d8SMatthew Dillon 	 */
73bb29b5d8SMatthew Dillon 	error = hammer_init_cursor(trans, &cursor2, NULL, NULL);
74bb29b5d8SMatthew Dillon 	if (error)
75bb29b5d8SMatthew Dillon 		goto done_cursors;
76bb29b5d8SMatthew Dillon 	cursor2.key_beg = dedup->elm2;
77bb29b5d8SMatthew Dillon 	cursor2.flags |= HAMMER_CURSOR_BACKEND;
78bb29b5d8SMatthew Dillon 
79bb29b5d8SMatthew Dillon 	error = hammer_btree_lookup(&cursor2);
80bb29b5d8SMatthew Dillon 	if (error)
81bb29b5d8SMatthew Dillon 		goto done_cursors;
820a6fabdbSTomohiro Kusumi 	error = hammer_btree_extract_data(&cursor2);
83bb29b5d8SMatthew Dillon 	if (error)
84bb29b5d8SMatthew Dillon 		goto done_cursors;
85bb29b5d8SMatthew Dillon 
86bb29b5d8SMatthew Dillon 	/*
87*66d704afSTomohiro Kusumi 	 * Zone validation.
88*66d704afSTomohiro Kusumi 	 * We can only de-dup data zones or bad things will happen.
89bb29b5d8SMatthew Dillon 	 *
90bb29b5d8SMatthew Dillon 	 * Return with error = 0, but set an INVALID_ZONE flag.
91bb29b5d8SMatthew Dillon 	 */
92*66d704afSTomohiro Kusumi 	if (!hammer_is_zone_data(cursor1.leaf->data_offset) ||
93*66d704afSTomohiro Kusumi 	    !hammer_is_zone_data(cursor2.leaf->data_offset)) {
94bb29b5d8SMatthew Dillon 		dedup->head.flags |= HAMMER_IOC_DEDUP_INVALID_ZONE;
95bb29b5d8SMatthew Dillon 		goto done_cursors;
96bb29b5d8SMatthew Dillon 	}
97bb29b5d8SMatthew Dillon 
98bb29b5d8SMatthew Dillon 	/*
99bb29b5d8SMatthew Dillon 	 * Comparison checks
100bb29b5d8SMatthew Dillon 	 *
101bb29b5d8SMatthew Dillon 	 * If zones don't match or data_len fields aren't the same
102bb29b5d8SMatthew Dillon 	 * we consider it to be a comparison failure.
103bb29b5d8SMatthew Dillon 	 *
104bb29b5d8SMatthew Dillon 	 * Return with error = 0, but set a CMP_FAILURE flag.
105bb29b5d8SMatthew Dillon 	 */
1065bce7157STomohiro Kusumi 	if (HAMMER_ZONE(cursor1.leaf->data_offset) !=
1075bce7157STomohiro Kusumi 	    HAMMER_ZONE(cursor2.leaf->data_offset)) {
108bb29b5d8SMatthew Dillon 		dedup->head.flags |= HAMMER_IOC_DEDUP_CMP_FAILURE;
109bb29b5d8SMatthew Dillon 		goto done_cursors;
110bb29b5d8SMatthew Dillon 	}
111bb29b5d8SMatthew Dillon 	if (cursor1.leaf->data_len != cursor2.leaf->data_len) {
112bb29b5d8SMatthew Dillon 		dedup->head.flags |= HAMMER_IOC_DEDUP_CMP_FAILURE;
113bb29b5d8SMatthew Dillon 		goto done_cursors;
114bb29b5d8SMatthew Dillon 	}
115bb29b5d8SMatthew Dillon 
116bb29b5d8SMatthew Dillon 	/* byte-by-byte comparison to be sure */
117bb29b5d8SMatthew Dillon 	if (bcmp(cursor1.data, cursor2.data, cursor1.leaf->data_len)) {
118bb29b5d8SMatthew Dillon 		dedup->head.flags |= HAMMER_IOC_DEDUP_CMP_FAILURE;
119bb29b5d8SMatthew Dillon 		goto done_cursors;
120bb29b5d8SMatthew Dillon 	}
121bb29b5d8SMatthew Dillon 
122bb29b5d8SMatthew Dillon 	/*
123bb29b5d8SMatthew Dillon 	 * Upgrade both cursors together to an exclusive lock
124bb29b5d8SMatthew Dillon 	 *
125bb29b5d8SMatthew Dillon 	 * Return an error -> candidate goes to pass2 list
126bb29b5d8SMatthew Dillon 	 */
127bb29b5d8SMatthew Dillon 	hammer_sync_lock_sh(trans);
128bb29b5d8SMatthew Dillon 	error = hammer_cursor_upgrade2(&cursor1, &cursor2);
129bb29b5d8SMatthew Dillon 	if (error) {
130bb29b5d8SMatthew Dillon 		hammer_sync_unlock(trans);
131bb29b5d8SMatthew Dillon 		goto done_cursors;
132bb29b5d8SMatthew Dillon 	}
133bb29b5d8SMatthew Dillon 
134bb29b5d8SMatthew Dillon 	error = hammer_blockmap_dedup(cursor1.trans,
135bb29b5d8SMatthew Dillon 			cursor1.leaf->data_offset, cursor1.leaf->data_len);
136bb29b5d8SMatthew Dillon 	if (error) {
137bb29b5d8SMatthew Dillon 		if (error == ERANGE) {
1381206edcbSSascha Wildner 			/* Return with error = 0, but set an UNDERFLOW flag */
139bb29b5d8SMatthew Dillon 			dedup->head.flags |= HAMMER_IOC_DEDUP_UNDERFLOW;
140bb29b5d8SMatthew Dillon 			error = 0;
141bb29b5d8SMatthew Dillon 		}
1421206edcbSSascha Wildner 
1431206edcbSSascha Wildner 		/* Return all other errors -> block goes to pass2 list */
1441206edcbSSascha Wildner 		goto downgrade_cursors;
145bb29b5d8SMatthew Dillon 	}
146bb29b5d8SMatthew Dillon 
147bb29b5d8SMatthew Dillon 	/*
148bb29b5d8SMatthew Dillon 	 * The cursor2's cache must be invalidated before calling
149bb29b5d8SMatthew Dillon 	 * hammer_blockmap_free(), otherwise it will not be able to
150bb29b5d8SMatthew Dillon 	 * invalidate the underlying data buffer.
151bb29b5d8SMatthew Dillon 	 */
152bb29b5d8SMatthew Dillon 	hammer_cursor_invalidate_cache(&cursor2);
153bb29b5d8SMatthew Dillon 	hammer_blockmap_free(cursor2.trans,
154bb29b5d8SMatthew Dillon 			cursor2.leaf->data_offset, cursor2.leaf->data_len);
155bb29b5d8SMatthew Dillon 
156bb29b5d8SMatthew Dillon 	hammer_modify_node(cursor2.trans, cursor2.node,
157bb29b5d8SMatthew Dillon 			&cursor2.leaf->data_offset, sizeof(hammer_off_t));
158bb29b5d8SMatthew Dillon 	cursor2.leaf->data_offset = cursor1.leaf->data_offset;
159bb29b5d8SMatthew Dillon 	hammer_modify_node_done(cursor2.node);
160bb29b5d8SMatthew Dillon 
161bb29b5d8SMatthew Dillon downgrade_cursors:
162bb29b5d8SMatthew Dillon 	hammer_cursor_downgrade2(&cursor1, &cursor2);
163bb29b5d8SMatthew Dillon 	hammer_sync_unlock(trans);
164bb29b5d8SMatthew Dillon done_cursors:
165bb29b5d8SMatthew Dillon 	hammer_done_cursor(&cursor2);
166bb29b5d8SMatthew Dillon done_cursor:
167bb29b5d8SMatthew Dillon 	hammer_done_cursor(&cursor1);
16855b50bd5SMatthew Dillon 
16955b50bd5SMatthew Dillon 	/*
17055b50bd5SMatthew Dillon 	 * Avoid deadlocking the buffer cache
17155b50bd5SMatthew Dillon 	 */
17255b50bd5SMatthew Dillon 	seq = trans->hmp->flusher.done;
17355b50bd5SMatthew Dillon 	while (hammer_flusher_meta_halflimit(trans->hmp) ||
17455b50bd5SMatthew Dillon 	       hammer_flusher_undo_exhausted(trans, 2)) {
17555b50bd5SMatthew Dillon 		hammer_flusher_wait(trans->hmp, seq);
17655b50bd5SMatthew Dillon 		seq = hammer_flusher_async_one(trans->hmp);
17755b50bd5SMatthew Dillon 	}
178bb29b5d8SMatthew Dillon 	return (error);
179bb29b5d8SMatthew Dillon }
180