From patchwork Tue Nov 6 06:41:10 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669695 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1410E1751 for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F1DD429DB8 for ; Tue, 6 Nov 2018 06:41:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E5F7C29E19; Tue, 6 Nov 2018 06:41:43 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 366D429DB8 for ; Tue, 6 Nov 2018 06:41:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387518AbeKFQFR (ORCPT ); Tue, 6 Nov 2018 11:05:17 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:32866 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387399AbeKFQFR (ORCPT ); Tue, 6 Nov 2018 11:05:17 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417653" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:32 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id 0EABF4B714AA; Tue, 6 Nov 2018 14:41:31 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:34 +0800 From: Lu Fengqi To: CC: Wang Xiaoguang , Qu Wenruo Subject: [PATCH v15.1 01/13] btrfs: dedupe: Introduce dedupe framework and its header Date: Tue, 6 Nov 2018 14:41:10 +0800 Message-ID: <20181106064122.6154-2-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: 0EABF4B714AA.AF835 X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Wang Xiaoguang Introduce the header for btrfs in-band(write time) de-duplication framework and needed header. The new de-duplication framework is going to support 2 different dedupe methods and 1 dedupe hash. Signed-off-by: Qu Wenruo Signed-off-by: Wang Xiaoguang Signed-off-by: Lu Fengqi Reviewed-by: Timofey Titovets --- fs/btrfs/ctree.h | 7 ++ fs/btrfs/dedupe.h | 128 ++++++++++++++++++++++++++++++++++++- fs/btrfs/disk-io.c | 1 + include/uapi/linux/btrfs.h | 34 ++++++++++ 4 files changed, 168 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 80953528572d..910050d904ef 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1118,6 +1118,13 @@ struct btrfs_fs_info { spinlock_t ref_verify_lock; struct rb_root block_tree; #endif + + /* + * Inband de-duplication related structures + */ + unsigned long dedupe_enabled:1; + struct btrfs_dedupe_info *dedupe_info; + struct mutex dedupe_ioctl_lock; }; static inline struct btrfs_fs_info *btrfs_sb(struct super_block *sb) diff --git a/fs/btrfs/dedupe.h b/fs/btrfs/dedupe.h index 90281a7a35a8..222ce7b4d827 100644 --- a/fs/btrfs/dedupe.h +++ b/fs/btrfs/dedupe.h @@ -6,7 +6,131 @@ #ifndef BTRFS_DEDUPE_H #define BTRFS_DEDUPE_H -/* later in-band dedupe will expand this struct */ -struct btrfs_dedupe_hash; +#include +/* 32 bytes for SHA256 */ +static const int btrfs_hash_sizes[] = { 32 }; + +/* + * For caller outside of dedupe.c + * + * Different dedupe backends should have their own hash structure + */ +struct btrfs_dedupe_hash { + u64 bytenr; + u32 num_bytes; + + /* last field is a variable length array of dedupe hash */ + u8 hash[]; +}; + +struct btrfs_dedupe_info { + /* dedupe blocksize */ + u64 blocksize; + u16 backend; + u16 hash_algo; + + struct crypto_shash *dedupe_driver; + + /* + * Use mutex to portect both backends + * Even for in-memory backends, the rb-tree can be quite large, + * so mutex is better for such use case. + */ + struct mutex lock; + + /* following members are only used in in-memory backend */ + struct rb_root hash_root; + struct rb_root bytenr_root; + struct list_head lru_list; + u64 limit_nr; + u64 current_nr; +}; + +static inline int btrfs_dedupe_hash_hit(struct btrfs_dedupe_hash *hash) +{ + return (hash && hash->bytenr); +} + +/* + * Initial inband dedupe info + * Called at dedupe enable time. + * + * Return 0 for success + * Return <0 for any error + * (from unsupported param to tree creation error for some backends) + */ +int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs); + +/* + * Disable dedupe and invalidate all its dedupe data. + * Called at dedupe disable time. + * + * Return 0 for success + * Return <0 for any error + * (tree operation error for some backends) + */ +int btrfs_dedupe_disable(struct btrfs_fs_info *fs_info); + +/* + * Get current dedupe status. + * Return 0 for success + * No possible error yet + */ +void btrfs_dedupe_status(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs); + +/* + * Calculate hash for dedupe. + * Caller must ensure [start, start + dedupe_bs) has valid data. + * + * Return 0 for success + * Return <0 for any error + * (error from hash codes) + */ +int btrfs_dedupe_calc_hash(struct btrfs_fs_info *fs_info, + struct inode *inode, u64 start, + struct btrfs_dedupe_hash *hash); + +/* + * Search for duplicated extents by calculated hash + * Caller must call btrfs_dedupe_calc_hash() first to get the hash. + * + * @inode: the inode for we are writing + * @file_pos: offset inside the inode + * As we will increase extent ref immediately after a hash match, + * we need @file_pos and @inode in this case. + * + * Return > 0 for a hash match, and the extent ref will be + * *INCREASED*, and hash->bytenr/num_bytes will record the existing + * extent data. + * Return 0 for a hash miss. Nothing is done + * Return <0 for any error + * (tree operation error for some backends) + */ +int btrfs_dedupe_search(struct btrfs_fs_info *fs_info, + struct inode *inode, u64 file_pos, + struct btrfs_dedupe_hash *hash); + +/* + * Add a dedupe hash into dedupe info + * Return 0 for success + * Return <0 for any error + * (tree operation error for some backends) + */ +int btrfs_dedupe_add(struct btrfs_fs_info *fs_info, + struct btrfs_dedupe_hash *hash); + +/* + * Remove a dedupe hash from dedupe info + * Return 0 for success + * Return <0 for any error + * (tree operation error for some backends) + * + * NOTE: if hash deletion error is not handled well, it will lead + * to corrupted fs, as later dedupe write can points to non-exist or even + * wrong extent. + */ +int btrfs_dedupe_del(struct btrfs_fs_info *fs_info, u64 bytenr); #endif diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index b0ab41da91d1..d1fa9d90cc8f 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2678,6 +2678,7 @@ int open_ctree(struct super_block *sb, mutex_init(&fs_info->reloc_mutex); mutex_init(&fs_info->delalloc_root_mutex); mutex_init(&fs_info->cleaner_delayed_iput_mutex); + mutex_init(&fs_info->dedupe_ioctl_lock); seqlock_init(&fs_info->profiles_lock); INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots); diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 5ca1d21fc4a7..9cd15d2a40aa 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -20,6 +20,7 @@ #ifndef _UAPI_LINUX_BTRFS_H #define _UAPI_LINUX_BTRFS_H #include +#include #include #define BTRFS_IOCTL_MAGIC 0x94 @@ -667,6 +668,39 @@ struct btrfs_ioctl_get_dev_stats { __u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k */ }; +/* In-band dedupe related */ +#define BTRFS_DEDUPE_BACKEND_INMEMORY 0 +#define BTRFS_DEDUPE_BACKEND_ONDISK 1 + +/* Only support inmemory yet, so count is still only 1 */ +#define BTRFS_DEDUPE_BACKEND_COUNT 1 + +/* Dedup block size limit and default value */ +#define BTRFS_DEDUPE_BLOCKSIZE_MAX SZ_8M +#define BTRFS_DEDUPE_BLOCKSIZE_MIN SZ_16K +#define BTRFS_DEDUPE_BLOCKSIZE_DEFAULT SZ_128K + +/* Hash algorithm, only support SHA256 yet */ +#define BTRFS_DEDUPE_HASH_SHA256 0 + +/* + * This structure is used for dedupe enable/disable/configure + * and status ioctl. + * Reserved range should be set to 0xff. + */ +struct btrfs_ioctl_dedupe_args { + __u16 cmd; /* In: command */ + __u64 blocksize; /* In/Out: blocksize */ + __u64 limit_nr; /* In/Out: limit nr for inmem backend */ + __u64 limit_mem; /* In/Out: limit mem for inmem backend */ + __u64 current_nr; /* Out: current hash nr */ + __u16 backend; /* In/Out: current backend */ + __u16 hash_algo; /* In/Out: hash algorithm */ + u8 status; /* Out: enabled or disabled */ + u8 flags; /* In: special flags for ioctl */ + u8 __unused[472]; /* Pad to 512 bytes */ +}; + #define BTRFS_QUOTA_CTL_ENABLE 1 #define BTRFS_QUOTA_CTL_DISABLE 2 #define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3 From patchwork Tue Nov 6 06:41:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669697 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5CE2E18FD for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 45DBB29DB8 for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 39BF629EAC; Tue, 6 Nov 2018 06:41:44 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 76A1A29C8A for ; Tue, 6 Nov 2018 06:41:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387552AbeKFQFT (ORCPT ); Tue, 6 Nov 2018 11:05:19 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:32866 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387399AbeKFQFT (ORCPT ); Tue, 6 Nov 2018 11:05:19 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417654" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:32 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id 9B32B4B714AF; Tue, 6 Nov 2018 14:41:31 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:35 +0800 From: Lu Fengqi To: CC: Wang Xiaoguang , Qu Wenruo Subject: [PATCH v15.1 02/13] btrfs: dedupe: Introduce function to initialize dedupe info Date: Tue, 6 Nov 2018 14:41:11 +0800 Message-ID: <20181106064122.6154-3-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: 9B32B4B714AF.A9ECA X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Wang Xiaoguang Add generic function to initialize dedupe info. Signed-off-by: Qu Wenruo Signed-off-by: Wang Xiaoguang Reviewed-by: Josef Bacik Signed-off-by: Lu Fengqi Reviewed-by: Timofey Titovets --- fs/btrfs/Makefile | 2 +- fs/btrfs/dedupe.c | 169 +++++++++++++++++++++++++++++++++++++ fs/btrfs/dedupe.h | 12 +++ include/uapi/linux/btrfs.h | 3 + 4 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 fs/btrfs/dedupe.c diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index ca693dd554e9..78fdc87dba39 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -10,7 +10,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ export.o tree-log.o free-space-cache.o zlib.o lzo.o zstd.o \ compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \ reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \ - uuid-tree.o props.o free-space-tree.o tree-checker.o + uuid-tree.o props.o free-space-tree.o tree-checker.o dedupe.o btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c new file mode 100644 index 000000000000..06523162753d --- /dev/null +++ b/fs/btrfs/dedupe.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Fujitsu. All rights reserved. + */ + +#include "ctree.h" +#include "dedupe.h" +#include "btrfs_inode.h" +#include "delayed-ref.h" + +struct inmem_hash { + struct rb_node hash_node; + struct rb_node bytenr_node; + struct list_head lru_list; + + u64 bytenr; + u32 num_bytes; + + u8 hash[]; +}; + +static struct btrfs_dedupe_info * +init_dedupe_info(struct btrfs_ioctl_dedupe_args *dargs) +{ + struct btrfs_dedupe_info *dedupe_info; + + dedupe_info = kzalloc(sizeof(*dedupe_info), GFP_NOFS); + if (!dedupe_info) + return ERR_PTR(-ENOMEM); + + dedupe_info->hash_algo = dargs->hash_algo; + dedupe_info->backend = dargs->backend; + dedupe_info->blocksize = dargs->blocksize; + dedupe_info->limit_nr = dargs->limit_nr; + + /* only support SHA256 yet */ + dedupe_info->dedupe_driver = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(dedupe_info->dedupe_driver)) { + kfree(dedupe_info); + return ERR_CAST(dedupe_info->dedupe_driver); + } + + dedupe_info->hash_root = RB_ROOT; + dedupe_info->bytenr_root = RB_ROOT; + dedupe_info->current_nr = 0; + INIT_LIST_HEAD(&dedupe_info->lru_list); + mutex_init(&dedupe_info->lock); + + return dedupe_info; +} + +/* + * Helper to check if parameters are valid. + * The first invalid field will be set to (-1), to info user which parameter + * is invalid. + * Except dargs->limit_nr or dargs->limit_mem, in that case, 0 will returned + * to info user, since user can specify any value to limit, except 0. + */ +static int check_dedupe_parameter(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs) +{ + u64 blocksize = dargs->blocksize; + u64 limit_nr = dargs->limit_nr; + u64 limit_mem = dargs->limit_mem; + u16 hash_algo = dargs->hash_algo; + u8 backend = dargs->backend; + + /* + * Set all reserved fields to -1, allow user to detect + * unsupported optional parameters. + */ + memset(dargs->__unused, -1, sizeof(dargs->__unused)); + if (blocksize > BTRFS_DEDUPE_BLOCKSIZE_MAX || + blocksize < BTRFS_DEDUPE_BLOCKSIZE_MIN || + blocksize < fs_info->sectorsize || + !is_power_of_2(blocksize) || + blocksize < PAGE_SIZE) { + dargs->blocksize = (u64)-1; + return -EINVAL; + } + if (hash_algo >= ARRAY_SIZE(btrfs_hash_sizes)) { + dargs->hash_algo = (u16)-1; + return -EINVAL; + } + if (backend >= BTRFS_DEDUPE_BACKEND_COUNT) { + dargs->backend = (u8)-1; + return -EINVAL; + } + + /* Backend specific check */ + if (backend == BTRFS_DEDUPE_BACKEND_INMEMORY) { + /* only one limit is accepted for enable*/ + if (dargs->limit_nr && dargs->limit_mem) { + dargs->limit_nr = 0; + dargs->limit_mem = 0; + return -EINVAL; + } + + if (!limit_nr && !limit_mem) + dargs->limit_nr = BTRFS_DEDUPE_LIMIT_NR_DEFAULT; + else { + u64 tmp = (u64)-1; + + if (limit_mem) { + tmp = div_u64(limit_mem, + (sizeof(struct inmem_hash)) + + btrfs_hash_sizes[hash_algo]); + /* Too small limit_mem to fill a hash item */ + if (!tmp) { + dargs->limit_mem = 0; + dargs->limit_nr = 0; + return -EINVAL; + } + } + if (!limit_nr) + limit_nr = (u64)-1; + + dargs->limit_nr = min(tmp, limit_nr); + } + } + if (backend == BTRFS_DEDUPE_BACKEND_ONDISK) + dargs->limit_nr = 0; + + return 0; +} + +int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs) +{ + struct btrfs_dedupe_info *dedupe_info; + int ret = 0; + + ret = check_dedupe_parameter(fs_info, dargs); + if (ret < 0) + return ret; + + dedupe_info = fs_info->dedupe_info; + if (dedupe_info) { + /* Check if we are re-enable for different dedupe config */ + if (dedupe_info->blocksize != dargs->blocksize || + dedupe_info->hash_algo != dargs->hash_algo || + dedupe_info->backend != dargs->backend) { + btrfs_dedupe_disable(fs_info); + goto enable; + } + + /* On-fly limit change is OK */ + mutex_lock(&dedupe_info->lock); + fs_info->dedupe_info->limit_nr = dargs->limit_nr; + mutex_unlock(&dedupe_info->lock); + return 0; + } + +enable: + dedupe_info = init_dedupe_info(dargs); + if (IS_ERR(dedupe_info)) + return PTR_ERR(dedupe_info); + fs_info->dedupe_info = dedupe_info; + /* We must ensure dedupe_bs is modified after dedupe_info */ + smp_wmb(); + fs_info->dedupe_enabled = 1; + return ret; +} + +int btrfs_dedupe_disable(struct btrfs_fs_info *fs_info) +{ + /* Place holder for bisect, will be implemented in later patches */ + return 0; +} diff --git a/fs/btrfs/dedupe.h b/fs/btrfs/dedupe.h index 222ce7b4d827..87f5b7ce7766 100644 --- a/fs/btrfs/dedupe.h +++ b/fs/btrfs/dedupe.h @@ -52,6 +52,18 @@ static inline int btrfs_dedupe_hash_hit(struct btrfs_dedupe_hash *hash) return (hash && hash->bytenr); } +static inline int btrfs_dedupe_hash_size(u16 algo) +{ + if (WARN_ON(algo >= ARRAY_SIZE(btrfs_hash_sizes))) + return -EINVAL; + return sizeof(struct btrfs_dedupe_hash) + btrfs_hash_sizes[algo]; +} + +static inline struct btrfs_dedupe_hash *btrfs_dedupe_alloc_hash(u16 algo) +{ + return kzalloc(btrfs_dedupe_hash_size(algo), GFP_NOFS); +} + /* * Initial inband dedupe info * Called at dedupe enable time. diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 9cd15d2a40aa..ba879ac931f2 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -683,6 +683,9 @@ struct btrfs_ioctl_get_dev_stats { /* Hash algorithm, only support SHA256 yet */ #define BTRFS_DEDUPE_HASH_SHA256 0 +/* Default dedupe limit on number of hash */ +#define BTRFS_DEDUPE_LIMIT_NR_DEFAULT (32 * 1024) + /* * This structure is used for dedupe enable/disable/configure * and status ioctl. From patchwork Tue Nov 6 06:41:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669709 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 30A7017D4 for ; Tue, 6 Nov 2018 06:41:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1DF1D29DCF for ; Tue, 6 Nov 2018 06:41:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 12AAF29E18; Tue, 6 Nov 2018 06:41:46 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 E3DE529DCF for ; Tue, 6 Nov 2018 06:41:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387444AbeKFQFP (ORCPT ); Tue, 6 Nov 2018 11:05:15 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:32866 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387399AbeKFQFP (ORCPT ); Tue, 6 Nov 2018 11:05:15 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417651" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:32 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id 383CF4B714D9; Tue, 6 Nov 2018 14:41:32 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:35 +0800 From: Lu Fengqi To: CC: Wang Xiaoguang , Qu Wenruo Subject: [PATCH v15.1 03/13] btrfs: dedupe: Introduce function to add hash into in-memory tree Date: Tue, 6 Nov 2018 14:41:12 +0800 Message-ID: <20181106064122.6154-4-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: 383CF4B714D9.A93F0 X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Wang Xiaoguang Introduce static function inmem_add() to add hash into in-memory tree. And now we can implement the btrfs_dedupe_add() interface. Signed-off-by: Qu Wenruo Signed-off-by: Wang Xiaoguang Reviewed-by: Josef Bacik Signed-off-by: Lu Fengqi Reviewed-by: Timofey Titovets --- fs/btrfs/dedupe.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c index 06523162753d..784bb3a8a5ab 100644 --- a/fs/btrfs/dedupe.c +++ b/fs/btrfs/dedupe.c @@ -19,6 +19,14 @@ struct inmem_hash { u8 hash[]; }; +static inline struct inmem_hash *inmem_alloc_hash(u16 algo) +{ + if (WARN_ON(algo >= ARRAY_SIZE(btrfs_hash_sizes))) + return NULL; + return kzalloc(sizeof(struct inmem_hash) + btrfs_hash_sizes[algo], + GFP_NOFS); +} + static struct btrfs_dedupe_info * init_dedupe_info(struct btrfs_ioctl_dedupe_args *dargs) { @@ -167,3 +175,145 @@ int btrfs_dedupe_disable(struct btrfs_fs_info *fs_info) /* Place holder for bisect, will be implemented in later patches */ return 0; } + +static int inmem_insert_hash(struct rb_root *root, + struct inmem_hash *hash, int hash_len) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct inmem_hash *entry = NULL; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct inmem_hash, hash_node); + if (memcmp(hash->hash, entry->hash, hash_len) < 0) + p = &(*p)->rb_left; + else if (memcmp(hash->hash, entry->hash, hash_len) > 0) + p = &(*p)->rb_right; + else + return 1; + } + rb_link_node(&hash->hash_node, parent, p); + rb_insert_color(&hash->hash_node, root); + return 0; +} + +static int inmem_insert_bytenr(struct rb_root *root, + struct inmem_hash *hash) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct inmem_hash *entry = NULL; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct inmem_hash, bytenr_node); + if (hash->bytenr < entry->bytenr) + p = &(*p)->rb_left; + else if (hash->bytenr > entry->bytenr) + p = &(*p)->rb_right; + else + return 1; + } + rb_link_node(&hash->bytenr_node, parent, p); + rb_insert_color(&hash->bytenr_node, root); + return 0; +} + +static void __inmem_del(struct btrfs_dedupe_info *dedupe_info, + struct inmem_hash *hash) +{ + list_del(&hash->lru_list); + rb_erase(&hash->hash_node, &dedupe_info->hash_root); + rb_erase(&hash->bytenr_node, &dedupe_info->bytenr_root); + + if (!WARN_ON(dedupe_info->current_nr == 0)) + dedupe_info->current_nr--; + + kfree(hash); +} + +/* + * Insert a hash into in-memory dedupe tree + * Will remove exceeding last recent use hash. + * + * If the hash mathced with existing one, we won't insert it, to + * save memory + */ +static int inmem_add(struct btrfs_dedupe_info *dedupe_info, + struct btrfs_dedupe_hash *hash) +{ + int ret = 0; + u16 algo = dedupe_info->hash_algo; + struct inmem_hash *ihash; + + ihash = inmem_alloc_hash(algo); + + if (!ihash) + return -ENOMEM; + + /* Copy the data out */ + ihash->bytenr = hash->bytenr; + ihash->num_bytes = hash->num_bytes; + memcpy(ihash->hash, hash->hash, btrfs_hash_sizes[algo]); + + mutex_lock(&dedupe_info->lock); + + ret = inmem_insert_bytenr(&dedupe_info->bytenr_root, ihash); + if (ret > 0) { + kfree(ihash); + ret = 0; + goto out; + } + + ret = inmem_insert_hash(&dedupe_info->hash_root, ihash, + btrfs_hash_sizes[algo]); + if (ret > 0) { + /* + * We only keep one hash in tree to save memory, so if + * hash conflicts, free the one to insert. + */ + rb_erase(&ihash->bytenr_node, &dedupe_info->bytenr_root); + kfree(ihash); + ret = 0; + goto out; + } + + list_add(&ihash->lru_list, &dedupe_info->lru_list); + dedupe_info->current_nr++; + + /* Remove the last dedupe hash if we exceed limit */ + while (dedupe_info->current_nr > dedupe_info->limit_nr) { + struct inmem_hash *last; + + last = list_entry(dedupe_info->lru_list.prev, + struct inmem_hash, lru_list); + __inmem_del(dedupe_info, last); + } +out: + mutex_unlock(&dedupe_info->lock); + return 0; +} + +int btrfs_dedupe_add(struct btrfs_fs_info *fs_info, + struct btrfs_dedupe_hash *hash) +{ + struct btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info; + + if (!fs_info->dedupe_enabled || !hash) + return 0; + + if (WARN_ON(dedupe_info == NULL)) + return -EINVAL; + + if (WARN_ON(!btrfs_dedupe_hash_hit(hash))) + return -EINVAL; + + /* ignore old hash */ + if (dedupe_info->blocksize != hash->num_bytes) + return 0; + + if (dedupe_info->backend == BTRFS_DEDUPE_BACKEND_INMEMORY) + return inmem_add(dedupe_info, hash); + return -EINVAL; +} From patchwork Tue Nov 6 06:41:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669711 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BE70B15A6 for ; Tue, 6 Nov 2018 06:41:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AD74729DCF for ; Tue, 6 Nov 2018 06:41:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A286329E14; Tue, 6 Nov 2018 06:41:46 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 5674229E29 for ; Tue, 6 Nov 2018 06:41:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387532AbeKFQFS (ORCPT ); Tue, 6 Nov 2018 11:05:18 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:31748 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387505AbeKFQFS (ORCPT ); Tue, 6 Nov 2018 11:05:18 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417652" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:32 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id CA1D44B714DF; Tue, 6 Nov 2018 14:41:32 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:36 +0800 From: Lu Fengqi To: CC: Wang Xiaoguang , Mark Fasheh , Qu Wenruo Subject: [PATCH v15.1 04/13] btrfs: dedupe: Introduce function to remove hash from in-memory tree Date: Tue, 6 Nov 2018 14:41:13 +0800 Message-ID: <20181106064122.6154-5-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: CA1D44B714DF.ABF51 X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Wang Xiaoguang Introduce static function inmem_del() to remove hash from in-memory dedupe tree. And implement btrfs_dedupe_del() and btrfs_dedup_disable() interfaces. Also for btrfs_dedupe_disable(), add new functions to wait existing writer and block incoming writers to eliminate all possible race. Cc: Mark Fasheh Signed-off-by: Qu Wenruo Signed-off-by: Wang Xiaoguang Signed-off-by: Lu Fengqi --- fs/btrfs/dedupe.c | 131 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c index 784bb3a8a5ab..951fefd19fde 100644 --- a/fs/btrfs/dedupe.c +++ b/fs/btrfs/dedupe.c @@ -170,12 +170,6 @@ int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info, return ret; } -int btrfs_dedupe_disable(struct btrfs_fs_info *fs_info) -{ - /* Place holder for bisect, will be implemented in later patches */ - return 0; -} - static int inmem_insert_hash(struct rb_root *root, struct inmem_hash *hash, int hash_len) { @@ -317,3 +311,128 @@ int btrfs_dedupe_add(struct btrfs_fs_info *fs_info, return inmem_add(dedupe_info, hash); return -EINVAL; } + +static struct inmem_hash * +inmem_search_bytenr(struct btrfs_dedupe_info *dedupe_info, u64 bytenr) +{ + struct rb_node **p = &dedupe_info->bytenr_root.rb_node; + struct rb_node *parent = NULL; + struct inmem_hash *entry = NULL; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct inmem_hash, bytenr_node); + + if (bytenr < entry->bytenr) + p = &(*p)->rb_left; + else if (bytenr > entry->bytenr) + p = &(*p)->rb_right; + else + return entry; + } + + return NULL; +} + +/* Delete a hash from in-memory dedupe tree */ +static int inmem_del(struct btrfs_dedupe_info *dedupe_info, u64 bytenr) +{ + struct inmem_hash *hash; + + mutex_lock(&dedupe_info->lock); + hash = inmem_search_bytenr(dedupe_info, bytenr); + if (!hash) { + mutex_unlock(&dedupe_info->lock); + return 0; + } + + __inmem_del(dedupe_info, hash); + mutex_unlock(&dedupe_info->lock); + return 0; +} + +/* Remove a dedupe hash from dedupe tree */ +int btrfs_dedupe_del(struct btrfs_fs_info *fs_info, u64 bytenr) +{ + struct btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info; + + if (!fs_info->dedupe_enabled) + return 0; + + if (WARN_ON(dedupe_info == NULL)) + return -EINVAL; + + if (dedupe_info->backend == BTRFS_DEDUPE_BACKEND_INMEMORY) + return inmem_del(dedupe_info, bytenr); + return -EINVAL; +} + +static void inmem_destroy(struct btrfs_dedupe_info *dedupe_info) +{ + struct inmem_hash *entry, *tmp; + + mutex_lock(&dedupe_info->lock); + list_for_each_entry_safe(entry, tmp, &dedupe_info->lru_list, lru_list) + __inmem_del(dedupe_info, entry); + mutex_unlock(&dedupe_info->lock); +} + +/* + * Helper function to wait and block all incoming writers + * + * Use rw_sem introduced for freeze to wait/block writers. + * So during the block time, no new write will happen, so we can + * do something quite safe, espcially helpful for dedupe disable, + * as it affect buffered write. + */ +static void block_all_writers(struct btrfs_fs_info *fs_info) +{ + struct super_block *sb = fs_info->sb; + + percpu_down_write(sb->s_writers.rw_sem + SB_FREEZE_WRITE - 1); + down_write(&sb->s_umount); +} + +static void unblock_all_writers(struct btrfs_fs_info *fs_info) +{ + struct super_block *sb = fs_info->sb; + + up_write(&sb->s_umount); + percpu_up_write(sb->s_writers.rw_sem + SB_FREEZE_WRITE - 1); +} + +int btrfs_dedupe_disable(struct btrfs_fs_info *fs_info) +{ + struct btrfs_dedupe_info *dedupe_info; + int ret; + + dedupe_info = fs_info->dedupe_info; + + if (!dedupe_info) + return 0; + + /* Don't allow disable status change in RO mount */ + if (fs_info->sb->s_flags & MS_RDONLY) + return -EROFS; + + /* + * Wait for all unfinished writers and block further writers. + * Then sync the whole fs so all current write will go through + * dedupe, and all later write won't go through dedupe. + */ + block_all_writers(fs_info); + ret = sync_filesystem(fs_info->sb); + fs_info->dedupe_enabled = 0; + fs_info->dedupe_info = NULL; + unblock_all_writers(fs_info); + if (ret < 0) + return ret; + + /* now we are OK to clean up everything */ + if (dedupe_info->backend == BTRFS_DEDUPE_BACKEND_INMEMORY) + inmem_destroy(dedupe_info); + + crypto_free_shash(dedupe_info->dedupe_driver); + kfree(dedupe_info); + return 0; +} From patchwork Tue Nov 6 06:41:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669701 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 82FCF1932 for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6DCA729DB8 for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6206C29EF2; Tue, 6 Nov 2018 06:41:44 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 9798129DE4 for ; Tue, 6 Nov 2018 06:41:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387556AbeKFQFU (ORCPT ); Tue, 6 Nov 2018 11:05:20 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:31748 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387527AbeKFQFT (ORCPT ); Tue, 6 Nov 2018 11:05:19 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417655" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:32 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id 6644B4B71EA0; Tue, 6 Nov 2018 14:41:33 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:36 +0800 From: Lu Fengqi To: CC: Qu Wenruo Subject: [PATCH v15.1 05/13] btrfs: delayed-ref: Add support for increasing data ref under spinlock Date: Tue, 6 Nov 2018 14:41:14 +0800 Message-ID: <20181106064122.6154-6-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: 6644B4B71EA0.A8E0F X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Qu Wenruo For in-band dedupe, btrfs needs to increase data ref with delayed_ref locked, so add a new function btrfs_add_delayed_data_ref_lock() to increase extent ref with delayed_refs already locked. Export init_delayed_ref_head and init_delayed_ref_common for inband dedupe. Signed-off-by: Qu Wenruo Reviewed-by: Josef Bacik Signed-off-by: Lu Fengqi --- fs/btrfs/delayed-ref.c | 53 +++++++++++++++++++++++++++++------------- fs/btrfs/delayed-ref.h | 15 ++++++++++++ 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 9301b3ad9217..ae8968f10ce0 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -533,7 +533,7 @@ update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, spin_unlock(&existing->lock); } -static void init_delayed_ref_head(struct btrfs_delayed_ref_head *head_ref, +void btrfs_init_delayed_ref_head(struct btrfs_delayed_ref_head *head_ref, struct btrfs_qgroup_extent_record *qrecord, u64 bytenr, u64 num_bytes, u64 ref_root, u64 reserved, int action, bool is_data, @@ -661,7 +661,7 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, } /* - * init_delayed_ref_common - Initialize the structure which represents a + * btrfs_init_delayed_ref_common - Initialize the structure which represents a * modification to a an extent. * * @fs_info: Internal to the mounted filesystem mount structure. @@ -685,7 +685,7 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, * when recording a metadata extent or BTRFS_SHARED_DATA_REF_KEY/ * BTRFS_EXTENT_DATA_REF_KEY when recording data extent */ -static void init_delayed_ref_common(struct btrfs_fs_info *fs_info, +void btrfs_init_delayed_ref_common(struct btrfs_fs_info *fs_info, struct btrfs_delayed_ref_node *ref, u64 bytenr, u64 num_bytes, u64 ref_root, int action, u8 ref_type) @@ -758,14 +758,14 @@ int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans, else ref_type = BTRFS_TREE_BLOCK_REF_KEY; - init_delayed_ref_common(fs_info, &ref->node, bytenr, num_bytes, - ref_root, action, ref_type); + btrfs_init_delayed_ref_common(fs_info, &ref->node, bytenr, num_bytes, + ref_root, action, ref_type); ref->root = ref_root; ref->parent = parent; ref->level = level; - init_delayed_ref_head(head_ref, record, bytenr, num_bytes, - ref_root, 0, action, false, is_system); + btrfs_init_delayed_ref_head(head_ref, record, bytenr, num_bytes, + ref_root, 0, action, false, is_system); head_ref->extent_op = extent_op; delayed_refs = &trans->transaction->delayed_refs; @@ -794,6 +794,29 @@ int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans, return 0; } +/* + * Do real delayed data ref insert. + * Caller must hold delayed_refs->lock and allocation memory + * for dref,head_ref and record. + */ +int btrfs_add_delayed_data_ref_locked(struct btrfs_trans_handle *trans, + struct btrfs_delayed_ref_head *head_ref, + struct btrfs_qgroup_extent_record *qrecord, + struct btrfs_delayed_data_ref *ref, int action, + int *qrecord_inserted_ret, int *old_ref_mod, + int *new_ref_mod) +{ + struct btrfs_delayed_ref_root *delayed_refs; + + head_ref = add_delayed_ref_head(trans, head_ref, qrecord, + action, qrecord_inserted_ret, + old_ref_mod, new_ref_mod); + + delayed_refs = &trans->transaction->delayed_refs; + + return insert_delayed_ref(trans, delayed_refs, head_ref, &ref->node); +} + /* * add a delayed data ref. it's similar to btrfs_add_delayed_tree_ref. */ @@ -820,7 +843,7 @@ int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans, ref_type = BTRFS_SHARED_DATA_REF_KEY; else ref_type = BTRFS_EXTENT_DATA_REF_KEY; - init_delayed_ref_common(fs_info, &ref->node, bytenr, num_bytes, + btrfs_init_delayed_ref_common(fs_info, &ref->node, bytenr, num_bytes, ref_root, action, ref_type); ref->root = ref_root; ref->parent = parent; @@ -845,8 +868,8 @@ int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans, } } - init_delayed_ref_head(head_ref, record, bytenr, num_bytes, ref_root, - reserved, action, true, false); + btrfs_init_delayed_ref_head(head_ref, record, bytenr, num_bytes, + ref_root, reserved, action, true, false); head_ref->extent_op = NULL; delayed_refs = &trans->transaction->delayed_refs; @@ -856,11 +879,9 @@ int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans, * insert both the head node and the new ref without dropping * the spin lock */ - head_ref = add_delayed_ref_head(trans, head_ref, record, - action, &qrecord_inserted, - old_ref_mod, new_ref_mod); - - ret = insert_delayed_ref(trans, delayed_refs, head_ref, &ref->node); + ret = btrfs_add_delayed_data_ref_locked(trans, head_ref, + record, ref, action, &qrecord_inserted, old_ref_mod, + new_ref_mod); spin_unlock(&delayed_refs->lock); trace_add_delayed_data_ref(trans->fs_info, &ref->node, ref, @@ -887,7 +908,7 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, if (!head_ref) return -ENOMEM; - init_delayed_ref_head(head_ref, NULL, bytenr, num_bytes, 0, 0, + btrfs_init_delayed_ref_head(head_ref, NULL, bytenr, num_bytes, 0, 0, BTRFS_UPDATE_DELAYED_HEAD, extent_op->is_data, false); head_ref->extent_op = extent_op; diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index 8e20c5cb5404..208941e3cca0 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -234,11 +234,26 @@ static inline void btrfs_put_delayed_ref_head(struct btrfs_delayed_ref_head *hea kmem_cache_free(btrfs_delayed_ref_head_cachep, head); } +void btrfs_init_delayed_ref_head(struct btrfs_delayed_ref_head *head_ref, + struct btrfs_qgroup_extent_record *qrecord, + u64 bytenr, u64 num_bytes, u64 ref_root, + u64 reserved, int action, bool is_data, + bool is_system); +void btrfs_init_delayed_ref_common(struct btrfs_fs_info *fs_info, + struct btrfs_delayed_ref_node *ref, + u64 bytenr, u64 num_bytes, u64 ref_root, + int action, u8 ref_type); int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, int level, int action, struct btrfs_delayed_extent_op *extent_op, int *old_ref_mod, int *new_ref_mod); +int btrfs_add_delayed_data_ref_locked(struct btrfs_trans_handle *trans, + struct btrfs_delayed_ref_head *head_ref, + struct btrfs_qgroup_extent_record *qrecord, + struct btrfs_delayed_data_ref *ref, int action, + int *qrecord_inserted_ret, int *old_ref_mod, + int *new_ref_mod); int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, From patchwork Tue Nov 6 06:41:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669713 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3BB811751 for ; Tue, 6 Nov 2018 06:41:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2A82429DB8 for ; Tue, 6 Nov 2018 06:41:47 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2907329DDA; Tue, 6 Nov 2018 06:41:47 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 1A27B29DC1 for ; Tue, 6 Nov 2018 06:41:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387589AbeKFQFW (ORCPT ); Tue, 6 Nov 2018 11:05:22 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:44988 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387505AbeKFQFW (ORCPT ); Tue, 6 Nov 2018 11:05:22 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417657" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:32 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id F1E2A4B71EB5; Tue, 6 Nov 2018 14:41:33 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:37 +0800 From: Lu Fengqi To: CC: Wang Xiaoguang , Qu Wenruo Subject: [PATCH v15.1 06/13] btrfs: dedupe: Introduce function to search for an existing hash Date: Tue, 6 Nov 2018 14:41:15 +0800 Message-ID: <20181106064122.6154-7-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: F1E2A4B71EB5.AB98E X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Wang Xiaoguang Introduce static function inmem_search() to handle the job for in-memory hash tree. The trick is, we must ensure the delayed ref head is not being run at the time we search the for the hash. With inmem_search(), we can implement the btrfs_dedupe_search() interface. Signed-off-by: Qu Wenruo Signed-off-by: Wang Xiaoguang Reviewed-by: Josef Bacik Signed-off-by: Lu Fengqi --- fs/btrfs/dedupe.c | 210 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c index 951fefd19fde..03ad41423c01 100644 --- a/fs/btrfs/dedupe.c +++ b/fs/btrfs/dedupe.c @@ -7,6 +7,8 @@ #include "dedupe.h" #include "btrfs_inode.h" #include "delayed-ref.h" +#include "qgroup.h" +#include "transaction.h" struct inmem_hash { struct rb_node hash_node; @@ -242,7 +244,6 @@ static int inmem_add(struct btrfs_dedupe_info *dedupe_info, struct inmem_hash *ihash; ihash = inmem_alloc_hash(algo); - if (!ihash) return -ENOMEM; @@ -436,3 +437,210 @@ int btrfs_dedupe_disable(struct btrfs_fs_info *fs_info) kfree(dedupe_info); return 0; } + +/* + * Caller must ensure the corresponding ref head is not being run. + */ +static struct inmem_hash * +inmem_search_hash(struct btrfs_dedupe_info *dedupe_info, u8 *hash) +{ + struct rb_node **p = &dedupe_info->hash_root.rb_node; + struct rb_node *parent = NULL; + struct inmem_hash *entry = NULL; + u16 hash_algo = dedupe_info->hash_algo; + int hash_len = btrfs_hash_sizes[hash_algo]; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct inmem_hash, hash_node); + + if (memcmp(hash, entry->hash, hash_len) < 0) { + p = &(*p)->rb_left; + } else if (memcmp(hash, entry->hash, hash_len) > 0) { + p = &(*p)->rb_right; + } else { + /* Found, need to re-add it to LRU list head */ + list_del(&entry->lru_list); + list_add(&entry->lru_list, &dedupe_info->lru_list); + return entry; + } + } + return NULL; +} + +static int inmem_search(struct btrfs_dedupe_info *dedupe_info, + struct inode *inode, u64 file_pos, + struct btrfs_dedupe_hash *hash) +{ + int ret; + struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_trans_handle *trans; + struct btrfs_delayed_ref_root *delayed_refs; + struct btrfs_delayed_ref_head *head; + struct btrfs_delayed_ref_head *insert_head; + struct btrfs_delayed_data_ref *insert_dref; + struct btrfs_qgroup_extent_record *insert_qrecord = NULL; + struct inmem_hash *found_hash; + int free_insert = 1; + int qrecord_inserted = 0; + u64 ref_root = root->root_key.objectid; + u64 bytenr; + u32 num_bytes; + + insert_head = kmem_cache_alloc(btrfs_delayed_ref_head_cachep, GFP_NOFS); + if (!insert_head) + return -ENOMEM; + insert_head->extent_op = NULL; + + insert_dref = kmem_cache_alloc(btrfs_delayed_data_ref_cachep, GFP_NOFS); + if (!insert_dref) { + kmem_cache_free(btrfs_delayed_ref_head_cachep, insert_head); + return -ENOMEM; + } + if (test_bit(BTRFS_FS_QUOTA_ENABLED, &root->fs_info->flags) && + is_fstree(ref_root)) { + insert_qrecord = kmalloc(sizeof(*insert_qrecord), GFP_NOFS); + if (!insert_qrecord) { + kmem_cache_free(btrfs_delayed_ref_head_cachep, + insert_head); + kmem_cache_free(btrfs_delayed_data_ref_cachep, + insert_dref); + return -ENOMEM; + } + } + + trans = btrfs_join_transaction(root); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto free_mem; + } + +again: + mutex_lock(&dedupe_info->lock); + found_hash = inmem_search_hash(dedupe_info, hash->hash); + /* If we don't find a duplicated extent, just return. */ + if (!found_hash) { + ret = 0; + goto out; + } + bytenr = found_hash->bytenr; + num_bytes = found_hash->num_bytes; + + btrfs_init_delayed_ref_head(insert_head, insert_qrecord, bytenr, + num_bytes, ref_root, 0, BTRFS_ADD_DELAYED_REF, true, + false); + + btrfs_init_delayed_ref_common(trans->fs_info, &insert_dref->node, + bytenr, num_bytes, ref_root, BTRFS_ADD_DELAYED_REF, + BTRFS_EXTENT_DATA_REF_KEY); + insert_dref->root = ref_root; + insert_dref->parent = 0; + insert_dref->objectid = btrfs_ino(BTRFS_I(inode)); + insert_dref->offset = file_pos; + + delayed_refs = &trans->transaction->delayed_refs; + + spin_lock(&delayed_refs->lock); + head = btrfs_find_delayed_ref_head(&trans->transaction->delayed_refs, + bytenr); + if (!head) { + /* + * We can safely insert a new delayed_ref as long as we + * hold delayed_refs->lock. + * Only need to use atomic inc_extent_ref() + */ + btrfs_add_delayed_data_ref_locked(trans, insert_head, + insert_qrecord, insert_dref, + BTRFS_ADD_DELAYED_REF, &qrecord_inserted, NULL, + NULL); + spin_unlock(&delayed_refs->lock); + + trace_add_delayed_data_ref(trans->fs_info, &insert_dref->node, + insert_dref, BTRFS_ADD_DELAYED_REF); + + if (ret > 0) + kmem_cache_free(btrfs_delayed_data_ref_cachep, + insert_dref); + + /* add_delayed_data_ref_locked will free unused memory */ + free_insert = 0; + hash->bytenr = bytenr; + hash->num_bytes = num_bytes; + ret = 1; + goto out; + } + + /* + * We can't lock ref head with dedupe_info->lock hold or we will cause + * ABBA dead lock. + */ + mutex_unlock(&dedupe_info->lock); + ret = btrfs_delayed_ref_lock(delayed_refs, head); + spin_unlock(&delayed_refs->lock); + if (ret == -EAGAIN) + goto again; + + mutex_lock(&dedupe_info->lock); + /* Search again to ensure the hash is still here */ + found_hash = inmem_search_hash(dedupe_info, hash->hash); + if (!found_hash) { + ret = 0; + mutex_unlock(&head->mutex); + goto out; + } + ret = 1; + hash->bytenr = bytenr; + hash->num_bytes = num_bytes; + + /* + * Increase the extent ref right now, to avoid delayed ref run + * Or we may increase ref on non-exist extent. + */ + btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, 0, + ref_root, + btrfs_ino(BTRFS_I(inode)), file_pos); + mutex_unlock(&head->mutex); +out: + mutex_unlock(&dedupe_info->lock); + btrfs_end_transaction(trans); + +free_mem: + if (free_insert) { + kmem_cache_free(btrfs_delayed_ref_head_cachep, insert_head); + kmem_cache_free(btrfs_delayed_data_ref_cachep, insert_dref); + } + if (!qrecord_inserted) + kfree(insert_qrecord); + return ret; +} + +int btrfs_dedupe_search(struct btrfs_fs_info *fs_info, + struct inode *inode, u64 file_pos, + struct btrfs_dedupe_hash *hash) +{ + struct btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info; + int ret = -EINVAL; + + if (!hash) + return 0; + + /* + * This function doesn't follow fs_info->dedupe_enabled as it will need + * to ensure any hashed extent to go through dedupe routine + */ + if (WARN_ON(dedupe_info == NULL)) + return -EINVAL; + + if (WARN_ON(btrfs_dedupe_hash_hit(hash))) + return -EINVAL; + + if (dedupe_info->backend == BTRFS_DEDUPE_BACKEND_INMEMORY) + ret = inmem_search(dedupe_info, inode, file_pos, hash); + + /* It's possible hash->bytenr/num_bytenr already changed */ + if (ret == 0) { + hash->num_bytes = 0; + hash->bytenr = 0; + } + return ret; +} From patchwork Tue Nov 6 06:41:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669703 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C567B15A6 for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B2C4229DDA for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A73CE29E47; Tue, 6 Nov 2018 06:41:44 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 267E729DED for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387598AbeKFQFX (ORCPT ); Tue, 6 Nov 2018 11:05:23 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:32866 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387527AbeKFQFW (ORCPT ); Tue, 6 Nov 2018 11:05:22 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417672" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:39 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id 8620B4B714D9; Tue, 6 Nov 2018 14:41:34 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:38 +0800 From: Lu Fengqi To: CC: Wang Xiaoguang , Qu Wenruo Subject: [PATCH v15.1 07/13] btrfs: dedupe: Implement btrfs_dedupe_calc_hash interface Date: Tue, 6 Nov 2018 14:41:16 +0800 Message-ID: <20181106064122.6154-8-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: 8620B4B714D9.A8FAC X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Wang Xiaoguang Unlike in-memory or on-disk dedupe method, only SHA256 hash method is supported yet, so implement btrfs_dedupe_calc_hash() interface using SHA256. Signed-off-by: Qu Wenruo Signed-off-by: Wang Xiaoguang Reviewed-by: Josef Bacik Signed-off-by: Lu Fengqi --- fs/btrfs/dedupe.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c index 03ad41423c01..6199215022e6 100644 --- a/fs/btrfs/dedupe.c +++ b/fs/btrfs/dedupe.c @@ -644,3 +644,53 @@ int btrfs_dedupe_search(struct btrfs_fs_info *fs_info, } return ret; } + +int btrfs_dedupe_calc_hash(struct btrfs_fs_info *fs_info, + struct inode *inode, u64 start, + struct btrfs_dedupe_hash *hash) +{ + int i; + int ret; + struct page *p; + struct shash_desc *shash; + struct btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info; + struct crypto_shash *tfm = dedupe_info->dedupe_driver; + u64 dedupe_bs; + u64 sectorsize = fs_info->sectorsize; + + shash = kmalloc(sizeof(*shash) + crypto_shash_descsize(tfm), GFP_NOFS); + if (!shash) + return -ENOMEM; + + if (!fs_info->dedupe_enabled || !hash) + return 0; + + if (WARN_ON(dedupe_info == NULL)) + return -EINVAL; + + WARN_ON(!IS_ALIGNED(start, sectorsize)); + + dedupe_bs = dedupe_info->blocksize; + + shash->tfm = tfm; + shash->flags = 0; + ret = crypto_shash_init(shash); + if (ret) + return ret; + for (i = 0; sectorsize * i < dedupe_bs; i++) { + char *d; + + p = find_get_page(inode->i_mapping, + (start >> PAGE_SHIFT) + i); + if (WARN_ON(!p)) + return -ENOENT; + d = kmap(p); + ret = crypto_shash_update(shash, d, sectorsize); + kunmap(p); + put_page(p); + if (ret) + return ret; + } + ret = crypto_shash_final(shash, hash->hash); + return ret; +} From patchwork Tue Nov 6 06:41:17 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669699 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 738DA17D4 for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5F0EB29EB5 for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4ABEB29E58; Tue, 6 Nov 2018 06:41:44 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 B781629DD6 for ; Tue, 6 Nov 2018 06:41:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387567AbeKFQFU (ORCPT ); Tue, 6 Nov 2018 11:05:20 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:32866 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387533AbeKFQFU (ORCPT ); Tue, 6 Nov 2018 11:05:20 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417666" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:38 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id 1E24A4B714AF; Tue, 6 Nov 2018 14:41:35 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:38 +0800 From: Lu Fengqi To: CC: Wang Xiaoguang , Qu Wenruo Subject: [PATCH v15.1 08/13] btrfs: ordered-extent: Add support for dedupe Date: Tue, 6 Nov 2018 14:41:17 +0800 Message-ID: <20181106064122.6154-9-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: 1E24A4B714AF.A84AC X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Wang Xiaoguang Add ordered-extent support for dedupe. Note, current ordered-extent support only supports non-compressed source extent. Support for compressed source extent will be added later. Signed-off-by: Qu Wenruo Signed-off-by: Wang Xiaoguang Reviewed-by: Josef Bacik --- fs/btrfs/ordered-data.c | 46 +++++++++++++++++++++++++++++++++++++---- fs/btrfs/ordered-data.h | 13 ++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 0c4ef208b8b9..4b112258a79b 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -12,6 +12,7 @@ #include "extent_io.h" #include "disk-io.h" #include "compression.h" +#include "dedupe.h" static struct kmem_cache *btrfs_ordered_extent_cache; @@ -170,7 +171,8 @@ static inline struct rb_node *tree_search(struct btrfs_ordered_inode_tree *tree, */ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset, u64 start, u64 len, u64 disk_len, - int type, int dio, int compress_type) + int type, int dio, int compress_type, + struct btrfs_dedupe_hash *hash) { struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_root *root = BTRFS_I(inode)->root; @@ -191,6 +193,33 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset, entry->inode = igrab(inode); entry->compress_type = compress_type; entry->truncated_len = (u64)-1; + entry->hash = NULL; + /* + * A hash hit means we have already incremented the extents delayed + * ref. + * We must handle this even if another process is trying to + * turn off dedupe, otherwise we will leak a reference. + */ + if (hash && (hash->bytenr || root->fs_info->dedupe_enabled)) { + struct btrfs_dedupe_info *dedupe_info; + + dedupe_info = root->fs_info->dedupe_info; + if (WARN_ON(dedupe_info == NULL)) { + kmem_cache_free(btrfs_ordered_extent_cache, + entry); + return -EINVAL; + } + entry->hash = btrfs_dedupe_alloc_hash(dedupe_info->hash_algo); + if (!entry->hash) { + kmem_cache_free(btrfs_ordered_extent_cache, entry); + return -ENOMEM; + } + entry->hash->bytenr = hash->bytenr; + entry->hash->num_bytes = hash->num_bytes; + memcpy(entry->hash->hash, hash->hash, + btrfs_hash_sizes[dedupe_info->hash_algo]); + } + if (type != BTRFS_ORDERED_IO_DONE && type != BTRFS_ORDERED_COMPLETE) set_bit(type, &entry->flags); @@ -245,15 +274,23 @@ int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset, { return __btrfs_add_ordered_extent(inode, file_offset, start, len, disk_len, type, 0, - BTRFS_COMPRESS_NONE); + BTRFS_COMPRESS_NONE, NULL); } +int btrfs_add_ordered_extent_dedupe(struct inode *inode, u64 file_offset, + u64 start, u64 len, u64 disk_len, int type, + struct btrfs_dedupe_hash *hash) +{ + return __btrfs_add_ordered_extent(inode, file_offset, start, len, + disk_len, type, 0, + BTRFS_COMPRESS_NONE, hash); +} int btrfs_add_ordered_extent_dio(struct inode *inode, u64 file_offset, u64 start, u64 len, u64 disk_len, int type) { return __btrfs_add_ordered_extent(inode, file_offset, start, len, disk_len, type, 1, - BTRFS_COMPRESS_NONE); + BTRFS_COMPRESS_NONE, NULL); } int btrfs_add_ordered_extent_compress(struct inode *inode, u64 file_offset, @@ -262,7 +299,7 @@ int btrfs_add_ordered_extent_compress(struct inode *inode, u64 file_offset, { return __btrfs_add_ordered_extent(inode, file_offset, start, len, disk_len, type, 0, - compress_type); + compress_type, NULL); } /* @@ -444,6 +481,7 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry) list_del(&sum->list); kfree(sum); } + kfree(entry->hash); kmem_cache_free(btrfs_ordered_extent_cache, entry); } } diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 02d813aaa261..08c7ee986bb9 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -124,6 +124,16 @@ struct btrfs_ordered_extent { struct completion completion; struct btrfs_work flush_work; struct list_head work_list; + + /* + * For inband deduplication + * If hash is NULL, no deduplication. + * If hash->bytenr is zero, means this is a dedupe miss, hash will + * be added into dedupe tree. + * If hash->bytenr is non-zero, this is a dedupe hit. Extent ref is + * *ALREADY* increased. + */ + struct btrfs_dedupe_hash *hash; }; /* @@ -159,6 +169,9 @@ int btrfs_dec_test_first_ordered_pending(struct inode *inode, int uptodate); int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset, u64 start, u64 len, u64 disk_len, int type); +int btrfs_add_ordered_extent_dedupe(struct inode *inode, u64 file_offset, + u64 start, u64 len, u64 disk_len, int type, + struct btrfs_dedupe_hash *hash); int btrfs_add_ordered_extent_dio(struct inode *inode, u64 file_offset, u64 start, u64 len, u64 disk_len, int type); int btrfs_add_ordered_extent_compress(struct inode *inode, u64 file_offset, From patchwork Tue Nov 6 06:41:18 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669717 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 55CB215A6 for ; Tue, 6 Nov 2018 06:41:48 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3792D29DC1 for ; Tue, 6 Nov 2018 06:41:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2C20129DDA; Tue, 6 Nov 2018 06:41:48 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 0307529DFA for ; Tue, 6 Nov 2018 06:41:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387609AbeKFQF0 (ORCPT ); Tue, 6 Nov 2018 11:05:26 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:44988 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387579AbeKFQFZ (ORCPT ); Tue, 6 Nov 2018 11:05:25 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417671" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:38 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id B463F4B714AA; Tue, 6 Nov 2018 14:41:35 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:39 +0800 From: Lu Fengqi To: CC: Wang Xiaoguang , Qu Wenruo Subject: [PATCH v15.1 09/13] btrfs: introduce type based delalloc metadata reserve Date: Tue, 6 Nov 2018 14:41:18 +0800 Message-ID: <20181106064122.6154-10-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: B463F4B714AA.AC81E X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Wang Xiaoguang Introduce type based metadata reserve parameter for delalloc space reservation/freeing function. The problem we are going to solve is, btrfs use different max extent size for different mount options. For de-duplication, the max extent size can be set by the dedupe ioctl, while for normal write it's 128M. And furthermore, split/merge extent hook highly depends that max extent size. Such situation contributes to quite a lot of false ENOSPC. So this patch introduces the facility to help solve these false ENOSPC related to different max extent size. Currently, only normal 128M extent size is supported. More types will follow soon. Signed-off-by: Wang Xiaoguang Signed-off-by: Qu Wenruo Signed-off-by: Lu Fengqi --- fs/btrfs/ctree.h | 43 ++++++++++--- fs/btrfs/extent-tree.c | 48 ++++++++++++--- fs/btrfs/file.c | 30 +++++---- fs/btrfs/free-space-cache.c | 6 +- fs/btrfs/inode-map.c | 9 ++- fs/btrfs/inode.c | 115 +++++++++++++++++++++++++---------- fs/btrfs/ioctl.c | 23 +++---- fs/btrfs/ordered-data.c | 6 +- fs/btrfs/ordered-data.h | 3 +- fs/btrfs/relocation.c | 22 ++++--- fs/btrfs/tests/inode-tests.c | 15 +++-- 11 files changed, 223 insertions(+), 97 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 910050d904ef..b119a19cbeaf 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -92,11 +92,24 @@ static const int btrfs_csum_sizes[] = { 4 }; /* * Count how many BTRFS_MAX_EXTENT_SIZE cover the @size */ -static inline u32 count_max_extents(u64 size) +static inline u32 count_max_extents(u64 size, u64 max_extent_size) { - return div_u64(size + BTRFS_MAX_EXTENT_SIZE - 1, BTRFS_MAX_EXTENT_SIZE); + return div_u64(size + max_extent_size - 1, max_extent_size); } +/* + * Type based metadata reserve type + * This affects how btrfs reserve metadata space for buffered write. + * + * This is caused by the different max extent size for normal COW + * and further in-band dedupe + */ +enum btrfs_metadata_reserve_type { + BTRFS_RESERVE_NORMAL, +}; + +u64 btrfs_max_extent_size(enum btrfs_metadata_reserve_type reserve_type); + struct btrfs_mapping_tree { struct extent_map_tree map_tree; }; @@ -2732,8 +2745,9 @@ int btrfs_check_data_free_space(struct inode *inode, void btrfs_free_reserved_data_space(struct inode *inode, struct extent_changeset *reserved, u64 start, u64 len); void btrfs_delalloc_release_space(struct inode *inode, - struct extent_changeset *reserved, - u64 start, u64 len, bool qgroup_free); + struct extent_changeset *reserved, + u64 start, u64 len, bool qgroup_free, + enum btrfs_metadata_reserve_type reserve_type); void btrfs_free_reserved_data_space_noquota(struct inode *inode, u64 start, u64 len); void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans); @@ -2743,13 +2757,17 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, void btrfs_subvolume_release_metadata(struct btrfs_fs_info *fs_info, struct btrfs_block_rsv *rsv); void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes, - bool qgroup_free); + bool qgroup_free, + enum btrfs_metadata_reserve_type reserve_type); -int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes); +int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes, + enum btrfs_metadata_reserve_type reserve_type); void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes, - bool qgroup_free); + bool qgroup_free, + enum btrfs_metadata_reserve_type reserve_type); int btrfs_delalloc_reserve_space(struct inode *inode, - struct extent_changeset **reserved, u64 start, u64 len); + struct extent_changeset **reserved, u64 start, u64 len, + enum btrfs_metadata_reserve_type reserve_type); void btrfs_init_block_rsv(struct btrfs_block_rsv *rsv, unsigned short type); struct btrfs_block_rsv *btrfs_alloc_block_rsv(struct btrfs_fs_info *fs_info, unsigned short type); @@ -3152,7 +3170,11 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root); int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int nr); int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end, unsigned int extra_bits, - struct extent_state **cached_state, int dedupe); + struct extent_state **cached_state, + enum btrfs_metadata_reserve_type reserve_type); +int btrfs_set_extent_defrag(struct inode *inode, u64 start, u64 end, + struct extent_state **cached_state, + enum btrfs_metadata_reserve_type reserve_type); int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, struct btrfs_root *new_root, struct btrfs_root *parent_root, @@ -3235,7 +3257,8 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, int btrfs_release_file(struct inode *inode, struct file *file); int btrfs_dirty_pages(struct inode *inode, struct page **pages, size_t num_pages, loff_t pos, size_t write_bytes, - struct extent_state **cached); + struct extent_state **cached, + enum btrfs_metadata_reserve_type reserve_type); int btrfs_fdatawrite_range(struct inode *inode, loff_t start, loff_t end); loff_t btrfs_remap_file_range(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index a1febf155747..2c8992b919ae 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5913,13 +5913,24 @@ static void btrfs_calculate_inode_block_rsv_size(struct btrfs_fs_info *fs_info, spin_unlock(&block_rsv->lock); } -int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes) +u64 btrfs_max_extent_size(enum btrfs_metadata_reserve_type reserve_type) +{ + if (reserve_type == BTRFS_RESERVE_NORMAL) + return BTRFS_MAX_EXTENT_SIZE; + + ASSERT(0); + return BTRFS_MAX_EXTENT_SIZE; +} + +int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes, + enum btrfs_metadata_reserve_type reserve_type) { struct btrfs_fs_info *fs_info = inode->root->fs_info; unsigned nr_extents; enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_FLUSH_ALL; int ret = 0; bool delalloc_lock = true; + u64 max_extent_size = btrfs_max_extent_size(reserve_type); /* If we are a free space inode we need to not flush since we will be in * the middle of a transaction commit. We also don't need the delalloc @@ -5947,7 +5958,7 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes) /* Add our new extents and calculate the new rsv size. */ spin_lock(&inode->lock); - nr_extents = count_max_extents(num_bytes); + nr_extents = count_max_extents(num_bytes, max_extent_size); btrfs_mod_outstanding_extents(inode, nr_extents); inode->csum_bytes += num_bytes; btrfs_calculate_inode_block_rsv_size(fs_info, inode); @@ -5963,7 +5974,7 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes) out_fail: spin_lock(&inode->lock); - nr_extents = count_max_extents(num_bytes); + nr_extents = count_max_extents(num_bytes, max_extent_size); btrfs_mod_outstanding_extents(inode, -nr_extents); inode->csum_bytes -= num_bytes; btrfs_calculate_inode_block_rsv_size(fs_info, inode); @@ -5980,13 +5991,16 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes) * @inode: the inode to release the reservation for. * @num_bytes: the number of bytes we are releasing. * @qgroup_free: free qgroup reservation or convert it to per-trans reservation + * @reserve_type: the type when we reserve delalloc space for this range. + * must be the same passed to btrfs_delalloc_reserve_metadata() * * This will release the metadata reservation for an inode. This can be called * once we complete IO for a given set of bytes to release their metadata * reservations, or on error for the same reason. */ void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes, - bool qgroup_free) + bool qgroup_free, + enum btrfs_metadata_reserve_type reserve_type) { struct btrfs_fs_info *fs_info = inode->root->fs_info; @@ -6015,13 +6029,15 @@ void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes, * with btrfs_delalloc_reserve_metadata. */ void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes, - bool qgroup_free) + bool qgroup_free, + enum btrfs_metadata_reserve_type reserve_type) { struct btrfs_fs_info *fs_info = inode->root->fs_info; + u64 max_extent_size = btrfs_max_extent_size(reserve_type); unsigned num_extents; spin_lock(&inode->lock); - num_extents = count_max_extents(num_bytes); + num_extents = count_max_extents(num_bytes, max_extent_size); btrfs_mod_outstanding_extents(inode, -num_extents); btrfs_calculate_inode_block_rsv_size(fs_info, inode); spin_unlock(&inode->lock); @@ -6040,6 +6056,8 @@ void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes, * @len: how long the range we are writing to * @reserved: mandatory parameter, record actually reserved qgroup ranges of * current reservation. + * @reserve_type: the type of write we're reserving for. + * determine the max extent size. * * This will do the following things * @@ -6058,14 +6076,16 @@ void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes, * Return <0 for error(-ENOSPC or -EQUOT) */ int btrfs_delalloc_reserve_space(struct inode *inode, - struct extent_changeset **reserved, u64 start, u64 len) + struct extent_changeset **reserved, u64 start, u64 len, + enum btrfs_metadata_reserve_type reserve_type) { int ret; ret = btrfs_check_data_free_space(inode, reserved, start, len); if (ret < 0) return ret; - ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), len); + ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), len, + reserve_type); if (ret < 0) btrfs_free_reserved_data_space(inode, *reserved, start, len); return ret; @@ -6077,6 +6097,12 @@ int btrfs_delalloc_reserve_space(struct inode *inode, * @start: start position of the space already reserved * @len: the len of the space already reserved * @release_bytes: the len of the space we consumed or didn't use + * @reserve_type: the type of write we're releasing for + * must match the type passed to btrfs_delalloc_reserve_space() + * + * This must be matched with a call to btrfs_delalloc_reserve_space. This is + * called in the case that we don't need the metadata AND data reservations + * anymore. So if there is an error or we insert an inline extent. * * This function will release the metadata space that was not used and will * decrement ->delalloc_bytes and remove it from the fs_info delalloc_inodes @@ -6085,9 +6111,11 @@ int btrfs_delalloc_reserve_space(struct inode *inode, */ void btrfs_delalloc_release_space(struct inode *inode, struct extent_changeset *reserved, - u64 start, u64 len, bool qgroup_free) + u64 start, u64 len, bool qgroup_free, + enum btrfs_metadata_reserve_type reserve_type) { - btrfs_delalloc_release_metadata(BTRFS_I(inode), len, qgroup_free); + btrfs_delalloc_release_metadata(BTRFS_I(inode), len, qgroup_free, + reserve_type); btrfs_free_reserved_data_space(inode, reserved, start, len); } diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index a3c22e16509b..d655c9356d9e 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -513,7 +513,8 @@ static int btrfs_find_new_delalloc_bytes(struct btrfs_inode *inode, */ int btrfs_dirty_pages(struct inode *inode, struct page **pages, size_t num_pages, loff_t pos, size_t write_bytes, - struct extent_state **cached) + struct extent_state **cached, + enum btrfs_metadata_reserve_type reserve_type) { struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); int err = 0; @@ -558,7 +559,7 @@ int btrfs_dirty_pages(struct inode *inode, struct page **pages, } err = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block, - extra_bits, cached, 0); + extra_bits, cached, reserve_type); if (err) return err; @@ -1601,6 +1602,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, int ret = 0; bool only_release_metadata = false; bool force_page_uptodate = false; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; nrptrs = min(DIV_ROUND_UP(iov_iter_count(i), PAGE_SIZE), PAGE_SIZE / (sizeof(struct page *))); @@ -1669,7 +1671,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, WARN_ON(reserve_bytes == 0); ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), - reserve_bytes); + reserve_bytes, reserve_type); if (ret) { if (!only_release_metadata) btrfs_free_reserved_data_space(inode, @@ -1692,7 +1694,8 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, force_page_uptodate); if (ret) { btrfs_delalloc_release_extents(BTRFS_I(inode), - reserve_bytes, true); + reserve_bytes, true, + reserve_type); break; } @@ -1704,7 +1707,8 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, if (extents_locked == -EAGAIN) goto again; btrfs_delalloc_release_extents(BTRFS_I(inode), - reserve_bytes, true); + reserve_bytes, true, + reserve_type); ret = extents_locked; break; } @@ -1739,7 +1743,8 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, fs_info->sb->s_blocksize_bits; if (only_release_metadata) { btrfs_delalloc_release_metadata(BTRFS_I(inode), - release_bytes, true); + release_bytes, true, + reserve_type); } else { u64 __pos; @@ -1748,7 +1753,8 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, (dirty_pages << PAGE_SHIFT); btrfs_delalloc_release_space(inode, data_reserved, __pos, - release_bytes, true); + release_bytes, true, + reserve_type); } } @@ -1757,12 +1763,13 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, if (copied > 0) ret = btrfs_dirty_pages(inode, pages, dirty_pages, - pos, copied, &cached_state); + pos, copied, &cached_state, + reserve_type); if (extents_locked) unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend, &cached_state); btrfs_delalloc_release_extents(BTRFS_I(inode), reserve_bytes, - true); + true, reserve_type); if (ret) { btrfs_drop_pages(pages, num_pages); break; @@ -1802,11 +1809,12 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, if (only_release_metadata) { btrfs_end_write_no_snapshotting(root); btrfs_delalloc_release_metadata(BTRFS_I(inode), - release_bytes, true); + release_bytes, true, + reserve_type); } else { btrfs_delalloc_release_space(inode, data_reserved, round_down(pos, fs_info->sectorsize), - release_bytes, true); + release_bytes, true, reserve_type); } } diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 4ba0aedc878b..3f6c7be4665e 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -1294,7 +1294,8 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, /* Everything is written out, now we dirty the pages in the file. */ ret = btrfs_dirty_pages(inode, io_ctl->pages, io_ctl->num_pages, 0, - i_size_read(inode), &cached_state); + i_size_read(inode), &cached_state, + BTRFS_RESERVE_NORMAL); if (ret) goto out_nospc; @@ -3546,7 +3547,8 @@ int btrfs_write_out_ino_cache(struct btrfs_root *root, if (ret) { if (release_metadata) btrfs_delalloc_release_metadata(BTRFS_I(inode), - inode->i_size, true); + inode->i_size, true, + BTRFS_RESERVE_NORMAL); #ifdef DEBUG btrfs_err(fs_info, "failed to write free ino cache for root %llu", diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c index ffca2abf13d0..4890e4053a2f 100644 --- a/fs/btrfs/inode-map.c +++ b/fs/btrfs/inode-map.c @@ -476,19 +476,22 @@ int btrfs_save_ino_cache(struct btrfs_root *root, /* Just to make sure we have enough space */ prealloc += 8 * PAGE_SIZE; - ret = btrfs_delalloc_reserve_space(inode, &data_reserved, 0, prealloc); + ret = btrfs_delalloc_reserve_space(inode, &data_reserved, 0, prealloc, + BTRFS_RESERVE_NORMAL); if (ret) goto out_put; ret = btrfs_prealloc_file_range_trans(inode, trans, 0, 0, prealloc, prealloc, prealloc, &alloc_hint); if (ret) { - btrfs_delalloc_release_extents(BTRFS_I(inode), prealloc, true); + btrfs_delalloc_release_extents(BTRFS_I(inode), prealloc, true, + BTRFS_RESERVE_NORMAL); goto out_put; } ret = btrfs_write_out_ino_cache(root, trans, path, inode); - btrfs_delalloc_release_extents(BTRFS_I(inode), prealloc, false); + btrfs_delalloc_release_extents(BTRFS_I(inode), prealloc, false, + BTRFS_RESERVE_NORMAL); out_put: iput(inode); out_release: diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d3df5b52278c..ff8af15c9039 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1615,13 +1615,17 @@ static void btrfs_split_extent_hook(void *private_data, { struct inode *inode = private_data; u64 size; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; + u64 max_extent_size; /* not delalloc, ignore it */ if (!(orig->state & EXTENT_DELALLOC)) return; + max_extent_size = btrfs_max_extent_size(reserve_type); + size = orig->end - orig->start + 1; - if (size > BTRFS_MAX_EXTENT_SIZE) { + if (size > max_extent_size) { u32 num_extents; u64 new_size; @@ -1630,10 +1634,10 @@ static void btrfs_split_extent_hook(void *private_data, * applies here, just in reverse. */ new_size = orig->end - split + 1; - num_extents = count_max_extents(new_size); + num_extents = count_max_extents(new_size, max_extent_size); new_size = split - orig->start; - num_extents += count_max_extents(new_size); - if (count_max_extents(size) >= num_extents) + num_extents += count_max_extents(new_size, max_extent_size); + if (count_max_extents(size, max_extent_size) >= num_extents) return; } @@ -1654,19 +1658,23 @@ static void btrfs_merge_extent_hook(void *private_data, { struct inode *inode = private_data; u64 new_size, old_size; + u64 max_extent_size; u32 num_extents; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; /* not delalloc, ignore it */ if (!(other->state & EXTENT_DELALLOC)) return; + max_extent_size = btrfs_max_extent_size(reserve_type); + if (new->start > other->start) new_size = new->end - other->start + 1; else new_size = other->end - new->start + 1; /* we're not bigger than the max, unreserve the space and go */ - if (new_size <= BTRFS_MAX_EXTENT_SIZE) { + if (new_size <= max_extent_size) { spin_lock(&BTRFS_I(inode)->lock); btrfs_mod_outstanding_extents(BTRFS_I(inode), -1); spin_unlock(&BTRFS_I(inode)->lock); @@ -1692,10 +1700,10 @@ static void btrfs_merge_extent_hook(void *private_data, * this case. */ old_size = other->end - other->start + 1; - num_extents = count_max_extents(old_size); + num_extents = count_max_extents(old_size, max_extent_size); old_size = new->end - new->start + 1; - num_extents += count_max_extents(old_size); - if (count_max_extents(new_size) >= num_extents) + num_extents += count_max_extents(old_size, max_extent_size); + if (count_max_extents(new_size, max_extent_size) >= num_extents) return; spin_lock(&BTRFS_I(inode)->lock); @@ -1777,9 +1785,15 @@ static void btrfs_set_bit_hook(void *private_data, if (!(state->state & EXTENT_DELALLOC) && (*bits & EXTENT_DELALLOC)) { struct btrfs_root *root = BTRFS_I(inode)->root; u64 len = state->end + 1 - state->start; - u32 num_extents = count_max_extents(len); + u64 max_extent_size; + u64 num_extents; + enum btrfs_metadata_reserve_type reserve_type = + BTRFS_RESERVE_NORMAL; bool do_list = !btrfs_is_free_space_inode(BTRFS_I(inode)); + max_extent_size = btrfs_max_extent_size(reserve_type); + num_extents = count_max_extents(len, max_extent_size); + spin_lock(&BTRFS_I(inode)->lock); btrfs_mod_outstanding_extents(BTRFS_I(inode), num_extents); spin_unlock(&BTRFS_I(inode)->lock); @@ -1818,8 +1832,10 @@ static void btrfs_clear_bit_hook(void *private_data, { struct btrfs_inode *inode = BTRFS_I((struct inode *)private_data); struct btrfs_fs_info *fs_info = btrfs_sb(inode->vfs_inode.i_sb); + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; u64 len = state->end + 1 - state->start; - u32 num_extents = count_max_extents(len); + u64 max_extent_size; + u32 num_extents; if ((state->state & EXTENT_DEFRAG) && (*bits & EXTENT_DEFRAG)) { spin_lock(&inode->lock); @@ -1836,6 +1852,9 @@ static void btrfs_clear_bit_hook(void *private_data, struct btrfs_root *root = inode->root; bool do_list = !btrfs_is_free_space_inode(inode); + max_extent_size = btrfs_max_extent_size(reserve_type); + num_extents = count_max_extents(len, max_extent_size); + spin_lock(&inode->lock); btrfs_mod_outstanding_extents(inode, -num_extents); spin_unlock(&inode->lock); @@ -1847,7 +1866,8 @@ static void btrfs_clear_bit_hook(void *private_data, */ if (*bits & EXTENT_CLEAR_META_RESV && root != fs_info->tree_root) - btrfs_delalloc_release_metadata(inode, len, false); + btrfs_delalloc_release_metadata(inode, len, false, + reserve_type); /* For sanity tests. */ if (btrfs_is_testing(fs_info)) @@ -2055,13 +2075,24 @@ static noinline int add_pending_csums(struct btrfs_trans_handle *trans, int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end, unsigned int extra_bits, - struct extent_state **cached_state, int dedupe) + struct extent_state **cached_state, + enum btrfs_metadata_reserve_type reserve_type) { WARN_ON((end & (PAGE_SIZE - 1)) == 0); return set_extent_delalloc(&BTRFS_I(inode)->io_tree, start, end, extra_bits, cached_state); } + +int btrfs_set_extent_defrag(struct inode *inode, u64 start, u64 end, + struct extent_state **cached_state, + enum btrfs_metadata_reserve_type reserve_type) +{ + WARN_ON((end & (PAGE_SIZE - 1)) == 0); + return set_extent_defrag(&BTRFS_I(inode)->io_tree, start, end, + cached_state); +} + /* see btrfs_writepage_start_hook for details on why this is required */ struct btrfs_writepage_fixup { struct page *page; @@ -2079,6 +2110,7 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work) u64 page_start; u64 page_end; int ret; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; fixup = container_of(work, struct btrfs_writepage_fixup, work); page = fixup->page; @@ -2112,7 +2144,7 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work) } ret = btrfs_delalloc_reserve_space(inode, &data_reserved, page_start, - PAGE_SIZE); + PAGE_SIZE, reserve_type); if (ret) { mapping_set_error(page->mapping, ret); end_extent_writepage(page, ret, page_start, page_end); @@ -2121,7 +2153,7 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work) } ret = btrfs_set_extent_delalloc(inode, page_start, page_end, 0, - &cached_state, 0); + &cached_state, reserve_type); if (ret) { mapping_set_error(page->mapping, ret); end_extent_writepage(page, ret, page_start, page_end); @@ -2131,7 +2163,8 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work) ClearPageChecked(page); set_page_dirty(page); - btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, false); + btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, false, + reserve_type); out: unlock_extent_cached(&BTRFS_I(inode)->io_tree, page_start, page_end, &cached_state); @@ -2942,6 +2975,7 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) bool range_locked = false; bool clear_new_delalloc_bytes = false; bool clear_reserved_extent = true; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; if (!test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) && !test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags) && @@ -3130,7 +3164,7 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) * This needs to be done to make sure anybody waiting knows we are done * updating everything for this ordered extent. */ - btrfs_remove_ordered_extent(inode, ordered_extent); + btrfs_remove_ordered_extent(inode, ordered_extent, reserve_type); /* for snapshot-aware defrag */ if (new) { @@ -4839,6 +4873,7 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len, int ret = 0; u64 block_start; u64 block_end; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; if (IS_ALIGNED(offset, blocksize) && (!len || IS_ALIGNED(len, blocksize))) @@ -4848,7 +4883,8 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len, block_end = block_start + blocksize - 1; ret = btrfs_delalloc_reserve_space(inode, &data_reserved, - block_start, blocksize); + block_start, blocksize, + reserve_type); if (ret) goto out; @@ -4856,8 +4892,10 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len, page = find_or_create_page(mapping, index, mask); if (!page) { btrfs_delalloc_release_space(inode, data_reserved, - block_start, blocksize, true); - btrfs_delalloc_release_extents(BTRFS_I(inode), blocksize, true); + block_start, blocksize, true, + reserve_type); + btrfs_delalloc_release_extents(BTRFS_I(inode), blocksize, true, + reserve_type); ret = -ENOMEM; goto out; } @@ -4897,7 +4935,7 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len, 0, 0, &cached_state); ret = btrfs_set_extent_delalloc(inode, block_start, block_end, 0, - &cached_state, 0); + &cached_state, reserve_type); if (ret) { unlock_extent_cached(io_tree, block_start, block_end, &cached_state); @@ -4924,8 +4962,9 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len, out_unlock: if (ret) btrfs_delalloc_release_space(inode, data_reserved, block_start, - blocksize, true); - btrfs_delalloc_release_extents(BTRFS_I(inode), blocksize, (ret != 0)); + blocksize, true, reserve_type); + btrfs_delalloc_release_extents(BTRFS_I(inode), blocksize, (ret != 0), + reserve_type); unlock_page(page); put_page(page); out: @@ -8520,7 +8559,8 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) goto out; } ret = btrfs_delalloc_reserve_space(inode, &data_reserved, - offset, count); + offset, count, + BTRFS_RESERVE_NORMAL); if (ret) goto out; @@ -8552,7 +8592,8 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) if (ret < 0 && ret != -EIOCBQUEUED) { if (dio_data.reserve) btrfs_delalloc_release_space(inode, data_reserved, - offset, dio_data.reserve, true); + offset, dio_data.reserve, true, + BTRFS_RESERVE_NORMAL); /* * On error we might have left some ordered extents * without submitting corresponding bios for them, so @@ -8568,8 +8609,10 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) false); } else if (ret >= 0 && (size_t)ret < count) btrfs_delalloc_release_space(inode, data_reserved, - offset, count - (size_t)ret, true); - btrfs_delalloc_release_extents(BTRFS_I(inode), count, false); + offset, count - (size_t)ret, true, + BTRFS_RESERVE_NORMAL); + btrfs_delalloc_release_extents(BTRFS_I(inode), count, false, + BTRFS_RESERVE_NORMAL); } out: if (wakeup) @@ -8797,6 +8840,7 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) struct btrfs_ordered_extent *ordered; struct extent_state *cached_state = NULL; struct extent_changeset *data_reserved = NULL; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; char *kaddr; unsigned long zero_start; loff_t size; @@ -8824,7 +8868,7 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) * being processed by btrfs_page_mkwrite() function. */ ret2 = btrfs_delalloc_reserve_space(inode, &data_reserved, page_start, - reserved_space); + reserved_space, reserve_type); if (!ret2) { ret2 = file_update_time(vmf->vma->vm_file); reserved = 1; @@ -8873,7 +8917,7 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) end = page_start + reserved_space - 1; btrfs_delalloc_release_space(inode, data_reserved, page_start, PAGE_SIZE - reserved_space, - true); + true, reserve_type); } } @@ -8890,7 +8934,7 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) 0, 0, &cached_state); ret2 = btrfs_set_extent_delalloc(inode, page_start, end, 0, - &cached_state, 0); + &cached_state, reserve_type); if (ret2) { unlock_extent_cached(io_tree, page_start, page_end, &cached_state); @@ -8922,7 +8966,8 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) unlock_extent_cached(io_tree, page_start, page_end, &cached_state); if (!ret2) { - btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, true); + btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, true, + reserve_type); sb_end_pagefault(inode->i_sb); extent_changeset_free(data_reserved); return VM_FAULT_LOCKED; @@ -8931,9 +8976,11 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) out_unlock: unlock_page(page); out: - btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, (ret != 0)); + btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, (ret != 0), + reserve_type); btrfs_delalloc_release_space(inode, data_reserved, page_start, - reserved_space, (ret != 0)); + reserved_space, (ret != 0), + reserve_type); out_noreserve: sb_end_pagefault(inode->i_sb); extent_changeset_free(data_reserved); @@ -9199,6 +9246,7 @@ void btrfs_destroy_inode(struct inode *inode) struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_ordered_extent *ordered; struct btrfs_root *root = BTRFS_I(inode)->root; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; WARN_ON(!hlist_empty(&inode->i_dentry)); WARN_ON(inode->i_data.nrpages); @@ -9226,7 +9274,8 @@ void btrfs_destroy_inode(struct inode *inode) btrfs_err(fs_info, "found ordered extent %llu %llu on inode cleanup", ordered->file_offset, ordered->len); - btrfs_remove_ordered_extent(inode, ordered); + btrfs_remove_ordered_extent(inode, ordered, + reserve_type); btrfs_put_ordered_extent(ordered); btrfs_put_ordered_extent(ordered); } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 3ca6943827ef..93c75f6323f6 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1229,6 +1229,7 @@ static int cluster_pages_for_defrag(struct inode *inode, struct extent_state *cached_state = NULL; struct extent_io_tree *tree; struct extent_changeset *data_reserved = NULL; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping); file_end = (isize - 1) >> PAGE_SHIFT; @@ -1239,7 +1240,7 @@ static int cluster_pages_for_defrag(struct inode *inode, ret = btrfs_delalloc_reserve_space(inode, &data_reserved, start_index << PAGE_SHIFT, - page_cnt << PAGE_SHIFT); + page_cnt << PAGE_SHIFT, reserve_type); if (ret) return ret; i_done = 0; @@ -1330,13 +1331,12 @@ static int cluster_pages_for_defrag(struct inode *inode, spin_unlock(&BTRFS_I(inode)->lock); btrfs_delalloc_release_space(inode, data_reserved, start_index << PAGE_SHIFT, - (page_cnt - i_done) << PAGE_SHIFT, true); + (page_cnt - i_done) << PAGE_SHIFT, true, + reserve_type); } - - set_extent_defrag(&BTRFS_I(inode)->io_tree, page_start, page_end - 1, - &cached_state); - + btrfs_set_extent_defrag(inode, page_start, + page_end - 1, &cached_state, reserve_type); unlock_extent_cached(&BTRFS_I(inode)->io_tree, page_start, page_end - 1, &cached_state); @@ -1349,7 +1349,7 @@ static int cluster_pages_for_defrag(struct inode *inode, put_page(pages[i]); } btrfs_delalloc_release_extents(BTRFS_I(inode), page_cnt << PAGE_SHIFT, - false); + false, reserve_type); extent_changeset_free(data_reserved); return i_done; out: @@ -1358,13 +1358,14 @@ static int cluster_pages_for_defrag(struct inode *inode, put_page(pages[i]); } btrfs_delalloc_release_space(inode, data_reserved, - start_index << PAGE_SHIFT, - page_cnt << PAGE_SHIFT, true); + start_index << PAGE_SHIFT, + page_cnt << PAGE_SHIFT, true, + reserve_type); btrfs_delalloc_release_extents(BTRFS_I(inode), page_cnt << PAGE_SHIFT, - true); + true, reserve_type); extent_changeset_free(data_reserved); - return ret; + return ret; } int btrfs_defrag_file(struct inode *inode, struct file *file, diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 4b112258a79b..47554e0550d7 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -491,7 +491,8 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry) * and waiters are woken up. */ void btrfs_remove_ordered_extent(struct inode *inode, - struct btrfs_ordered_extent *entry) + struct btrfs_ordered_extent *entry, + enum btrfs_metadata_reserve_type reserve_type) { struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_ordered_inode_tree *tree; @@ -505,7 +506,8 @@ void btrfs_remove_ordered_extent(struct inode *inode, btrfs_mod_outstanding_extents(btrfs_inode, -1); spin_unlock(&btrfs_inode->lock); if (root != fs_info->tree_root) - btrfs_delalloc_release_metadata(btrfs_inode, entry->len, false); + btrfs_delalloc_release_metadata(btrfs_inode, entry->len, false, + reserve_type); tree = &btrfs_inode->ordered_tree; spin_lock_irq(&tree->lock); diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 08c7ee986bb9..0124b14e56e7 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -159,7 +159,8 @@ btrfs_ordered_inode_tree_init(struct btrfs_ordered_inode_tree *t) void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry); void btrfs_remove_ordered_extent(struct inode *inode, - struct btrfs_ordered_extent *entry); + struct btrfs_ordered_extent *entry, + enum btrfs_metadata_reserve_type reserve_type); int btrfs_dec_test_ordered_pending(struct inode *inode, struct btrfs_ordered_extent **cached, u64 file_offset, u64 io_size, int uptodate); diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 924116f654a1..11476ad7387a 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -3143,6 +3143,7 @@ static int relocate_file_extent_cluster(struct inode *inode, unsigned long last_index; struct page *page; struct file_ra_state *ra; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping); int nr = 0; int ret = 0; @@ -3169,7 +3170,7 @@ static int relocate_file_extent_cluster(struct inode *inode, last_index = (cluster->end - offset) >> PAGE_SHIFT; while (index <= last_index) { ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), - PAGE_SIZE); + PAGE_SIZE, reserve_type); if (ret) goto out; @@ -3182,7 +3183,8 @@ static int relocate_file_extent_cluster(struct inode *inode, mask); if (!page) { btrfs_delalloc_release_metadata(BTRFS_I(inode), - PAGE_SIZE, true); + PAGE_SIZE, true, + reserve_type); ret = -ENOMEM; goto out; } @@ -3201,9 +3203,11 @@ static int relocate_file_extent_cluster(struct inode *inode, unlock_page(page); put_page(page); btrfs_delalloc_release_metadata(BTRFS_I(inode), - PAGE_SIZE, true); + PAGE_SIZE, true, + reserve_type); btrfs_delalloc_release_extents(BTRFS_I(inode), - PAGE_SIZE, true); + PAGE_SIZE, true, + reserve_type); ret = -EIO; goto out; } @@ -3225,14 +3229,16 @@ static int relocate_file_extent_cluster(struct inode *inode, } ret = btrfs_set_extent_delalloc(inode, page_start, page_end, 0, - NULL, 0); + NULL, reserve_type); if (ret) { unlock_page(page); put_page(page); btrfs_delalloc_release_metadata(BTRFS_I(inode), - PAGE_SIZE, true); + PAGE_SIZE, true, + reserve_type); btrfs_delalloc_release_extents(BTRFS_I(inode), - PAGE_SIZE, true); + PAGE_SIZE, true, + reserve_type); clear_extent_bits(&BTRFS_I(inode)->io_tree, page_start, page_end, @@ -3249,7 +3255,7 @@ static int relocate_file_extent_cluster(struct inode *inode, index++; btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE, - false); + false, reserve_type); balance_dirty_pages_ratelimited(inode->i_mapping); btrfs_throttle(fs_info); } diff --git a/fs/btrfs/tests/inode-tests.c b/fs/btrfs/tests/inode-tests.c index 64043f028820..f885beff4b11 100644 --- a/fs/btrfs/tests/inode-tests.c +++ b/fs/btrfs/tests/inode-tests.c @@ -931,6 +931,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) struct btrfs_fs_info *fs_info = NULL; struct inode *inode = NULL; struct btrfs_root *root = NULL; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; int ret = -ENOMEM; inode = btrfs_new_test_inode(); @@ -956,7 +957,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) /* [BTRFS_MAX_EXTENT_SIZE] */ ret = btrfs_set_extent_delalloc(inode, 0, BTRFS_MAX_EXTENT_SIZE - 1, 0, - NULL, 0); + NULL, reserve_type); if (ret) { test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; @@ -971,7 +972,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) /* [BTRFS_MAX_EXTENT_SIZE][sectorsize] */ ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE, BTRFS_MAX_EXTENT_SIZE + sectorsize - 1, - 0, NULL, 0); + 0, NULL, reserve_type); if (ret) { test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; @@ -1004,7 +1005,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE >> 1, (BTRFS_MAX_EXTENT_SIZE >> 1) + sectorsize - 1, - 0, NULL, 0); + 0, NULL, reserve_type); if (ret) { test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; @@ -1022,7 +1023,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize, (BTRFS_MAX_EXTENT_SIZE << 1) + 3 * sectorsize - 1, - 0, NULL, 0); + 0, NULL, reserve_type); if (ret) { test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; @@ -1039,7 +1040,8 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) */ ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE + sectorsize, - BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, 0, NULL, 0); + BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, 0, NULL, + reserve_type); if (ret) { test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; @@ -1074,7 +1076,8 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) */ ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE + sectorsize, - BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, 0, NULL, 0); + BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, 0, NULL, + reserve_type); if (ret) { test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; From patchwork Tue Nov 6 06:41:19 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669715 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A0D4417D4 for ; Tue, 6 Nov 2018 06:41:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8B1CD29D12 for ; Tue, 6 Nov 2018 06:41:47 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7F8A329DDA; Tue, 6 Nov 2018 06:41:47 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 AA0B529D12 for ; Tue, 6 Nov 2018 06:41:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387604AbeKFQFZ (ORCPT ); Tue, 6 Nov 2018 11:05:25 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:31748 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387505AbeKFQFY (ORCPT ); Tue, 6 Nov 2018 11:05:24 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417667" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:38 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id 4B5E94B714DB; Tue, 6 Nov 2018 14:41:36 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:39 +0800 From: Lu Fengqi To: CC: Qu Wenruo , Wang Xiaoguang Subject: [PATCH v15.1 10/13] btrfs: dedupe: Inband in-memory only de-duplication implement Date: Tue, 6 Nov 2018 14:41:19 +0800 Message-ID: <20181106064122.6154-11-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: 4B5E94B714DB.AC7AA X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Qu Wenruo Core implement for inband de-duplication. It reuses the async_cow_start() facility to do the calculate dedupe hash. And use dedupe hash to do inband de-duplication at extent level. The workflow is as below: 1) Run delalloc range for an inode 2) Calculate hash for the delalloc range at the unit of dedupe_bs 3) For hash match(duplicated) case, just increase source extent ref and insert file extent. For hash mismatch case, go through the normal cow_file_range() fallback, and add hash into dedupe_tree. Compress for hash miss case is not supported yet. Current implement restore all dedupe hash in memory rb-tree, with LRU behavior to control the limit. Signed-off-by: Wang Xiaoguang Signed-off-by: Qu Wenruo Signed-off-by: Lu Fengqi --- fs/btrfs/ctree.h | 4 +- fs/btrfs/dedupe.h | 15 ++ fs/btrfs/extent-tree.c | 31 +++- fs/btrfs/extent_io.c | 7 +- fs/btrfs/extent_io.h | 1 + fs/btrfs/file.c | 4 + fs/btrfs/inode.c | 319 ++++++++++++++++++++++++++++++++++------- fs/btrfs/ioctl.c | 1 + fs/btrfs/relocation.c | 18 +++ 9 files changed, 343 insertions(+), 57 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index b119a19cbeaf..3a8e35b5328a 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -106,9 +106,11 @@ static inline u32 count_max_extents(u64 size, u64 max_extent_size) */ enum btrfs_metadata_reserve_type { BTRFS_RESERVE_NORMAL, + BTRFS_RESERVE_DEDUPE, }; -u64 btrfs_max_extent_size(enum btrfs_metadata_reserve_type reserve_type); +u64 btrfs_max_extent_size(struct btrfs_inode *inode, + enum btrfs_metadata_reserve_type reserve_type); struct btrfs_mapping_tree { struct extent_map_tree map_tree; diff --git a/fs/btrfs/dedupe.h b/fs/btrfs/dedupe.h index 87f5b7ce7766..8157b17c4d11 100644 --- a/fs/btrfs/dedupe.h +++ b/fs/btrfs/dedupe.h @@ -7,6 +7,7 @@ #define BTRFS_DEDUPE_H #include +#include "btrfs_inode.h" /* 32 bytes for SHA256 */ static const int btrfs_hash_sizes[] = { 32 }; @@ -47,6 +48,20 @@ struct btrfs_dedupe_info { u64 current_nr; }; +static inline u64 btrfs_dedupe_blocksize(struct btrfs_inode *inode) +{ + struct btrfs_fs_info *fs_info = inode->root->fs_info; + + return fs_info->dedupe_info->blocksize; +} + +static inline int inode_need_dedupe(struct inode *inode) +{ + struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; + + return fs_info->dedupe_enabled; +} + static inline int btrfs_dedupe_hash_hit(struct btrfs_dedupe_hash *hash) { return (hash && hash->bytenr); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 2c8992b919ae..fa3654045ba8 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -28,6 +28,7 @@ #include "sysfs.h" #include "qgroup.h" #include "ref-verify.h" +#include "dedupe.h" #undef SCRAMBLE_DELAYED_REFS @@ -2492,6 +2493,17 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans, btrfs_pin_extent(fs_info, head->bytenr, head->num_bytes, 1); if (head->is_data) { + /* + * If insert_reserved is given, it means + * a new extent is revered, then deleted + * in one tran, and inc/dec get merged to 0. + * + * In this case, we need to remove its dedupe + * hash. + */ + ret = btrfs_dedupe_del(fs_info, head->bytenr); + if (ret < 0) + return ret; ret = btrfs_del_csums(trans, fs_info, head->bytenr, head->num_bytes); } @@ -5913,13 +5925,15 @@ static void btrfs_calculate_inode_block_rsv_size(struct btrfs_fs_info *fs_info, spin_unlock(&block_rsv->lock); } -u64 btrfs_max_extent_size(enum btrfs_metadata_reserve_type reserve_type) +u64 btrfs_max_extent_size(struct btrfs_inode *inode, + enum btrfs_metadata_reserve_type reserve_type) { if (reserve_type == BTRFS_RESERVE_NORMAL) return BTRFS_MAX_EXTENT_SIZE; - - ASSERT(0); - return BTRFS_MAX_EXTENT_SIZE; + else if (reserve_type == BTRFS_RESERVE_DEDUPE) + return btrfs_dedupe_blocksize(inode); + else + return BTRFS_MAX_EXTENT_SIZE; } int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes, @@ -5930,7 +5944,7 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes, enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_FLUSH_ALL; int ret = 0; bool delalloc_lock = true; - u64 max_extent_size = btrfs_max_extent_size(reserve_type); + u64 max_extent_size = btrfs_max_extent_size(inode, reserve_type); /* If we are a free space inode we need to not flush since we will be in * the middle of a transaction commit. We also don't need the delalloc @@ -6033,7 +6047,7 @@ void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes, enum btrfs_metadata_reserve_type reserve_type) { struct btrfs_fs_info *fs_info = inode->root->fs_info; - u64 max_extent_size = btrfs_max_extent_size(reserve_type); + u64 max_extent_size = btrfs_max_extent_size(inode, reserve_type); unsigned num_extents; spin_lock(&inode->lock); @@ -6934,6 +6948,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, btrfs_release_path(path); if (is_data) { + ret = btrfs_dedupe_del(info, bytenr); + if (ret < 0) { + btrfs_abort_transaction(trans, ret); + goto out; + } ret = btrfs_del_csums(trans, info, bytenr, num_bytes); if (ret) { btrfs_abort_transaction(trans, ret); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index d228f706ff3e..d1d580dc1731 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -590,7 +590,7 @@ int __clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, btrfs_debug_check_extent_io_range(tree, start, end); if (bits & EXTENT_DELALLOC) - bits |= EXTENT_NORESERVE; + bits |= EXTENT_NORESERVE | EXTENT_DEDUPE; if (delete) bits |= ~EXTENT_CTLBITS; @@ -1470,6 +1470,7 @@ static noinline u64 find_delalloc_range(struct extent_io_tree *tree, u64 cur_start = *start; u64 found = 0; u64 total_bytes = 0; + unsigned int pre_state; spin_lock(&tree->lock); @@ -1487,7 +1488,8 @@ static noinline u64 find_delalloc_range(struct extent_io_tree *tree, while (1) { state = rb_entry(node, struct extent_state, rb_node); if (found && (state->start != cur_start || - (state->state & EXTENT_BOUNDARY))) { + (state->state & EXTENT_BOUNDARY) || + (state->state ^ pre_state) & EXTENT_DEDUPE)) { goto out; } if (!(state->state & EXTENT_DELALLOC)) { @@ -1503,6 +1505,7 @@ static noinline u64 find_delalloc_range(struct extent_io_tree *tree, found++; *end = state->end; cur_start = state->end + 1; + pre_state = state->state; node = rb_next(node); total_bytes += state->end - state->start + 1; if (total_bytes >= max_bytes) diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 369daa5d4f73..5c802139dfb6 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -25,6 +25,7 @@ #define EXTENT_QGROUP_RESERVED (1U << 16) #define EXTENT_CLEAR_DATA_RESV (1U << 17) #define EXTENT_DELALLOC_NEW (1U << 18) +#define EXTENT_DEDUPE (1U << 19) #define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK) #define EXTENT_DO_ACCOUNTING (EXTENT_CLEAR_META_RESV | \ EXTENT_CLEAR_DATA_RESV) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index d655c9356d9e..3b84da9d3c00 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -26,6 +26,7 @@ #include "volumes.h" #include "qgroup.h" #include "compression.h" +#include "dedupe.h" static struct kmem_cache *btrfs_inode_defrag_cachep; /* @@ -1612,6 +1613,9 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, if (!pages) return -ENOMEM; + if (inode_need_dedupe(inode)) + reserve_type = BTRFS_RESERVE_DEDUPE; + while (iov_iter_count(i) > 0) { size_t offset = pos & (PAGE_SIZE - 1); size_t sector_offset; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index ff8af15c9039..b0e718076929 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -352,6 +352,7 @@ struct async_extent { struct page **pages; unsigned long nr_pages; int compress_type; + struct btrfs_dedupe_hash *hash; struct list_head list; }; @@ -364,6 +365,7 @@ struct async_cow { unsigned int write_flags; struct list_head extents; struct btrfs_work work; + enum btrfs_metadata_reserve_type reserve_type; }; static noinline int add_async_extent(struct async_cow *cow, @@ -371,7 +373,8 @@ static noinline int add_async_extent(struct async_cow *cow, u64 compressed_size, struct page **pages, unsigned long nr_pages, - int compress_type) + int compress_type, + struct btrfs_dedupe_hash *hash) { struct async_extent *async_extent; @@ -383,6 +386,7 @@ static noinline int add_async_extent(struct async_cow *cow, async_extent->pages = pages; async_extent->nr_pages = nr_pages; async_extent->compress_type = compress_type; + async_extent->hash = hash; list_add_tail(&async_extent->list, &cow->extents); return 0; } @@ -623,7 +627,7 @@ static noinline void compress_file_range(struct inode *inode, */ add_async_extent(async_cow, start, total_in, total_compressed, pages, nr_pages, - compress_type); + compress_type, NULL); if (start + total_in < end) { start += total_in; @@ -669,7 +673,7 @@ static noinline void compress_file_range(struct inode *inode, if (redirty) extent_range_redirty_for_io(inode, start, end); add_async_extent(async_cow, start, end - start + 1, 0, NULL, 0, - BTRFS_COMPRESS_NONE); + BTRFS_COMPRESS_NONE, NULL); *num_added += 1; return; @@ -698,6 +702,38 @@ static void free_async_extent_pages(struct async_extent *async_extent) async_extent->pages = NULL; } +static void end_dedupe_extent(struct inode *inode, u64 start, + u32 len, unsigned long page_ops) +{ + int i; + unsigned int nr_pages = len / PAGE_SIZE; + struct page *page; + + for (i = 0; i < nr_pages; i++) { + page = find_get_page(inode->i_mapping, + start >> PAGE_SHIFT); + /* page should be already locked by caller */ + if (WARN_ON(!page)) + continue; + + /* We need to do this by ourselves as we skipped IO */ + if (page_ops & PAGE_CLEAR_DIRTY) + clear_page_dirty_for_io(page); + if (page_ops & PAGE_SET_WRITEBACK) + set_page_writeback(page); + + end_extent_writepage(page, 0, start, + start + PAGE_SIZE - 1); + if (page_ops & PAGE_END_WRITEBACK) + end_page_writeback(page); + if (page_ops & PAGE_UNLOCK) + unlock_page(page); + + start += PAGE_SIZE; + put_page(page); + } +} + /* * phase two of compressed writeback. This is the ordered portion * of the code, which only gets called in the order the work was @@ -714,6 +750,7 @@ static noinline void submit_compressed_extents(struct inode *inode, struct extent_map *em; struct btrfs_root *root = BTRFS_I(inode)->root; struct extent_io_tree *io_tree; + struct btrfs_dedupe_hash *hash; int ret = 0; again: @@ -723,6 +760,7 @@ static noinline void submit_compressed_extents(struct inode *inode, list_del(&async_extent->list); io_tree = &BTRFS_I(inode)->io_tree; + hash = async_extent->hash; retry: /* did the compression code fall back to uncompressed IO? */ @@ -742,7 +780,7 @@ static noinline void submit_compressed_extents(struct inode *inode, async_extent->start + async_extent->ram_size - 1, &page_started, &nr_written, 0, - NULL); + hash); /* JDM XXX */ @@ -752,14 +790,26 @@ static noinline void submit_compressed_extents(struct inode *inode, * and IO for us. Otherwise, we need to submit * all those pages down to the drive. */ - if (!page_started && !ret) - extent_write_locked_range(inode, - async_extent->start, - async_extent->start + - async_extent->ram_size - 1, - WB_SYNC_ALL); - else if (ret) + if (!page_started && !ret) { + /* Skip IO for dedupe async_extent */ + if (btrfs_dedupe_hash_hit(hash)) + end_dedupe_extent(inode, + async_extent->start, + async_extent->ram_size, + PAGE_CLEAR_DIRTY | + PAGE_SET_WRITEBACK | + PAGE_END_WRITEBACK | + PAGE_UNLOCK); + else + extent_write_locked_range(inode, + async_extent->start, + async_extent->start + + async_extent->ram_size - 1, + WB_SYNC_ALL); + } else if (ret) { unlock_page(async_cow->locked_page); + } + kfree(hash); kfree(async_extent); cond_resched(); continue; @@ -863,6 +913,7 @@ static noinline void submit_compressed_extents(struct inode *inode, free_async_extent_pages(async_extent); } alloc_hint = ins.objectid + ins.offset; + kfree(hash); kfree(async_extent); cond_resched(); } @@ -883,6 +934,7 @@ static noinline void submit_compressed_extents(struct inode *inode, PAGE_SET_WRITEBACK | PAGE_END_WRITEBACK | PAGE_SET_ERROR); free_async_extent_pages(async_extent); + kfree(hash); kfree(async_extent); goto again; } @@ -997,13 +1049,19 @@ static noinline int cow_file_range(struct inode *inode, while (num_bytes > 0) { cur_alloc_size = num_bytes; - ret = btrfs_reserve_extent(root, cur_alloc_size, cur_alloc_size, + if (btrfs_dedupe_hash_hit(hash)) { + ins.objectid = hash->bytenr; + ins.offset = hash->num_bytes; + } else { + ret = btrfs_reserve_extent(root, cur_alloc_size, + cur_alloc_size, fs_info->sectorsize, 0, alloc_hint, &ins, 1, 1); - if (ret < 0) - goto out_unlock; + if (ret < 0) + goto out_unlock; + extent_reserved = true; + } cur_alloc_size = ins.offset; - extent_reserved = true; ram_size = ins.offset; em = create_io_em(inode, start, ins.offset, /* len */ @@ -1020,8 +1078,9 @@ static noinline int cow_file_range(struct inode *inode, } free_extent_map(em); - ret = btrfs_add_ordered_extent(inode, start, ins.objectid, - ram_size, cur_alloc_size, 0); + ret = btrfs_add_ordered_extent_dedupe(inode, start, + ins.objectid, cur_alloc_size, ins.offset, + 0, hash); if (ret) goto out_drop_extent_cache; @@ -1045,7 +1104,14 @@ static noinline int cow_file_range(struct inode *inode, start + ram_size - 1, 0); } - btrfs_dec_block_group_reservations(fs_info, ins.objectid); + /* + * Hash hit didn't allocate extent, no need to dec bg + * reservation. + * Or we will underflow reservations and block balance. + */ + if (!btrfs_dedupe_hash_hit(hash)) + btrfs_dec_block_group_reservations(fs_info, + ins.objectid); /* we're not doing compressed IO, don't unlock the first * page (which the caller expects to stay locked), don't @@ -1119,6 +1185,79 @@ static noinline int cow_file_range(struct inode *inode, goto out; } +static int hash_file_ranges(struct inode *inode, u64 start, u64 end, + struct async_cow *async_cow, int *num_added) +{ + struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info; + struct page *locked_page = async_cow->locked_page; + u16 hash_algo; + u64 dedupe_bs; + u64 cur_offset = start; + int ret = 0; + + /* If dedupe is not enabled, don't split extent into dedupe_bs */ + if (fs_info->dedupe_enabled && dedupe_info) { + dedupe_bs = dedupe_info->blocksize; + hash_algo = dedupe_info->hash_algo; + } else { + dedupe_bs = SZ_128M; + /* Just dummy, to avoid access NULL pointer */ + hash_algo = BTRFS_DEDUPE_HASH_SHA256; + } + + while (cur_offset < end) { + struct btrfs_dedupe_hash *hash = NULL; + u64 len; + + len = min(end + 1 - cur_offset, dedupe_bs); + if (len < dedupe_bs) + goto next; + + hash = btrfs_dedupe_alloc_hash(hash_algo); + if (!hash) { + ret = -ENOMEM; + goto out; + } + ret = btrfs_dedupe_calc_hash(fs_info, inode, cur_offset, hash); + if (ret < 0) { + kfree(hash); + goto out; + } + + ret = btrfs_dedupe_search(fs_info, inode, cur_offset, hash); + if (ret < 0) { + kfree(hash); + goto out; + } + ret = 0; + +next: + /* Redirty the locked page if it corresponds to our extent */ + if (page_offset(locked_page) >= start && + page_offset(locked_page) <= end) + __set_page_dirty_nobuffers(locked_page); + + add_async_extent(async_cow, cur_offset, len, 0, NULL, 0, + BTRFS_COMPRESS_NONE, hash); + cur_offset += len; + (*num_added)++; + } +out: + /* + * Caller won't unlock pages, so if error happens, we must unlock + * pages by ourselves. + */ + if (ret) + extent_clear_unlock_delalloc(inode, cur_offset, + end, end, NULL, EXTENT_LOCKED | EXTENT_DO_ACCOUNTING | + EXTENT_DELALLOC | EXTENT_DEFRAG, PAGE_UNLOCK | + PAGE_CLEAR_DIRTY | PAGE_SET_WRITEBACK | + PAGE_END_WRITEBACK | PAGE_SET_ERROR); + return ret; +} + /* * work queue call back to started compression on a file and pages */ @@ -1126,11 +1265,17 @@ static noinline void async_cow_start(struct btrfs_work *work) { struct async_cow *async_cow; int num_added = 0; + int ret = 0; async_cow = container_of(work, struct async_cow, work); - compress_file_range(async_cow->inode, async_cow->locked_page, - async_cow->start, async_cow->end, async_cow, - &num_added); + if (async_cow->reserve_type == BTRFS_RESERVE_DEDUPE) + ret = hash_file_ranges(async_cow->inode, async_cow->start, + async_cow->end, async_cow, &num_added); + else + compress_file_range(async_cow->inode, async_cow->locked_page, + async_cow->start, async_cow->end, async_cow, + &num_added); + if (num_added == 0) { btrfs_add_delayed_iput(async_cow->inode); async_cow->inode = NULL; @@ -1175,11 +1320,13 @@ static noinline void async_cow_free(struct btrfs_work *work) static int cow_file_range_async(struct inode *inode, struct page *locked_page, u64 start, u64 end, int *page_started, unsigned long *nr_written, - unsigned int write_flags) + unsigned int write_flags, + enum btrfs_metadata_reserve_type reserve_type) { struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct async_cow *async_cow; struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info; unsigned long nr_pages; u64 cur_end; @@ -1193,11 +1340,16 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page, async_cow->locked_page = locked_page; async_cow->start = start; async_cow->write_flags = write_flags; + async_cow->reserve_type = reserve_type; if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS && !btrfs_test_opt(fs_info, FORCE_COMPRESS)) cur_end = end; - else + else if (reserve_type == BTRFS_RESERVE_DEDUPE) { + u64 len = max_t(u64, SZ_512K, dedupe_info->blocksize); + + cur_end = min(end, start + len - 1); + } else cur_end = min(end, start + SZ_512K - 1); async_cow->end = cur_end; @@ -1588,6 +1740,14 @@ static int run_delalloc_range(void *private_data, struct page *locked_page, int ret; int force_cow = need_force_cow(inode, start, end); unsigned int write_flags = wbc_to_write_flags(wbc); + struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; + enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; + int need_dedupe; + + need_dedupe = test_range_bit(io_tree, start, end, + EXTENT_DEDUPE, 1, NULL); + if (need_dedupe) + reserve_type = BTRFS_RESERVE_DEDUPE; if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW && !force_cow) { ret = run_delalloc_nocow(inode, locked_page, start, end, @@ -1595,7 +1755,7 @@ static int run_delalloc_range(void *private_data, struct page *locked_page, } else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC && !force_cow) { ret = run_delalloc_nocow(inode, locked_page, start, end, page_started, 0, nr_written); - } else if (!inode_need_compress(inode, start, end)) { + } else if (!inode_need_compress(inode, start, end) && !need_dedupe) { ret = cow_file_range(inode, locked_page, start, end, end, page_started, nr_written, 1, NULL); } else { @@ -1603,7 +1763,7 @@ static int run_delalloc_range(void *private_data, struct page *locked_page, &BTRFS_I(inode)->runtime_flags); ret = cow_file_range_async(inode, locked_page, start, end, page_started, nr_written, - write_flags); + write_flags, reserve_type); } if (ret) btrfs_cleanup_ordered_extents(inode, start, end - start + 1); @@ -1622,7 +1782,9 @@ static void btrfs_split_extent_hook(void *private_data, if (!(orig->state & EXTENT_DELALLOC)) return; - max_extent_size = btrfs_max_extent_size(reserve_type); + if (orig->state & EXTENT_DEDUPE) + reserve_type = BTRFS_RESERVE_DEDUPE; + max_extent_size = btrfs_max_extent_size(BTRFS_I(inode), reserve_type); size = orig->end - orig->start + 1; if (size > max_extent_size) { @@ -1666,7 +1828,9 @@ static void btrfs_merge_extent_hook(void *private_data, if (!(other->state & EXTENT_DELALLOC)) return; - max_extent_size = btrfs_max_extent_size(reserve_type); + if (other->state & EXTENT_DEDUPE) + reserve_type = BTRFS_RESERVE_DEDUPE; + max_extent_size = btrfs_max_extent_size(BTRFS_I(inode), reserve_type); if (new->start > other->start) new_size = new->end - other->start + 1; @@ -1791,7 +1955,10 @@ static void btrfs_set_bit_hook(void *private_data, BTRFS_RESERVE_NORMAL; bool do_list = !btrfs_is_free_space_inode(BTRFS_I(inode)); - max_extent_size = btrfs_max_extent_size(reserve_type); + if (*bits & EXTENT_DEDUPE) + reserve_type = BTRFS_RESERVE_DEDUPE; + max_extent_size = btrfs_max_extent_size(BTRFS_I(inode), + reserve_type); num_extents = count_max_extents(len, max_extent_size); spin_lock(&BTRFS_I(inode)->lock); @@ -1852,7 +2019,9 @@ static void btrfs_clear_bit_hook(void *private_data, struct btrfs_root *root = inode->root; bool do_list = !btrfs_is_free_space_inode(inode); - max_extent_size = btrfs_max_extent_size(reserve_type); + if (state->state & EXTENT_DEDUPE) + reserve_type = BTRFS_RESERVE_DEDUPE; + max_extent_size = btrfs_max_extent_size(inode, reserve_type); num_extents = count_max_extents(len, max_extent_size); spin_lock(&inode->lock); @@ -2078,9 +2247,17 @@ int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end, struct extent_state **cached_state, enum btrfs_metadata_reserve_type reserve_type) { + unsigned int bits; + + if (reserve_type == BTRFS_RESERVE_DEDUPE) + bits = EXTENT_DELALLOC | EXTENT_DEDUPE | EXTENT_UPTODATE | + extra_bits; + else + bits = EXTENT_DELALLOC | EXTENT_UPTODATE | extra_bits; + WARN_ON((end & (PAGE_SIZE - 1)) == 0); return set_extent_delalloc(&BTRFS_I(inode)->io_tree, start, end, - extra_bits, cached_state); + bits, cached_state); } @@ -2143,6 +2320,9 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work) goto again; } + if (inode_need_dedupe(inode)) + reserve_type = BTRFS_RESERVE_DEDUPE; + ret = btrfs_delalloc_reserve_space(inode, &data_reserved, page_start, PAGE_SIZE, reserve_type); if (ret) { @@ -2217,7 +2397,8 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, u64 disk_bytenr, u64 disk_num_bytes, u64 num_bytes, u64 ram_bytes, u8 compression, u8 encryption, - u16 other_encoding, int extent_type) + u16 other_encoding, int extent_type, + struct btrfs_dedupe_hash *hash) { struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_file_extent_item *fi; @@ -2281,17 +2462,43 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, ins.offset = disk_num_bytes; ins.type = BTRFS_EXTENT_ITEM_KEY; - /* - * Release the reserved range from inode dirty range map, as it is - * already moved into delayed_ref_head - */ - ret = btrfs_qgroup_release_data(inode, file_pos, ram_bytes); - if (ret < 0) - goto out; - qg_released = ret; - ret = btrfs_alloc_reserved_file_extent(trans, root, - btrfs_ino(BTRFS_I(inode)), - file_pos, qg_released, &ins); + if (btrfs_dedupe_hash_hit(hash)) { + /* + * Hash hit won't create a new data extent, so its reserved + * space won't be freed by new delayed_ref_head. + * Manually free it. + */ + btrfs_free_reserved_data_space(inode, NULL, file_pos, + ram_bytes); + } else { + /* + * Hash miss or none-dedupe write, will create a new data + * extent, we need to release the qgroup reserved data space. + */ + ret = btrfs_qgroup_release_data(inode, file_pos, ram_bytes); + if (ret < 0) + goto out; + qg_released = ret; + ret = btrfs_alloc_reserved_file_extent(trans, root, + btrfs_ino(BTRFS_I(inode)), file_pos, + qg_released, &ins); + if (ret < 0) + goto out; + } + + /* Add missed hash into dedupe tree */ + if (hash && hash->bytenr == 0) { + hash->bytenr = ins.objectid; + hash->num_bytes = ins.offset; + + /* + * Here we ignore dedupe_add error, as even it failed, + * it won't corrupt the filesystem. It will only only slightly + * reduce dedup rate + */ + btrfs_dedupe_add(root->fs_info, hash); + } + out: btrfs_free_path(path); @@ -2976,6 +3183,7 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) bool clear_new_delalloc_bytes = false; bool clear_reserved_extent = true; enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; + int hash_hit = btrfs_dedupe_hash_hit(ordered_extent->hash); if (!test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) && !test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags) && @@ -3062,6 +3270,9 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) if (test_bit(BTRFS_ORDERED_COMPRESSED, &ordered_extent->flags)) compress_type = ordered_extent->compress_type; + else if (ordered_extent->hash) + reserve_type = BTRFS_RESERVE_DEDUPE; + if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) { BUG_ON(compress_type); btrfs_qgroup_free_data(inode, NULL, ordered_extent->file_offset, @@ -3078,13 +3289,16 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) ordered_extent->disk_len, logical_len, logical_len, compress_type, 0, 0, - BTRFS_FILE_EXTENT_REG); - if (!ret) { + BTRFS_FILE_EXTENT_REG, + ordered_extent->hash); + if (!ret) clear_reserved_extent = false; + + /* Hash hit case doesn't reserve delalloc bytes */ + if (!ret && !hash_hit) btrfs_release_delalloc_bytes(fs_info, ordered_extent->start, ordered_extent->disk_len); - } } unpin_extent_cache(&BTRFS_I(inode)->extent_tree, ordered_extent->file_offset, ordered_extent->len, @@ -3149,9 +3363,12 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) * If we made it past insert_reserved_file_extent before we * errored out then we don't need to do this as the accounting * has already been done. + * + * For hash hit case, never free that extent, as it's being used + * by others. */ if ((ret || !logical_len) && - clear_reserved_extent && + clear_reserved_extent && !hash_hit && !test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) && !test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) btrfs_free_reserved_extent(fs_info, @@ -3159,7 +3376,6 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) ordered_extent->disk_len, 1); } - /* * This needs to be done to make sure anybody waiting knows we are done * updating everything for this ordered extent. @@ -4875,6 +5091,9 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len, u64 block_end; enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL; + if (inode_need_dedupe(inode)) + reserve_type = BTRFS_RESERVE_DEDUPE; + if (IS_ALIGNED(offset, blocksize) && (!len || IS_ALIGNED(len, blocksize))) goto out; @@ -8859,6 +9078,9 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) page_end = page_start + PAGE_SIZE - 1; end = page_end; + if (inode_need_dedupe(inode)) + reserve_type = BTRFS_RESERVE_DEDUPE; + /* * Reserving delalloc space after obtaining the page lock can lead to * deadlock. For example, if a dirty page is locked by this function @@ -10301,7 +10523,8 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, cur_offset, ins.objectid, ins.offset, ins.offset, ins.offset, 0, 0, 0, - BTRFS_FILE_EXTENT_PREALLOC); + BTRFS_FILE_EXTENT_PREALLOC, + NULL); if (ret) { btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 0); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 93c75f6323f6..4b84048e7e9e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -43,6 +43,7 @@ #include "qgroup.h" #include "tree-log.h" #include "compression.h" +#include "dedupe.h" #ifdef CONFIG_64BIT /* If we have a 32-bit userspace and 64-bit kernel, then the UAPI diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 11476ad7387a..b7c304c6e741 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -20,6 +20,7 @@ #include "inode-map.h" #include "qgroup.h" #include "print-tree.h" +#include "dedupe.h" /* * backref_node, mapping_node and tree_block start with this @@ -3151,6 +3152,9 @@ static int relocate_file_extent_cluster(struct inode *inode, if (!cluster->nr) return 0; + if (inode_need_dedupe(inode)) + reserve_type = BTRFS_RESERVE_DEDUPE; + ra = kzalloc(sizeof(*ra), GFP_NOFS); if (!ra) return -ENOMEM; @@ -4023,6 +4027,20 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) rc->search_start = key.objectid; } } + /* + * This data extent will be replaced, but normal dedupe_del() + * will only happen at run_delayed_ref() time, which is too + * late, so delete dedupe_hash early to prevent its ref get + * increased during relocation + */ + if (rc->stage == MOVE_DATA_EXTENTS && + (flags & BTRFS_EXTENT_FLAG_DATA)) { + ret = btrfs_dedupe_del(fs_info, key.objectid); + if (ret < 0) { + err = ret; + break; + } + } btrfs_end_transaction_throttle(trans); btrfs_btree_balance_dirty(fs_info); From patchwork Tue Nov 6 06:41:20 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669719 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6B39715A6 for ; Tue, 6 Nov 2018 06:41:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5706129DD6 for ; Tue, 6 Nov 2018 06:41:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4913629DD2; Tue, 6 Nov 2018 06:41:50 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 774FE29DD6 for ; Tue, 6 Nov 2018 06:41:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387610AbeKFQF3 (ORCPT ); Tue, 6 Nov 2018 11:05:29 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:32866 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387566AbeKFQFV (ORCPT ); Tue, 6 Nov 2018 11:05:21 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417669" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:38 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id D8B744B714DF; Tue, 6 Nov 2018 14:41:36 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:40 +0800 From: Lu Fengqi To: CC: Wang Xiaoguang , Qu Wenruo Subject: [PATCH v15.1 11/13] btrfs: dedupe: Add ioctl for inband deduplication Date: Tue, 6 Nov 2018 14:41:20 +0800 Message-ID: <20181106064122.6154-12-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: D8B744B714DF.AD615 X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Wang Xiaoguang Add ioctl interface for inband deduplication, which includes: 1) enable 2) disable 3) status And a pseudo RO compat flag, to imply that btrfs now supports inband dedup. However we don't add any ondisk format change, it's just a pseudo RO compat flag. All these ioctl interfaces are state-less, which means caller don't need to bother previous dedupe state before calling them, and only need to care the final desired state. For example, if user want to enable dedupe with specified block size and limit, just fill the ioctl structure and call enable ioctl. No need to check if dedupe is already running. These ioctls will handle things like re-configure or disable quite well. Also, for invalid parameters, enable ioctl interface will set the field of the first encountered invalid parameter to (-1) to inform caller. While for limit_nr/limit_mem, the value will be (0). Signed-off-by: Qu Wenruo Signed-off-by: Wang Xiaoguang Signed-off-by: Lu Fengqi --- fs/btrfs/dedupe.c | 50 ++++++++++++++++++++++ fs/btrfs/dedupe.h | 17 +++++--- fs/btrfs/disk-io.c | 3 ++ fs/btrfs/ioctl.c | 85 ++++++++++++++++++++++++++++++++++++++ fs/btrfs/sysfs.c | 2 + include/uapi/linux/btrfs.h | 12 +++++- 6 files changed, 163 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c index 6199215022e6..76a967cca68e 100644 --- a/fs/btrfs/dedupe.c +++ b/fs/btrfs/dedupe.c @@ -29,6 +29,35 @@ static inline struct inmem_hash *inmem_alloc_hash(u16 algo) GFP_NOFS); } +void btrfs_dedupe_status(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs) +{ + struct btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info; + + if (!fs_info->dedupe_enabled || !dedupe_info) { + dargs->status = 0; + dargs->blocksize = 0; + dargs->backend = 0; + dargs->hash_algo = 0; + dargs->limit_nr = 0; + dargs->current_nr = 0; + memset(dargs->__unused, -1, sizeof(dargs->__unused)); + return; + } + mutex_lock(&dedupe_info->lock); + dargs->status = 1; + dargs->blocksize = dedupe_info->blocksize; + dargs->backend = dedupe_info->backend; + dargs->hash_algo = dedupe_info->hash_algo; + dargs->limit_nr = dedupe_info->limit_nr; + dargs->limit_mem = dedupe_info->limit_nr * + (sizeof(struct inmem_hash) + + btrfs_hash_sizes[dedupe_info->hash_algo]); + dargs->current_nr = dedupe_info->current_nr; + mutex_unlock(&dedupe_info->lock); + memset(dargs->__unused, -1, sizeof(dargs->__unused)); +} + static struct btrfs_dedupe_info * init_dedupe_info(struct btrfs_ioctl_dedupe_args *dargs) { @@ -402,6 +431,27 @@ static void unblock_all_writers(struct btrfs_fs_info *fs_info) percpu_up_write(sb->s_writers.rw_sem + SB_FREEZE_WRITE - 1); } +int btrfs_dedupe_cleanup(struct btrfs_fs_info *fs_info) +{ + struct btrfs_dedupe_info *dedupe_info; + + fs_info->dedupe_enabled = 0; + /* same as disable */ + smp_wmb(); + dedupe_info = fs_info->dedupe_info; + fs_info->dedupe_info = NULL; + + if (!dedupe_info) + return 0; + + if (dedupe_info->backend == BTRFS_DEDUPE_BACKEND_INMEMORY) + inmem_destroy(dedupe_info); + + crypto_free_shash(dedupe_info->dedupe_driver); + kfree(dedupe_info); + return 0; +} + int btrfs_dedupe_disable(struct btrfs_fs_info *fs_info) { struct btrfs_dedupe_info *dedupe_info; diff --git a/fs/btrfs/dedupe.h b/fs/btrfs/dedupe.h index 8157b17c4d11..fdd00355d6b5 100644 --- a/fs/btrfs/dedupe.h +++ b/fs/btrfs/dedupe.h @@ -90,6 +90,15 @@ static inline struct btrfs_dedupe_hash *btrfs_dedupe_alloc_hash(u16 algo) int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_dedupe_args *dargs); + +/* + * Get inband dedupe info + * Since it needs to access different backends' hash size, which + * is not exported, we need such simple function. + */ +void btrfs_dedupe_status(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs); + /* * Disable dedupe and invalidate all its dedupe data. * Called at dedupe disable time. @@ -101,12 +110,10 @@ int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info, int btrfs_dedupe_disable(struct btrfs_fs_info *fs_info); /* - * Get current dedupe status. - * Return 0 for success - * No possible error yet + * Cleanup current btrfs_dedupe_info + * Called in umount time */ -void btrfs_dedupe_status(struct btrfs_fs_info *fs_info, - struct btrfs_ioctl_dedupe_args *dargs); +int btrfs_dedupe_cleanup(struct btrfs_fs_info *fs_info); /* * Calculate hash for dedupe. diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index d1fa9d90cc8f..f15e89d9d26a 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -38,6 +38,7 @@ #include "compression.h" #include "tree-checker.h" #include "ref-verify.h" +#include "dedupe.h" #ifdef CONFIG_X86 #include @@ -3983,6 +3984,8 @@ void close_ctree(struct btrfs_fs_info *fs_info) btrfs_free_qgroup_config(fs_info); ASSERT(list_empty(&fs_info->delalloc_roots)); + btrfs_dedupe_cleanup(fs_info); + if (percpu_counter_sum(&fs_info->delalloc_bytes)) { btrfs_info(fs_info, "at unmount delalloc count %lld", percpu_counter_sum(&fs_info->delalloc_bytes)); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4b84048e7e9e..5dd0c318fd40 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3631,6 +3631,89 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, return ret; } +int btrfs_dedupe_file_range(struct file *src_file, loff_t src_loff, + struct file *dst_file, loff_t dst_loff, + u64 olen) +{ + struct inode *src = file_inode(src_file); + struct inode *dst = file_inode(dst_file); + u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize; + + if (WARN_ON_ONCE(bs < PAGE_SIZE)) { + /* + * Btrfs does not support blocksize < page_size. As a + * result, btrfs_cmp_data() won't correctly handle + * this situation without an update. + */ + return -EINVAL; + } + + return btrfs_extent_same(src, src_loff, olen, dst, dst_loff); +} + +static long btrfs_ioctl_dedupe_ctl(struct btrfs_root *root, void __user *args) +{ + struct btrfs_ioctl_dedupe_args *dargs; + struct btrfs_fs_info *fs_info = root->fs_info; + int ret = 0; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + dargs = memdup_user(args, sizeof(*dargs)); + if (IS_ERR(dargs)) { + ret = PTR_ERR(dargs); + return ret; + } + + if (dargs->cmd >= BTRFS_DEDUPE_CTL_LAST) { + ret = -EINVAL; + goto out; + } + switch (dargs->cmd) { + case BTRFS_DEDUPE_CTL_ENABLE: + mutex_lock(&fs_info->dedupe_ioctl_lock); + ret = btrfs_dedupe_enable(fs_info, dargs); + /* + * Also copy the result to caller for further use + * if enable succeeded. + * For error case, dargs is already set up with + * special values indicating error reason. + */ + if (!ret) + btrfs_dedupe_status(fs_info, dargs); + mutex_unlock(&fs_info->dedupe_ioctl_lock); + break; + case BTRFS_DEDUPE_CTL_DISABLE: + mutex_lock(&fs_info->dedupe_ioctl_lock); + ret = btrfs_dedupe_disable(fs_info); + btrfs_dedupe_status(fs_info, dargs); + mutex_unlock(&fs_info->dedupe_ioctl_lock); + break; + case BTRFS_DEDUPE_CTL_STATUS: + mutex_lock(&fs_info->dedupe_ioctl_lock); + btrfs_dedupe_status(fs_info, dargs); + mutex_unlock(&fs_info->dedupe_ioctl_lock); + break; + default: + /* + * Use this return value to inform progs that kernel + * doesn't support such new command. + */ + ret = -EOPNOTSUPP; + goto out; + } + /* + * All ioctl subcommand will modify user dargs, + * Don't override return value unless copy fails + */ + if (copy_to_user(args, dargs, sizeof(*dargs))) + ret = -EFAULT; +out: + kfree(dargs); + return ret; +} + static int clone_finish_inode_update(struct btrfs_trans_handle *trans, struct inode *inode, u64 endoff, @@ -5974,6 +6057,8 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_get_fslabel(file, argp); case BTRFS_IOC_SET_FSLABEL: return btrfs_ioctl_set_fslabel(file, argp); + case BTRFS_IOC_DEDUPE_CTL: + return btrfs_ioctl_dedupe_ctl(root, argp); case BTRFS_IOC_GET_SUPPORTED_FEATURES: return btrfs_ioctl_get_supported_features(argp); case BTRFS_IOC_GET_FEATURES: diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 3717c864ba23..26f6f283fac2 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -192,6 +192,7 @@ BTRFS_FEAT_ATTR_INCOMPAT(raid56, RAID56); BTRFS_FEAT_ATTR_INCOMPAT(skinny_metadata, SKINNY_METADATA); BTRFS_FEAT_ATTR_INCOMPAT(no_holes, NO_HOLES); BTRFS_FEAT_ATTR_COMPAT_RO(free_space_tree, FREE_SPACE_TREE); +BTRFS_FEAT_ATTR_COMPAT_RO(dedupe, DEDUPE); static struct attribute *btrfs_supported_feature_attrs[] = { BTRFS_FEAT_ATTR_PTR(mixed_backref), @@ -205,6 +206,7 @@ static struct attribute *btrfs_supported_feature_attrs[] = { BTRFS_FEAT_ATTR_PTR(skinny_metadata), BTRFS_FEAT_ATTR_PTR(no_holes), BTRFS_FEAT_ATTR_PTR(free_space_tree), + BTRFS_FEAT_ATTR_PTR(dedupe), NULL }; diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index ba879ac931f2..5ee51fac3652 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -253,6 +253,7 @@ struct btrfs_ioctl_fs_info_args { * first mount when booting older kernel versions. */ #define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID (1ULL << 1) +#define BTRFS_FEATURE_COMPAT_RO_DEDUPE (1ULL << 2) #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0) #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1) @@ -685,7 +686,14 @@ struct btrfs_ioctl_get_dev_stats { /* Default dedupe limit on number of hash */ #define BTRFS_DEDUPE_LIMIT_NR_DEFAULT (32 * 1024) - +/* + * de-duplication control modes + * For re-config, re-enable will handle it + */ +#define BTRFS_DEDUPE_CTL_ENABLE 1 +#define BTRFS_DEDUPE_CTL_DISABLE 2 +#define BTRFS_DEDUPE_CTL_STATUS 3 +#define BTRFS_DEDUPE_CTL_LAST 4 /* * This structure is used for dedupe enable/disable/configure * and status ioctl. @@ -961,6 +969,8 @@ enum btrfs_err_code { struct btrfs_ioctl_dev_replace_args) #define BTRFS_IOC_FILE_EXTENT_SAME _IOWR(BTRFS_IOCTL_MAGIC, 54, \ struct btrfs_ioctl_same_args) +#define BTRFS_IOC_DEDUPE_CTL _IOWR(BTRFS_IOCTL_MAGIC, 55, \ + struct btrfs_ioctl_dedupe_args) #define BTRFS_IOC_GET_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \ struct btrfs_ioctl_feature_flags) #define BTRFS_IOC_SET_FEATURES _IOW(BTRFS_IOCTL_MAGIC, 57, \ From patchwork Tue Nov 6 06:41:21 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669707 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B7A1915A6 for ; Tue, 6 Nov 2018 06:41:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A68DE29E1E for ; Tue, 6 Nov 2018 06:41:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A48B029DED; Tue, 6 Nov 2018 06:41:45 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 D805529E89 for ; Tue, 6 Nov 2018 06:41:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387578AbeKFQFV (ORCPT ); Tue, 6 Nov 2018 11:05:21 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:31748 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387527AbeKFQFV (ORCPT ); Tue, 6 Nov 2018 11:05:21 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417668" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:38 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id 705414B71EA0; Tue, 6 Nov 2018 14:41:37 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:41 +0800 From: Lu Fengqi To: CC: Qu Wenruo Subject: [PATCH v15.1 12/13] btrfs: relocation: Enhance error handling to avoid BUG_ON Date: Tue, 6 Nov 2018 14:41:21 +0800 Message-ID: <20181106064122.6154-13-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: 705414B71EA0.A8AF1 X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Qu Wenruo Since the introduction of btrfs dedupe tree, it's possible that balance can race with dedupe disabling. When this happens, dedupe_enabled will make btrfs_get_fs_root() return PTR_ERR(-ENOENT). But due to a bug in error handling branch, when this happens backref_cache->nr_nodes is increased but the node is neither added to backref_cache or nr_nodes decreased. Causing BUG_ON() in backref_cache_cleanup() [ 2611.668810] ------------[ cut here ]------------ [ 2611.669946] kernel BUG at /home/sat/ktest/linux/fs/btrfs/relocation.c:243! [ 2611.670572] invalid opcode: 0000 [#1] SMP [ 2611.686797] Call Trace: [ 2611.687034] [] btrfs_relocate_block_group+0x1b3/0x290 [btrfs] [ 2611.687706] [] btrfs_relocate_chunk.isra.40+0x47/0xd0 [btrfs] [ 2611.688385] [] btrfs_balance+0xb22/0x11e0 [btrfs] [ 2611.688966] [] btrfs_ioctl_balance+0x391/0x3a0 [btrfs] [ 2611.689587] [] btrfs_ioctl+0x1650/0x2290 [btrfs] [ 2611.690145] [] ? lru_cache_add+0x3a/0x80 [ 2611.690647] [] ? lru_cache_add_active_or_unevictable+0x4c/0xc0 [ 2611.691310] [] ? handle_mm_fault+0xcd4/0x17f0 [ 2611.691842] [] ? cp_new_stat+0x153/0x180 [ 2611.692342] [] ? __vma_link_rb+0xfd/0x110 [ 2611.692842] [] ? vma_link+0xb9/0xc0 [ 2611.693303] [] do_vfs_ioctl+0xa1/0x5a0 [ 2611.693781] [] ? __do_page_fault+0x1b4/0x400 [ 2611.694310] [] SyS_ioctl+0x41/0x70 [ 2611.694758] [] entry_SYSCALL_64_fastpath+0x12/0x71 [ 2611.695331] Code: ff 48 8b 45 bf 49 83 af a8 05 00 00 01 49 89 87 a0 05 00 00 e9 2e fd ff ff b8 f4 ff ff ff e9 e4 fb ff ff 0f 0b 0f 0b 0f 0b 0f 0b <0f> 0b 0f 0b 41 89 c6 e9 b8 fb ff ff e8 9e a6 e8 e0 4c 89 e7 44 [ 2611.697870] RIP [] relocate_block_group+0x741/0x7a0 [btrfs] [ 2611.698818] RSP This patch will call remove_backref_node() in error handling branch, and cache the returned -ENOENT in relocate_tree_block() and continue balancing. Reported-by: Satoru Takeuchi Signed-off-by: Qu Wenruo Signed-off-by: Lu Fengqi --- fs/btrfs/relocation.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index b7c304c6e741..ee96390d1e42 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -854,6 +854,13 @@ struct backref_node *build_backref_tree(struct reloc_control *rc, root = read_fs_root(rc->extent_root->fs_info, key.offset); if (IS_ERR(root)) { err = PTR_ERR(root); + /* + * Don't forget to cleanup current node. + * As it may not be added to backref_cache but nr_node + * increased. + * This will cause BUG_ON() in backref_cache_cleanup(). + */ + remove_backref_node(&rc->backref_cache, cur); goto out; } @@ -3021,8 +3028,15 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans, node = build_backref_tree(rc, &block->key, block->level, block->bytenr); if (IS_ERR(node)) { + /* + * The root(dedupe tree yet) of the tree block is + * going to be freed and can't be reached. + * Just skip it and continue balancing. + */ + if (PTR_ERR(node) == -ENOENT) + continue; err = PTR_ERR(node); - goto out; + break; } ret = relocate_tree_block(trans, rc, node, &block->key, @@ -3030,10 +3044,9 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans, if (ret < 0) { if (ret != -EAGAIN || &block->rb_node == rb_first(blocks)) err = ret; - goto out; + break; } } -out: err = finish_pending_nodes(trans, rc, path, err); out_free_path: From patchwork Tue Nov 6 06:41:22 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lu Fengqi X-Patchwork-Id: 10669705 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 076D11923 for ; Tue, 6 Nov 2018 06:41:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E9CD829DC1 for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DE93B29E58; Tue, 6 Nov 2018 06:41:44 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 058F929DFA for ; Tue, 6 Nov 2018 06:41:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387595AbeKFQFW (ORCPT ); Tue, 6 Nov 2018 11:05:22 -0500 Received: from mail.cn.fujitsu.com ([183.91.158.132]:31748 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387533AbeKFQFW (ORCPT ); Tue, 6 Nov 2018 11:05:22 -0500 X-IronPort-AV: E=Sophos;i="5.43,368,1503331200"; d="scan'208";a="47417670" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 06 Nov 2018 14:41:38 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (unknown [10.167.33.80]) by cn.fujitsu.com (Postfix) with ESMTP id 061C64B71EA2; Tue, 6 Nov 2018 14:41:38 +0800 (CST) Received: from fnst.lan (10.167.226.155) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 6 Nov 2018 14:41:41 +0800 From: Lu Fengqi To: CC: Qu Wenruo Subject: [PATCH v15.1 13/13] btrfs: dedupe: Introduce new reconfigure ioctl Date: Tue, 6 Nov 2018 14:41:22 +0800 Message-ID: <20181106064122.6154-14-lufq.fnst@cn.fujitsu.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> References: <20181106064122.6154-1-lufq.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.155] X-yoursite-MailScanner-ID: 061C64B71EA2.AEC67 X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: lufq.fnst@cn.fujitsu.com 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: Qu Wenruo Introduce new reconfigure ioctl and new FORCE flag for in-band dedupe ioctls. Now dedupe enable and reconfigure ioctl are stateful. -------------------------------------------- | Current state | Ioctl | Next state | -------------------------------------------- | Disabled | enable | Enabled | | Enabled | enable | Not allowed | | Enabled | reconf | Enabled | | Enabled | disable | Disabled | | Disabled | dsiable | Disabled | | Disabled | reconf | Not allowed | -------------------------------------------- (While disable is always stateless) While for guys prefer stateless ioctl (myself for example), new FORCE flag is introduced. In FORCE mode, enable/disable is completely stateless. -------------------------------------------- | Current state | Ioctl | Next state | -------------------------------------------- | Disabled | enable | Enabled | | Enabled | enable | Enabled | | Enabled | disable | Disabled | | Disabled | disable | Disabled | -------------------------------------------- Also, re-configure ioctl will only modify specified fields. Unlike enable, un-specified fields will be filled with default value. For example: # btrfs dedupe enable --block-size 64k /mnt # btrfs dedupe reconfigure --limit-hash 1m /mnt Will leads to: dedupe blocksize: 64K dedupe hash limit nr: 1m While for enable: # btrfs dedupe enable --force --block-size 64k /mnt # btrfs dedupe enable --force --limit-hash 1m /mnt Will reset blocksize to default value: dedupe blocksize: 128K << reset dedupe hash limit nr: 1m Suggested-by: David Sterba Signed-off-by: Qu Wenruo Signed-off-by: Lu Fengqi --- fs/btrfs/dedupe.c | 132 ++++++++++++++++++++++++++++++------- fs/btrfs/dedupe.h | 13 ++++ fs/btrfs/ioctl.c | 13 ++++ include/uapi/linux/btrfs.h | 11 +++- 4 files changed, 143 insertions(+), 26 deletions(-) diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c index 76a967cca68e..92152134d3c0 100644 --- a/fs/btrfs/dedupe.c +++ b/fs/btrfs/dedupe.c @@ -29,6 +29,40 @@ static inline struct inmem_hash *inmem_alloc_hash(u16 algo) GFP_NOFS); } +/* + * Copy from current dedupe info to fill dargs. + * For reconf case, only fill members which is uninitialized. + */ +static void get_dedupe_status(struct btrfs_dedupe_info *dedupe_info, + struct btrfs_ioctl_dedupe_args *dargs) +{ + int reconf = (dargs->cmd == BTRFS_DEDUPE_CTL_RECONF); + + dargs->status = 1; + + if (!reconf || (reconf && dargs->blocksize == (u64)-1)) + dargs->blocksize = dedupe_info->blocksize; + if (!reconf || (reconf && dargs->backend == (u16)-1)) + dargs->backend = dedupe_info->backend; + if (!reconf || (reconf && dargs->hash_algo == (u16)-1)) + dargs->hash_algo = dedupe_info->hash_algo; + + /* + * For re-configure case, if not modifying limit, + * therir limit will be set to 0, unlike other fields + */ + if (!reconf || !(dargs->limit_nr || dargs->limit_mem)) { + dargs->limit_nr = dedupe_info->limit_nr; + dargs->limit_mem = dedupe_info->limit_nr * + (sizeof(struct inmem_hash) + + btrfs_hash_sizes[dedupe_info->hash_algo]); + } + + /* current_nr doesn't makes sense for reconfig case */ + if (!reconf) + dargs->current_nr = dedupe_info->current_nr; +} + void btrfs_dedupe_status(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_dedupe_args *dargs) { @@ -45,15 +79,7 @@ void btrfs_dedupe_status(struct btrfs_fs_info *fs_info, return; } mutex_lock(&dedupe_info->lock); - dargs->status = 1; - dargs->blocksize = dedupe_info->blocksize; - dargs->backend = dedupe_info->backend; - dargs->hash_algo = dedupe_info->hash_algo; - dargs->limit_nr = dedupe_info->limit_nr; - dargs->limit_mem = dedupe_info->limit_nr * - (sizeof(struct inmem_hash) + - btrfs_hash_sizes[dedupe_info->hash_algo]); - dargs->current_nr = dedupe_info->current_nr; + get_dedupe_status(dedupe_info, dargs); mutex_unlock(&dedupe_info->lock); memset(dargs->__unused, -1, sizeof(dargs->__unused)); } @@ -98,17 +124,50 @@ init_dedupe_info(struct btrfs_ioctl_dedupe_args *dargs) static int check_dedupe_parameter(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_dedupe_args *dargs) { - u64 blocksize = dargs->blocksize; - u64 limit_nr = dargs->limit_nr; - u64 limit_mem = dargs->limit_mem; - u16 hash_algo = dargs->hash_algo; - u8 backend = dargs->backend; + struct btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info; + + u64 blocksize; + u64 limit_nr; + u64 limit_mem; + u16 hash_algo; + u8 backend; /* * Set all reserved fields to -1, allow user to detect * unsupported optional parameters. */ memset(dargs->__unused, -1, sizeof(dargs->__unused)); + + /* + * For dedupe enabled fs, enable without FORCE flag is not allowed + */ + if (dargs->cmd == BTRFS_DEDUPE_CTL_ENABLE && dedupe_info && + !(dargs->flags & BTRFS_DEDUPE_FLAG_FORCE)) { + dargs->status = 1; + dargs->flags = (u8)-1; + return -EINVAL; + } + + /* Check and copy parameters from existing dedupe info */ + if (dargs->cmd == BTRFS_DEDUPE_CTL_RECONF) { + if (!dedupe_info) { + /* Info caller that dedupe is not enabled */ + dargs->status = 0; + return -EINVAL; + } + get_dedupe_status(dedupe_info, dargs); + /* + * All unmodified parameter are already copied out + * go through normal validation check. + */ + } + + blocksize = dargs->blocksize; + limit_nr = dargs->limit_nr; + limit_mem = dargs->limit_mem; + hash_algo = dargs->hash_algo; + backend = dargs->backend; + if (blocksize > BTRFS_DEDUPE_BLOCKSIZE_MAX || blocksize < BTRFS_DEDUPE_BLOCKSIZE_MIN || blocksize < fs_info->sectorsize || @@ -129,7 +188,8 @@ static int check_dedupe_parameter(struct btrfs_fs_info *fs_info, /* Backend specific check */ if (backend == BTRFS_DEDUPE_BACKEND_INMEMORY) { /* only one limit is accepted for enable*/ - if (dargs->limit_nr && dargs->limit_mem) { + if (dargs->cmd == BTRFS_DEDUPE_CTL_ENABLE && + dargs->limit_nr && dargs->limit_mem) { dargs->limit_nr = 0; dargs->limit_mem = 0; return -EINVAL; @@ -163,18 +223,18 @@ static int check_dedupe_parameter(struct btrfs_fs_info *fs_info, return 0; } -int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info, - struct btrfs_ioctl_dedupe_args *dargs) +/* + * Enable or re-configure dedupe. + * + * Caller must call check_dedupe_parameters first + */ +static int enable_reconfig_dedupe(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs) { - struct btrfs_dedupe_info *dedupe_info; - int ret = 0; - - ret = check_dedupe_parameter(fs_info, dargs); - if (ret < 0) - return ret; + struct btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info; - dedupe_info = fs_info->dedupe_info; if (dedupe_info) { + /* Check if we are re-enable for different dedupe config */ if (dedupe_info->blocksize != dargs->blocksize || dedupe_info->hash_algo != dargs->hash_algo || @@ -198,7 +258,29 @@ int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info, /* We must ensure dedupe_bs is modified after dedupe_info */ smp_wmb(); fs_info->dedupe_enabled = 1; - return ret; + return 0; +} + +int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs) +{ + int ret = 0; + + ret = check_dedupe_parameter(fs_info, dargs); + if (ret < 0) + return ret; + return enable_reconfig_dedupe(fs_info, dargs); +} + +int btrfs_dedupe_reconfigure(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs) +{ + /* + * btrfs_dedupe_enable will handle everything well, + * since dargs contains all info we need to distinguish enable + * and reconfigure + */ + return btrfs_dedupe_enable(fs_info, dargs); } static int inmem_insert_hash(struct rb_root *root, diff --git a/fs/btrfs/dedupe.h b/fs/btrfs/dedupe.h index fdd00355d6b5..94e97fd19011 100644 --- a/fs/btrfs/dedupe.h +++ b/fs/btrfs/dedupe.h @@ -90,6 +90,19 @@ static inline struct btrfs_dedupe_hash *btrfs_dedupe_alloc_hash(u16 algo) int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_dedupe_args *dargs); +/* + * Reconfigure given parameter for dedupe + * Can only be called when dedupe is already enabled + * + * dargs member which don't need to be modified should be left + * with 0 for limit_nr/limit_offset or -1 for other fields + * + * Return 0 for success + * Return <0 for any error + * (Same error return value with dedupe_enable) + */ +int btrfs_dedupe_reconfigure(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_dedupe_args *dargs); /* * Get inband dedupe info diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 5dd0c318fd40..d6b39af72bae 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3695,6 +3695,19 @@ static long btrfs_ioctl_dedupe_ctl(struct btrfs_root *root, void __user *args) btrfs_dedupe_status(fs_info, dargs); mutex_unlock(&fs_info->dedupe_ioctl_lock); break; + case BTRFS_DEDUPE_CTL_RECONF: + mutex_lock(&fs_info->dedupe_ioctl_lock); + ret = btrfs_dedupe_reconfigure(fs_info, dargs); + /* + * Also copy the result to caller for further use + * if enable succeeded. + * For error case, dargs is already set up with + * special values indicating error reason. + */ + if (!ret) + btrfs_dedupe_status(fs_info, dargs); + mutex_unlock(&fs_info->dedupe_ioctl_lock); + break; default: /* * Use this return value to inform progs that kernel diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 5ee51fac3652..5ee90e23e137 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -693,7 +693,16 @@ struct btrfs_ioctl_get_dev_stats { #define BTRFS_DEDUPE_CTL_ENABLE 1 #define BTRFS_DEDUPE_CTL_DISABLE 2 #define BTRFS_DEDUPE_CTL_STATUS 3 -#define BTRFS_DEDUPE_CTL_LAST 4 +#define BTRFS_DEDUPE_CTL_RECONF 4 +#define BTRFS_DEDUPE_CTL_LAST 5 + +/* + * Allow enable command to be executed on dedupe enabled fs. + * Make dedupe_enable ioctl to be stateless. + * + * Or only dedup_reconf ioctl can be executed on dedupe enabled fs + */ +#define BTRFS_DEDUPE_FLAG_FORCE (1 << 0) /* * This structure is used for dedupe enable/disable/configure * and status ioctl.