From patchwork Mon May 7 08:42:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: robbieko X-Patchwork-Id: 10383491 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 452C360353 for ; Mon, 7 May 2018 08:42:33 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 380C928A96 for ; Mon, 7 May 2018 08:42:33 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2CE2028B3E; Mon, 7 May 2018 08:42:33 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8D22528A96 for ; Mon, 7 May 2018 08:42:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752395AbeEGIma (ORCPT ); Mon, 7 May 2018 04:42:30 -0400 Received: from synology.com ([59.124.61.242]:45744 "EHLO synology.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752313AbeEGImV (ORCPT ); Mon, 7 May 2018 04:42:21 -0400 Received: from localhost.localdomain (unknown [10.13.20.241]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) by synology.com (Postfix) with ESMTPSA id 888811E11197B; Mon, 7 May 2018 16:42:20 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=synology.com; s=123; t=1525682540; bh=L+a6bFP2RqCUy/uWJ3wSmoc84kRu2eI9wHhVU9xsIsk=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=iw9iUjHhqoeIcNOcuE6hFqRb4+uevD4ETc22BBra+o9aTnmGA2SdQCukbS8+GGdT4 a0zy+mvDa94Z4plVRAkXRUTjSBRLAipkdm7OhU8oUcuKFvsy35UzGxvmOm40+396cC V4SMgnjyRqud6XWDep++ATg5mYpgMcYehAhgWhsg= From: robbieko To: linux-btrfs@vger.kernel.org Cc: Robbie Ko Subject: [PATCH v2 2/2] Btrfs: fix fiemap extent SHARED flag error with range clone. Date: Mon, 7 May 2018 16:42:05 +0800 Message-Id: <1525682525-1424-3-git-send-email-robbieko@synology.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1525682525-1424-1-git-send-email-robbieko@synology.com> References: <1525682525-1424-1-git-send-email-robbieko@synology.com> X-Synology-MCP-Status: no X-Synology-Spam-Flag: no X-Synology-Spam-Status: score=0, required 5, WHITELIST_FROM_ADDRESS 0 X-Synology-Virus-Status: no Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Robbie Ko [BUG] Range clone can cause fiemap to return error result. Like: # mount /dev/vdb5 /mnt/btrfs # dd if=/dev/zero bs=16K count=2 oflag=dsync of=/mnt/btrfs/file # btrfs-debugfs -f file (276 0): ram 16384 disk 2171609088 disk_size 16384 (276 16384): ram 16384 disk 2171625472 disk_size 16384 (276 32768): ram 16384 disk 2171641856 disk_size 16384 (276 49152): ram 16384 disk 2171658240 disk_size 16384 file: file extents 4 disk size 65536 logical size 65536 ratio 1.00 # xfs_io -c "fiemap -v" /mnt/btrfs/file /mnt/btrfs/file: EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS 0: [0..63]: 4241424..4241487 64 0x1 # cloner -s $((16*1024)) /mnt/btrfs/file /mnt/btrfs/file_clone # xfs_io -c "fiemap -v" /mnt/btrfs/file /mnt/btrfs/file: EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS 0: [0..63]: 4241424..4241487 64 0x1 If we clone second file extent, we will get error FLAGS, SHARED bit is not set. [REASON] Btrfs only checks if the first extent in extent map is shared, but in fact it can correspond to many extents and each one has the potential to be cloned so we need to examine each one individually. [FIX] Here we will check each extent with extent map range, if one of them is shared, extent map is shared. [PATCH RESULT] # xfs_io -c "fiemap -v" /mnt/btrfs/file /mnt/btrfs/file: EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS 0: [0..63]: 4241424..4241487 64 0x2001 Signed-off-by: Robbie Ko --- V2: fix comments and coding style. fs/btrfs/extent_io.c | 147 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 132 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 066b6df..b2267dd 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4394,8 +4394,8 @@ static int emit_fiemap_extent(struct fiemap_extent_info *fieinfo, */ if (cache->offset + cache->len == offset && cache->phys + cache->len == phys && - (cache->flags & ~FIEMAP_EXTENT_LAST) == - (flags & ~FIEMAP_EXTENT_LAST)) { + (cache->flags & ~(FIEMAP_EXTENT_LAST|FIEMAP_EXTENT_SHARED)) == + (flags & ~(FIEMAP_EXTENT_LAST|FIEMAP_EXTENT_SHARED))) { cache->len += len; cache->flags |= flags; goto try_submit_last; @@ -4450,6 +4450,134 @@ static int emit_last_fiemap_cache(struct btrfs_fs_info *fs_info, return ret; } +/* + * Helper to check the file range is shared. + * + * Fiemap extent will be combined with many extents, so we need to examine + * each extent, and if shared, the results are shared. + * + * Return: 0 if file range is not shared, 1 if it is shared, < 0 on error. + */ +static int extent_map_check_shared(struct inode *inode, u64 start, u64 end) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); + struct btrfs_root *root = BTRFS_I(inode)->root; + int ret = 0; + struct extent_buffer *leaf; + struct btrfs_path *path; + struct btrfs_file_extent_item *fi; + struct btrfs_key found_key; + int check_prev = 1; + int extent_type; + int shared = 0; + u64 cur_offset; + u64 extent_end; + u64 ino = btrfs_ino(BTRFS_I(inode)); + u64 disk_bytenr; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + cur_offset = start; + while (1) { + ret = btrfs_lookup_file_extent(NULL, root, path, ino, + cur_offset, 0); + if (ret < 0) + goto error; + if (ret > 0 && path->slots[0] > 0 && check_prev) { + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &found_key, + path->slots[0] - 1); + if (found_key.objectid == ino && + found_key.type == BTRFS_EXTENT_DATA_KEY) + path->slots[0]--; + } + check_prev = 0; +next_slot: + leaf = path->nodes[0]; + if (path->slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, path); + if (ret < 0) + goto error; + if (ret > 0) + break; + leaf = path->nodes[0]; + } + + disk_bytenr = 0; + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); + + if (found_key.objectid > ino) + break; + if (WARN_ON_ONCE(found_key.objectid < ino) || + found_key.type < BTRFS_EXTENT_DATA_KEY) { + path->slots[0]++; + goto next_slot; + } + if (found_key.type > BTRFS_EXTENT_DATA_KEY || + found_key.offset > end) + break; + + fi = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_file_extent_item); + extent_type = btrfs_file_extent_type(leaf, fi); + + if (extent_type == BTRFS_FILE_EXTENT_REG || + extent_type == BTRFS_FILE_EXTENT_PREALLOC) { + disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); + extent_end = found_key.offset + + btrfs_file_extent_num_bytes(leaf, fi); + if (extent_end <= start) { + path->slots[0]++; + goto next_slot; + } + if (disk_bytenr == 0) { + path->slots[0]++; + goto next_slot; + } + + btrfs_release_path(path); + + /* + * As btrfs supports shared space, this information + * can be exported to userspace tools via + * flag FIEMAP_EXTENT_SHARED. If fi_extents_max == 0 + * then we're just getting a count and we can skip the + * lookup stuff. + */ + ret = btrfs_check_shared(root, + ino, disk_bytenr); + if (ret < 0) + goto error; + if (ret) + shared = 1; + ret = 0; + if (shared) + break; + } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) { + extent_end = found_key.offset + + btrfs_file_extent_inline_len(leaf, + path->slots[0], fi); + extent_end = ALIGN(extent_end, fs_info->sectorsize); + path->slots[0]++; + goto next_slot; + } else { + WARN_ON(1); + ret = -EIO; + goto error; + } + cur_offset = extent_end; + if (cur_offset > end) + break; + } + + ret = 0; +error: + btrfs_free_path(path); + return !ret ? shared : ret; +} + int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len, get_extent_t *get_extent) { @@ -4587,19 +4715,8 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, flags |= (FIEMAP_EXTENT_DELALLOC | FIEMAP_EXTENT_UNKNOWN); } else if (fieinfo->fi_extents_max) { - u64 bytenr = em->block_start - - (em->start - em->orig_start); - - /* - * As btrfs supports shared space, this information - * can be exported to userspace tools via - * flag FIEMAP_EXTENT_SHARED. If fi_extents_max == 0 - * then we're just getting a count and we can skip the - * lookup stuff. - */ - ret = btrfs_check_shared(root, - btrfs_ino(BTRFS_I(inode)), - bytenr); + ret = extent_map_check_shared(inode, em->start, + extent_map_end(em) - 1); if (ret < 0) goto out_free; if (ret)