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