16cbd5570SChris Mason /* 26cbd5570SChris Mason * Copyright (C) 2007 Oracle. All rights reserved. 36cbd5570SChris Mason * 46cbd5570SChris Mason * This program is free software; you can redistribute it and/or 56cbd5570SChris Mason * modify it under the terms of the GNU General Public 66cbd5570SChris Mason * License v2 as published by the Free Software Foundation. 76cbd5570SChris Mason * 86cbd5570SChris Mason * This program is distributed in the hope that it will be useful, 96cbd5570SChris Mason * but WITHOUT ANY WARRANTY; without even the implied warranty of 106cbd5570SChris Mason * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 116cbd5570SChris Mason * General Public License for more details. 126cbd5570SChris Mason * 136cbd5570SChris Mason * You should have received a copy of the GNU General Public 146cbd5570SChris Mason * License along with this program; if not, write to the 156cbd5570SChris Mason * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 166cbd5570SChris Mason * Boston, MA 021110-1307, USA. 176cbd5570SChris Mason */ 186cbd5570SChris Mason 19065631f6SChris Mason #include <linux/bio.h> 20065631f6SChris Mason #include <linux/pagemap.h> 21065631f6SChris Mason #include <linux/highmem.h> 221e1d2701SChris Mason #include "ctree.h" 23dee26a9fSChris Mason #include "disk-io.h" 249f5fae2fSChris Mason #include "transaction.h" 251de037a4SChris Mason #include "print-tree.h" 261e1d2701SChris Mason 27607d432dSJosef Bacik #define MAX_CSUM_ITEMS(r,size) ((((BTRFS_LEAF_DATA_SIZE(r) - \ 28a429e513SChris Mason sizeof(struct btrfs_item) * 2) / \ 29607d432dSJosef Bacik size) - 1)) 30b18c6685SChris Mason int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, 31dee26a9fSChris Mason struct btrfs_root *root, 32b18c6685SChris Mason u64 objectid, u64 pos, 33f2eb0a24SSage Weil u64 disk_offset, u64 disk_num_bytes, 34c8b97818SChris Mason u64 num_bytes, u64 offset, u64 ram_bytes, 35c8b97818SChris Mason u8 compression, u8 encryption, u16 other_encoding) 369f5fae2fSChris Mason { 37dee26a9fSChris Mason int ret = 0; 38dee26a9fSChris Mason struct btrfs_file_extent_item *item; 39dee26a9fSChris Mason struct btrfs_key file_key; 405caf2a00SChris Mason struct btrfs_path *path; 415f39d397SChris Mason struct extent_buffer *leaf; 42dee26a9fSChris Mason 435caf2a00SChris Mason path = btrfs_alloc_path(); 445caf2a00SChris Mason BUG_ON(!path); 45dee26a9fSChris Mason file_key.objectid = objectid; 46b18c6685SChris Mason file_key.offset = pos; 47dee26a9fSChris Mason btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); 48dee26a9fSChris Mason 495caf2a00SChris Mason ret = btrfs_insert_empty_item(trans, root, path, &file_key, 50dee26a9fSChris Mason sizeof(*item)); 5154aa1f4dSChris Mason if (ret < 0) 5254aa1f4dSChris Mason goto out; 539773a788SChris Mason BUG_ON(ret); 545f39d397SChris Mason leaf = path->nodes[0]; 555f39d397SChris Mason item = btrfs_item_ptr(leaf, path->slots[0], 56dee26a9fSChris Mason struct btrfs_file_extent_item); 57f2eb0a24SSage Weil btrfs_set_file_extent_disk_bytenr(leaf, item, disk_offset); 58db94535dSChris Mason btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes); 59f2eb0a24SSage Weil btrfs_set_file_extent_offset(leaf, item, offset); 60db94535dSChris Mason btrfs_set_file_extent_num_bytes(leaf, item, num_bytes); 61c8b97818SChris Mason btrfs_set_file_extent_ram_bytes(leaf, item, ram_bytes); 625f39d397SChris Mason btrfs_set_file_extent_generation(leaf, item, trans->transid); 635f39d397SChris Mason btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG); 64c8b97818SChris Mason btrfs_set_file_extent_compression(leaf, item, compression); 65c8b97818SChris Mason btrfs_set_file_extent_encryption(leaf, item, encryption); 66c8b97818SChris Mason btrfs_set_file_extent_other_encoding(leaf, item, other_encoding); 67c8b97818SChris Mason 685f39d397SChris Mason btrfs_mark_buffer_dirty(leaf); 6954aa1f4dSChris Mason out: 705caf2a00SChris Mason btrfs_free_path(path); 7154aa1f4dSChris Mason return ret; 729f5fae2fSChris Mason } 73dee26a9fSChris Mason 74b18c6685SChris Mason struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans, 75b18c6685SChris Mason struct btrfs_root *root, 766567e837SChris Mason struct btrfs_path *path, 77d20f7043SChris Mason u64 bytenr, int cow) 786567e837SChris Mason { 796567e837SChris Mason int ret; 806567e837SChris Mason struct btrfs_key file_key; 816567e837SChris Mason struct btrfs_key found_key; 826567e837SChris Mason struct btrfs_csum_item *item; 835f39d397SChris Mason struct extent_buffer *leaf; 846567e837SChris Mason u64 csum_offset = 0; 85607d432dSJosef Bacik u16 csum_size = 86607d432dSJosef Bacik btrfs_super_csum_size(&root->fs_info->super_copy); 87a429e513SChris Mason int csums_in_item; 886567e837SChris Mason 89d20f7043SChris Mason file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 90d20f7043SChris Mason file_key.offset = bytenr; 91d20f7043SChris Mason btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY); 92b18c6685SChris Mason ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); 936567e837SChris Mason if (ret < 0) 946567e837SChris Mason goto fail; 955f39d397SChris Mason leaf = path->nodes[0]; 966567e837SChris Mason if (ret > 0) { 976567e837SChris Mason ret = 1; 9870b2befdSChris Mason if (path->slots[0] == 0) 996567e837SChris Mason goto fail; 1006567e837SChris Mason path->slots[0]--; 1015f39d397SChris Mason btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); 102d20f7043SChris Mason if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY) 1036567e837SChris Mason goto fail; 104d20f7043SChris Mason 105d20f7043SChris Mason csum_offset = (bytenr - found_key.offset) >> 1066567e837SChris Mason root->fs_info->sb->s_blocksize_bits; 1075f39d397SChris Mason csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]); 108607d432dSJosef Bacik csums_in_item /= csum_size; 109a429e513SChris Mason 110a429e513SChris Mason if (csum_offset >= csums_in_item) { 111a429e513SChris Mason ret = -EFBIG; 1126567e837SChris Mason goto fail; 1136567e837SChris Mason } 1146567e837SChris Mason } 1156567e837SChris Mason item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); 116509659cdSChris Mason item = (struct btrfs_csum_item *)((unsigned char *)item + 117607d432dSJosef Bacik csum_offset * csum_size); 1186567e837SChris Mason return item; 1196567e837SChris Mason fail: 1206567e837SChris Mason if (ret > 0) 121b18c6685SChris Mason ret = -ENOENT; 1226567e837SChris Mason return ERR_PTR(ret); 1236567e837SChris Mason } 1246567e837SChris Mason 1256567e837SChris Mason 126dee26a9fSChris Mason int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, 127dee26a9fSChris Mason struct btrfs_root *root, 128dee26a9fSChris Mason struct btrfs_path *path, u64 objectid, 1299773a788SChris Mason u64 offset, int mod) 130dee26a9fSChris Mason { 131dee26a9fSChris Mason int ret; 132dee26a9fSChris Mason struct btrfs_key file_key; 133dee26a9fSChris Mason int ins_len = mod < 0 ? -1 : 0; 134dee26a9fSChris Mason int cow = mod != 0; 135dee26a9fSChris Mason 136dee26a9fSChris Mason file_key.objectid = objectid; 13770b2befdSChris Mason file_key.offset = offset; 138dee26a9fSChris Mason btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); 139dee26a9fSChris Mason ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow); 140dee26a9fSChris Mason return ret; 141dee26a9fSChris Mason } 142f254e52cSChris Mason 143*17d217feSYan Zheng 14461b49440SChris Mason int btrfs_lookup_bio_sums(struct btrfs_root *root, struct inode *inode, 145d20f7043SChris Mason struct bio *bio, u32 *dst) 14661b49440SChris Mason { 14761b49440SChris Mason u32 sum; 14861b49440SChris Mason struct bio_vec *bvec = bio->bi_io_vec; 14961b49440SChris Mason int bio_index = 0; 15061b49440SChris Mason u64 offset; 15161b49440SChris Mason u64 item_start_offset = 0; 15261b49440SChris Mason u64 item_last_offset = 0; 153d20f7043SChris Mason u64 disk_bytenr; 15461b49440SChris Mason u32 diff; 155607d432dSJosef Bacik u16 csum_size = 156607d432dSJosef Bacik btrfs_super_csum_size(&root->fs_info->super_copy); 15761b49440SChris Mason int ret; 15861b49440SChris Mason struct btrfs_path *path; 15961b49440SChris Mason struct btrfs_csum_item *item = NULL; 16061b49440SChris Mason struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; 16161b49440SChris Mason 16261b49440SChris Mason path = btrfs_alloc_path(); 1634d1b5fb4SChris Mason if (bio->bi_size > PAGE_CACHE_SIZE * 8) 1644d1b5fb4SChris Mason path->reada = 2; 16561b49440SChris Mason 16661b49440SChris Mason WARN_ON(bio->bi_vcnt <= 0); 16761b49440SChris Mason 168d20f7043SChris Mason disk_bytenr = (u64)bio->bi_sector << 9; 16961b49440SChris Mason while(bio_index < bio->bi_vcnt) { 17061b49440SChris Mason offset = page_offset(bvec->bv_page) + bvec->bv_offset; 171d20f7043SChris Mason ret = btrfs_find_ordered_sum(inode, offset, disk_bytenr, &sum); 17261b49440SChris Mason if (ret == 0) 17361b49440SChris Mason goto found; 17461b49440SChris Mason 175d20f7043SChris Mason if (!item || disk_bytenr < item_start_offset || 176d20f7043SChris Mason disk_bytenr >= item_last_offset) { 17761b49440SChris Mason struct btrfs_key found_key; 17861b49440SChris Mason u32 item_size; 17961b49440SChris Mason 18061b49440SChris Mason if (item) 18161b49440SChris Mason btrfs_release_path(root, path); 182d20f7043SChris Mason item = btrfs_lookup_csum(NULL, root->fs_info->csum_root, 183d20f7043SChris Mason path, disk_bytenr, 0); 18461b49440SChris Mason if (IS_ERR(item)) { 18561b49440SChris Mason ret = PTR_ERR(item); 18661b49440SChris Mason if (ret == -ENOENT || ret == -EFBIG) 18761b49440SChris Mason ret = 0; 18861b49440SChris Mason sum = 0; 189*17d217feSYan Zheng if (BTRFS_I(inode)->root->root_key.objectid == 190*17d217feSYan Zheng BTRFS_DATA_RELOC_TREE_OBJECTID) { 191*17d217feSYan Zheng set_extent_bits(io_tree, offset, 192*17d217feSYan Zheng offset + bvec->bv_len - 1, 193*17d217feSYan Zheng EXTENT_NODATASUM, GFP_NOFS); 194*17d217feSYan Zheng } else { 195*17d217feSYan Zheng printk("no csum found for inode %lu " 196*17d217feSYan Zheng "start %llu\n", inode->i_ino, 19761b49440SChris Mason (unsigned long long)offset); 198*17d217feSYan Zheng } 1996dab8157SChris Mason item = NULL; 20039be25cdSChris Mason btrfs_release_path(root, path); 20161b49440SChris Mason goto found; 20261b49440SChris Mason } 20361b49440SChris Mason btrfs_item_key_to_cpu(path->nodes[0], &found_key, 20461b49440SChris Mason path->slots[0]); 20561b49440SChris Mason 20661b49440SChris Mason item_start_offset = found_key.offset; 20761b49440SChris Mason item_size = btrfs_item_size_nr(path->nodes[0], 20861b49440SChris Mason path->slots[0]); 20961b49440SChris Mason item_last_offset = item_start_offset + 210607d432dSJosef Bacik (item_size / csum_size) * 21161b49440SChris Mason root->sectorsize; 21261b49440SChris Mason item = btrfs_item_ptr(path->nodes[0], path->slots[0], 21361b49440SChris Mason struct btrfs_csum_item); 21461b49440SChris Mason } 21561b49440SChris Mason /* 21661b49440SChris Mason * this byte range must be able to fit inside 21761b49440SChris Mason * a single leaf so it will also fit inside a u32 21861b49440SChris Mason */ 219d20f7043SChris Mason diff = disk_bytenr - item_start_offset; 22061b49440SChris Mason diff = diff / root->sectorsize; 221607d432dSJosef Bacik diff = diff * csum_size; 22261b49440SChris Mason 22361b49440SChris Mason read_extent_buffer(path->nodes[0], &sum, 2243de9d6b6SChris Mason ((unsigned long)item) + diff, 225607d432dSJosef Bacik csum_size); 22661b49440SChris Mason found: 227d20f7043SChris Mason if (dst) 228d20f7043SChris Mason *dst++ = sum; 229d20f7043SChris Mason else 23061b49440SChris Mason set_state_private(io_tree, offset, sum); 231d20f7043SChris Mason disk_bytenr += bvec->bv_len; 23261b49440SChris Mason bio_index++; 23361b49440SChris Mason bvec++; 23461b49440SChris Mason } 23561b49440SChris Mason btrfs_free_path(path); 23661b49440SChris Mason return 0; 23761b49440SChris Mason } 23861b49440SChris Mason 239*17d217feSYan Zheng int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, 240*17d217feSYan Zheng struct list_head *list) 241*17d217feSYan Zheng { 242*17d217feSYan Zheng struct btrfs_key key; 243*17d217feSYan Zheng struct btrfs_path *path; 244*17d217feSYan Zheng struct extent_buffer *leaf; 245*17d217feSYan Zheng struct btrfs_ordered_sum *sums; 246*17d217feSYan Zheng struct btrfs_sector_sum *sector_sum; 247*17d217feSYan Zheng struct btrfs_csum_item *item; 248*17d217feSYan Zheng unsigned long offset; 249*17d217feSYan Zheng int ret; 250*17d217feSYan Zheng size_t size; 251*17d217feSYan Zheng u64 csum_end; 252*17d217feSYan Zheng u16 csum_size = btrfs_super_csum_size(&root->fs_info->super_copy); 253*17d217feSYan Zheng 254*17d217feSYan Zheng path = btrfs_alloc_path(); 255*17d217feSYan Zheng BUG_ON(!path); 256*17d217feSYan Zheng 257*17d217feSYan Zheng key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 258*17d217feSYan Zheng key.offset = start; 259*17d217feSYan Zheng key.type = BTRFS_EXTENT_CSUM_KEY; 260*17d217feSYan Zheng 261*17d217feSYan Zheng ret = btrfs_search_slot(NULL, root->fs_info->csum_root, 262*17d217feSYan Zheng &key, path, 0, 0); 263*17d217feSYan Zheng if (ret < 0) 264*17d217feSYan Zheng goto fail; 265*17d217feSYan Zheng if (ret > 0 && path->slots[0] > 0) { 266*17d217feSYan Zheng leaf = path->nodes[0]; 267*17d217feSYan Zheng btrfs_item_key_to_cpu(leaf, &key, path->slots[0] - 1); 268*17d217feSYan Zheng if (key.objectid == BTRFS_EXTENT_CSUM_OBJECTID && 269*17d217feSYan Zheng key.type == BTRFS_EXTENT_CSUM_KEY) { 270*17d217feSYan Zheng offset = (start - key.offset) >> 271*17d217feSYan Zheng root->fs_info->sb->s_blocksize_bits; 272*17d217feSYan Zheng if (offset * csum_size < 273*17d217feSYan Zheng btrfs_item_size_nr(leaf, path->slots[0] - 1)) 274*17d217feSYan Zheng path->slots[0]--; 275*17d217feSYan Zheng } 276*17d217feSYan Zheng } 277*17d217feSYan Zheng 278*17d217feSYan Zheng while (start <= end) { 279*17d217feSYan Zheng leaf = path->nodes[0]; 280*17d217feSYan Zheng if (path->slots[0] >= btrfs_header_nritems(leaf)) { 281*17d217feSYan Zheng ret = btrfs_next_leaf(root->fs_info->csum_root, path); 282*17d217feSYan Zheng if (ret < 0) 283*17d217feSYan Zheng goto fail; 284*17d217feSYan Zheng if (ret > 0) 285*17d217feSYan Zheng break; 286*17d217feSYan Zheng leaf = path->nodes[0]; 287*17d217feSYan Zheng } 288*17d217feSYan Zheng 289*17d217feSYan Zheng btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); 290*17d217feSYan Zheng if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 291*17d217feSYan Zheng key.type != BTRFS_EXTENT_CSUM_KEY) 292*17d217feSYan Zheng break; 293*17d217feSYan Zheng 294*17d217feSYan Zheng btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); 295*17d217feSYan Zheng if (key.offset > end) 296*17d217feSYan Zheng break; 297*17d217feSYan Zheng 298*17d217feSYan Zheng if (key.offset > start) 299*17d217feSYan Zheng start = key.offset; 300*17d217feSYan Zheng 301*17d217feSYan Zheng size = btrfs_item_size_nr(leaf, path->slots[0]); 302*17d217feSYan Zheng csum_end = key.offset + (size / csum_size) * root->sectorsize; 303*17d217feSYan Zheng 304*17d217feSYan Zheng size = min(csum_end, end + 1) - start; 305*17d217feSYan Zheng sums = kzalloc(btrfs_ordered_sum_size(root, size), GFP_NOFS); 306*17d217feSYan Zheng BUG_ON(!sums); 307*17d217feSYan Zheng 308*17d217feSYan Zheng sector_sum = sums->sums; 309*17d217feSYan Zheng sums->bytenr = start; 310*17d217feSYan Zheng sums->len = size; 311*17d217feSYan Zheng 312*17d217feSYan Zheng offset = (start - key.offset) >> 313*17d217feSYan Zheng root->fs_info->sb->s_blocksize_bits; 314*17d217feSYan Zheng offset *= csum_size; 315*17d217feSYan Zheng 316*17d217feSYan Zheng item = btrfs_item_ptr(path->nodes[0], path->slots[0], 317*17d217feSYan Zheng struct btrfs_csum_item); 318*17d217feSYan Zheng while (size > 0) { 319*17d217feSYan Zheng read_extent_buffer(path->nodes[0], §or_sum->sum, 320*17d217feSYan Zheng ((unsigned long)item) + offset, 321*17d217feSYan Zheng csum_size); 322*17d217feSYan Zheng sector_sum->bytenr = start; 323*17d217feSYan Zheng 324*17d217feSYan Zheng size -= root->sectorsize; 325*17d217feSYan Zheng start += root->sectorsize; 326*17d217feSYan Zheng offset += csum_size; 327*17d217feSYan Zheng sector_sum++; 328*17d217feSYan Zheng } 329*17d217feSYan Zheng list_add_tail(&sums->list, list); 330*17d217feSYan Zheng 331*17d217feSYan Zheng path->slots[0]++; 332*17d217feSYan Zheng } 333*17d217feSYan Zheng ret = 0; 334*17d217feSYan Zheng fail: 335*17d217feSYan Zheng btrfs_free_path(path); 336*17d217feSYan Zheng return ret; 337*17d217feSYan Zheng } 338*17d217feSYan Zheng 3393edf7d33SChris Mason int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode, 340d20f7043SChris Mason struct bio *bio, u64 file_start, int contig) 341e015640fSChris Mason { 342e6dcd2dcSChris Mason struct btrfs_ordered_sum *sums; 343e6dcd2dcSChris Mason struct btrfs_sector_sum *sector_sum; 3443edf7d33SChris Mason struct btrfs_ordered_extent *ordered; 345e015640fSChris Mason char *data; 346e015640fSChris Mason struct bio_vec *bvec = bio->bi_io_vec; 347e015640fSChris Mason int bio_index = 0; 3483edf7d33SChris Mason unsigned long total_bytes = 0; 3493edf7d33SChris Mason unsigned long this_sum_bytes = 0; 3503edf7d33SChris Mason u64 offset; 351d20f7043SChris Mason u64 disk_bytenr; 352e015640fSChris Mason 353e6dcd2dcSChris Mason WARN_ON(bio->bi_vcnt <= 0); 354e6dcd2dcSChris Mason sums = kzalloc(btrfs_ordered_sum_size(root, bio->bi_size), GFP_NOFS); 355e015640fSChris Mason if (!sums) 356e015640fSChris Mason return -ENOMEM; 3573edf7d33SChris Mason 358ed98b56aSChris Mason sector_sum = sums->sums; 359d20f7043SChris Mason disk_bytenr = (u64)bio->bi_sector << 9; 360e6dcd2dcSChris Mason sums->len = bio->bi_size; 361e6dcd2dcSChris Mason INIT_LIST_HEAD(&sums->list); 362d20f7043SChris Mason 363d20f7043SChris Mason if (contig) 364d20f7043SChris Mason offset = file_start; 365d20f7043SChris Mason else 366d20f7043SChris Mason offset = page_offset(bvec->bv_page) + bvec->bv_offset; 367d20f7043SChris Mason 368d20f7043SChris Mason ordered = btrfs_lookup_ordered_extent(inode, offset); 3693edf7d33SChris Mason BUG_ON(!ordered); 370d20f7043SChris Mason sums->bytenr = ordered->start; 371e015640fSChris Mason 372e015640fSChris Mason while(bio_index < bio->bi_vcnt) { 373d20f7043SChris Mason if (!contig) 3743edf7d33SChris Mason offset = page_offset(bvec->bv_page) + bvec->bv_offset; 375d20f7043SChris Mason 376d20f7043SChris Mason if (!contig && (offset >= ordered->file_offset + ordered->len || 377d20f7043SChris Mason offset < ordered->file_offset)) { 3783edf7d33SChris Mason unsigned long bytes_left; 3793edf7d33SChris Mason sums->len = this_sum_bytes; 3803edf7d33SChris Mason this_sum_bytes = 0; 3813edf7d33SChris Mason btrfs_add_ordered_sum(inode, ordered, sums); 3823edf7d33SChris Mason btrfs_put_ordered_extent(ordered); 3833edf7d33SChris Mason 3843edf7d33SChris Mason bytes_left = bio->bi_size - total_bytes; 3853edf7d33SChris Mason 3863edf7d33SChris Mason sums = kzalloc(btrfs_ordered_sum_size(root, bytes_left), 3873edf7d33SChris Mason GFP_NOFS); 3883edf7d33SChris Mason BUG_ON(!sums); 389ed98b56aSChris Mason sector_sum = sums->sums; 3903edf7d33SChris Mason sums->len = bytes_left; 391d20f7043SChris Mason ordered = btrfs_lookup_ordered_extent(inode, offset); 3923edf7d33SChris Mason BUG_ON(!ordered); 393d20f7043SChris Mason sums->bytenr = ordered->start; 3943edf7d33SChris Mason } 3953edf7d33SChris Mason 396e015640fSChris Mason data = kmap_atomic(bvec->bv_page, KM_USER0); 397e6dcd2dcSChris Mason sector_sum->sum = ~(u32)0; 398e6dcd2dcSChris Mason sector_sum->sum = btrfs_csum_data(root, 399e6dcd2dcSChris Mason data + bvec->bv_offset, 400e6dcd2dcSChris Mason sector_sum->sum, 401e6dcd2dcSChris Mason bvec->bv_len); 402e015640fSChris Mason kunmap_atomic(data, KM_USER0); 403e6dcd2dcSChris Mason btrfs_csum_final(sector_sum->sum, 404e6dcd2dcSChris Mason (char *)§or_sum->sum); 405d20f7043SChris Mason sector_sum->bytenr = disk_bytenr; 406ed98b56aSChris Mason 407e6dcd2dcSChris Mason sector_sum++; 408e015640fSChris Mason bio_index++; 4093edf7d33SChris Mason total_bytes += bvec->bv_len; 4103edf7d33SChris Mason this_sum_bytes += bvec->bv_len; 411d20f7043SChris Mason disk_bytenr += bvec->bv_len; 412d20f7043SChris Mason offset += bvec->bv_len; 413e015640fSChris Mason bvec++; 414e015640fSChris Mason } 415ed98b56aSChris Mason this_sum_bytes = 0; 4163edf7d33SChris Mason btrfs_add_ordered_sum(inode, ordered, sums); 4173edf7d33SChris Mason btrfs_put_ordered_extent(ordered); 418e015640fSChris Mason return 0; 419e015640fSChris Mason } 420e015640fSChris Mason 421459931ecSChris Mason /* 422459931ecSChris Mason * helper function for csum removal, this expects the 423459931ecSChris Mason * key to describe the csum pointed to by the path, and it expects 424459931ecSChris Mason * the csum to overlap the range [bytenr, len] 425459931ecSChris Mason * 426459931ecSChris Mason * The csum should not be entirely contained in the range and the 427459931ecSChris Mason * range should not be entirely contained in the csum. 428459931ecSChris Mason * 429459931ecSChris Mason * This calls btrfs_truncate_item with the correct args based on the 430459931ecSChris Mason * overlap, and fixes up the key as required. 431459931ecSChris Mason */ 432459931ecSChris Mason static noinline int truncate_one_csum(struct btrfs_trans_handle *trans, 433459931ecSChris Mason struct btrfs_root *root, 434459931ecSChris Mason struct btrfs_path *path, 435459931ecSChris Mason struct btrfs_key *key, 436459931ecSChris Mason u64 bytenr, u64 len) 437459931ecSChris Mason { 438459931ecSChris Mason struct extent_buffer *leaf; 439459931ecSChris Mason u16 csum_size = 440459931ecSChris Mason btrfs_super_csum_size(&root->fs_info->super_copy); 441459931ecSChris Mason u64 csum_end; 442459931ecSChris Mason u64 end_byte = bytenr + len; 443459931ecSChris Mason u32 blocksize_bits = root->fs_info->sb->s_blocksize_bits; 444459931ecSChris Mason int ret; 445459931ecSChris Mason 446459931ecSChris Mason leaf = path->nodes[0]; 447459931ecSChris Mason csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; 448459931ecSChris Mason csum_end <<= root->fs_info->sb->s_blocksize_bits; 449459931ecSChris Mason csum_end += key->offset; 450459931ecSChris Mason 451459931ecSChris Mason if (key->offset < bytenr && csum_end <= end_byte) { 452459931ecSChris Mason /* 453459931ecSChris Mason * [ bytenr - len ] 454459931ecSChris Mason * [ ] 455459931ecSChris Mason * [csum ] 456459931ecSChris Mason * A simple truncate off the end of the item 457459931ecSChris Mason */ 458459931ecSChris Mason u32 new_size = (bytenr - key->offset) >> blocksize_bits; 459459931ecSChris Mason new_size *= csum_size; 460459931ecSChris Mason ret = btrfs_truncate_item(trans, root, path, new_size, 1); 461459931ecSChris Mason BUG_ON(ret); 462459931ecSChris Mason } else if (key->offset >= bytenr && csum_end > end_byte && 463459931ecSChris Mason end_byte > key->offset) { 464459931ecSChris Mason /* 465459931ecSChris Mason * [ bytenr - len ] 466459931ecSChris Mason * [ ] 467459931ecSChris Mason * [csum ] 468459931ecSChris Mason * we need to truncate from the beginning of the csum 469459931ecSChris Mason */ 470459931ecSChris Mason u32 new_size = (csum_end - end_byte) >> blocksize_bits; 471459931ecSChris Mason new_size *= csum_size; 472459931ecSChris Mason 473459931ecSChris Mason ret = btrfs_truncate_item(trans, root, path, new_size, 0); 474459931ecSChris Mason BUG_ON(ret); 475459931ecSChris Mason 476459931ecSChris Mason key->offset = end_byte; 477459931ecSChris Mason ret = btrfs_set_item_key_safe(trans, root, path, key); 478459931ecSChris Mason BUG_ON(ret); 479459931ecSChris Mason } else { 480459931ecSChris Mason BUG(); 481459931ecSChris Mason } 482459931ecSChris Mason return 0; 483459931ecSChris Mason } 484459931ecSChris Mason 485459931ecSChris Mason /* 486459931ecSChris Mason * deletes the csum items from the csum tree for a given 487459931ecSChris Mason * range of bytes. 488459931ecSChris Mason */ 489459931ecSChris Mason int btrfs_del_csums(struct btrfs_trans_handle *trans, 490459931ecSChris Mason struct btrfs_root *root, u64 bytenr, u64 len) 491459931ecSChris Mason { 492459931ecSChris Mason struct btrfs_path *path; 493459931ecSChris Mason struct btrfs_key key; 494459931ecSChris Mason u64 end_byte = bytenr + len; 495459931ecSChris Mason u64 csum_end; 496459931ecSChris Mason struct extent_buffer *leaf; 497459931ecSChris Mason int ret; 498459931ecSChris Mason u16 csum_size = 499459931ecSChris Mason btrfs_super_csum_size(&root->fs_info->super_copy); 500459931ecSChris Mason int blocksize_bits = root->fs_info->sb->s_blocksize_bits; 501459931ecSChris Mason 502459931ecSChris Mason root = root->fs_info->csum_root; 503459931ecSChris Mason 504459931ecSChris Mason path = btrfs_alloc_path(); 505459931ecSChris Mason 506459931ecSChris Mason while(1) { 507459931ecSChris Mason key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 508459931ecSChris Mason key.offset = end_byte - 1; 509459931ecSChris Mason key.type = BTRFS_EXTENT_CSUM_KEY; 510459931ecSChris Mason 511459931ecSChris Mason ret = btrfs_search_slot(trans, root, &key, path, -1, 1); 512459931ecSChris Mason if (ret > 0) { 513459931ecSChris Mason if (path->slots[0] == 0) 514459931ecSChris Mason goto out; 515459931ecSChris Mason path->slots[0]--; 516459931ecSChris Mason } 517459931ecSChris Mason leaf = path->nodes[0]; 518459931ecSChris Mason btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); 519459931ecSChris Mason 520459931ecSChris Mason if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 521459931ecSChris Mason key.type != BTRFS_EXTENT_CSUM_KEY) { 522459931ecSChris Mason break; 523459931ecSChris Mason } 524459931ecSChris Mason 525459931ecSChris Mason if (key.offset >= end_byte) 526459931ecSChris Mason break; 527459931ecSChris Mason 528459931ecSChris Mason csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; 529459931ecSChris Mason csum_end <<= blocksize_bits; 530459931ecSChris Mason csum_end += key.offset; 531459931ecSChris Mason 532459931ecSChris Mason /* this csum ends before we start, we're done */ 533459931ecSChris Mason if (csum_end <= bytenr) 534459931ecSChris Mason break; 535459931ecSChris Mason 536459931ecSChris Mason /* delete the entire item, it is inside our range */ 537459931ecSChris Mason if (key.offset >= bytenr && csum_end <= end_byte) { 538459931ecSChris Mason ret = btrfs_del_item(trans, root, path); 539459931ecSChris Mason BUG_ON(ret); 540459931ecSChris Mason } else if (key.offset < bytenr && csum_end > end_byte) { 541459931ecSChris Mason unsigned long offset; 542459931ecSChris Mason unsigned long shift_len; 543459931ecSChris Mason unsigned long item_offset; 544459931ecSChris Mason /* 545459931ecSChris Mason * [ bytenr - len ] 546459931ecSChris Mason * [csum ] 547459931ecSChris Mason * 548459931ecSChris Mason * Our bytes are in the middle of the csum, 549459931ecSChris Mason * we need to split this item and insert a new one. 550459931ecSChris Mason * 551459931ecSChris Mason * But we can't drop the path because the 552459931ecSChris Mason * csum could change, get removed, extended etc. 553459931ecSChris Mason * 554459931ecSChris Mason * The trick here is the max size of a csum item leaves 555459931ecSChris Mason * enough room in the tree block for a single 556459931ecSChris Mason * item header. So, we split the item in place, 557459931ecSChris Mason * adding a new header pointing to the existing 558459931ecSChris Mason * bytes. Then we loop around again and we have 559459931ecSChris Mason * a nicely formed csum item that we can neatly 560459931ecSChris Mason * truncate. 561459931ecSChris Mason */ 562459931ecSChris Mason offset = (bytenr - key.offset) >> blocksize_bits; 563459931ecSChris Mason offset *= csum_size; 564459931ecSChris Mason 565459931ecSChris Mason shift_len = (len >> blocksize_bits) * csum_size; 566459931ecSChris Mason 567459931ecSChris Mason item_offset = btrfs_item_ptr_offset(leaf, 568459931ecSChris Mason path->slots[0]); 569459931ecSChris Mason 570459931ecSChris Mason memset_extent_buffer(leaf, 0, item_offset + offset, 571459931ecSChris Mason shift_len); 572459931ecSChris Mason key.offset = bytenr; 573459931ecSChris Mason 574459931ecSChris Mason /* 575459931ecSChris Mason * btrfs_split_item returns -EAGAIN when the 576459931ecSChris Mason * item changed size or key 577459931ecSChris Mason */ 578459931ecSChris Mason ret = btrfs_split_item(trans, root, path, &key, offset); 579459931ecSChris Mason BUG_ON(ret && ret != -EAGAIN); 580459931ecSChris Mason 581459931ecSChris Mason key.offset = end_byte - 1; 582459931ecSChris Mason } else { 583459931ecSChris Mason ret = truncate_one_csum(trans, root, path, 584459931ecSChris Mason &key, bytenr, len); 585459931ecSChris Mason BUG_ON(ret); 586459931ecSChris Mason } 587459931ecSChris Mason btrfs_release_path(root, path); 588459931ecSChris Mason } 589459931ecSChris Mason out: 590459931ecSChris Mason btrfs_free_path(path); 591459931ecSChris Mason return 0; 592459931ecSChris Mason } 593459931ecSChris Mason 594065631f6SChris Mason int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, 595d20f7043SChris Mason struct btrfs_root *root, 596e6dcd2dcSChris Mason struct btrfs_ordered_sum *sums) 597f254e52cSChris Mason { 598d20f7043SChris Mason u64 bytenr; 599f254e52cSChris Mason int ret; 600f254e52cSChris Mason struct btrfs_key file_key; 6016567e837SChris Mason struct btrfs_key found_key; 602065631f6SChris Mason u64 next_offset; 603e6dcd2dcSChris Mason u64 total_bytes = 0; 604065631f6SChris Mason int found_next; 6055caf2a00SChris Mason struct btrfs_path *path; 606f254e52cSChris Mason struct btrfs_csum_item *item; 607065631f6SChris Mason struct btrfs_csum_item *item_end; 608ff79f819SChris Mason struct extent_buffer *leaf = NULL; 6096567e837SChris Mason u64 csum_offset; 610e6dcd2dcSChris Mason struct btrfs_sector_sum *sector_sum; 611f578d4bdSChris Mason u32 nritems; 612f578d4bdSChris Mason u32 ins_size; 6136e92f5e6SChris Mason char *eb_map; 6146e92f5e6SChris Mason char *eb_token; 6156e92f5e6SChris Mason unsigned long map_len; 6166e92f5e6SChris Mason unsigned long map_start; 617607d432dSJosef Bacik u16 csum_size = 618607d432dSJosef Bacik btrfs_super_csum_size(&root->fs_info->super_copy); 6196e92f5e6SChris Mason 6205caf2a00SChris Mason path = btrfs_alloc_path(); 6215caf2a00SChris Mason BUG_ON(!path); 622ed98b56aSChris Mason sector_sum = sums->sums; 623065631f6SChris Mason again: 624065631f6SChris Mason next_offset = (u64)-1; 625065631f6SChris Mason found_next = 0; 626d20f7043SChris Mason file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 627d20f7043SChris Mason file_key.offset = sector_sum->bytenr; 628d20f7043SChris Mason bytenr = sector_sum->bytenr; 629d20f7043SChris Mason btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY); 630a429e513SChris Mason 631d20f7043SChris Mason item = btrfs_lookup_csum(trans, root, path, sector_sum->bytenr, 1); 632ff79f819SChris Mason if (!IS_ERR(item)) { 633ff79f819SChris Mason leaf = path->nodes[0]; 634639cb586SChris Mason ret = 0; 635a429e513SChris Mason goto found; 636ff79f819SChris Mason } 637a429e513SChris Mason ret = PTR_ERR(item); 638a429e513SChris Mason if (ret == -EFBIG) { 639a429e513SChris Mason u32 item_size; 640a429e513SChris Mason /* we found one, but it isn't big enough yet */ 6415f39d397SChris Mason leaf = path->nodes[0]; 6425f39d397SChris Mason item_size = btrfs_item_size_nr(leaf, path->slots[0]); 643607d432dSJosef Bacik if ((item_size / csum_size) >= 644607d432dSJosef Bacik MAX_CSUM_ITEMS(root, csum_size)) { 645a429e513SChris Mason /* already at max size, make a new one */ 646a429e513SChris Mason goto insert; 647a429e513SChris Mason } 648a429e513SChris Mason } else { 649f578d4bdSChris Mason int slot = path->slots[0] + 1; 650a429e513SChris Mason /* we didn't find a csum item, insert one */ 651f578d4bdSChris Mason nritems = btrfs_header_nritems(path->nodes[0]); 652f578d4bdSChris Mason if (path->slots[0] >= nritems - 1) { 653f578d4bdSChris Mason ret = btrfs_next_leaf(root, path); 654b56baf5bSYan if (ret == 1) 655f578d4bdSChris Mason found_next = 1; 656b56baf5bSYan if (ret != 0) 657f578d4bdSChris Mason goto insert; 658b56baf5bSYan slot = 0; 659f578d4bdSChris Mason } 660f578d4bdSChris Mason btrfs_item_key_to_cpu(path->nodes[0], &found_key, slot); 661d20f7043SChris Mason if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 662d20f7043SChris Mason found_key.type != BTRFS_EXTENT_CSUM_KEY) { 663f578d4bdSChris Mason found_next = 1; 664f578d4bdSChris Mason goto insert; 665f578d4bdSChris Mason } 666f578d4bdSChris Mason next_offset = found_key.offset; 667f578d4bdSChris Mason found_next = 1; 668a429e513SChris Mason goto insert; 669a429e513SChris Mason } 670a429e513SChris Mason 671a429e513SChris Mason /* 672a429e513SChris Mason * at this point, we know the tree has an item, but it isn't big 673a429e513SChris Mason * enough yet to put our csum in. Grow it 674a429e513SChris Mason */ 675a429e513SChris Mason btrfs_release_path(root, path); 6766567e837SChris Mason ret = btrfs_search_slot(trans, root, &file_key, path, 677607d432dSJosef Bacik csum_size, 1); 6786567e837SChris Mason if (ret < 0) 67953863232SChris Mason goto fail_unlock; 680459931ecSChris Mason 681459931ecSChris Mason if (ret > 0) { 682459931ecSChris Mason if (path->slots[0] == 0) 6836567e837SChris Mason goto insert; 6846567e837SChris Mason path->slots[0]--; 685459931ecSChris Mason } 686459931ecSChris Mason 6875f39d397SChris Mason leaf = path->nodes[0]; 6885f39d397SChris Mason btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); 689d20f7043SChris Mason csum_offset = (bytenr - found_key.offset) >> 6906567e837SChris Mason root->fs_info->sb->s_blocksize_bits; 691459931ecSChris Mason 692d20f7043SChris Mason if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY || 693d20f7043SChris Mason found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 694607d432dSJosef Bacik csum_offset >= MAX_CSUM_ITEMS(root, csum_size)) { 6956567e837SChris Mason goto insert; 6966567e837SChris Mason } 697459931ecSChris Mason 6985f39d397SChris Mason if (csum_offset >= btrfs_item_size_nr(leaf, path->slots[0]) / 699607d432dSJosef Bacik csum_size) { 700607d432dSJosef Bacik u32 diff = (csum_offset + 1) * csum_size; 701459931ecSChris Mason 702459931ecSChris Mason /* 703459931ecSChris Mason * is the item big enough already? we dropped our lock 704459931ecSChris Mason * before and need to recheck 705459931ecSChris Mason */ 706459931ecSChris Mason if (diff < btrfs_item_size_nr(leaf, path->slots[0])) 707459931ecSChris Mason goto csum; 708459931ecSChris Mason 7095f39d397SChris Mason diff = diff - btrfs_item_size_nr(leaf, path->slots[0]); 710459931ecSChris Mason if (diff != csum_size) { 7113a686375SChris Mason goto insert; 712459931ecSChris Mason } 713459931ecSChris Mason 714a429e513SChris Mason ret = btrfs_extend_item(trans, root, path, diff); 7156567e837SChris Mason BUG_ON(ret); 7166567e837SChris Mason goto csum; 7176567e837SChris Mason } 7186567e837SChris Mason 7196567e837SChris Mason insert: 720a429e513SChris Mason btrfs_release_path(root, path); 7216567e837SChris Mason csum_offset = 0; 722f578d4bdSChris Mason if (found_next) { 723d20f7043SChris Mason u64 tmp = total_bytes + root->sectorsize; 724d20f7043SChris Mason u64 next_sector = sector_sum->bytenr; 725d20f7043SChris Mason struct btrfs_sector_sum *next = sector_sum + 1; 726d20f7043SChris Mason 727d20f7043SChris Mason while(tmp < sums->len) { 728d20f7043SChris Mason if (next_sector + root->sectorsize != next->bytenr) 729d20f7043SChris Mason break; 730d20f7043SChris Mason tmp += root->sectorsize; 731d20f7043SChris Mason next_sector = next->bytenr; 732d20f7043SChris Mason next++; 733d20f7043SChris Mason } 734d20f7043SChris Mason tmp = min(tmp, next_offset - file_key.offset); 735f578d4bdSChris Mason tmp >>= root->fs_info->sb->s_blocksize_bits; 736f578d4bdSChris Mason tmp = max((u64)1, tmp); 737607d432dSJosef Bacik tmp = min(tmp, (u64)MAX_CSUM_ITEMS(root, csum_size)); 738607d432dSJosef Bacik ins_size = csum_size * tmp; 739f578d4bdSChris Mason } else { 740607d432dSJosef Bacik ins_size = csum_size; 741f578d4bdSChris Mason } 7425caf2a00SChris Mason ret = btrfs_insert_empty_item(trans, root, path, &file_key, 743f578d4bdSChris Mason ins_size); 74454aa1f4dSChris Mason if (ret < 0) 74553863232SChris Mason goto fail_unlock; 746a429e513SChris Mason if (ret != 0) { 747a429e513SChris Mason WARN_ON(1); 74853863232SChris Mason goto fail_unlock; 749a429e513SChris Mason } 7506567e837SChris Mason csum: 7515f39d397SChris Mason leaf = path->nodes[0]; 7525f39d397SChris Mason item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); 753f254e52cSChris Mason ret = 0; 754509659cdSChris Mason item = (struct btrfs_csum_item *)((unsigned char *)item + 755607d432dSJosef Bacik csum_offset * csum_size); 756b18c6685SChris Mason found: 757065631f6SChris Mason item_end = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); 758065631f6SChris Mason item_end = (struct btrfs_csum_item *)((unsigned char *)item_end + 759065631f6SChris Mason btrfs_item_size_nr(leaf, path->slots[0])); 7606e92f5e6SChris Mason eb_token = NULL; 76153863232SChris Mason cond_resched(); 762e6dcd2dcSChris Mason next_sector: 763aadfeb6eSChris Mason 7646e92f5e6SChris Mason if (!eb_token || 765607d432dSJosef Bacik (unsigned long)item + csum_size >= map_start + map_len) { 7666e92f5e6SChris Mason int err; 7676e92f5e6SChris Mason 7686e92f5e6SChris Mason if (eb_token) 769eb20978fSChris Mason unmap_extent_buffer(leaf, eb_token, KM_USER1); 7706e92f5e6SChris Mason eb_token = NULL; 7716e92f5e6SChris Mason err = map_private_extent_buffer(leaf, (unsigned long)item, 772607d432dSJosef Bacik csum_size, 7736e92f5e6SChris Mason &eb_token, &eb_map, 774eb20978fSChris Mason &map_start, &map_len, KM_USER1); 7756e92f5e6SChris Mason if (err) 7766e92f5e6SChris Mason eb_token = NULL; 7776e92f5e6SChris Mason } 7786e92f5e6SChris Mason if (eb_token) { 7796e92f5e6SChris Mason memcpy(eb_token + ((unsigned long)item & (PAGE_CACHE_SIZE - 1)), 780607d432dSJosef Bacik §or_sum->sum, csum_size); 7816e92f5e6SChris Mason } else { 782e6dcd2dcSChris Mason write_extent_buffer(leaf, §or_sum->sum, 783607d432dSJosef Bacik (unsigned long)item, csum_size); 7846e92f5e6SChris Mason } 7857f3c74fbSChris Mason 786e6dcd2dcSChris Mason total_bytes += root->sectorsize; 787e6dcd2dcSChris Mason sector_sum++; 788e6dcd2dcSChris Mason if (total_bytes < sums->len) { 7896e92f5e6SChris Mason item = (struct btrfs_csum_item *)((char *)item + 790607d432dSJosef Bacik csum_size); 791d20f7043SChris Mason if (item < item_end && bytenr + PAGE_CACHE_SIZE == 792d20f7043SChris Mason sector_sum->bytenr) { 793d20f7043SChris Mason bytenr = sector_sum->bytenr; 794e6dcd2dcSChris Mason goto next_sector; 795065631f6SChris Mason } 7962e1a992eSChris Mason } 7976e92f5e6SChris Mason if (eb_token) { 798eb20978fSChris Mason unmap_extent_buffer(leaf, eb_token, KM_USER1); 7996e92f5e6SChris Mason eb_token = NULL; 8006e92f5e6SChris Mason } 8015caf2a00SChris Mason btrfs_mark_buffer_dirty(path->nodes[0]); 80253863232SChris Mason cond_resched(); 803e6dcd2dcSChris Mason if (total_bytes < sums->len) { 8045caf2a00SChris Mason btrfs_release_path(root, path); 805065631f6SChris Mason goto again; 806065631f6SChris Mason } 80753863232SChris Mason out: 8085caf2a00SChris Mason btrfs_free_path(path); 809f254e52cSChris Mason return ret; 81053863232SChris Mason 81153863232SChris Mason fail_unlock: 81253863232SChris Mason goto out; 813f254e52cSChris Mason } 814