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