1*c1d7c514SDavid Sterba // SPDX-License-Identifier: GPL-2.0 26cbd5570SChris Mason /* 36cbd5570SChris Mason * Copyright (C) 2007 Oracle. All rights reserved. 46cbd5570SChris Mason */ 56cbd5570SChris Mason 6065631f6SChris Mason #include <linux/bio.h> 75a0e3ad6STejun Heo #include <linux/slab.h> 8065631f6SChris Mason #include <linux/pagemap.h> 9065631f6SChris Mason #include <linux/highmem.h> 101e1d2701SChris Mason #include "ctree.h" 11dee26a9fSChris Mason #include "disk-io.h" 129f5fae2fSChris Mason #include "transaction.h" 13facc8a22SMiao Xie #include "volumes.h" 141de037a4SChris Mason #include "print-tree.h" 15ebb8765bSAnand Jain #include "compression.h" 161e1d2701SChris Mason 1742049bf6SChris Mason #define __MAX_CSUM_ITEMS(r, size) ((unsigned long)(((BTRFS_LEAF_DATA_SIZE(r) - \ 1842049bf6SChris Mason sizeof(struct btrfs_item) * 2) / \ 1942049bf6SChris Mason size) - 1)) 2007d400a6SYan Zheng 21221b8318SZach Brown #define MAX_CSUM_ITEMS(r, size) (min_t(u32, __MAX_CSUM_ITEMS(r, size), \ 2209cbfeafSKirill A. Shutemov PAGE_SIZE)) 237ca4be45SChris Mason 24da17066cSJeff Mahoney #define MAX_ORDERED_SUM_BYTES(fs_info) ((PAGE_SIZE - \ 2507d400a6SYan Zheng sizeof(struct btrfs_ordered_sum)) / \ 26da17066cSJeff Mahoney sizeof(u32) * (fs_info)->sectorsize) 2707d400a6SYan Zheng 28b18c6685SChris Mason int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, 29dee26a9fSChris Mason struct btrfs_root *root, 30b18c6685SChris Mason u64 objectid, u64 pos, 31f2eb0a24SSage Weil u64 disk_offset, u64 disk_num_bytes, 32c8b97818SChris Mason u64 num_bytes, u64 offset, u64 ram_bytes, 33c8b97818SChris Mason u8 compression, u8 encryption, u16 other_encoding) 349f5fae2fSChris Mason { 35dee26a9fSChris Mason int ret = 0; 36dee26a9fSChris Mason struct btrfs_file_extent_item *item; 37dee26a9fSChris Mason struct btrfs_key file_key; 385caf2a00SChris Mason struct btrfs_path *path; 395f39d397SChris Mason struct extent_buffer *leaf; 40dee26a9fSChris Mason 415caf2a00SChris Mason path = btrfs_alloc_path(); 42db5b493aSTsutomu Itoh if (!path) 43db5b493aSTsutomu Itoh return -ENOMEM; 44dee26a9fSChris Mason file_key.objectid = objectid; 45b18c6685SChris Mason file_key.offset = pos; 46962a298fSDavid Sterba file_key.type = BTRFS_EXTENT_DATA_KEY; 47dee26a9fSChris Mason 48b9473439SChris Mason path->leave_spinning = 1; 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; 5379787eaaSJeff Mahoney BUG_ON(ret); /* Can't happen */ 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 7448a3b636SEric Sandeen static struct btrfs_csum_item * 7548a3b636SEric Sandeen btrfs_lookup_csum(struct btrfs_trans_handle *trans, 76b18c6685SChris Mason struct btrfs_root *root, 776567e837SChris Mason struct btrfs_path *path, 78d20f7043SChris Mason u64 bytenr, int cow) 796567e837SChris Mason { 800b246afaSJeff Mahoney struct btrfs_fs_info *fs_info = root->fs_info; 816567e837SChris Mason int ret; 826567e837SChris Mason struct btrfs_key file_key; 836567e837SChris Mason struct btrfs_key found_key; 846567e837SChris Mason struct btrfs_csum_item *item; 855f39d397SChris Mason struct extent_buffer *leaf; 866567e837SChris Mason u64 csum_offset = 0; 870b246afaSJeff Mahoney u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 88a429e513SChris Mason int csums_in_item; 896567e837SChris Mason 90d20f7043SChris Mason file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 91d20f7043SChris Mason file_key.offset = bytenr; 92962a298fSDavid Sterba file_key.type = BTRFS_EXTENT_CSUM_KEY; 93b18c6685SChris Mason ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); 946567e837SChris Mason if (ret < 0) 956567e837SChris Mason goto fail; 965f39d397SChris Mason leaf = path->nodes[0]; 976567e837SChris Mason if (ret > 0) { 986567e837SChris Mason ret = 1; 9970b2befdSChris Mason if (path->slots[0] == 0) 1006567e837SChris Mason goto fail; 1016567e837SChris Mason path->slots[0]--; 1025f39d397SChris Mason btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); 103962a298fSDavid Sterba if (found_key.type != BTRFS_EXTENT_CSUM_KEY) 1046567e837SChris Mason goto fail; 105d20f7043SChris Mason 106d20f7043SChris Mason csum_offset = (bytenr - found_key.offset) >> 1070b246afaSJeff Mahoney fs_info->sb->s_blocksize_bits; 1085f39d397SChris Mason csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]); 109607d432dSJosef Bacik csums_in_item /= csum_size; 110a429e513SChris Mason 11182d130ffSMiao Xie if (csum_offset == csums_in_item) { 112a429e513SChris Mason ret = -EFBIG; 1136567e837SChris Mason goto fail; 11482d130ffSMiao Xie } else if (csum_offset > csums_in_item) { 11582d130ffSMiao Xie goto fail; 1166567e837SChris Mason } 1176567e837SChris Mason } 1186567e837SChris Mason item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); 119509659cdSChris Mason item = (struct btrfs_csum_item *)((unsigned char *)item + 120607d432dSJosef Bacik csum_offset * csum_size); 1216567e837SChris Mason return item; 1226567e837SChris Mason fail: 1236567e837SChris Mason if (ret > 0) 124b18c6685SChris Mason ret = -ENOENT; 1256567e837SChris Mason return ERR_PTR(ret); 1266567e837SChris Mason } 1276567e837SChris Mason 128dee26a9fSChris Mason int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, 129dee26a9fSChris Mason struct btrfs_root *root, 130dee26a9fSChris Mason struct btrfs_path *path, u64 objectid, 1319773a788SChris Mason u64 offset, int mod) 132dee26a9fSChris Mason { 133dee26a9fSChris Mason int ret; 134dee26a9fSChris Mason struct btrfs_key file_key; 135dee26a9fSChris Mason int ins_len = mod < 0 ? -1 : 0; 136dee26a9fSChris Mason int cow = mod != 0; 137dee26a9fSChris Mason 138dee26a9fSChris Mason file_key.objectid = objectid; 13970b2befdSChris Mason file_key.offset = offset; 140962a298fSDavid Sterba file_key.type = BTRFS_EXTENT_DATA_KEY; 141dee26a9fSChris Mason ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow); 142dee26a9fSChris Mason return ret; 143dee26a9fSChris Mason } 144f254e52cSChris Mason 145facc8a22SMiao Xie static void btrfs_io_bio_endio_readpage(struct btrfs_io_bio *bio, int err) 146facc8a22SMiao Xie { 147facc8a22SMiao Xie kfree(bio->csum_allocated); 148facc8a22SMiao Xie } 149facc8a22SMiao Xie 1504e4cbee9SChristoph Hellwig static blk_status_t __btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, 1514b46fce2SJosef Bacik u64 logical_offset, u32 *dst, int dio) 15261b49440SChris Mason { 1530b246afaSJeff Mahoney struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 15417347cecSLiu Bo struct bio_vec bvec; 15517347cecSLiu Bo struct bvec_iter iter; 156facc8a22SMiao Xie struct btrfs_io_bio *btrfs_bio = btrfs_io_bio(bio); 157facc8a22SMiao Xie struct btrfs_csum_item *item = NULL; 158facc8a22SMiao Xie struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; 159facc8a22SMiao Xie struct btrfs_path *path; 160facc8a22SMiao Xie u8 *csum; 1614b46fce2SJosef Bacik u64 offset = 0; 16261b49440SChris Mason u64 item_start_offset = 0; 16361b49440SChris Mason u64 item_last_offset = 0; 164d20f7043SChris Mason u64 disk_bytenr; 165c40a3d38SChandan Rajendra u64 page_bytes_left; 16661b49440SChris Mason u32 diff; 167facc8a22SMiao Xie int nblocks; 16817347cecSLiu Bo int count = 0; 1690b246afaSJeff Mahoney u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 17061b49440SChris Mason 17161b49440SChris Mason path = btrfs_alloc_path(); 172c2db1073STsutomu Itoh if (!path) 1734e4cbee9SChristoph Hellwig return BLK_STS_RESOURCE; 174facc8a22SMiao Xie 1754f024f37SKent Overstreet nblocks = bio->bi_iter.bi_size >> inode->i_sb->s_blocksize_bits; 176facc8a22SMiao Xie if (!dst) { 177facc8a22SMiao Xie if (nblocks * csum_size > BTRFS_BIO_INLINE_CSUM_SIZE) { 17831e818feSDavid Sterba btrfs_bio->csum_allocated = kmalloc_array(nblocks, 17931e818feSDavid Sterba csum_size, GFP_NOFS); 180facc8a22SMiao Xie if (!btrfs_bio->csum_allocated) { 181facc8a22SMiao Xie btrfs_free_path(path); 1824e4cbee9SChristoph Hellwig return BLK_STS_RESOURCE; 183facc8a22SMiao Xie } 184facc8a22SMiao Xie btrfs_bio->csum = btrfs_bio->csum_allocated; 185facc8a22SMiao Xie btrfs_bio->end_io = btrfs_io_bio_endio_readpage; 186facc8a22SMiao Xie } else { 187facc8a22SMiao Xie btrfs_bio->csum = btrfs_bio->csum_inline; 188facc8a22SMiao Xie } 189facc8a22SMiao Xie csum = btrfs_bio->csum; 190facc8a22SMiao Xie } else { 191facc8a22SMiao Xie csum = (u8 *)dst; 192facc8a22SMiao Xie } 193facc8a22SMiao Xie 19409cbfeafSKirill A. Shutemov if (bio->bi_iter.bi_size > PAGE_SIZE * 8) 195e4058b54SDavid Sterba path->reada = READA_FORWARD; 19661b49440SChris Mason 1972cf8572dSChris Mason /* 1982cf8572dSChris Mason * the free space stuff is only read when it hasn't been 1992cf8572dSChris Mason * updated in the current transaction. So, we can safely 2002cf8572dSChris Mason * read from the commit root and sidestep a nasty deadlock 2012cf8572dSChris Mason * between reading the free space cache and updating the csum tree. 2022cf8572dSChris Mason */ 20370ddc553SNikolay Borisov if (btrfs_is_free_space_inode(BTRFS_I(inode))) { 2042cf8572dSChris Mason path->search_commit_root = 1; 205ddf23b3fSJosef Bacik path->skip_locking = 1; 206ddf23b3fSJosef Bacik } 2072cf8572dSChris Mason 2084f024f37SKent Overstreet disk_bytenr = (u64)bio->bi_iter.bi_sector << 9; 2094b46fce2SJosef Bacik if (dio) 2104b46fce2SJosef Bacik offset = logical_offset; 211c40a3d38SChandan Rajendra 21217347cecSLiu Bo bio_for_each_segment(bvec, bio, iter) { 21317347cecSLiu Bo page_bytes_left = bvec.bv_len; 2144989d277SChristoph Hellwig if (count) 2154989d277SChristoph Hellwig goto next; 2164989d277SChristoph Hellwig 2174b46fce2SJosef Bacik if (!dio) 21817347cecSLiu Bo offset = page_offset(bvec.bv_page) + bvec.bv_offset; 219facc8a22SMiao Xie count = btrfs_find_ordered_sum(inode, offset, disk_bytenr, 220facc8a22SMiao Xie (u32 *)csum, nblocks); 221e4100d98SMiao Xie if (count) 22261b49440SChris Mason goto found; 22361b49440SChris Mason 224d20f7043SChris Mason if (!item || disk_bytenr < item_start_offset || 225d20f7043SChris Mason disk_bytenr >= item_last_offset) { 22661b49440SChris Mason struct btrfs_key found_key; 22761b49440SChris Mason u32 item_size; 22861b49440SChris Mason 22961b49440SChris Mason if (item) 230b3b4aa74SDavid Sterba btrfs_release_path(path); 2310b246afaSJeff Mahoney item = btrfs_lookup_csum(NULL, fs_info->csum_root, 232d20f7043SChris Mason path, disk_bytenr, 0); 23361b49440SChris Mason if (IS_ERR(item)) { 234e4100d98SMiao Xie count = 1; 235facc8a22SMiao Xie memset(csum, 0, csum_size); 23617d217feSYan Zheng if (BTRFS_I(inode)->root->root_key.objectid == 23717d217feSYan Zheng BTRFS_DATA_RELOC_TREE_OBJECTID) { 23817d217feSYan Zheng set_extent_bits(io_tree, offset, 2390b246afaSJeff Mahoney offset + fs_info->sectorsize - 1, 240ceeb0ae7SDavid Sterba EXTENT_NODATASUM); 24117d217feSYan Zheng } else { 2420b246afaSJeff Mahoney btrfs_info_rl(fs_info, 243efe120a0SFrank Holton "no csum found for inode %llu start %llu", 2444a0cc7caSNikolay Borisov btrfs_ino(BTRFS_I(inode)), offset); 24517d217feSYan Zheng } 2466dab8157SChris Mason item = NULL; 247b3b4aa74SDavid Sterba btrfs_release_path(path); 24861b49440SChris Mason goto found; 24961b49440SChris Mason } 25061b49440SChris Mason btrfs_item_key_to_cpu(path->nodes[0], &found_key, 25161b49440SChris Mason path->slots[0]); 25261b49440SChris Mason 25361b49440SChris Mason item_start_offset = found_key.offset; 25461b49440SChris Mason item_size = btrfs_item_size_nr(path->nodes[0], 25561b49440SChris Mason path->slots[0]); 25661b49440SChris Mason item_last_offset = item_start_offset + 257607d432dSJosef Bacik (item_size / csum_size) * 2580b246afaSJeff Mahoney fs_info->sectorsize; 25961b49440SChris Mason item = btrfs_item_ptr(path->nodes[0], path->slots[0], 26061b49440SChris Mason struct btrfs_csum_item); 26161b49440SChris Mason } 26261b49440SChris Mason /* 26361b49440SChris Mason * this byte range must be able to fit inside 26461b49440SChris Mason * a single leaf so it will also fit inside a u32 26561b49440SChris Mason */ 266d20f7043SChris Mason diff = disk_bytenr - item_start_offset; 2670b246afaSJeff Mahoney diff = diff / fs_info->sectorsize; 268607d432dSJosef Bacik diff = diff * csum_size; 269facc8a22SMiao Xie count = min_t(int, nblocks, (item_last_offset - disk_bytenr) >> 270e4100d98SMiao Xie inode->i_sb->s_blocksize_bits); 271facc8a22SMiao Xie read_extent_buffer(path->nodes[0], csum, 2723de9d6b6SChris Mason ((unsigned long)item) + diff, 273e4100d98SMiao Xie csum_size * count); 27461b49440SChris Mason found: 275facc8a22SMiao Xie csum += count * csum_size; 276facc8a22SMiao Xie nblocks -= count; 2774989d277SChristoph Hellwig next: 278e4100d98SMiao Xie while (count--) { 2790b246afaSJeff Mahoney disk_bytenr += fs_info->sectorsize; 2800b246afaSJeff Mahoney offset += fs_info->sectorsize; 2810b246afaSJeff Mahoney page_bytes_left -= fs_info->sectorsize; 2824989d277SChristoph Hellwig if (!page_bytes_left) 2834989d277SChristoph Hellwig break; /* move to next bio */ 2844989d277SChristoph Hellwig } 2854989d277SChristoph Hellwig } 2864989d277SChristoph Hellwig 287389f239cSChris Mason WARN_ON_ONCE(count); 28861b49440SChris Mason btrfs_free_path(path); 28961b49440SChris Mason return 0; 29061b49440SChris Mason } 29161b49440SChris Mason 2924e4cbee9SChristoph Hellwig blk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, u32 *dst) 2934b46fce2SJosef Bacik { 2942ff7e61eSJeff Mahoney return __btrfs_lookup_bio_sums(inode, bio, 0, dst, 0); 2954b46fce2SJosef Bacik } 2964b46fce2SJosef Bacik 2974e4cbee9SChristoph Hellwig blk_status_t btrfs_lookup_bio_sums_dio(struct inode *inode, struct bio *bio, u64 offset) 2984b46fce2SJosef Bacik { 2992ff7e61eSJeff Mahoney return __btrfs_lookup_bio_sums(inode, bio, offset, NULL, 1); 3004b46fce2SJosef Bacik } 3014b46fce2SJosef Bacik 30217d217feSYan Zheng int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, 303a2de733cSArne Jansen struct list_head *list, int search_commit) 30417d217feSYan Zheng { 3050b246afaSJeff Mahoney struct btrfs_fs_info *fs_info = root->fs_info; 30617d217feSYan Zheng struct btrfs_key key; 30717d217feSYan Zheng struct btrfs_path *path; 30817d217feSYan Zheng struct extent_buffer *leaf; 30917d217feSYan Zheng struct btrfs_ordered_sum *sums; 31017d217feSYan Zheng struct btrfs_csum_item *item; 3110678b618SMark Fasheh LIST_HEAD(tmplist); 31217d217feSYan Zheng unsigned long offset; 31317d217feSYan Zheng int ret; 31417d217feSYan Zheng size_t size; 31517d217feSYan Zheng u64 csum_end; 3160b246afaSJeff Mahoney u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 31717d217feSYan Zheng 3180b246afaSJeff Mahoney ASSERT(IS_ALIGNED(start, fs_info->sectorsize) && 3190b246afaSJeff Mahoney IS_ALIGNED(end + 1, fs_info->sectorsize)); 3204277a9c3SJosef Bacik 32117d217feSYan Zheng path = btrfs_alloc_path(); 322d8926bb3SMark Fasheh if (!path) 323d8926bb3SMark Fasheh return -ENOMEM; 32417d217feSYan Zheng 325a2de733cSArne Jansen if (search_commit) { 326a2de733cSArne Jansen path->skip_locking = 1; 327e4058b54SDavid Sterba path->reada = READA_FORWARD; 328a2de733cSArne Jansen path->search_commit_root = 1; 329a2de733cSArne Jansen } 330a2de733cSArne Jansen 33117d217feSYan Zheng key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 33217d217feSYan Zheng key.offset = start; 33317d217feSYan Zheng key.type = BTRFS_EXTENT_CSUM_KEY; 33417d217feSYan Zheng 33507d400a6SYan Zheng ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); 33617d217feSYan Zheng if (ret < 0) 33717d217feSYan Zheng goto fail; 33817d217feSYan Zheng if (ret > 0 && path->slots[0] > 0) { 33917d217feSYan Zheng leaf = path->nodes[0]; 34017d217feSYan Zheng btrfs_item_key_to_cpu(leaf, &key, path->slots[0] - 1); 34117d217feSYan Zheng if (key.objectid == BTRFS_EXTENT_CSUM_OBJECTID && 34217d217feSYan Zheng key.type == BTRFS_EXTENT_CSUM_KEY) { 34317d217feSYan Zheng offset = (start - key.offset) >> 3440b246afaSJeff Mahoney fs_info->sb->s_blocksize_bits; 34517d217feSYan Zheng if (offset * csum_size < 34617d217feSYan Zheng btrfs_item_size_nr(leaf, path->slots[0] - 1)) 34717d217feSYan Zheng path->slots[0]--; 34817d217feSYan Zheng } 34917d217feSYan Zheng } 35017d217feSYan Zheng 35117d217feSYan Zheng while (start <= end) { 35217d217feSYan Zheng leaf = path->nodes[0]; 35317d217feSYan Zheng if (path->slots[0] >= btrfs_header_nritems(leaf)) { 35407d400a6SYan Zheng ret = btrfs_next_leaf(root, path); 35517d217feSYan Zheng if (ret < 0) 35617d217feSYan Zheng goto fail; 35717d217feSYan Zheng if (ret > 0) 35817d217feSYan Zheng break; 35917d217feSYan Zheng leaf = path->nodes[0]; 36017d217feSYan Zheng } 36117d217feSYan Zheng 36217d217feSYan Zheng btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); 36317d217feSYan Zheng if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 364628c8282SZhi Yong Wu key.type != BTRFS_EXTENT_CSUM_KEY || 365628c8282SZhi Yong Wu key.offset > end) 36617d217feSYan Zheng break; 36717d217feSYan Zheng 36817d217feSYan Zheng if (key.offset > start) 36917d217feSYan Zheng start = key.offset; 37017d217feSYan Zheng 37117d217feSYan Zheng size = btrfs_item_size_nr(leaf, path->slots[0]); 3720b246afaSJeff Mahoney csum_end = key.offset + (size / csum_size) * fs_info->sectorsize; 37387b29b20SYan Zheng if (csum_end <= start) { 37487b29b20SYan Zheng path->slots[0]++; 37587b29b20SYan Zheng continue; 37687b29b20SYan Zheng } 37717d217feSYan Zheng 37807d400a6SYan Zheng csum_end = min(csum_end, end + 1); 37907d400a6SYan Zheng item = btrfs_item_ptr(path->nodes[0], path->slots[0], 38007d400a6SYan Zheng struct btrfs_csum_item); 38107d400a6SYan Zheng while (start < csum_end) { 38207d400a6SYan Zheng size = min_t(size_t, csum_end - start, 3830b246afaSJeff Mahoney MAX_ORDERED_SUM_BYTES(fs_info)); 3840b246afaSJeff Mahoney sums = kzalloc(btrfs_ordered_sum_size(fs_info, size), 38507d400a6SYan Zheng GFP_NOFS); 3860678b618SMark Fasheh if (!sums) { 3870678b618SMark Fasheh ret = -ENOMEM; 3880678b618SMark Fasheh goto fail; 3890678b618SMark Fasheh } 39017d217feSYan Zheng 39117d217feSYan Zheng sums->bytenr = start; 392f51a4a18SMiao Xie sums->len = (int)size; 39317d217feSYan Zheng 39417d217feSYan Zheng offset = (start - key.offset) >> 3950b246afaSJeff Mahoney fs_info->sb->s_blocksize_bits; 39617d217feSYan Zheng offset *= csum_size; 3970b246afaSJeff Mahoney size >>= fs_info->sb->s_blocksize_bits; 39817d217feSYan Zheng 39907d400a6SYan Zheng read_extent_buffer(path->nodes[0], 400f51a4a18SMiao Xie sums->sums, 401f51a4a18SMiao Xie ((unsigned long)item) + offset, 402f51a4a18SMiao Xie csum_size * size); 40317d217feSYan Zheng 4040b246afaSJeff Mahoney start += fs_info->sectorsize * size; 4050678b618SMark Fasheh list_add_tail(&sums->list, &tmplist); 40607d400a6SYan Zheng } 40717d217feSYan Zheng path->slots[0]++; 40817d217feSYan Zheng } 40917d217feSYan Zheng ret = 0; 41017d217feSYan Zheng fail: 4110678b618SMark Fasheh while (ret < 0 && !list_empty(&tmplist)) { 4126e5aafb2SChris Mason sums = list_entry(tmplist.next, struct btrfs_ordered_sum, list); 4130678b618SMark Fasheh list_del(&sums->list); 4140678b618SMark Fasheh kfree(sums); 4150678b618SMark Fasheh } 4160678b618SMark Fasheh list_splice_tail(&tmplist, list); 4170678b618SMark Fasheh 41817d217feSYan Zheng btrfs_free_path(path); 41917d217feSYan Zheng return ret; 42017d217feSYan Zheng } 42117d217feSYan Zheng 4224e4cbee9SChristoph Hellwig blk_status_t btrfs_csum_one_bio(struct inode *inode, struct bio *bio, 4232ff7e61eSJeff Mahoney u64 file_start, int contig) 424e015640fSChris Mason { 4250b246afaSJeff Mahoney struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 426e6dcd2dcSChris Mason struct btrfs_ordered_sum *sums; 4276cd7ce49SChristoph Hellwig struct btrfs_ordered_extent *ordered = NULL; 428e015640fSChris Mason char *data; 42917347cecSLiu Bo struct bvec_iter iter; 43017347cecSLiu Bo struct bio_vec bvec; 431f51a4a18SMiao Xie int index; 432c40a3d38SChandan Rajendra int nr_sectors; 4333edf7d33SChris Mason unsigned long total_bytes = 0; 4343edf7d33SChris Mason unsigned long this_sum_bytes = 0; 43517347cecSLiu Bo int i; 4363edf7d33SChris Mason u64 offset; 437e015640fSChris Mason 4380b246afaSJeff Mahoney sums = kzalloc(btrfs_ordered_sum_size(fs_info, bio->bi_iter.bi_size), 4394f024f37SKent Overstreet GFP_NOFS); 440e015640fSChris Mason if (!sums) 4414e4cbee9SChristoph Hellwig return BLK_STS_RESOURCE; 4423edf7d33SChris Mason 4434f024f37SKent Overstreet sums->len = bio->bi_iter.bi_size; 444e6dcd2dcSChris Mason INIT_LIST_HEAD(&sums->list); 445d20f7043SChris Mason 446d20f7043SChris Mason if (contig) 447d20f7043SChris Mason offset = file_start; 448d20f7043SChris Mason else 4496cd7ce49SChristoph Hellwig offset = 0; /* shut up gcc */ 450d20f7043SChris Mason 4514f024f37SKent Overstreet sums->bytenr = (u64)bio->bi_iter.bi_sector << 9; 452f51a4a18SMiao Xie index = 0; 453e015640fSChris Mason 45417347cecSLiu Bo bio_for_each_segment(bvec, bio, iter) { 455d20f7043SChris Mason if (!contig) 45617347cecSLiu Bo offset = page_offset(bvec.bv_page) + bvec.bv_offset; 457d20f7043SChris Mason 4586cd7ce49SChristoph Hellwig if (!ordered) { 4596cd7ce49SChristoph Hellwig ordered = btrfs_lookup_ordered_extent(inode, offset); 4606cd7ce49SChristoph Hellwig BUG_ON(!ordered); /* Logic error */ 4616cd7ce49SChristoph Hellwig } 4626cd7ce49SChristoph Hellwig 46317347cecSLiu Bo data = kmap_atomic(bvec.bv_page); 464c40a3d38SChandan Rajendra 4650b246afaSJeff Mahoney nr_sectors = BTRFS_BYTES_TO_BLKS(fs_info, 46617347cecSLiu Bo bvec.bv_len + fs_info->sectorsize 467c40a3d38SChandan Rajendra - 1); 468c40a3d38SChandan Rajendra 469c40a3d38SChandan Rajendra for (i = 0; i < nr_sectors; i++) { 470e58dd74bSJosef Bacik if (offset >= ordered->file_offset + ordered->len || 471e58dd74bSJosef Bacik offset < ordered->file_offset) { 4723edf7d33SChris Mason unsigned long bytes_left; 473c40a3d38SChandan Rajendra 474c40a3d38SChandan Rajendra kunmap_atomic(data); 4753edf7d33SChris Mason sums->len = this_sum_bytes; 4763edf7d33SChris Mason this_sum_bytes = 0; 4773edf7d33SChris Mason btrfs_add_ordered_sum(inode, ordered, sums); 4783edf7d33SChris Mason btrfs_put_ordered_extent(ordered); 4793edf7d33SChris Mason 4804f024f37SKent Overstreet bytes_left = bio->bi_iter.bi_size - total_bytes; 4813edf7d33SChris Mason 4820b246afaSJeff Mahoney sums = kzalloc(btrfs_ordered_sum_size(fs_info, bytes_left), 4833edf7d33SChris Mason GFP_NOFS); 48479787eaaSJeff Mahoney BUG_ON(!sums); /* -ENOMEM */ 4853edf7d33SChris Mason sums->len = bytes_left; 486c40a3d38SChandan Rajendra ordered = btrfs_lookup_ordered_extent(inode, 487c40a3d38SChandan Rajendra offset); 488c40a3d38SChandan Rajendra ASSERT(ordered); /* Logic error */ 489c40a3d38SChandan Rajendra sums->bytenr = ((u64)bio->bi_iter.bi_sector << 9) 490c40a3d38SChandan Rajendra + total_bytes; 491f51a4a18SMiao Xie index = 0; 4923edf7d33SChris Mason 49317347cecSLiu Bo data = kmap_atomic(bvec.bv_page); 494c40a3d38SChandan Rajendra } 495c40a3d38SChandan Rajendra 496f51a4a18SMiao Xie sums->sums[index] = ~(u32)0; 497c40a3d38SChandan Rajendra sums->sums[index] 49817347cecSLiu Bo = btrfs_csum_data(data + bvec.bv_offset 4990b246afaSJeff Mahoney + (i * fs_info->sectorsize), 500f51a4a18SMiao Xie sums->sums[index], 5010b246afaSJeff Mahoney fs_info->sectorsize); 502f51a4a18SMiao Xie btrfs_csum_final(sums->sums[index], 503f51a4a18SMiao Xie (char *)(sums->sums + index)); 504c40a3d38SChandan Rajendra index++; 5050b246afaSJeff Mahoney offset += fs_info->sectorsize; 5060b246afaSJeff Mahoney this_sum_bytes += fs_info->sectorsize; 5070b246afaSJeff Mahoney total_bytes += fs_info->sectorsize; 508c40a3d38SChandan Rajendra } 509c40a3d38SChandan Rajendra 510c40a3d38SChandan Rajendra kunmap_atomic(data); 511e015640fSChris Mason } 512ed98b56aSChris Mason this_sum_bytes = 0; 5133edf7d33SChris Mason btrfs_add_ordered_sum(inode, ordered, sums); 5143edf7d33SChris Mason btrfs_put_ordered_extent(ordered); 515e015640fSChris Mason return 0; 516e015640fSChris Mason } 517e015640fSChris Mason 518459931ecSChris Mason /* 519459931ecSChris Mason * helper function for csum removal, this expects the 520459931ecSChris Mason * key to describe the csum pointed to by the path, and it expects 521459931ecSChris Mason * the csum to overlap the range [bytenr, len] 522459931ecSChris Mason * 523459931ecSChris Mason * The csum should not be entirely contained in the range and the 524459931ecSChris Mason * range should not be entirely contained in the csum. 525459931ecSChris Mason * 526459931ecSChris Mason * This calls btrfs_truncate_item with the correct args based on the 527459931ecSChris Mason * overlap, and fixes up the key as required. 528459931ecSChris Mason */ 5292ff7e61eSJeff Mahoney static noinline void truncate_one_csum(struct btrfs_fs_info *fs_info, 530459931ecSChris Mason struct btrfs_path *path, 531459931ecSChris Mason struct btrfs_key *key, 532459931ecSChris Mason u64 bytenr, u64 len) 533459931ecSChris Mason { 534459931ecSChris Mason struct extent_buffer *leaf; 5350b246afaSJeff Mahoney u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 536459931ecSChris Mason u64 csum_end; 537459931ecSChris Mason u64 end_byte = bytenr + len; 5380b246afaSJeff Mahoney u32 blocksize_bits = fs_info->sb->s_blocksize_bits; 539459931ecSChris Mason 540459931ecSChris Mason leaf = path->nodes[0]; 541459931ecSChris Mason csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; 5420b246afaSJeff Mahoney csum_end <<= fs_info->sb->s_blocksize_bits; 543459931ecSChris Mason csum_end += key->offset; 544459931ecSChris Mason 545459931ecSChris Mason if (key->offset < bytenr && csum_end <= end_byte) { 546459931ecSChris Mason /* 547459931ecSChris Mason * [ bytenr - len ] 548459931ecSChris Mason * [ ] 549459931ecSChris Mason * [csum ] 550459931ecSChris Mason * A simple truncate off the end of the item 551459931ecSChris Mason */ 552459931ecSChris Mason u32 new_size = (bytenr - key->offset) >> blocksize_bits; 553459931ecSChris Mason new_size *= csum_size; 5542ff7e61eSJeff Mahoney btrfs_truncate_item(fs_info, path, new_size, 1); 555459931ecSChris Mason } else if (key->offset >= bytenr && csum_end > end_byte && 556459931ecSChris Mason end_byte > key->offset) { 557459931ecSChris Mason /* 558459931ecSChris Mason * [ bytenr - len ] 559459931ecSChris Mason * [ ] 560459931ecSChris Mason * [csum ] 561459931ecSChris Mason * we need to truncate from the beginning of the csum 562459931ecSChris Mason */ 563459931ecSChris Mason u32 new_size = (csum_end - end_byte) >> blocksize_bits; 564459931ecSChris Mason new_size *= csum_size; 565459931ecSChris Mason 5662ff7e61eSJeff Mahoney btrfs_truncate_item(fs_info, path, new_size, 0); 567459931ecSChris Mason 568459931ecSChris Mason key->offset = end_byte; 5690b246afaSJeff Mahoney btrfs_set_item_key_safe(fs_info, path, key); 570459931ecSChris Mason } else { 571459931ecSChris Mason BUG(); 572459931ecSChris Mason } 573459931ecSChris Mason } 574459931ecSChris Mason 575459931ecSChris Mason /* 576459931ecSChris Mason * deletes the csum items from the csum tree for a given 577459931ecSChris Mason * range of bytes. 578459931ecSChris Mason */ 579459931ecSChris Mason int btrfs_del_csums(struct btrfs_trans_handle *trans, 5805b4aacefSJeff Mahoney struct btrfs_fs_info *fs_info, u64 bytenr, u64 len) 581459931ecSChris Mason { 5825b4aacefSJeff Mahoney struct btrfs_root *root = fs_info->csum_root; 583459931ecSChris Mason struct btrfs_path *path; 584459931ecSChris Mason struct btrfs_key key; 585459931ecSChris Mason u64 end_byte = bytenr + len; 586459931ecSChris Mason u64 csum_end; 587459931ecSChris Mason struct extent_buffer *leaf; 588459931ecSChris Mason int ret; 5890b246afaSJeff Mahoney u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 5900b246afaSJeff Mahoney int blocksize_bits = fs_info->sb->s_blocksize_bits; 591459931ecSChris Mason 592459931ecSChris Mason path = btrfs_alloc_path(); 5932a29edc6Sliubo if (!path) 5942a29edc6Sliubo return -ENOMEM; 595459931ecSChris Mason 596459931ecSChris Mason while (1) { 597459931ecSChris Mason key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 598459931ecSChris Mason key.offset = end_byte - 1; 599459931ecSChris Mason key.type = BTRFS_EXTENT_CSUM_KEY; 600459931ecSChris Mason 601b9473439SChris Mason path->leave_spinning = 1; 602459931ecSChris Mason ret = btrfs_search_slot(trans, root, &key, path, -1, 1); 603459931ecSChris Mason if (ret > 0) { 604459931ecSChris Mason if (path->slots[0] == 0) 60565a246c5STsutomu Itoh break; 606459931ecSChris Mason path->slots[0]--; 607ad0397a7SJosef Bacik } else if (ret < 0) { 60865a246c5STsutomu Itoh break; 609459931ecSChris Mason } 610ad0397a7SJosef Bacik 611459931ecSChris Mason leaf = path->nodes[0]; 612459931ecSChris Mason btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); 613459931ecSChris Mason 614459931ecSChris Mason if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 615459931ecSChris Mason key.type != BTRFS_EXTENT_CSUM_KEY) { 616459931ecSChris Mason break; 617459931ecSChris Mason } 618459931ecSChris Mason 619459931ecSChris Mason if (key.offset >= end_byte) 620459931ecSChris Mason break; 621459931ecSChris Mason 622459931ecSChris Mason csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; 623459931ecSChris Mason csum_end <<= blocksize_bits; 624459931ecSChris Mason csum_end += key.offset; 625459931ecSChris Mason 626459931ecSChris Mason /* this csum ends before we start, we're done */ 627459931ecSChris Mason if (csum_end <= bytenr) 628459931ecSChris Mason break; 629459931ecSChris Mason 630459931ecSChris Mason /* delete the entire item, it is inside our range */ 631459931ecSChris Mason if (key.offset >= bytenr && csum_end <= end_byte) { 6326f546216SFilipe Manana int del_nr = 1; 6336f546216SFilipe Manana 6346f546216SFilipe Manana /* 6356f546216SFilipe Manana * Check how many csum items preceding this one in this 6366f546216SFilipe Manana * leaf correspond to our range and then delete them all 6376f546216SFilipe Manana * at once. 6386f546216SFilipe Manana */ 6396f546216SFilipe Manana if (key.offset > bytenr && path->slots[0] > 0) { 6406f546216SFilipe Manana int slot = path->slots[0] - 1; 6416f546216SFilipe Manana 6426f546216SFilipe Manana while (slot >= 0) { 6436f546216SFilipe Manana struct btrfs_key pk; 6446f546216SFilipe Manana 6456f546216SFilipe Manana btrfs_item_key_to_cpu(leaf, &pk, slot); 6466f546216SFilipe Manana if (pk.offset < bytenr || 6476f546216SFilipe Manana pk.type != BTRFS_EXTENT_CSUM_KEY || 6486f546216SFilipe Manana pk.objectid != 6496f546216SFilipe Manana BTRFS_EXTENT_CSUM_OBJECTID) 6506f546216SFilipe Manana break; 6516f546216SFilipe Manana path->slots[0] = slot; 6526f546216SFilipe Manana del_nr++; 6536f546216SFilipe Manana key.offset = pk.offset; 6546f546216SFilipe Manana slot--; 6556f546216SFilipe Manana } 6566f546216SFilipe Manana } 6576f546216SFilipe Manana ret = btrfs_del_items(trans, root, path, 6586f546216SFilipe Manana path->slots[0], del_nr); 65965a246c5STsutomu Itoh if (ret) 66065a246c5STsutomu Itoh goto out; 661dcbdd4dcSChris Mason if (key.offset == bytenr) 662dcbdd4dcSChris Mason break; 663459931ecSChris Mason } else if (key.offset < bytenr && csum_end > end_byte) { 664459931ecSChris Mason unsigned long offset; 665459931ecSChris Mason unsigned long shift_len; 666459931ecSChris Mason unsigned long item_offset; 667459931ecSChris Mason /* 668459931ecSChris Mason * [ bytenr - len ] 669459931ecSChris Mason * [csum ] 670459931ecSChris Mason * 671459931ecSChris Mason * Our bytes are in the middle of the csum, 672459931ecSChris Mason * we need to split this item and insert a new one. 673459931ecSChris Mason * 674459931ecSChris Mason * But we can't drop the path because the 675459931ecSChris Mason * csum could change, get removed, extended etc. 676459931ecSChris Mason * 677459931ecSChris Mason * The trick here is the max size of a csum item leaves 678459931ecSChris Mason * enough room in the tree block for a single 679459931ecSChris Mason * item header. So, we split the item in place, 680459931ecSChris Mason * adding a new header pointing to the existing 681459931ecSChris Mason * bytes. Then we loop around again and we have 682459931ecSChris Mason * a nicely formed csum item that we can neatly 683459931ecSChris Mason * truncate. 684459931ecSChris Mason */ 685459931ecSChris Mason offset = (bytenr - key.offset) >> blocksize_bits; 686459931ecSChris Mason offset *= csum_size; 687459931ecSChris Mason 688459931ecSChris Mason shift_len = (len >> blocksize_bits) * csum_size; 689459931ecSChris Mason 690459931ecSChris Mason item_offset = btrfs_item_ptr_offset(leaf, 691459931ecSChris Mason path->slots[0]); 692459931ecSChris Mason 693b159fa28SDavid Sterba memzero_extent_buffer(leaf, item_offset + offset, 694459931ecSChris Mason shift_len); 695459931ecSChris Mason key.offset = bytenr; 696459931ecSChris Mason 697459931ecSChris Mason /* 698459931ecSChris Mason * btrfs_split_item returns -EAGAIN when the 699459931ecSChris Mason * item changed size or key 700459931ecSChris Mason */ 701459931ecSChris Mason ret = btrfs_split_item(trans, root, path, &key, offset); 70279787eaaSJeff Mahoney if (ret && ret != -EAGAIN) { 70366642832SJeff Mahoney btrfs_abort_transaction(trans, ret); 70479787eaaSJeff Mahoney goto out; 70579787eaaSJeff Mahoney } 706459931ecSChris Mason 707459931ecSChris Mason key.offset = end_byte - 1; 708459931ecSChris Mason } else { 7092ff7e61eSJeff Mahoney truncate_one_csum(fs_info, path, &key, bytenr, len); 710dcbdd4dcSChris Mason if (key.offset < bytenr) 711dcbdd4dcSChris Mason break; 712459931ecSChris Mason } 713b3b4aa74SDavid Sterba btrfs_release_path(path); 714459931ecSChris Mason } 71565a246c5STsutomu Itoh ret = 0; 716459931ecSChris Mason out: 717459931ecSChris Mason btrfs_free_path(path); 71865a246c5STsutomu Itoh return ret; 719459931ecSChris Mason } 720459931ecSChris Mason 721065631f6SChris Mason int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, 722d20f7043SChris Mason struct btrfs_root *root, 723e6dcd2dcSChris Mason struct btrfs_ordered_sum *sums) 724f254e52cSChris Mason { 7250b246afaSJeff Mahoney struct btrfs_fs_info *fs_info = root->fs_info; 726f254e52cSChris Mason struct btrfs_key file_key; 7276567e837SChris Mason struct btrfs_key found_key; 7285caf2a00SChris Mason struct btrfs_path *path; 729f254e52cSChris Mason struct btrfs_csum_item *item; 730065631f6SChris Mason struct btrfs_csum_item *item_end; 731ff79f819SChris Mason struct extent_buffer *leaf = NULL; 732f51a4a18SMiao Xie u64 next_offset; 733f51a4a18SMiao Xie u64 total_bytes = 0; 7346567e837SChris Mason u64 csum_offset; 735f51a4a18SMiao Xie u64 bytenr; 736f578d4bdSChris Mason u32 nritems; 737f578d4bdSChris Mason u32 ins_size; 738f51a4a18SMiao Xie int index = 0; 739f51a4a18SMiao Xie int found_next; 740f51a4a18SMiao Xie int ret; 7410b246afaSJeff Mahoney u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 7426e92f5e6SChris Mason 7435caf2a00SChris Mason path = btrfs_alloc_path(); 744d8926bb3SMark Fasheh if (!path) 745d8926bb3SMark Fasheh return -ENOMEM; 746065631f6SChris Mason again: 747065631f6SChris Mason next_offset = (u64)-1; 748065631f6SChris Mason found_next = 0; 749f51a4a18SMiao Xie bytenr = sums->bytenr + total_bytes; 750d20f7043SChris Mason file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 751f51a4a18SMiao Xie file_key.offset = bytenr; 752962a298fSDavid Sterba file_key.type = BTRFS_EXTENT_CSUM_KEY; 753a429e513SChris Mason 754f51a4a18SMiao Xie item = btrfs_lookup_csum(trans, root, path, bytenr, 1); 755ff79f819SChris Mason if (!IS_ERR(item)) { 756639cb586SChris Mason ret = 0; 757f51a4a18SMiao Xie leaf = path->nodes[0]; 758f51a4a18SMiao Xie item_end = btrfs_item_ptr(leaf, path->slots[0], 759f51a4a18SMiao Xie struct btrfs_csum_item); 760f51a4a18SMiao Xie item_end = (struct btrfs_csum_item *)((char *)item_end + 761f51a4a18SMiao Xie btrfs_item_size_nr(leaf, path->slots[0])); 762a429e513SChris Mason goto found; 763ff79f819SChris Mason } 764a429e513SChris Mason ret = PTR_ERR(item); 7654a500fd1SYan, Zheng if (ret != -EFBIG && ret != -ENOENT) 7664a500fd1SYan, Zheng goto fail_unlock; 7674a500fd1SYan, Zheng 768a429e513SChris Mason if (ret == -EFBIG) { 769a429e513SChris Mason u32 item_size; 770a429e513SChris Mason /* we found one, but it isn't big enough yet */ 7715f39d397SChris Mason leaf = path->nodes[0]; 7725f39d397SChris Mason item_size = btrfs_item_size_nr(leaf, path->slots[0]); 773607d432dSJosef Bacik if ((item_size / csum_size) >= 7740b246afaSJeff Mahoney MAX_CSUM_ITEMS(fs_info, csum_size)) { 775a429e513SChris Mason /* already at max size, make a new one */ 776a429e513SChris Mason goto insert; 777a429e513SChris Mason } 778a429e513SChris Mason } else { 779f578d4bdSChris Mason int slot = path->slots[0] + 1; 780a429e513SChris Mason /* we didn't find a csum item, insert one */ 781f578d4bdSChris Mason nritems = btrfs_header_nritems(path->nodes[0]); 78235045bf2SFilipe Manana if (!nritems || (path->slots[0] >= nritems - 1)) { 783f578d4bdSChris Mason ret = btrfs_next_leaf(root, path); 784b56baf5bSYan if (ret == 1) 785f578d4bdSChris Mason found_next = 1; 786b56baf5bSYan if (ret != 0) 787f578d4bdSChris Mason goto insert; 78827b9a812SFilipe Manana slot = path->slots[0]; 789f578d4bdSChris Mason } 790f578d4bdSChris Mason btrfs_item_key_to_cpu(path->nodes[0], &found_key, slot); 791d20f7043SChris Mason if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 792d20f7043SChris Mason found_key.type != BTRFS_EXTENT_CSUM_KEY) { 793f578d4bdSChris Mason found_next = 1; 794f578d4bdSChris Mason goto insert; 795f578d4bdSChris Mason } 796f578d4bdSChris Mason next_offset = found_key.offset; 797f578d4bdSChris Mason found_next = 1; 798a429e513SChris Mason goto insert; 799a429e513SChris Mason } 800a429e513SChris Mason 801a429e513SChris Mason /* 802a429e513SChris Mason * at this point, we know the tree has an item, but it isn't big 803a429e513SChris Mason * enough yet to put our csum in. Grow it 804a429e513SChris Mason */ 805b3b4aa74SDavid Sterba btrfs_release_path(path); 8066567e837SChris Mason ret = btrfs_search_slot(trans, root, &file_key, path, 807607d432dSJosef Bacik csum_size, 1); 8086567e837SChris Mason if (ret < 0) 80953863232SChris Mason goto fail_unlock; 810459931ecSChris Mason 811459931ecSChris Mason if (ret > 0) { 812459931ecSChris Mason if (path->slots[0] == 0) 8136567e837SChris Mason goto insert; 8146567e837SChris Mason path->slots[0]--; 815459931ecSChris Mason } 816459931ecSChris Mason 8175f39d397SChris Mason leaf = path->nodes[0]; 8185f39d397SChris Mason btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); 819d20f7043SChris Mason csum_offset = (bytenr - found_key.offset) >> 8200b246afaSJeff Mahoney fs_info->sb->s_blocksize_bits; 821459931ecSChris Mason 822962a298fSDavid Sterba if (found_key.type != BTRFS_EXTENT_CSUM_KEY || 823d20f7043SChris Mason found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 8240b246afaSJeff Mahoney csum_offset >= MAX_CSUM_ITEMS(fs_info, csum_size)) { 8256567e837SChris Mason goto insert; 8266567e837SChris Mason } 827459931ecSChris Mason 8282f697dc6SLiu Bo if (csum_offset == btrfs_item_size_nr(leaf, path->slots[0]) / 829607d432dSJosef Bacik csum_size) { 8302f697dc6SLiu Bo int extend_nr; 8312f697dc6SLiu Bo u64 tmp; 8322f697dc6SLiu Bo u32 diff; 8332f697dc6SLiu Bo u32 free_space; 834459931ecSChris Mason 8352ff7e61eSJeff Mahoney if (btrfs_leaf_free_space(fs_info, leaf) < 8362f697dc6SLiu Bo sizeof(struct btrfs_item) + csum_size * 2) 8372f697dc6SLiu Bo goto insert; 8382f697dc6SLiu Bo 8392ff7e61eSJeff Mahoney free_space = btrfs_leaf_free_space(fs_info, leaf) - 8402f697dc6SLiu Bo sizeof(struct btrfs_item) - csum_size; 841f51a4a18SMiao Xie tmp = sums->len - total_bytes; 8420b246afaSJeff Mahoney tmp >>= fs_info->sb->s_blocksize_bits; 8432f697dc6SLiu Bo WARN_ON(tmp < 1); 8442f697dc6SLiu Bo 8452f697dc6SLiu Bo extend_nr = max_t(int, 1, (int)tmp); 8462f697dc6SLiu Bo diff = (csum_offset + extend_nr) * csum_size; 8470b246afaSJeff Mahoney diff = min(diff, 8480b246afaSJeff Mahoney MAX_CSUM_ITEMS(fs_info, csum_size) * csum_size); 849459931ecSChris Mason 8505f39d397SChris Mason diff = diff - btrfs_item_size_nr(leaf, path->slots[0]); 8512f697dc6SLiu Bo diff = min(free_space, diff); 8522f697dc6SLiu Bo diff /= csum_size; 8532f697dc6SLiu Bo diff *= csum_size; 854459931ecSChris Mason 8552ff7e61eSJeff Mahoney btrfs_extend_item(fs_info, path, diff); 856f51a4a18SMiao Xie ret = 0; 8576567e837SChris Mason goto csum; 8586567e837SChris Mason } 8596567e837SChris Mason 8606567e837SChris Mason insert: 861b3b4aa74SDavid Sterba btrfs_release_path(path); 8626567e837SChris Mason csum_offset = 0; 863f578d4bdSChris Mason if (found_next) { 8642f697dc6SLiu Bo u64 tmp; 865d20f7043SChris Mason 866f51a4a18SMiao Xie tmp = sums->len - total_bytes; 8670b246afaSJeff Mahoney tmp >>= fs_info->sb->s_blocksize_bits; 8682f697dc6SLiu Bo tmp = min(tmp, (next_offset - file_key.offset) >> 8690b246afaSJeff Mahoney fs_info->sb->s_blocksize_bits); 8702f697dc6SLiu Bo 87150d0446eSSeraphime Kirkovski tmp = max_t(u64, 1, tmp); 87250d0446eSSeraphime Kirkovski tmp = min_t(u64, tmp, MAX_CSUM_ITEMS(fs_info, csum_size)); 873607d432dSJosef Bacik ins_size = csum_size * tmp; 874f578d4bdSChris Mason } else { 875607d432dSJosef Bacik ins_size = csum_size; 876f578d4bdSChris Mason } 877b9473439SChris Mason path->leave_spinning = 1; 8785caf2a00SChris Mason ret = btrfs_insert_empty_item(trans, root, path, &file_key, 879f578d4bdSChris Mason ins_size); 880b9473439SChris Mason path->leave_spinning = 0; 88154aa1f4dSChris Mason if (ret < 0) 88253863232SChris Mason goto fail_unlock; 883fae7f21cSDulshani Gunawardhana if (WARN_ON(ret != 0)) 88453863232SChris Mason goto fail_unlock; 8855f39d397SChris Mason leaf = path->nodes[0]; 886f51a4a18SMiao Xie csum: 8875f39d397SChris Mason item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); 888f51a4a18SMiao Xie item_end = (struct btrfs_csum_item *)((unsigned char *)item + 889f51a4a18SMiao Xie btrfs_item_size_nr(leaf, path->slots[0])); 890509659cdSChris Mason item = (struct btrfs_csum_item *)((unsigned char *)item + 891607d432dSJosef Bacik csum_offset * csum_size); 892b18c6685SChris Mason found: 893f51a4a18SMiao Xie ins_size = (u32)(sums->len - total_bytes) >> 8940b246afaSJeff Mahoney fs_info->sb->s_blocksize_bits; 895f51a4a18SMiao Xie ins_size *= csum_size; 896f51a4a18SMiao Xie ins_size = min_t(u32, (unsigned long)item_end - (unsigned long)item, 897f51a4a18SMiao Xie ins_size); 898f51a4a18SMiao Xie write_extent_buffer(leaf, sums->sums + index, (unsigned long)item, 899f51a4a18SMiao Xie ins_size); 900aadfeb6eSChris Mason 901f51a4a18SMiao Xie ins_size /= csum_size; 9020b246afaSJeff Mahoney total_bytes += ins_size * fs_info->sectorsize; 903f51a4a18SMiao Xie index += ins_size; 904a6591715SChris Mason 9055caf2a00SChris Mason btrfs_mark_buffer_dirty(path->nodes[0]); 906e6dcd2dcSChris Mason if (total_bytes < sums->len) { 907b3b4aa74SDavid Sterba btrfs_release_path(path); 908b9473439SChris Mason cond_resched(); 909065631f6SChris Mason goto again; 910065631f6SChris Mason } 91153863232SChris Mason out: 9125caf2a00SChris Mason btrfs_free_path(path); 913f254e52cSChris Mason return ret; 91453863232SChris Mason 91553863232SChris Mason fail_unlock: 91653863232SChris Mason goto out; 917f254e52cSChris Mason } 9187ffbb598SFilipe Manana 9199cdc5124SNikolay Borisov void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, 9207ffbb598SFilipe Manana const struct btrfs_path *path, 9217ffbb598SFilipe Manana struct btrfs_file_extent_item *fi, 9227ffbb598SFilipe Manana const bool new_inline, 9237ffbb598SFilipe Manana struct extent_map *em) 9247ffbb598SFilipe Manana { 9259cdc5124SNikolay Borisov struct btrfs_fs_info *fs_info = btrfs_sb(inode->vfs_inode.i_sb); 9269cdc5124SNikolay Borisov struct btrfs_root *root = inode->root; 9277ffbb598SFilipe Manana struct extent_buffer *leaf = path->nodes[0]; 9287ffbb598SFilipe Manana const int slot = path->slots[0]; 9297ffbb598SFilipe Manana struct btrfs_key key; 9307ffbb598SFilipe Manana u64 extent_start, extent_end; 9317ffbb598SFilipe Manana u64 bytenr; 9327ffbb598SFilipe Manana u8 type = btrfs_file_extent_type(leaf, fi); 9337ffbb598SFilipe Manana int compress_type = btrfs_file_extent_compression(leaf, fi); 9347ffbb598SFilipe Manana 9350b246afaSJeff Mahoney em->bdev = fs_info->fs_devices->latest_bdev; 9367ffbb598SFilipe Manana btrfs_item_key_to_cpu(leaf, &key, slot); 9377ffbb598SFilipe Manana extent_start = key.offset; 9387ffbb598SFilipe Manana 9397ffbb598SFilipe Manana if (type == BTRFS_FILE_EXTENT_REG || 9407ffbb598SFilipe Manana type == BTRFS_FILE_EXTENT_PREALLOC) { 9417ffbb598SFilipe Manana extent_end = extent_start + 9427ffbb598SFilipe Manana btrfs_file_extent_num_bytes(leaf, fi); 9437ffbb598SFilipe Manana } else if (type == BTRFS_FILE_EXTENT_INLINE) { 9447ffbb598SFilipe Manana size_t size; 9457ffbb598SFilipe Manana size = btrfs_file_extent_inline_len(leaf, slot, fi); 946da17066cSJeff Mahoney extent_end = ALIGN(extent_start + size, 9470b246afaSJeff Mahoney fs_info->sectorsize); 9487ffbb598SFilipe Manana } 9497ffbb598SFilipe Manana 9507ffbb598SFilipe Manana em->ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi); 9517ffbb598SFilipe Manana if (type == BTRFS_FILE_EXTENT_REG || 9527ffbb598SFilipe Manana type == BTRFS_FILE_EXTENT_PREALLOC) { 9537ffbb598SFilipe Manana em->start = extent_start; 9547ffbb598SFilipe Manana em->len = extent_end - extent_start; 9557ffbb598SFilipe Manana em->orig_start = extent_start - 9567ffbb598SFilipe Manana btrfs_file_extent_offset(leaf, fi); 9577ffbb598SFilipe Manana em->orig_block_len = btrfs_file_extent_disk_num_bytes(leaf, fi); 9587ffbb598SFilipe Manana bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); 9597ffbb598SFilipe Manana if (bytenr == 0) { 9607ffbb598SFilipe Manana em->block_start = EXTENT_MAP_HOLE; 9617ffbb598SFilipe Manana return; 9627ffbb598SFilipe Manana } 9637ffbb598SFilipe Manana if (compress_type != BTRFS_COMPRESS_NONE) { 9647ffbb598SFilipe Manana set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); 9657ffbb598SFilipe Manana em->compress_type = compress_type; 9667ffbb598SFilipe Manana em->block_start = bytenr; 9677ffbb598SFilipe Manana em->block_len = em->orig_block_len; 9687ffbb598SFilipe Manana } else { 9697ffbb598SFilipe Manana bytenr += btrfs_file_extent_offset(leaf, fi); 9707ffbb598SFilipe Manana em->block_start = bytenr; 9717ffbb598SFilipe Manana em->block_len = em->len; 9727ffbb598SFilipe Manana if (type == BTRFS_FILE_EXTENT_PREALLOC) 9737ffbb598SFilipe Manana set_bit(EXTENT_FLAG_PREALLOC, &em->flags); 9747ffbb598SFilipe Manana } 9757ffbb598SFilipe Manana } else if (type == BTRFS_FILE_EXTENT_INLINE) { 9767ffbb598SFilipe Manana em->block_start = EXTENT_MAP_INLINE; 9777ffbb598SFilipe Manana em->start = extent_start; 9787ffbb598SFilipe Manana em->len = extent_end - extent_start; 9797ffbb598SFilipe Manana /* 9807ffbb598SFilipe Manana * Initialize orig_start and block_len with the same values 9817ffbb598SFilipe Manana * as in inode.c:btrfs_get_extent(). 9827ffbb598SFilipe Manana */ 9837ffbb598SFilipe Manana em->orig_start = EXTENT_MAP_HOLE; 9847ffbb598SFilipe Manana em->block_len = (u64)-1; 9857ffbb598SFilipe Manana if (!new_inline && compress_type != BTRFS_COMPRESS_NONE) { 9867ffbb598SFilipe Manana set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); 9877ffbb598SFilipe Manana em->compress_type = compress_type; 9887ffbb598SFilipe Manana } 9897ffbb598SFilipe Manana } else { 9900b246afaSJeff Mahoney btrfs_err(fs_info, 9919cdc5124SNikolay Borisov "unknown file extent item type %d, inode %llu, offset %llu, " 9929cdc5124SNikolay Borisov "root %llu", type, btrfs_ino(inode), extent_start, 9937ffbb598SFilipe Manana root->root_key.objectid); 9947ffbb598SFilipe Manana } 9957ffbb598SFilipe Manana } 996