* [PATCH v14.2 00/16] Btrfs In-band De-duplication
@ 2017-03-16 7:58 Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 01/16] btrfs: improve inode's outstanding_extents computation Qu Wenruo
` (15 more replies)
0 siblings, 16 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs
This patchset can be fetched from github:
https://github.com/adam900710/linux.git wang_dedupe_latest
This is just a normal rebase update.
Now the new base is v4.11-rc2, and the patchset no longer rely qgroup fixes.
Normal test cases from auto group exposes no regression, and ib-dedupe
group can pass without problem.
Due to recent cleanups, the rebase is a little complex than before, but some
cleanup, like count_max_extents() is quite handy for in-band dedupe.
These 3 patches are unrelated to dedupe, but the problem is the same so
we put they together into the dedupe patchset.
btrfs: improve inode's outstanding_extents computation
btrfs: introduce type based delalloc metadata reserve
btrfs: Introduce COMPRESS reserve type to fix false enospc for
compression
And sorry for the mail bombing.
Changelog:
v2:
Totally reworked to handle multiple backends
v3:
Fix a stupid but deadly on-disk backend bug
Add handle for multiple hash on same bytenr corner case to fix abort
trans error
Increase dedup rate by enhancing delayed ref handler for both backend.
Move dedup_add() to run_delayed_ref() time, to fix abort trans error.
Increase dedup block size up limit to 8M.
v4:
Add dedup prop for disabling dedup for given files/dirs.
Merge inmem_search() and ondisk_search() into generic_search() to save
some code
Fix another delayed_ref related bug.
Use the same mutex for both inmem and ondisk backend.
Move dedup_add() back to btrfs_finish_ordered_io() to increase dedup
rate.
v5:
Reuse compress routine for much simpler dedup function.
Slightly improved performance due to above modification.
Fix race between dedup enable/disable
Fix for false ENOSPC report
v6:
Further enable/disable race window fix.
Minor format change according to checkpatch.
v7:
Fix one concurrency bug with balance.
Slightly modify return value from -EINVAL to -EOPNOTSUPP for
btrfs_dedup_ioctl() to allow progs to distinguish unsupported commands
and wrong parameter.
Rebased to integration-4.6.
v8:
Rename 'dedup' to 'dedupe'.
Add support to allow dedupe and compression work at the same time.
Fix several balance related bugs. Special thanks to Satoru Takeuchi,
who exposed most of them.
Small dedupe hit case performance improvement.
v9:
Re-order the patchset to completely separate pure in-memory and any
on-disk format change.
Fold bug fixes into its original patch.
v10:
Adding back missing bug fix patch.
Reduce on-disk item size.
Hide dedupe ioctl under CONFIG_BTRFS_DEBUG.
v11:
Remove other backend and props support to focus on the framework and
in-memory backend. Suggested by David.
Better disable and buffered write race protection.
Comprehensive fix to dedupe metadata ENOSPC problem.
v12:
Stateful 'enable' ioctl and new 'reconf' ioctl
New FORCE flag for enable ioctl to allow stateless ioctl
Precise error report and extendable ioctl structure.
v12.1
Rebase to David's for-next-20160704 branch
Add co-ordinate patch for subpage and dedupe patchset.
v12.2
Rebase to David's for-next-20160715 branch
Add co-ordinate patch for other patchset.
v13
Rebase to David's for-next-20160906 branch
Fix a reserved space leak bug, which only frees quota reserved space
but not space_info->byte_may_use.
v13.1
Rebase to Chris' for-linux-4.9 branch
v14
Use generic ENOSPC fix for both compression and dedupe.
v14.1
Further split ENOSPC fix.
v14.2
Rebase to v4.11-rc2.
Co-operate with count_max_extent() to calculate num_extents.
No longer rely on qgroup fixes.
Qu Wenruo (4):
btrfs: delayed-ref: Add support for increasing data ref under spinlock
btrfs: dedupe: Inband in-memory only de-duplication implement
btrfs: relocation: Enhance error handling to avoid BUG_ON
btrfs: dedupe: Introduce new reconfigure ioctl
Wang Xiaoguang (12):
btrfs: improve inode's outstanding_extents computation
btrfs: introduce type based delalloc metadata reserve
btrfs: Introduce COMPRESS reserve type to fix false enospc for
compression
btrfs: dedupe: Introduce dedupe framework and its header
btrfs: dedupe: Introduce function to initialize dedupe info
btrfs: dedupe: Introduce function to add hash into in-memory tree
btrfs: dedupe: Introduce function to remove hash from in-memory tree
btrfs: dedupe: Introduce function to search for an existing hash
btrfs: dedupe: Implement btrfs_dedupe_calc_hash interface
btrfs: ordered-extent: Add support for dedupe
btrfs: Introduce DEDUPE reserve type to fix false enospc for in-band
dedupe
btrfs: dedupe: Add ioctl for inband dedupelication
fs/btrfs/Makefile | 2 +-
fs/btrfs/ctree.h | 49 ++-
fs/btrfs/dedupe.c | 823 +++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/dedupe.h | 184 +++++++++-
fs/btrfs/delayed-ref.c | 31 +-
fs/btrfs/delayed-ref.h | 9 +
fs/btrfs/disk-io.c | 4 +
fs/btrfs/extent-tree.c | 66 +++-
fs/btrfs/extent_io.c | 63 +++-
fs/btrfs/extent_io.h | 6 +
fs/btrfs/file.c | 27 +-
fs/btrfs/free-space-cache.c | 5 +-
fs/btrfs/inode-map.c | 6 +-
fs/btrfs/inode.c | 522 +++++++++++++++++++++++----
fs/btrfs/ioctl.c | 97 ++++-
fs/btrfs/ordered-data.c | 46 ++-
fs/btrfs/ordered-data.h | 13 +
fs/btrfs/relocation.c | 52 ++-
fs/btrfs/sysfs.c | 2 +
fs/btrfs/tests/inode-tests.c | 15 +-
include/uapi/linux/btrfs.h | 55 +++
21 files changed, 1931 insertions(+), 146 deletions(-)
create mode 100644 fs/btrfs/dedupe.c
--
2.12.0
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v14.2 01/16] btrfs: improve inode's outstanding_extents computation
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 02/16] btrfs: introduce type based delalloc metadata reserve Qu Wenruo
` (14 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
This issue was revealed by modifying BTRFS_MAX_EXTENT_SIZE(128MB) to 64KB,
When modifying BTRFS_MAX_EXTENT_SIZE(128MB) to 64KB, fsstress test often
gets these warnings from btrfs_destroy_inode():
WARN_ON(BTRFS_I(inode)->outstanding_extents);
WARN_ON(BTRFS_I(inode)->reserved_extents);
Simple test program below can reproduce this issue steadily.
Note: you need to modify BTRFS_MAX_EXTENT_SIZE to 64KB to have test,
otherwise there won't be such WARNING.
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd;
char buf[68 *1024];
memset(buf, 0, 68 * 1024);
fd = open("testfile", O_CREAT | O_EXCL | O_RDWR);
pwrite(fd, buf, 68 * 1024, 64 * 1024);
return;
}
When BTRFS_MAX_EXTENT_SIZE is 64KB, and buffered data range is:
64KB 128K 132KB
|-----------------------------------------------|---------------|
64 + 4KB
1) for above data range, btrfs_delalloc_reserve_metadata() will reserve
metadata and set BTRFS_I(inode)->outstanding_extents to 2.
(68KB + 64KB - 1) / 64KB == 2
Outstanding_extents: 2
2) then btrfs_dirty_page() will be called to dirty pages and set
EXTENT_DELALLOC flag. In this case, btrfs_set_bit_hook() will be called
twice.
The 1st set_bit_hook() call will set DEALLOC flag for the first 64K.
64KB 128KB
|-----------------------------------------------|
64KB DELALLOC
Outstanding_extents: 2
Set_bit_hooks() uses FIRST_DELALLOC flag to avoid re-increase
outstanding_extents counter.
So for 1st set_bit_hooks() call, it won't modify outstanding_extents,
it's still 2.
Then FIRST_DELALLOC flag is *CLEARED*.
3) 2nd btrfs_set_bit_hook() call.
Because FIRST_DELALLOC have been cleared by previous set_bit_hook(),
btrfs_set_bit_hook() will increase BTRFS_I(inode)->outstanding_extents by
one, so now BTRFS_I(inode)->outstanding_extents is 3.
64KB 128KB 132KB
|-----------------------------------------------|----------------|
64K DELALLOC 4K DELALLOC
Outstanding_extents: 3
But the correct outstanding_extents number should be 2, not 3.
The 2nd btrfs_set_bit_hook() call just screwed up this, and leads to the
WARN_ON().
Normally, we can solve it by only increasing outstanding_extents in
set_bit_hook().
But the problem is for delalloc_reserve/release_metadata(), we only have
a 'length' parameter, and calculate in-accurate outstanding_extents.
If we only rely on set_bit_hook() release_metadata() will crew things up
as it will decrease inaccurate number.
So the fix we use is:
1) Increase *INACCURATE* outstanding_extents at delalloc_reserve_meta
Just as a place holder.
2) Increase *accurate* outstanding_extents at set_bit_hooks()
This is the real increaser.
3) Decrease *INACCURATE* outstanding_extents before returning
This makes outstanding_extents to correct value.
For 128M BTRFS_MAX_EXTENT_SIZE, due to limitation of
__btrfs_buffered_write(), each iteration will only handle about 2MB
data.
So btrfs_dirty_pages() won't need to handle cases cross 2 extents.
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
---
fs/btrfs/ctree.h | 2 ++
fs/btrfs/inode.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++------
fs/btrfs/ioctl.c | 6 ++----
3 files changed, 61 insertions(+), 10 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 29b7fc28c607..73612ca10fd5 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3148,6 +3148,8 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput,
int nr);
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
struct extent_state **cached_state, int dedupe);
+int btrfs_set_extent_defrag(struct inode *inode, u64 start, u64 end,
+ struct extent_state **cached_state);
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
struct btrfs_root *new_root,
struct btrfs_root *parent_root,
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index c40060cc481f..c7bcb658e891 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1521,6 +1521,9 @@ static void btrfs_split_extent_hook(struct inode *inode,
if (!(orig->state & EXTENT_DELALLOC))
return;
+ if (btrfs_is_free_space_inode(BTRFS_I(inode)))
+ return;
+
size = orig->end - orig->start + 1;
if (size > BTRFS_MAX_EXTENT_SIZE) {
u32 num_extents;
@@ -1560,6 +1563,9 @@ static void btrfs_merge_extent_hook(struct inode *inode,
if (!(other->state & EXTENT_DELALLOC))
return;
+ if (btrfs_is_free_space_inode(BTRFS_I(inode)))
+ return;
+
if (new->start > other->start)
new_size = new->end - other->start + 1;
else
@@ -1655,7 +1661,6 @@ static void btrfs_del_delalloc_inode(struct btrfs_root *root,
static void btrfs_set_bit_hook(struct inode *inode,
struct extent_state *state, unsigned *bits)
{
-
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
if ((*bits & EXTENT_DEFRAG) && !(*bits & EXTENT_DELALLOC))
@@ -1668,13 +1673,16 @@ static void btrfs_set_bit_hook(struct inode *inode,
if (!(state->state & EXTENT_DELALLOC) && (*bits & EXTENT_DELALLOC)) {
struct btrfs_root *root = BTRFS_I(inode)->root;
u64 len = state->end + 1 - state->start;
+ u64 num_extents = div64_u64(len + BTRFS_MAX_EXTENT_SIZE - 1,
+ BTRFS_MAX_EXTENT_SIZE);
bool do_list = !btrfs_is_free_space_inode(BTRFS_I(inode));
- if (*bits & EXTENT_FIRST_DELALLOC) {
+ if (*bits & EXTENT_FIRST_DELALLOC)
*bits &= ~EXTENT_FIRST_DELALLOC;
- } else {
+
+ if (do_list) {
spin_lock(&BTRFS_I(inode)->lock);
- BTRFS_I(inode)->outstanding_extents++;
+ BTRFS_I(inode)->outstanding_extents += num_extents;
spin_unlock(&BTRFS_I(inode)->lock);
}
@@ -1920,9 +1928,52 @@ static noinline int add_pending_csums(struct btrfs_trans_handle *trans,
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
struct extent_state **cached_state, int dedupe)
{
+ int ret;
+ u64 num_extents = div64_u64(end - start + BTRFS_MAX_EXTENT_SIZE,
+ BTRFS_MAX_EXTENT_SIZE);
+
+ WARN_ON((end & (PAGE_SIZE - 1)) == 0);
+ ret = set_extent_delalloc(&BTRFS_I(inode)->io_tree, start, end,
+ cached_state);
+
+ /*
+ * btrfs_delalloc_reserve_metadata() will first add number of
+ * outstanding extents according to data length, which is inaccurate
+ * for case like dirtying already dirty pages.
+ * so here we will decrease such inaccurate numbers, to make
+ * outstanding_extents only rely on the correct values added by
+ * set_bit_hook()
+ *
+ * Also, we skipped the metadata space reserve for space cache inodes,
+ * so don't modify the outstanding_extents value.
+ */
+ if (ret == 0 && !btrfs_is_free_space_inode(BTRFS_I(inode))) {
+ spin_lock(&BTRFS_I(inode)->lock);
+ BTRFS_I(inode)->outstanding_extents -= num_extents;
+ spin_unlock(&BTRFS_I(inode)->lock);
+ }
+
+ return ret;
+}
+
+int btrfs_set_extent_defrag(struct inode *inode, u64 start, u64 end,
+ struct extent_state **cached_state)
+{
+ int ret;
+ u64 num_extents = div64_u64(end - start + BTRFS_MAX_EXTENT_SIZE,
+ BTRFS_MAX_EXTENT_SIZE);
+
WARN_ON((end & (PAGE_SIZE - 1)) == 0);
- return set_extent_delalloc(&BTRFS_I(inode)->io_tree, start, end,
- cached_state);
+ ret = set_extent_defrag(&BTRFS_I(inode)->io_tree, start, end,
+ cached_state);
+
+ if (ret == 0 && !btrfs_is_free_space_inode(BTRFS_I(inode))) {
+ spin_lock(&BTRFS_I(inode)->lock);
+ BTRFS_I(inode)->outstanding_extents -= num_extents;
+ spin_unlock(&BTRFS_I(inode)->lock);
+ }
+
+ return ret;
}
/* see btrfs_writepage_start_hook for details on why this is required */
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index dabfc7ac48a6..cc7382ec820f 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1231,10 +1231,8 @@ static int cluster_pages_for_defrag(struct inode *inode,
(page_cnt - i_done) << PAGE_SHIFT);
}
-
- 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);
unlock_extent_cached(&BTRFS_I(inode)->io_tree,
page_start, page_end - 1, &cached_state,
GFP_NOFS);
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 02/16] btrfs: introduce type based delalloc metadata reserve
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 01/16] btrfs: improve inode's outstanding_extents computation Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 03/16] btrfs: Introduce COMPRESS reserve type to fix false enospc for compression Qu Wenruo
` (13 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
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 compression, the max extent size is 128K, while for non-compress write
it's 128M.
And further more, split/merge extent hook highly depends that max extent
size.
Such situation contributes to quite a lot of false ENOSPC.
So this patch introduce 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 <wangxg.fnst@cn.fujitsu.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
---
fs/btrfs/ctree.h | 38 ++++++++++++----
fs/btrfs/extent-tree.c | 43 +++++++++++++-----
fs/btrfs/file.c | 21 +++++----
fs/btrfs/free-space-cache.c | 5 ++-
fs/btrfs/inode-map.c | 6 ++-
fs/btrfs/inode.c | 105 ++++++++++++++++++++++++++++---------------
fs/btrfs/ioctl.c | 10 +++--
fs/btrfs/relocation.c | 10 +++--
fs/btrfs/tests/inode-tests.c | 15 ++++---
9 files changed, 172 insertions(+), 81 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 73612ca10fd5..67a291bd3374 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -101,11 +101,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 compression, 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;
};
@@ -2704,10 +2717,14 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root,
u64 *qgroup_reserved, bool use_global_rsv);
void btrfs_subvolume_release_metadata(struct btrfs_fs_info *fs_info,
struct btrfs_block_rsv *rsv);
-int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes);
-void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes);
-int btrfs_delalloc_reserve_space(struct inode *inode, u64 start, u64 len);
-void btrfs_delalloc_release_space(struct inode *inode, u64 start, u64 len);
+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,
+ enum btrfs_metadata_reserve_type reserve_type);
+int btrfs_delalloc_reserve_space(struct inode *inode, u64 start, u64 len,
+ enum btrfs_metadata_reserve_type reserve_type);
+void btrfs_delalloc_release_space(struct inode *inode, 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);
@@ -3147,9 +3164,11 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput);
int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput,
int nr);
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
- 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);
+ 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,
@@ -3241,7 +3260,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);
int btrfs_clone_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out, u64 len);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index be5477676cc8..a86af07462d2 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5848,13 +5848,14 @@ void btrfs_subvolume_release_metadata(struct btrfs_fs_info *fs_info,
* BTRFS_I(inode)->lock held.
*/
static unsigned drop_outstanding_extent(struct btrfs_inode *inode,
- u64 num_bytes)
+ u64 num_bytes, enum btrfs_metadata_reserve_type reserve_type)
{
unsigned drop_inode_space = 0;
unsigned dropped_extents = 0;
unsigned num_extents;
+ u64 max_extent_size = btrfs_max_extent_size(reserve_type);
- num_extents = count_max_extents(num_bytes);
+ num_extents = count_max_extents(num_bytes, max_extent_size);
ASSERT(num_extents);
ASSERT(inode->outstanding_extents >= num_extents);
inode->outstanding_extents -= num_extents;
@@ -5921,7 +5922,17 @@ static u64 calc_csum_metadata_size(struct btrfs_inode *inode, u64 num_bytes,
return btrfs_calc_trans_metadata_size(fs_info, old_csums - num_csums);
}
-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 = btrfs_sb(inode->vfs_inode.i_sb);
struct btrfs_root *root = inode->root;
@@ -5933,6 +5944,7 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes)
int ret = 0;
bool delalloc_lock = true;
u64 to_free = 0;
+ u64 max_extent_size = btrfs_max_extent_size(reserve_type);
unsigned dropped;
bool release_extra = false;
@@ -5961,7 +5973,7 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes)
num_bytes = ALIGN(num_bytes, fs_info->sectorsize);
spin_lock(&inode->lock);
- nr_extents = count_max_extents(num_bytes);
+ nr_extents = count_max_extents(num_bytes, max_extent_size);
inode->outstanding_extents += nr_extents;
nr_extents = 0;
@@ -6011,7 +6023,7 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes)
out_fail:
spin_lock(&inode->lock);
- dropped = drop_outstanding_extent(inode, num_bytes);
+ dropped = drop_outstanding_extent(inode, num_bytes, reserve_type);
/*
* If the inodes csum_bytes is the same as the original
* csum_bytes then we know we haven't raced with any free()ers
@@ -6077,12 +6089,15 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes)
* btrfs_delalloc_release_metadata - release a metadata reservation for an inode
* @inode: the inode to release the reservation for
* @num_bytes: the number of bytes we're releasing
+ * @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.
*/
-void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes)
+void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes,
+ enum btrfs_metadata_reserve_type reserve_type)
{
struct btrfs_fs_info *fs_info = btrfs_sb(inode->vfs_inode.i_sb);
u64 to_free = 0;
@@ -6090,7 +6105,7 @@ void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes)
num_bytes = ALIGN(num_bytes, fs_info->sectorsize);
spin_lock(&inode->lock);
- dropped = drop_outstanding_extent(inode, num_bytes);
+ dropped = drop_outstanding_extent(inode, num_bytes, reserve_type);
if (num_bytes)
to_free = calc_csum_metadata_size(inode, num_bytes, 0);
@@ -6113,6 +6128,8 @@ void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes)
* @inode: inode we're writing to
* @start: start range we are writing to
* @len: how long the range we are writing to
+ * @reserve_type: the type of write we're reserving for.
+ * determine the max extent size.
*
* This will do the following things
*
@@ -6130,14 +6147,15 @@ void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes)
* Return 0 for success
* Return <0 for error(-ENOSPC or -EQUOT)
*/
-int btrfs_delalloc_reserve_space(struct inode *inode, u64 start, u64 len)
+int btrfs_delalloc_reserve_space(struct inode *inode, u64 start, u64 len,
+ enum btrfs_metadata_reserve_type reserve_type)
{
int ret;
ret = btrfs_check_data_free_space(inode, 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, start, len);
return ret;
@@ -6148,6 +6166,8 @@ int btrfs_delalloc_reserve_space(struct inode *inode, u64 start, u64 len)
* @inode: inode we're releasing space for
* @start: start position of the space already reserved
* @len: the len of the space already reserved
+ * @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
@@ -6158,9 +6178,10 @@ int btrfs_delalloc_reserve_space(struct inode *inode, u64 start, u64 len)
* list if there are no delalloc bytes left.
* Also it will handle the qgroup reserved space.
*/
-void btrfs_delalloc_release_space(struct inode *inode, u64 start, u64 len)
+void btrfs_delalloc_release_space(struct inode *inode, u64 start, u64 len,
+ enum btrfs_metadata_reserve_type reserve_type)
{
- btrfs_delalloc_release_metadata(BTRFS_I(inode), len);
+ btrfs_delalloc_release_metadata(BTRFS_I(inode), len, reserve_type);
btrfs_free_reserved_data_space(inode, start, len);
}
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 520cb7230b2d..ee8f3a2a1d7a 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -487,7 +487,8 @@ static void btrfs_drop_pages(struct page **pages, size_t num_pages)
*/
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;
@@ -504,7 +505,7 @@ int btrfs_dirty_pages(struct inode *inode, struct page **pages,
end_of_last_block = start_pos + num_bytes - 1;
err = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block,
- cached, 0);
+ cached, reserve_type);
if (err)
return err;
@@ -1538,6 +1539,7 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
bool only_release_metadata = false;
bool force_page_uptodate = false;
bool need_unlock;
+ 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 *)));
@@ -1602,7 +1604,7 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
}
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, pos,
@@ -1681,7 +1683,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
}
if (only_release_metadata) {
btrfs_delalloc_release_metadata(BTRFS_I(inode),
- release_bytes);
+ release_bytes,
+ reserve_type);
} else {
u64 __pos;
@@ -1689,7 +1692,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
fs_info->sectorsize) +
(dirty_pages << PAGE_SHIFT);
btrfs_delalloc_release_space(inode, __pos,
- release_bytes);
+ release_bytes,
+ reserve_type);
}
}
@@ -1698,7 +1702,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
if (copied > 0)
ret = btrfs_dirty_pages(inode, pages, dirty_pages,
- pos, copied, NULL);
+ pos, copied, NULL,
+ reserve_type);
if (need_unlock)
unlock_extent_cached(&BTRFS_I(inode)->io_tree,
lockstart, lockend, &cached_state,
@@ -1742,11 +1747,11 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
if (only_release_metadata) {
btrfs_end_write_no_snapshoting(root);
btrfs_delalloc_release_metadata(BTRFS_I(inode),
- release_bytes);
+ release_bytes, reserve_type);
} else {
btrfs_delalloc_release_space(inode,
round_down(pos, fs_info->sectorsize),
- release_bytes);
+ release_bytes, reserve_type);
}
}
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index da6841efac26..2e802d9b6272 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -1309,7 +1309,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;
@@ -3547,7 +3548,7 @@ 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);
+ inode->i_size, 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 5c6c20ec64d8..c2f37469919f 100644
--- a/fs/btrfs/inode-map.c
+++ b/fs/btrfs/inode-map.c
@@ -492,14 +492,16 @@ 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, 0, prealloc);
+ ret = btrfs_delalloc_reserve_space(inode, 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_metadata(BTRFS_I(inode), prealloc);
+ btrfs_delalloc_release_metadata(BTRFS_I(inode), prealloc,
+ BTRFS_RESERVE_NORMAL);
goto out_put;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index c7bcb658e891..f32e3792eaca 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -316,7 +316,8 @@ static noinline int cow_file_range_inline(struct btrfs_root *root,
}
set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags);
- btrfs_delalloc_release_metadata(BTRFS_I(inode), end + 1 - start);
+ btrfs_delalloc_release_metadata(BTRFS_I(inode), end + 1 - start,
+ BTRFS_RESERVE_NORMAL);
btrfs_drop_extent_cache(BTRFS_I(inode), start, aligned_end - 1, 0);
out:
/*
@@ -1516,6 +1517,8 @@ static void btrfs_split_extent_hook(struct inode *inode,
struct extent_state *orig, u64 split)
{
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))
@@ -1524,8 +1527,10 @@ static void btrfs_split_extent_hook(struct inode *inode,
if (btrfs_is_free_space_inode(BTRFS_I(inode)))
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;
@@ -1534,10 +1539,10 @@ static void btrfs_split_extent_hook(struct inode *inode,
* 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;
}
@@ -1557,7 +1562,9 @@ static void btrfs_merge_extent_hook(struct inode *inode,
struct extent_state *other)
{
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))
@@ -1566,13 +1573,15 @@ static void btrfs_merge_extent_hook(struct inode *inode,
if (btrfs_is_free_space_inode(BTRFS_I(inode)))
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_I(inode)->outstanding_extents--;
spin_unlock(&BTRFS_I(inode)->lock);
@@ -1598,10 +1607,10 @@ static void btrfs_merge_extent_hook(struct inode *inode,
* 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);
@@ -1673,10 +1682,15 @@ static void btrfs_set_bit_hook(struct inode *inode,
if (!(state->state & EXTENT_DELALLOC) && (*bits & EXTENT_DELALLOC)) {
struct btrfs_root *root = BTRFS_I(inode)->root;
u64 len = state->end + 1 - state->start;
- u64 num_extents = div64_u64(len + BTRFS_MAX_EXTENT_SIZE - 1,
- BTRFS_MAX_EXTENT_SIZE);
+ 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);
+
if (*bits & EXTENT_FIRST_DELALLOC)
*bits &= ~EXTENT_FIRST_DELALLOC;
@@ -1711,8 +1725,10 @@ static void btrfs_clear_bit_hook(struct btrfs_inode *inode,
unsigned *bits)
{
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;
spin_lock(&inode->lock);
if ((state->state & EXTENT_DEFRAG) && (*bits & EXTENT_DEFRAG))
@@ -1728,6 +1744,9 @@ static void btrfs_clear_bit_hook(struct btrfs_inode *inode,
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);
+
if (*bits & EXTENT_FIRST_DELALLOC) {
*bits &= ~EXTENT_FIRST_DELALLOC;
} else if (!(*bits & EXTENT_DO_ACCOUNTING)) {
@@ -1743,7 +1762,8 @@ static void btrfs_clear_bit_hook(struct btrfs_inode *inode,
*/
if (*bits & EXTENT_DO_ACCOUNTING &&
root != fs_info->tree_root)
- btrfs_delalloc_release_metadata(inode, len);
+ btrfs_delalloc_release_metadata(inode, len,
+ reserve_type);
/* For sanity tests. */
if (btrfs_is_testing(fs_info))
@@ -1926,11 +1946,13 @@ static noinline int add_pending_csums(struct btrfs_trans_handle *trans,
}
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
- struct extent_state **cached_state, int dedupe)
+ struct extent_state **cached_state,
+ enum btrfs_metadata_reserve_type reserve_type)
{
int ret;
- u64 num_extents = div64_u64(end - start + BTRFS_MAX_EXTENT_SIZE,
- BTRFS_MAX_EXTENT_SIZE);
+ u64 max_extent_size = btrfs_max_extent_size(reserve_type);
+ u64 num_extents = div64_u64(end - start + max_extent_size,
+ max_extent_size);
WARN_ON((end & (PAGE_SIZE - 1)) == 0);
ret = set_extent_delalloc(&BTRFS_I(inode)->io_tree, start, end,
@@ -1957,11 +1979,13 @@ int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
}
int btrfs_set_extent_defrag(struct inode *inode, u64 start, u64 end,
- struct extent_state **cached_state)
+ struct extent_state **cached_state,
+ enum btrfs_metadata_reserve_type reserve_type)
{
int ret;
- u64 num_extents = div64_u64(end - start + BTRFS_MAX_EXTENT_SIZE,
- BTRFS_MAX_EXTENT_SIZE);
+ u64 max_extent_size = btrfs_max_extent_size(reserve_type);
+ u64 num_extents = div64_u64(end - start + max_extent_size,
+ max_extent_size);
WARN_ON((end & (PAGE_SIZE - 1)) == 0);
ret = set_extent_defrag(&BTRFS_I(inode)->io_tree, start, end,
@@ -1992,6 +2016,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;
@@ -2025,7 +2050,7 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
}
ret = btrfs_delalloc_reserve_space(inode, 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);
@@ -2034,7 +2059,7 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
}
btrfs_set_extent_delalloc(inode, page_start, page_end, &cached_state,
- 0);
+ reserve_type);
ClearPageChecked(page);
set_page_dirty(page);
out:
@@ -2842,6 +2867,7 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
u64 logical_len = ordered_extent->len;
bool nolock;
bool truncated = false;
+ enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
nolock = btrfs_is_free_space_inode(BTRFS_I(inode));
@@ -2966,7 +2992,7 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
out:
if (root != fs_info->tree_root)
btrfs_delalloc_release_metadata(BTRFS_I(inode),
- ordered_extent->len);
+ ordered_extent->len, reserve_type);
if (trans)
btrfs_end_transaction(trans);
@@ -4702,13 +4728,14 @@ 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 ((offset & (blocksize - 1)) == 0 &&
(!len || ((len & (blocksize - 1)) == 0)))
goto out;
ret = btrfs_delalloc_reserve_space(inode,
- round_down(from, blocksize), blocksize);
+ round_down(from, blocksize), blocksize, reserve_type);
if (ret)
goto out;
@@ -4717,7 +4744,7 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
if (!page) {
btrfs_delalloc_release_space(inode,
round_down(from, blocksize),
- blocksize);
+ blocksize, reserve_type);
ret = -ENOMEM;
goto out;
}
@@ -4760,7 +4787,7 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
0, 0, &cached_state, GFP_NOFS);
ret = btrfs_set_extent_delalloc(inode, block_start, block_end,
- &cached_state, 0);
+ &cached_state, reserve_type);
if (ret) {
unlock_extent_cached(io_tree, block_start, block_end,
&cached_state, GFP_NOFS);
@@ -4788,7 +4815,7 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
out_unlock:
if (ret)
btrfs_delalloc_release_space(inode, block_start,
- blocksize);
+ blocksize, reserve_type);
unlock_page(page);
put_page(page);
out:
@@ -7604,7 +7631,8 @@ static void adjust_dio_outstanding_extents(struct inode *inode,
struct btrfs_dio_data *dio_data,
const u64 len)
{
- unsigned num_extents = count_max_extents(len);
+ u64 max_extent_size = btrfs_max_extent_size(BTRFS_RESERVE_NORMAL);
+ unsigned num_extents = count_max_extents(len, max_extent_size);
/*
* If we have an outstanding_extents count still set then we're
@@ -8635,6 +8663,7 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
bool wakeup = true;
bool relock = false;
ssize_t ret;
+ u64 max_extent_size = btrfs_max_extent_size(BTRFS_RESERVE_NORMAL);
if (check_direct_IO(fs_info, iocb, iter, offset))
return 0;
@@ -8665,10 +8694,12 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
inode_unlock(inode);
relock = true;
}
- ret = btrfs_delalloc_reserve_space(inode, offset, count);
+ ret = btrfs_delalloc_reserve_space(inode, offset, count,
+ BTRFS_RESERVE_NORMAL);
if (ret)
goto out;
- dio_data.outstanding_extents = count_max_extents(count);
+ dio_data.outstanding_extents = count_max_extents(count,
+ max_extent_size);
/*
* We need to know how many extents we reserved so that we can
@@ -8698,7 +8729,7 @@ 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, offset,
- dio_data.reserve);
+ dio_data.reserve, BTRFS_RESERVE_NORMAL);
/*
* On error we might have left some ordered extents
* without submitting corresponding bios for them, so
@@ -8714,7 +8745,8 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
0);
} else if (ret >= 0 && (size_t)ret < count)
btrfs_delalloc_release_space(inode, offset,
- count - (size_t)ret);
+ count - (size_t)ret,
+ BTRFS_RESERVE_NORMAL);
}
out:
if (wakeup)
@@ -8953,6 +8985,7 @@ int btrfs_page_mkwrite(struct vm_fault *vmf)
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
struct btrfs_ordered_extent *ordered;
struct extent_state *cached_state = NULL;
+ enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
char *kaddr;
unsigned long zero_start;
loff_t size;
@@ -8979,7 +9012,7 @@ int btrfs_page_mkwrite(struct vm_fault *vmf)
* being processed by btrfs_page_mkwrite() function.
*/
ret = btrfs_delalloc_reserve_space(inode, page_start,
- reserved_space);
+ reserved_space, reserve_type);
if (!ret) {
ret = file_update_time(vmf->vma->vm_file);
reserved = 1;
@@ -9033,7 +9066,8 @@ int btrfs_page_mkwrite(struct vm_fault *vmf)
BTRFS_I(inode)->outstanding_extents++;
spin_unlock(&BTRFS_I(inode)->lock);
btrfs_delalloc_release_space(inode, page_start,
- PAGE_SIZE - reserved_space);
+ PAGE_SIZE - reserved_space,
+ reserve_type);
}
}
@@ -9050,7 +9084,7 @@ int btrfs_page_mkwrite(struct vm_fault *vmf)
0, 0, &cached_state, GFP_NOFS);
ret = btrfs_set_extent_delalloc(inode, page_start, end,
- &cached_state, 0);
+ &cached_state, reserve_type);
if (ret) {
unlock_extent_cached(io_tree, page_start, page_end,
&cached_state, GFP_NOFS);
@@ -9088,7 +9122,8 @@ int btrfs_page_mkwrite(struct vm_fault *vmf)
}
unlock_page(page);
out:
- btrfs_delalloc_release_space(inode, page_start, reserved_space);
+ btrfs_delalloc_release_space(inode, page_start, reserved_space,
+ reserve_type);
out_noreserve:
sb_end_pagefault(inode->i_sb);
return ret;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index cc7382ec820f..e1bd2ffabdc9 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1127,6 +1127,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
struct btrfs_ordered_extent *ordered;
struct extent_state *cached_state = NULL;
struct extent_io_tree *tree;
+ 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;
@@ -1137,7 +1138,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
ret = btrfs_delalloc_reserve_space(inode,
start_index << PAGE_SHIFT,
- page_cnt << PAGE_SHIFT);
+ page_cnt << PAGE_SHIFT, reserve_type);
if (ret)
return ret;
i_done = 0;
@@ -1228,11 +1229,12 @@ static int cluster_pages_for_defrag(struct inode *inode,
spin_unlock(&BTRFS_I(inode)->lock);
btrfs_delalloc_release_space(inode,
start_index << PAGE_SHIFT,
- (page_cnt - i_done) << PAGE_SHIFT);
+ (page_cnt - i_done) << PAGE_SHIFT,
+ reserve_type);
}
btrfs_set_extent_defrag(inode, page_start,
- page_end - 1, &cached_state);
+ page_end - 1, &cached_state, reserve_type);
unlock_extent_cached(&BTRFS_I(inode)->io_tree,
page_start, page_end - 1, &cached_state,
GFP_NOFS);
@@ -1253,7 +1255,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
}
btrfs_delalloc_release_space(inode,
start_index << PAGE_SHIFT,
- page_cnt << PAGE_SHIFT);
+ page_cnt << PAGE_SHIFT, reserve_type);
return ret;
}
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index d60df51959f7..cb7c65b430d4 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -3178,6 +3178,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;
@@ -3204,7 +3205,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;
@@ -3217,7 +3218,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
mask);
if (!page) {
btrfs_delalloc_release_metadata(BTRFS_I(inode),
- PAGE_SIZE);
+ PAGE_SIZE, reserve_type);
ret = -ENOMEM;
goto out;
}
@@ -3236,7 +3237,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
unlock_page(page);
put_page(page);
btrfs_delalloc_release_metadata(BTRFS_I(inode),
- PAGE_SIZE);
+ PAGE_SIZE, reserve_type);
ret = -EIO;
goto out;
}
@@ -3257,7 +3258,8 @@ static int relocate_file_extent_cluster(struct inode *inode,
nr++;
}
- btrfs_set_extent_delalloc(inode, page_start, page_end, NULL, 0);
+ btrfs_set_extent_delalloc(inode, page_start, page_end, NULL,
+ reserve_type);
set_page_dirty(page);
unlock_extent(&BTRFS_I(inode)->io_tree,
diff --git a/fs/btrfs/tests/inode-tests.c b/fs/btrfs/tests/inode-tests.c
index 8c91d03cc82d..88354c639c43 100644
--- a/fs/btrfs/tests/inode-tests.c
+++ b/fs/btrfs/tests/inode-tests.c
@@ -944,6 +944,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();
@@ -970,7 +971,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
/* [BTRFS_MAX_EXTENT_SIZE] */
BTRFS_I(inode)->outstanding_extents++;
ret = btrfs_set_extent_delalloc(inode, 0, BTRFS_MAX_EXTENT_SIZE - 1,
- NULL, 0);
+ NULL, reserve_type);
if (ret) {
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
goto out;
@@ -986,7 +987,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
BTRFS_I(inode)->outstanding_extents++;
ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE,
BTRFS_MAX_EXTENT_SIZE + sectorsize - 1,
- NULL, 0);
+ NULL, reserve_type);
if (ret) {
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
goto out;
@@ -1021,7 +1022,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,
- NULL, 0);
+ NULL, reserve_type);
if (ret) {
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
goto out;
@@ -1044,7 +1045,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,
- NULL, 0);
+ NULL, reserve_type);
if (ret) {
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
goto out;
@@ -1062,7 +1063,8 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
BTRFS_I(inode)->outstanding_extents++;
ret = btrfs_set_extent_delalloc(inode,
BTRFS_MAX_EXTENT_SIZE + sectorsize,
- BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, NULL, 0);
+ BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, NULL,
+ reserve_type);
if (ret) {
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
goto out;
@@ -1099,7 +1101,8 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
BTRFS_I(inode)->outstanding_extents++;
ret = btrfs_set_extent_delalloc(inode,
BTRFS_MAX_EXTENT_SIZE + sectorsize,
- BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, NULL, 0);
+ BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, NULL,
+ reserve_type);
if (ret) {
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
goto out;
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 03/16] btrfs: Introduce COMPRESS reserve type to fix false enospc for compression
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 01/16] btrfs: improve inode's outstanding_extents computation Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 02/16] btrfs: introduce type based delalloc metadata reserve Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 04/16] btrfs: dedupe: Introduce dedupe framework and its header Qu Wenruo
` (12 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
When testing btrfs compression, sometimes we got ENOSPC error, though fs
still has much free space, xfstests generic/171, generic/172, generic/173,
generic/174, generic/175 can reveal this bug in my test environment when
compression is enabled.
After some debuging work, we found that it's
btrfs_delalloc_reserve_metadata() which sometimes tries to reserve too
much metadata space, even for very small data range.
In btrfs_delalloc_reserve_metadata(), the number of metadata bytes to
reserve is calculated by the difference between outstanding extents and
reserved extents.
But due to bad designed drop_outstanding_extent() function, it can make
the difference too big, and cause problem.
The problem happens in the following flow with compression enabled.
1) Buffered write 128M data with 128K blocksize
outstanding_extents = 1
reserved_extents = 1024 (128M / 128K, one blocksize will get one
reserved_extent)
Note: it's btrfs_merge_extent_hook() to merge outstanding extents.
But reserved extents are still 1024.
2) Allocate extents for dirty range
cow_file_range_async() split above large extent into small 128K
extents.
Let's assume 2 compressed extents have been split.
So we have:
outstanding_extents = 3
reserved_extents = 1024
range [0, 256K) has extents allocated
3) One ordered extent get finished
btrfs_finish_ordered_io()
|- btrfs_delalloc_release_metadata()
|- drop_outstanding_extent()
drop_outstanding_extent() will free *ALL* redundant reserved extents.
So we have:
outstanding_extents = 2 (One has finished)
reserved_extents = 2
4) Continue allocating extents for dirty range
cow_file_range_async() continue handling the remaining range.
When the whole 128M range is done and assume no more ordered extents
have finished.
outstanding_extents = 1023 (One has finished in Step 3)
reserved_extents = 2 (*ALL* freed in Step 3)
5) Another buffered write happens to the file
btrfs_delalloc_reserve_metadata() will calculate metadata space.
The calculation is:
meta_to_reserve = (outstanding_extents - reserved_extents) * \
nodesize * max_tree_level(8) * 2
If nodesize is 16K, it's 1021 * 16K * 8 * 2, near 256M.
If nodesize is 64K, it's about 1G.
That's totally insane.
The fix is to introduce new reserve type, COMPRESSION, to info outstanding
extents calculation algorithm, to get correct outstanding_extents based
extent size.
So in Step 1), outstanding_extents = 1024 reserved_extents = 1024
Step 2): outstanding_extents = 1024 reserved_extents = 1024
Step 3): outstanding_extents = 1023 reserved_extents = 1023
And in Step 5) we reserve correct amount of metadata space.
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
---
fs/btrfs/ctree.h | 2 ++
fs/btrfs/extent-tree.c | 2 ++
fs/btrfs/extent_io.c | 61 ++++++++++++++++++++++++++++++++--
fs/btrfs/extent_io.h | 5 +++
fs/btrfs/file.c | 3 ++
fs/btrfs/inode.c | 88 ++++++++++++++++++++++++++++++++++++++++++--------
fs/btrfs/ioctl.c | 2 ++
fs/btrfs/relocation.c | 3 ++
8 files changed, 151 insertions(+), 15 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 67a291bd3374..d986c0e88e56 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -115,9 +115,11 @@ static inline u32 count_max_extents(u64 size, u64 max_extent_size)
*/
enum btrfs_metadata_reserve_type {
BTRFS_RESERVE_NORMAL,
+ BTRFS_RESERVE_COMPRESS,
};
u64 btrfs_max_extent_size(enum btrfs_metadata_reserve_type reserve_type);
+int inode_need_compress(struct inode *inode);
struct btrfs_mapping_tree {
struct extent_map_tree map_tree;
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index a86af07462d2..54105ba2f429 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5926,6 +5926,8 @@ u64 btrfs_max_extent_size(enum btrfs_metadata_reserve_type reserve_type)
{
if (reserve_type == BTRFS_RESERVE_NORMAL)
return BTRFS_MAX_EXTENT_SIZE;
+ else if (reserve_type == BTRFS_RESERVE_COMPRESS)
+ return SZ_128K;
ASSERT(0);
return BTRFS_MAX_EXTENT_SIZE;
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 28e81922a21c..3065367a3703 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -609,7 +609,7 @@ static 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_COMPRESS;
if (delete)
bits |= ~EXTENT_CTLBITS;
@@ -748,6 +748,60 @@ static int __clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
}
+static void adjust_one_outstanding_extent(struct inode *inode, u64 len,
+ enum btrfs_metadata_reserve_type reserve_type)
+{
+ unsigned old_extents, new_extents;
+ u64 max_extent_size = btrfs_max_extent_size(reserve_type);
+
+ old_extents = div64_u64(len + max_extent_size - 1, max_extent_size);
+ new_extents = div64_u64(len + BTRFS_MAX_EXTENT_SIZE - 1,
+ BTRFS_MAX_EXTENT_SIZE);
+ if (old_extents <= new_extents)
+ return;
+
+ spin_lock(&BTRFS_I(inode)->lock);
+ BTRFS_I(inode)->outstanding_extents -= old_extents - new_extents;
+ spin_unlock(&BTRFS_I(inode)->lock);
+}
+
+/*
+ * For a extent with EXTENT_COMPRESS flag, if later it does not go through
+ * compress path, we need to adjust the number of outstanding_extents.
+ * It's because for extent with EXTENT_COMPRESS flag, its number of outstanding
+ * extents is calculated by 128KB, so here we need to adjust it.
+ */
+void adjust_outstanding_extents(struct inode *inode, u64 start, u64 end,
+ enum btrfs_metadata_reserve_type reserve_type)
+{
+ struct rb_node *node;
+ struct extent_state *state;
+ struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree;
+
+ spin_lock(&tree->lock);
+ node = tree_search(tree, start);
+ if (!node)
+ goto out;
+
+ while (1) {
+ state = rb_entry(node, struct extent_state, rb_node);
+ if (state->start > end)
+ goto out;
+ /*
+ * The whole range is locked, so we can safely clear
+ * EXTENT_COMPRESS flag.
+ */
+ state->state &= ~EXTENT_COMPRESS;
+ adjust_one_outstanding_extent(inode,
+ state->end - state->start + 1, reserve_type);
+ node = rb_next(node);
+ if (!node)
+ break;
+ }
+out:
+ spin_unlock(&tree->lock);
+}
+
static void wait_on_state(struct extent_io_tree *tree,
struct extent_state *state)
__releases(tree->lock)
@@ -1510,6 +1564,7 @@ static noinline u64 find_delalloc_range(struct extent_io_tree *tree,
u64 cur_start = *start;
u64 found = 0;
u64 total_bytes = 0;
+ unsigned pre_state;
spin_lock(&tree->lock);
@@ -1527,7 +1582,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_COMPRESS)) {
goto out;
}
if (!(state->state & EXTENT_DELALLOC)) {
@@ -1543,6 +1599,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 3e4fad4a909d..0f4478dae822 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -21,6 +21,7 @@
#define EXTENT_NORESERVE (1U << 15)
#define EXTENT_QGROUP_RESERVED (1U << 16)
#define EXTENT_CLEAR_DATA_RESV (1U << 17)
+#define EXTENT_COMPRESS (1U << 18)
#define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK)
#define EXTENT_CTLBITS (EXTENT_DO_ACCOUNTING | EXTENT_FIRST_DELALLOC)
@@ -260,6 +261,10 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
unsigned bits, int wake, int delete,
struct extent_state **cached, gfp_t mask);
+enum btrfs_metadata_reserve_type;
+void adjust_outstanding_extents(struct inode *inode, u64 start, u64 end,
+ enum btrfs_metadata_reserve_type reserve_type);
+
static inline int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end)
{
return clear_extent_bit(tree, start, end, EXTENT_LOCKED, 1, 0, NULL,
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index ee8f3a2a1d7a..8cefcef3c79e 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1549,6 +1549,9 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
if (!pages)
return -ENOMEM;
+ if (inode_need_compress(inode))
+ reserve_type = BTRFS_RESERVE_COMPRESS;
+
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 f32e3792eaca..1afa03d3dc5f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -373,7 +373,7 @@ static noinline int add_async_extent(struct async_cow *cow,
return 0;
}
-static inline int inode_need_compress(struct inode *inode)
+int inode_need_compress(struct inode *inode)
{
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
@@ -709,6 +709,17 @@ static noinline void submit_compressed_extents(struct inode *inode,
async_extent->start +
async_extent->ram_size - 1);
+ /*
+ * We use 128KB as max extent size to calculate number
+ * of outstanding extents for this extent before, now
+ * it'll go throuth uncompressed IO, we need to use
+ * 128MB as max extent size to re-calculate number of
+ * outstanding extents for this extent.
+ */
+ adjust_outstanding_extents(inode, async_extent->start,
+ async_extent->start +
+ async_extent->ram_size - 1,
+ BTRFS_RESERVE_COMPRESS);
/* allocate blocks */
ret = cow_file_range(inode, async_cow->locked_page,
async_extent->start,
@@ -1104,7 +1115,8 @@ 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 long *nr_written,
+ enum btrfs_metadata_reserve_type reserve_type)
{
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
struct async_cow *async_cow;
@@ -1122,10 +1134,8 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
async_cow->locked_page = locked_page;
async_cow->start = start;
- if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS &&
- !btrfs_test_opt(fs_info, FORCE_COMPRESS))
- cur_end = end;
- else
+ cur_end = end;
+ if (reserve_type == BTRFS_RESERVE_COMPRESS)
cur_end = min(end, start + SZ_512K - 1);
async_cow->end = cur_end;
@@ -1494,21 +1504,37 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
{
int ret;
int force_cow = need_force_cow(inode, start, end);
+ struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
+ int need_compress;
+ enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
+
+ need_compress = test_range_bit(io_tree, start, end,
+ EXTENT_COMPRESS, 1, NULL);
+ if (need_compress)
+ reserve_type = BTRFS_RESERVE_COMPRESS;
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW && !force_cow) {
+ if (need_compress)
+ adjust_outstanding_extents(inode, start, end,
+ reserve_type);
+
ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 1, nr_written);
} else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC && !force_cow) {
+ if (need_compress)
+ adjust_outstanding_extents(inode, start, end,
+ reserve_type);
+
ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 0, nr_written);
- } else if (!inode_need_compress(inode)) {
+ } else if (!need_compress) {
ret = cow_file_range(inode, locked_page, start, end, end,
page_started, nr_written, 1, NULL);
} else {
set_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
&BTRFS_I(inode)->runtime_flags);
ret = cow_file_range_async(inode, locked_page, start, end,
- page_started, nr_written);
+ page_started, nr_written, reserve_type);
}
return ret;
}
@@ -1527,6 +1553,8 @@ static void btrfs_split_extent_hook(struct inode *inode,
if (btrfs_is_free_space_inode(BTRFS_I(inode)))
return;
+ if (orig->state & EXTENT_COMPRESS)
+ reserve_type = BTRFS_RESERVE_COMPRESS;
max_extent_size = btrfs_max_extent_size(reserve_type);
size = orig->end - orig->start + 1;
@@ -1573,6 +1601,8 @@ static void btrfs_merge_extent_hook(struct inode *inode,
if (btrfs_is_free_space_inode(BTRFS_I(inode)))
return;
+ if (other->state & EXTENT_COMPRESS)
+ reserve_type = BTRFS_RESERVE_COMPRESS;
max_extent_size = btrfs_max_extent_size(reserve_type);
if (new->start > other->start)
@@ -1688,6 +1718,8 @@ static void btrfs_set_bit_hook(struct inode *inode,
BTRFS_RESERVE_NORMAL;
bool do_list = !btrfs_is_free_space_inode(BTRFS_I(inode));
+ if (*bits & EXTENT_COMPRESS)
+ reserve_type = BTRFS_RESERVE_COMPRESS;
max_extent_size = btrfs_max_extent_size(reserve_type);
num_extents = count_max_extents(len, max_extent_size);
@@ -1744,6 +1776,8 @@ static void btrfs_clear_bit_hook(struct btrfs_inode *inode,
struct btrfs_root *root = inode->root;
bool do_list = !btrfs_is_free_space_inode(inode);
+ if (state->state & EXTENT_COMPRESS)
+ reserve_type = BTRFS_RESERVE_COMPRESS;
max_extent_size = btrfs_max_extent_size(reserve_type);
num_extents = count_max_extents(len, max_extent_size);
@@ -1945,18 +1979,30 @@ static noinline int add_pending_csums(struct btrfs_trans_handle *trans,
return 0;
}
+/*
+ * Normally flag should be 0, but if a data range will go through compress path,
+ * set flag to 1. Note: here we should ensure enum btrfs_metadata_reserve_type
+ * and flag's values are consistent.
+ */
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
struct extent_state **cached_state,
enum btrfs_metadata_reserve_type reserve_type)
{
int ret;
+ unsigned bits;
u64 max_extent_size = btrfs_max_extent_size(reserve_type);
u64 num_extents = div64_u64(end - start + max_extent_size,
max_extent_size);
+ /* compression path */
+ if (reserve_type == BTRFS_RESERVE_COMPRESS)
+ bits = EXTENT_DELALLOC | EXTENT_COMPRESS | EXTENT_UPTODATE;
+ else
+ bits = EXTENT_DELALLOC | EXTENT_UPTODATE;
+
WARN_ON((end & (PAGE_SIZE - 1)) == 0);
- ret = set_extent_delalloc(&BTRFS_I(inode)->io_tree, start, end,
- cached_state);
+ ret = set_extent_bit(&BTRFS_I(inode)->io_tree, start, end,
+ bits, NULL, cached_state, GFP_NOFS);
/*
* btrfs_delalloc_reserve_metadata() will first add number of
@@ -1983,14 +2029,20 @@ int btrfs_set_extent_defrag(struct inode *inode, u64 start, u64 end,
enum btrfs_metadata_reserve_type reserve_type)
{
int ret;
+ unsigned bits;
u64 max_extent_size = btrfs_max_extent_size(reserve_type);
u64 num_extents = div64_u64(end - start + max_extent_size,
max_extent_size);
WARN_ON((end & (PAGE_SIZE - 1)) == 0);
- ret = set_extent_defrag(&BTRFS_I(inode)->io_tree, start, end,
- cached_state);
+ if (reserve_type == BTRFS_RESERVE_COMPRESS)
+ bits = EXTENT_DELALLOC | EXTENT_UPTODATE | EXTENT_DEFRAG |
+ EXTENT_COMPRESS;
+ else
+ bits = EXTENT_DELALLOC | EXTENT_UPTODATE | EXTENT_DEFRAG;
+ ret = set_extent_bit(&BTRFS_I(inode)->io_tree, start, end,
+ bits, NULL, cached_state, GFP_NOFS);
if (ret == 0 && !btrfs_is_free_space_inode(BTRFS_I(inode))) {
spin_lock(&BTRFS_I(inode)->lock);
BTRFS_I(inode)->outstanding_extents -= num_extents;
@@ -2049,6 +2101,8 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
goto again;
}
+ if (inode_need_compress(inode))
+ reserve_type = BTRFS_RESERVE_COMPRESS;
ret = btrfs_delalloc_reserve_space(inode, page_start,
PAGE_SIZE, reserve_type);
if (ret) {
@@ -2946,8 +3000,11 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
trans->block_rsv = &fs_info->delalloc_block_rsv;
- if (test_bit(BTRFS_ORDERED_COMPRESSED, &ordered_extent->flags))
+ if (test_bit(BTRFS_ORDERED_COMPRESSED, &ordered_extent->flags)) {
compress_type = ordered_extent->compress_type;
+ reserve_type = BTRFS_RESERVE_COMPRESS;
+ }
+
if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) {
BUG_ON(compress_type);
ret = btrfs_mark_extent_written(trans, BTRFS_I(inode),
@@ -4730,6 +4787,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_compress(inode))
+ reserve_type = BTRFS_RESERVE_COMPRESS;
+
if ((offset & (blocksize - 1)) == 0 &&
(!len || ((len & (blocksize - 1)) == 0)))
goto out;
@@ -9003,6 +9063,8 @@ int btrfs_page_mkwrite(struct vm_fault *vmf)
page_end = page_start + PAGE_SIZE - 1;
end = page_end;
+ if (inode_need_compress(inode))
+ reserve_type = BTRFS_RESERVE_COMPRESS;
/*
* Reserving delalloc space after obtaining the page lock can lead to
* deadlock. For example, if a dirty page is locked by this function
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index e1bd2ffabdc9..cd0afe1fb3ca 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1136,6 +1136,8 @@ static int cluster_pages_for_defrag(struct inode *inode,
page_cnt = min_t(u64, (u64)num_pages, (u64)file_end - start_index + 1);
+ if (inode_need_compress(inode))
+ reserve_type = BTRFS_RESERVE_COMPRESS;
ret = btrfs_delalloc_reserve_space(inode,
start_index << PAGE_SHIFT,
page_cnt << PAGE_SHIFT, reserve_type);
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index cb7c65b430d4..f70f4df422d8 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -3186,6 +3186,9 @@ static int relocate_file_extent_cluster(struct inode *inode,
if (!cluster->nr)
return 0;
+ if (inode_need_compress(inode))
+ reserve_type = BTRFS_RESERVE_COMPRESS;
+
ra = kzalloc(sizeof(*ra), GFP_NOFS);
if (!ra)
return -ENOMEM;
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 04/16] btrfs: dedupe: Introduce dedupe framework and its header
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (2 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 03/16] btrfs: Introduce COMPRESS reserve type to fix false enospc for compression Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 05/16] btrfs: dedupe: Introduce function to initialize dedupe info Qu Wenruo
` (11 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
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 <quwenruo@cn.fujitsu.com>
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
---
fs/btrfs/ctree.h | 7 +++
fs/btrfs/dedupe.h | 137 ++++++++++++++++++++++++++++++++++++++++++++-
fs/btrfs/disk-io.c | 1 +
include/uapi/linux/btrfs.h | 34 +++++++++++
4 files changed, 177 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index d986c0e88e56..df49517c4150 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1113,6 +1113,13 @@ struct btrfs_fs_info {
u32 nodesize;
u32 sectorsize;
u32 stripesize;
+
+ /*
+ * 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 83ebfe28da9e..5ecc32179a9c 100644
--- a/fs/btrfs/dedupe.h
+++ b/fs/btrfs/dedupe.h
@@ -19,6 +19,139 @@
#ifndef __BTRFS_DEDUPE__
#define __BTRFS_DEDUPE__
-/* later in-band dedupe will expand this struct */
-struct btrfs_dedupe_hash;
+#include <linux/btrfs.h>
+#include <linux/wait.h>
+#include <crypto/hash.h>
+
+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;
+};
+
+struct btrfs_trans_handle;
+
+static inline int btrfs_dedupe_hash_hit(struct btrfs_dedupe_hash *hash)
+{
+ return (hash && hash->bytenr);
+}
+
+int btrfs_dedupe_hash_size(u16 algo);
+struct btrfs_dedupe_hash *btrfs_dedupe_alloc_hash(u16 algo);
+
+/*
+ * 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_trans_handle *trans,
+ 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_trans_handle *trans,
+ struct btrfs_fs_info *fs_info, u64 bytenr);
#endif
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 08b74daf35d0..b99e9eb124ae 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -2659,6 +2659,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 db4c253f8011..db1596ce5356 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -629,6 +629,40 @@ 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 (8 * 1024 * 1024)
+#define BTRFS_DEDUPE_BLOCKSIZE_MIN (16 * 1024)
+#define BTRFS_DEDUPE_BLOCKSIZE_DEFAULT (128 * 1024)
+
+/* 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
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 05/16] btrfs: dedupe: Introduce function to initialize dedupe info
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (3 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 04/16] btrfs: dedupe: Introduce dedupe framework and its header Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 06/16] btrfs: dedupe: Introduce function to add hash into in-memory tree Qu Wenruo
` (10 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
Add generic function to initialize dedupe info.
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
Reviewed-by: Josef Bacik <jbacik@fb.com>
---
fs/btrfs/Makefile | 2 +-
fs/btrfs/dedupe.c | 185 +++++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/dedupe.h | 13 +++-
include/uapi/linux/btrfs.h | 4 +-
4 files changed, 200 insertions(+), 4 deletions(-)
create mode 100644 fs/btrfs/dedupe.c
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 128ce17a80b0..1b8c627cddb6 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -9,7 +9,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 \
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 hash.o free-space-tree.o
+ uuid-tree.o props.o hash.o free-space-tree.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..ae9146eb6bad
--- /dev/null
+++ b/fs/btrfs/dedupe.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2016 Fujitsu. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+#include "ctree.h"
+#include "dedupe.h"
+#include "btrfs_inode.h"
+#include "transaction.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 int init_dedupe_info(struct btrfs_dedupe_info **ret_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 -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)) {
+ int ret;
+
+ ret = PTR_ERR(dedupe_info->dedupe_driver);
+ kfree(dedupe_info);
+ return ret;
+ }
+
+ 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);
+
+ *ret_info = dedupe_info;
+ return 0;
+}
+
+/*
+ * 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 = 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:
+ ret = init_dedupe_info(&dedupe_info, dargs);
+ if (ret < 0)
+ return ret;
+ 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 5ecc32179a9c..8311ee13ca83 100644
--- a/fs/btrfs/dedupe.h
+++ b/fs/btrfs/dedupe.h
@@ -68,8 +68,17 @@ static inline int btrfs_dedupe_hash_hit(struct btrfs_dedupe_hash *hash)
return (hash && hash->bytenr);
}
-int btrfs_dedupe_hash_size(u16 algo);
-struct btrfs_dedupe_hash *btrfs_dedupe_alloc_hash(u16 algo);
+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
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index db1596ce5356..3dc237c589ed 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -644,6 +644,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.
@@ -662,7 +665,6 @@ struct btrfs_ioctl_dedupe_args {
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
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 06/16] btrfs: dedupe: Introduce function to add hash into in-memory tree
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (4 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 05/16] btrfs: dedupe: Introduce function to initialize dedupe info Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 07/16] btrfs: dedupe: Introduce function to remove hash from " Qu Wenruo
` (9 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
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 <quwenruo@cn.fujitsu.com>
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
Reviewed-by: Josef Bacik <jbacik@fb.com>
---
fs/btrfs/dedupe.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 151 insertions(+)
diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c
index ae9146eb6bad..49f28d08d28d 100644
--- a/fs/btrfs/dedupe.c
+++ b/fs/btrfs/dedupe.c
@@ -32,6 +32,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 int init_dedupe_info(struct btrfs_dedupe_info **ret_info,
struct btrfs_ioctl_dedupe_args *dargs)
{
@@ -183,3 +191,146 @@ 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_trans_handle *trans,
+ 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;
+}
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 07/16] btrfs: dedupe: Introduce function to remove hash from in-memory tree
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (5 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 06/16] btrfs: dedupe: Introduce function to add hash into in-memory tree Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 08/16] btrfs: delayed-ref: Add support for increasing data ref under spinlock Qu Wenruo
` (8 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang, Mark Fasheh
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
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 <mfasheh@suse.de>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
---
fs/btrfs/dedupe.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 126 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c
index 49f28d08d28d..6b8247d77648 100644
--- a/fs/btrfs/dedupe.c
+++ b/fs/btrfs/dedupe.c
@@ -186,12 +186,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)
{
@@ -334,3 +328,129 @@ int btrfs_dedupe_add(struct btrfs_trans_handle *trans,
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_trans_handle *trans,
+ 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;
+}
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 08/16] btrfs: delayed-ref: Add support for increasing data ref under spinlock
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (6 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 07/16] btrfs: dedupe: Introduce function to remove hash from " Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 09/16] btrfs: dedupe: Introduce function to search for an existing hash Qu Wenruo
` (7 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs
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.
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Reviewed-by: Josef Bacik <jbacik@fb.com>
---
fs/btrfs/delayed-ref.c | 31 ++++++++++++++++++++++++-------
fs/btrfs/delayed-ref.h | 9 +++++++++
2 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
index 6eb80952efb3..18111563eef1 100644
--- a/fs/btrfs/delayed-ref.c
+++ b/fs/btrfs/delayed-ref.c
@@ -832,6 +832,27 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
}
/*
+ * Do real delayed data ref insert.
+ * Caller must hold delayed_refs->lock and allocation memory
+ * for dref,head_ref and record.
+ */
+void btrfs_add_delayed_data_ref_locked(struct btrfs_fs_info *fs_info,
+ struct btrfs_trans_handle *trans,
+ struct btrfs_delayed_data_ref *dref,
+ struct btrfs_delayed_ref_head *head_ref,
+ struct btrfs_qgroup_extent_record *qrecord,
+ u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root,
+ u64 owner, u64 offset, u64 reserved, int action,
+ int *qrecord_inserted_ret)
+{
+ head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node,
+ qrecord, bytenr, num_bytes, ref_root, reserved,
+ action, 1, qrecord_inserted_ret);
+ add_delayed_data_ref(fs_info, trans, head_ref, &dref->node, bytenr,
+ num_bytes, parent, ref_root, owner, offset, action);
+}
+
+/*
* add a delayed data ref. it's similar to btrfs_add_delayed_tree_ref.
*/
int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
@@ -876,13 +897,9 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
* insert both the head node and the new ref without dropping
* the spin lock
*/
- head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, record,
- bytenr, num_bytes, ref_root, reserved,
- action, 1, &qrecord_inserted);
-
- add_delayed_data_ref(fs_info, trans, head_ref, &ref->node, bytenr,
- num_bytes, parent, ref_root, owner, offset,
- action);
+ btrfs_add_delayed_data_ref_locked(fs_info, trans, ref, head_ref, record,
+ bytenr, num_bytes, parent, ref_root, owner, offset,
+ reserved, action, &qrecord_inserted);
spin_unlock(&delayed_refs->lock);
if (qrecord_inserted)
diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h
index 0e537f98f1a1..94ee33a4d618 100644
--- a/fs/btrfs/delayed-ref.h
+++ b/fs/btrfs/delayed-ref.h
@@ -241,11 +241,20 @@ static inline void btrfs_put_delayed_ref(struct btrfs_delayed_ref_node *ref)
}
}
+struct btrfs_qgroup_extent_record;
int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
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);
+void btrfs_add_delayed_data_ref_locked(struct btrfs_fs_info *fs_info,
+ struct btrfs_trans_handle *trans,
+ struct btrfs_delayed_data_ref *dref,
+ struct btrfs_delayed_ref_head *head_ref,
+ struct btrfs_qgroup_extent_record *qrecord,
+ u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root,
+ u64 owner, u64 offset, u64 reserved, int action,
+ int *qrecord_inserted_ret);
int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes,
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 09/16] btrfs: dedupe: Introduce function to search for an existing hash
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (7 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 08/16] btrfs: delayed-ref: Add support for increasing data ref under spinlock Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 10/16] btrfs: dedupe: Implement btrfs_dedupe_calc_hash interface Qu Wenruo
` (6 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
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 <quwenruo@cn.fujitsu.com>
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
Reviewed-by: Josef Bacik <jbacik@fb.com>
---
fs/btrfs/dedupe.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 188 insertions(+)
diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c
index 6b8247d77648..4708dc08c4d1 100644
--- a/fs/btrfs/dedupe.c
+++ b/fs/btrfs/dedupe.c
@@ -20,6 +20,7 @@
#include "btrfs_inode.h"
#include "transaction.h"
#include "delayed-ref.h"
+#include "qgroup.h"
struct inmem_hash {
struct rb_node hash_node;
@@ -454,3 +455,190 @@ 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 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(root->root_key.objectid)) {
+ 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;
+
+ 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(root->fs_info, trans,
+ insert_dref, insert_head, insert_qrecord,
+ bytenr, num_bytes, 0, root->root_key.objectid,
+ btrfs_ino(BTRFS_I(inode)), file_pos, 0,
+ BTRFS_ADD_DELAYED_REF, &qrecord_inserted);
+ spin_unlock(&delayed_refs->lock);
+
+ /* 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(trans, 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->fs_info, bytenr, num_bytes, 0,
+ root->root_key.objectid,
+ 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;
+}
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 10/16] btrfs: dedupe: Implement btrfs_dedupe_calc_hash interface
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (8 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 09/16] btrfs: dedupe: Introduce function to search for an existing hash Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 11/16] btrfs: ordered-extent: Add support for dedupe Qu Wenruo
` (5 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
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 <quwenruo@cn.fujitsu.com>
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
Reviewed-by: Josef Bacik <jbacik@fb.com>
---
fs/btrfs/dedupe.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c
index 4708dc08c4d1..f938997c6d93 100644
--- a/fs/btrfs/dedupe.c
+++ b/fs/btrfs/dedupe.c
@@ -642,3 +642,49 @@ 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 btrfs_dedupe_info *dedupe_info = fs_info->dedupe_info;
+ struct crypto_shash *tfm = dedupe_info->dedupe_driver;
+ SHASH_DESC_ON_STACK(sdesc, tfm);
+ u64 dedupe_bs;
+ u64 sectorsize = fs_info->sectorsize;
+
+ 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;
+
+ sdesc->tfm = tfm;
+ sdesc->flags = 0;
+ ret = crypto_shash_init(sdesc);
+ 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(sdesc, d, sectorsize);
+ kunmap(p);
+ put_page(p);
+ if (ret)
+ return ret;
+ }
+ ret = crypto_shash_final(sdesc, hash->hash);
+ return ret;
+}
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 11/16] btrfs: ordered-extent: Add support for dedupe
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (9 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 10/16] btrfs: dedupe: Implement btrfs_dedupe_calc_hash interface Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 12/16] btrfs: dedupe: Inband in-memory only de-duplication implement Qu Wenruo
` (4 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
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 <quwenruo@cn.fujitsu.com>
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
Reviewed-by: Josef Bacik <jbacik@fb.com>
---
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 9a46878ba60f..511cbb86604e 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -26,6 +26,7 @@
#include "extent_io.h"
#include "disk-io.h"
#include "compression.h"
+#include "dedupe.h"
static struct kmem_cache *btrfs_ordered_extent_cache;
@@ -184,7 +185,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;
@@ -205,6 +207,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);
@@ -250,15 +279,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,
@@ -267,7 +304,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);
}
/*
@@ -578,6 +615,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 195c93b67fe0..172c0bd03879 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -141,6 +141,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;
};
/*
@@ -174,6 +184,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,
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 12/16] btrfs: dedupe: Inband in-memory only de-duplication implement
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (10 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 11/16] btrfs: ordered-extent: Add support for dedupe Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 13/16] btrfs: Introduce DEDUPE reserve type to fix false enospc for in-band dedupe Qu Wenruo
` (3 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
Core implement for inband de-duplication.
It reuse the async_cow_start() facility to do the calculate dedupe hash.
And use dedupe hash to do inband de-duplication at extent level.
The work flow 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 <wangxg.fnst@cn.fujitsu.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
---
fs/btrfs/extent-tree.c | 19 ++++
fs/btrfs/inode.c | 259 ++++++++++++++++++++++++++++++++++++++++++-------
fs/btrfs/relocation.c | 15 +++
3 files changed, 258 insertions(+), 35 deletions(-)
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 54105ba2f429..ad34a69a77f2 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -38,6 +38,7 @@
#include "math.h"
#include "sysfs.h"
#include "qgroup.h"
+#include "dedupe.h"
#undef SCRAMBLE_DELAYED_REFS
@@ -2391,6 +2392,7 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
if (btrfs_delayed_ref_is_head(node)) {
struct btrfs_delayed_ref_head *head;
+
/*
* we've hit the end of the chain and we were supposed
* to insert this extent into the tree. But, it got
@@ -2405,6 +2407,18 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
btrfs_pin_extent(fs_info, node->bytenr,
node->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(trans, fs_info,
+ node->bytenr);
+ if (ret < 0)
+ return ret;
ret = btrfs_del_csums(trans, fs_info,
node->bytenr,
node->num_bytes);
@@ -7074,6 +7088,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
btrfs_release_path(path);
if (is_data) {
+ ret = btrfs_dedupe_del(trans, 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/inode.c b/fs/btrfs/inode.c
index 1afa03d3dc5f..6289582e7caa 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -339,6 +339,7 @@ struct async_extent {
struct page **pages;
unsigned long nr_pages;
int compress_type;
+ struct btrfs_dedupe_hash *hash;
struct list_head list;
};
@@ -350,6 +351,7 @@ struct async_cow {
u64 end;
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,
@@ -357,7 +359,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;
@@ -369,6 +372,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;
}
@@ -598,7 +602,7 @@ static noinline void compress_file_range(struct inode *inode,
*/
add_async_extent(async_cow, start, num_bytes,
total_compressed, pages, nr_pages,
- compress_type);
+ compress_type, NULL);
if (start + num_bytes < end) {
start += num_bytes;
@@ -644,7 +648,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;
@@ -673,6 +677,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
@@ -689,6 +725,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:
@@ -698,6 +735,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? */
@@ -728,7 +766,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 */
@@ -738,15 +776,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(io_tree,
- inode, async_extent->start,
- async_extent->start +
- async_extent->ram_size - 1,
- btrfs_get_extent,
- 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(io_tree,
+ inode, async_extent->start,
+ async_extent->start +
+ async_extent->ram_size - 1,
+ btrfs_get_extent,
+ WB_SYNC_ALL);
+ } else if (ret)
unlock_page(async_cow->locked_page);
+ kfree(hash);
kfree(async_extent);
cond_resched();
continue;
@@ -850,6 +899,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();
}
@@ -869,6 +919,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;
}
@@ -981,11 +1032,17 @@ static noinline int cow_file_range(struct inode *inode,
unsigned long op;
cur_alloc_size = disk_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;
+ }
ram_size = ins.offset;
em = create_io_em(inode, start, ins.offset, /* len */
@@ -1001,8 +1058,9 @@ static noinline int cow_file_range(struct inode *inode,
free_extent_map(em);
cur_alloc_size = ins.offset;
- 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;
@@ -1014,7 +1072,14 @@ static noinline int cow_file_range(struct inode *inode,
goto out_drop_extent_cache;
}
- 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);
if (disk_num_bytes < cur_alloc_size)
break;
@@ -1057,6 +1122,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
*/
@@ -1064,11 +1202,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_COMPRESS)
+ compress_file_range(async_cow->inode, async_cow->locked_page,
+ async_cow->start, async_cow->end, async_cow,
+ &num_added);
+ else
+ ret = hash_file_ranges(async_cow->inode, 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;
@@ -1121,6 +1265,7 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
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;
@@ -1133,10 +1278,15 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
async_cow->root = root;
async_cow->locked_page = locked_page;
async_cow->start = start;
+ async_cow->reserve_type = reserve_type;
cur_end = end;
if (reserve_type == BTRFS_RESERVE_COMPRESS)
cur_end = min(end, start + SZ_512K - 1);
+ else if (fs_info->dedupe_enabled && dedupe_info) {
+ u64 len = max_t(u64, SZ_512K, dedupe_info->blocksize);
+ cur_end = min(end, start + len - 1);
+ }
async_cow->end = cur_end;
INIT_LIST_HEAD(&async_cow->extents);
@@ -1507,6 +1657,8 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
int need_compress;
enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_fs_info *fs_info = root->fs_info;
need_compress = test_range_bit(io_tree, start, end,
EXTENT_COMPRESS, 1, NULL);
@@ -1527,7 +1679,7 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 0, nr_written);
- } else if (!need_compress) {
+ } else if (!need_compress && !fs_info->dedupe_enabled) {
ret = cow_file_range(inode, locked_page, start, end, end,
page_started, nr_written, 1, NULL);
} else {
@@ -2167,7 +2319,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;
@@ -2229,13 +2382,43 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
ins.objectid = disk_bytenr;
ins.offset = disk_num_bytes;
ins.type = BTRFS_EXTENT_ITEM_KEY;
- ret = btrfs_alloc_reserved_file_extent(trans, root->root_key.objectid,
- btrfs_ino(BTRFS_I(inode)), file_pos, ram_bytes, &ins);
- /*
- * Release the reserved range from inode dirty range map, as it is
- * already moved into delayed_ref_head
- */
- btrfs_qgroup_release_data(inode, file_pos, ram_bytes);
+
+ 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, 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;
+ ret = btrfs_alloc_reserved_file_extent(trans,
+ root->root_key.objectid,
+ btrfs_ino(BTRFS_I(inode)), file_pos, ram_bytes,
+ &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(trans, root->fs_info, hash);
+ }
+
out:
btrfs_free_path(path);
@@ -2922,6 +3105,7 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
bool nolock;
bool truncated = false;
enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
+ int hash_hit = btrfs_dedupe_hash_hit(ordered_extent->hash);
nolock = btrfs_is_free_space_inode(BTRFS_I(inode));
@@ -3019,8 +3203,10 @@ 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);
+ /* 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);
@@ -3071,8 +3257,11 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
* wrong we need to return the space for this ordered extent
* back to the allocator. We only free the extent in the
* truncated case if we didn't write out the extent at all.
+ *
+ * For hash hit case, never free that extent, as it's being used
+ * by others.
*/
- if ((ret || !logical_len) &&
+ if ((ret || !logical_len) && !hash_hit &&
!test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) &&
!test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags))
btrfs_free_reserved_extent(fs_info,
@@ -3080,7 +3269,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.
@@ -10467,7 +10655,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/relocation.c b/fs/btrfs/relocation.c
index f70f4df422d8..32ba88053939 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -32,6 +32,7 @@
#include "free-space-cache.h"
#include "inode-map.h"
#include "qgroup.h"
+#include "dedupe.h"
/*
* backref_node, mapping_node and tree_block start with this
@@ -4116,6 +4117,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(trans, fs_info, key.objectid);
+ if (ret < 0) {
+ err = ret;
+ break;
+ }
+ }
btrfs_end_transaction_throttle(trans);
btrfs_btree_balance_dirty(fs_info);
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 13/16] btrfs: Introduce DEDUPE reserve type to fix false enospc for in-band dedupe
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (11 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 12/16] btrfs: dedupe: Inband in-memory only de-duplication implement Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 14/16] btrfs: dedupe: Add ioctl for inband dedupelication Qu Wenruo
` (2 subsequent siblings)
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
We can trigger false ENOSPC error if enabling in-band dedupe.
This is the same reason of compress false ENOSPC error:
Difference in max extent size can lead to too much space reserved for
metadata.
Since we already have type-based reserve facilities, add DEDUP reserve
type to fix such false ENOSPC error.
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
---
fs/btrfs/ctree.h | 4 ++-
fs/btrfs/dedupe.h | 18 +++++++++++
fs/btrfs/extent-tree.c | 14 +++++----
fs/btrfs/extent_io.c | 10 ++++---
fs/btrfs/extent_io.h | 1 +
fs/btrfs/file.c | 3 ++
fs/btrfs/inode.c | 81 ++++++++++++++++++++++++++++++++++----------------
fs/btrfs/ioctl.c | 1 +
fs/btrfs/relocation.c | 2 ++
9 files changed, 98 insertions(+), 36 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index df49517c4150..8905a2b490ba 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -116,9 +116,11 @@ static inline u32 count_max_extents(u64 size, u64 max_extent_size)
enum btrfs_metadata_reserve_type {
BTRFS_RESERVE_NORMAL,
BTRFS_RESERVE_COMPRESS,
+ 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);
int inode_need_compress(struct inode *inode);
struct btrfs_mapping_tree {
diff --git a/fs/btrfs/dedupe.h b/fs/btrfs/dedupe.h
index 8311ee13ca83..3a15fc2069b9 100644
--- a/fs/btrfs/dedupe.h
+++ b/fs/btrfs/dedupe.h
@@ -22,6 +22,7 @@
#include <linux/btrfs.h>
#include <linux/wait.h>
#include <crypto/hash.h>
+#include "btrfs_inode.h"
static const int btrfs_hash_sizes[] = { 32 };
@@ -63,6 +64,23 @@ struct btrfs_dedupe_info {
struct btrfs_trans_handle;
+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;
+
+ if (!fs_info->dedupe_enabled)
+ return 0;
+
+ return 1;
+}
+
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 ad34a69a77f2..53779f407d65 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5867,7 +5867,7 @@ static unsigned drop_outstanding_extent(struct btrfs_inode *inode,
unsigned drop_inode_space = 0;
unsigned dropped_extents = 0;
unsigned num_extents;
- u64 max_extent_size = btrfs_max_extent_size(reserve_type);
+ u64 max_extent_size = btrfs_max_extent_size(inode, reserve_type);
num_extents = count_max_extents(num_bytes, max_extent_size);
ASSERT(num_extents);
@@ -5936,15 +5936,17 @@ static u64 calc_csum_metadata_size(struct btrfs_inode *inode, u64 num_bytes,
return btrfs_calc_trans_metadata_size(fs_info, old_csums - num_csums);
}
-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;
else if (reserve_type == BTRFS_RESERVE_COMPRESS)
return SZ_128K;
-
- 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,
@@ -5960,7 +5962,7 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes,
int ret = 0;
bool delalloc_lock = true;
u64 to_free = 0;
- u64 max_extent_size = btrfs_max_extent_size(reserve_type);
+ u64 max_extent_size = btrfs_max_extent_size(inode, reserve_type);
unsigned dropped;
bool release_extra = false;
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 3065367a3703..fce21362ccce 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -609,7 +609,7 @@ static 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 | EXTENT_COMPRESS;
+ bits |= EXTENT_NORESERVE | EXTENT_COMPRESS | EXTENT_DEDUPE;
if (delete)
bits |= ~EXTENT_CTLBITS;
@@ -752,7 +752,8 @@ static void adjust_one_outstanding_extent(struct inode *inode, u64 len,
enum btrfs_metadata_reserve_type reserve_type)
{
unsigned old_extents, new_extents;
- u64 max_extent_size = btrfs_max_extent_size(reserve_type);
+ u64 max_extent_size = btrfs_max_extent_size(BTRFS_I(inode),
+ reserve_type);
old_extents = div64_u64(len + max_extent_size - 1, max_extent_size);
new_extents = div64_u64(len + BTRFS_MAX_EXTENT_SIZE - 1,
@@ -791,7 +792,7 @@ void adjust_outstanding_extents(struct inode *inode, u64 start, u64 end,
* The whole range is locked, so we can safely clear
* EXTENT_COMPRESS flag.
*/
- state->state &= ~EXTENT_COMPRESS;
+ state->state &= ~(EXTENT_COMPRESS | EXTENT_DEDUPE);
adjust_one_outstanding_extent(inode,
state->end - state->start + 1, reserve_type);
node = rb_next(node);
@@ -1583,7 +1584,8 @@ static noinline u64 find_delalloc_range(struct extent_io_tree *tree,
state = rb_entry(node, struct extent_state, rb_node);
if (found && (state->start != cur_start ||
(state->state & EXTENT_BOUNDARY) ||
- (state->state ^ pre_state) & EXTENT_COMPRESS)) {
+ (state->state ^ pre_state) & (EXTENT_COMPRESS |
+ EXTENT_DEDUPE))) {
goto out;
}
if (!(state->state & EXTENT_DELALLOC)) {
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 0f4478dae822..b69d2566cc4d 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -22,6 +22,7 @@
#define EXTENT_QGROUP_RESERVED (1U << 16)
#define EXTENT_CLEAR_DATA_RESV (1U << 17)
#define EXTENT_COMPRESS (1U << 18)
+#define EXTENT_DEDUPE (1U << 19)
#define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK)
#define EXTENT_CTLBITS (EXTENT_DO_ACCOUNTING | EXTENT_FIRST_DELALLOC)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 8cefcef3c79e..0557fc90d516 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -41,6 +41,7 @@
#include "volumes.h"
#include "qgroup.h"
#include "compression.h"
+#include "dedupe.h"
static struct kmem_cache *btrfs_inode_defrag_cachep;
/*
@@ -1551,6 +1552,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
if (inode_need_compress(inode))
reserve_type = BTRFS_RESERVE_COMPRESS;
+ else if (inode_need_dedupe(inode))
+ reserve_type = BTRFS_RESERVE_DEDUPE;
while (iov_iter_count(i) > 0) {
size_t offset = pos & (PAGE_SIZE - 1);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 6289582e7caa..5e52ba693756 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -339,6 +339,7 @@ struct async_extent {
struct page **pages;
unsigned long nr_pages;
int compress_type;
+ int dedupe;
struct btrfs_dedupe_hash *hash;
struct list_head list;
};
@@ -359,7 +360,7 @@ 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, int dedupe,
struct btrfs_dedupe_hash *hash)
{
struct async_extent *async_extent;
@@ -372,6 +373,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->dedupe = dedupe;
async_extent->hash = hash;
list_add_tail(&async_extent->list, &cow->extents);
return 0;
@@ -602,7 +604,7 @@ static noinline void compress_file_range(struct inode *inode,
*/
add_async_extent(async_cow, start, num_bytes,
total_compressed, pages, nr_pages,
- compress_type, NULL);
+ compress_type, 0, NULL);
if (start + num_bytes < end) {
start += num_bytes;
@@ -648,7 +650,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, NULL);
+ BTRFS_COMPRESS_NONE, 0, NULL);
*num_added += 1;
return;
@@ -754,10 +756,12 @@ static noinline void submit_compressed_extents(struct inode *inode,
* 128MB as max extent size to re-calculate number of
* outstanding extents for this extent.
*/
- adjust_outstanding_extents(inode, async_extent->start,
- async_extent->start +
- async_extent->ram_size - 1,
- BTRFS_RESERVE_COMPRESS);
+ if (!async_extent->dedupe)
+ adjust_outstanding_extents(inode,
+ async_extent->start,
+ async_extent->start +
+ async_extent->ram_size - 1,
+ BTRFS_RESERVE_COMPRESS);
/* allocate blocks */
ret = cow_file_range(inode, async_cow->locked_page,
async_extent->start,
@@ -1177,7 +1181,7 @@ static int hash_file_ranges(struct inode *inode, u64 start, u64 end,
__set_page_dirty_nobuffers(locked_page);
add_async_extent(async_cow, cur_offset, len, 0, NULL, 0,
- BTRFS_COMPRESS_NONE, hash);
+ BTRFS_COMPRESS_NONE, 1, hash);
cur_offset += len;
(*num_added)++;
}
@@ -1283,10 +1287,11 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
cur_end = end;
if (reserve_type == BTRFS_RESERVE_COMPRESS)
cur_end = min(end, start + SZ_512K - 1);
- else if (fs_info->dedupe_enabled && dedupe_info) {
+ 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
+ ASSERT(0);
async_cow->end = cur_end;
INIT_LIST_HEAD(&async_cow->extents);
@@ -1655,31 +1660,33 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
int ret;
int force_cow = need_force_cow(inode, start, end);
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
- int need_compress;
enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
- struct btrfs_root *root = BTRFS_I(inode)->root;
- struct btrfs_fs_info *fs_info = root->fs_info;
+ int need_compress, need_dedupe;
need_compress = test_range_bit(io_tree, start, end,
EXTENT_COMPRESS, 1, NULL);
+ need_dedupe = test_range_bit(io_tree, start, end,
+ EXTENT_DEDUPE, 1, NULL);
if (need_compress)
reserve_type = BTRFS_RESERVE_COMPRESS;
+ else if (need_dedupe)
+ reserve_type = BTRFS_RESERVE_DEDUPE;
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW && !force_cow) {
- if (need_compress)
+ if (need_compress || need_dedupe)
adjust_outstanding_extents(inode, start, end,
reserve_type);
ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 1, nr_written);
} else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC && !force_cow) {
- if (need_compress)
+ if (need_compress || need_dedupe)
adjust_outstanding_extents(inode, start, end,
reserve_type);
ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 0, nr_written);
- } else if (!need_compress && !fs_info->dedupe_enabled) {
+ } else if (!need_compress && !need_dedupe) {
ret = cow_file_range(inode, locked_page, start, end, end,
page_started, nr_written, 1, NULL);
} else {
@@ -1707,7 +1714,9 @@ static void btrfs_split_extent_hook(struct inode *inode,
if (orig->state & EXTENT_COMPRESS)
reserve_type = BTRFS_RESERVE_COMPRESS;
- max_extent_size = btrfs_max_extent_size(reserve_type);
+ else 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) {
@@ -1755,7 +1764,9 @@ static void btrfs_merge_extent_hook(struct inode *inode,
if (other->state & EXTENT_COMPRESS)
reserve_type = BTRFS_RESERVE_COMPRESS;
- max_extent_size = btrfs_max_extent_size(reserve_type);
+ else 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;
@@ -1872,7 +1883,10 @@ static void btrfs_set_bit_hook(struct inode *inode,
if (*bits & EXTENT_COMPRESS)
reserve_type = BTRFS_RESERVE_COMPRESS;
- max_extent_size = btrfs_max_extent_size(reserve_type);
+ else 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);
if (*bits & EXTENT_FIRST_DELALLOC)
@@ -1930,7 +1944,9 @@ static void btrfs_clear_bit_hook(struct btrfs_inode *inode,
if (state->state & EXTENT_COMPRESS)
reserve_type = BTRFS_RESERVE_COMPRESS;
- max_extent_size = btrfs_max_extent_size(reserve_type);
+ else 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);
if (*bits & EXTENT_FIRST_DELALLOC) {
@@ -2142,13 +2158,16 @@ int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
{
int ret;
unsigned bits;
- u64 max_extent_size = btrfs_max_extent_size(reserve_type);
+ u64 max_extent_size = btrfs_max_extent_size(BTRFS_I(inode),
+ reserve_type);
u64 num_extents = div64_u64(end - start + max_extent_size,
max_extent_size);
/* compression path */
if (reserve_type == BTRFS_RESERVE_COMPRESS)
bits = EXTENT_DELALLOC | EXTENT_COMPRESS | EXTENT_UPTODATE;
+ else if (reserve_type == BTRFS_RESERVE_DEDUPE)
+ bits = EXTENT_DELALLOC | EXTENT_DEDUPE | EXTENT_UPTODATE;
else
bits = EXTENT_DELALLOC | EXTENT_UPTODATE;
@@ -2182,7 +2201,8 @@ int btrfs_set_extent_defrag(struct inode *inode, u64 start, u64 end,
{
int ret;
unsigned bits;
- u64 max_extent_size = btrfs_max_extent_size(reserve_type);
+ u64 max_extent_size = btrfs_max_extent_size(BTRFS_I(inode),
+ reserve_type);
u64 num_extents = div64_u64(end - start + max_extent_size,
max_extent_size);
@@ -2255,6 +2275,9 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
if (inode_need_compress(inode))
reserve_type = BTRFS_RESERVE_COMPRESS;
+ else if (inode_need_dedupe(inode))
+ reserve_type = BTRFS_RESERVE_DEDUPE;
+
ret = btrfs_delalloc_reserve_space(inode, page_start,
PAGE_SIZE, reserve_type);
if (ret) {
@@ -3187,7 +3210,8 @@ 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;
reserve_type = BTRFS_RESERVE_COMPRESS;
- }
+ } else if (ordered_extent->hash)
+ reserve_type = BTRFS_RESERVE_DEDUPE;
if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) {
BUG_ON(compress_type);
@@ -4977,6 +5001,8 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
if (inode_need_compress(inode))
reserve_type = BTRFS_RESERVE_COMPRESS;
+ else if (inode_need_dedupe(inode))
+ reserve_type = BTRFS_RESERVE_DEDUPE;
if ((offset & (blocksize - 1)) == 0 &&
(!len || ((len & (blocksize - 1)) == 0)))
@@ -7879,7 +7905,8 @@ static void adjust_dio_outstanding_extents(struct inode *inode,
struct btrfs_dio_data *dio_data,
const u64 len)
{
- u64 max_extent_size = btrfs_max_extent_size(BTRFS_RESERVE_NORMAL);
+ u64 max_extent_size = btrfs_max_extent_size(BTRFS_I(inode),
+ BTRFS_RESERVE_NORMAL);
unsigned num_extents = count_max_extents(len, max_extent_size);
/*
@@ -8911,7 +8938,8 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
bool wakeup = true;
bool relock = false;
ssize_t ret;
- u64 max_extent_size = btrfs_max_extent_size(BTRFS_RESERVE_NORMAL);
+ u64 max_extent_size = btrfs_max_extent_size(BTRFS_I(inode),
+ BTRFS_RESERVE_NORMAL);
if (check_direct_IO(fs_info, iocb, iter, offset))
return 0;
@@ -9253,6 +9281,9 @@ int btrfs_page_mkwrite(struct vm_fault *vmf)
if (inode_need_compress(inode))
reserve_type = BTRFS_RESERVE_COMPRESS;
+ else 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
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index cd0afe1fb3ca..8ca64a203fe5 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -60,6 +60,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 32ba88053939..01320ada307b 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -3189,6 +3189,8 @@ static int relocate_file_extent_cluster(struct inode *inode,
if (inode_need_compress(inode))
reserve_type = BTRFS_RESERVE_COMPRESS;
+ else if (inode_need_dedupe(inode))
+ reserve_type = BTRFS_RESERVE_DEDUPE;
ra = kzalloc(sizeof(*ra), GFP_NOFS);
if (!ra)
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 14/16] btrfs: dedupe: Add ioctl for inband dedupelication
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (12 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 13/16] btrfs: Introduce DEDUPE reserve type to fix false enospc for in-band dedupe Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 15/16] btrfs: relocation: Enhance error handling to avoid BUG_ON Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 16/16] btrfs: dedupe: Introduce new reconfigure ioctl Qu Wenruo
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs; +Cc: Wang Xiaoguang
From: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
Add ioctl interface for inband dedupelication, 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 encounted invalid parameter to (-1) to inform caller.
While for limit_nr/limit_mem, the value will be (0).
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: Wang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
---
fs/btrfs/dedupe.c | 50 ++++++++++++++++++++++++++++++++++
fs/btrfs/dedupe.h | 17 ++++++++----
fs/btrfs/disk-io.c | 3 +++
fs/btrfs/ioctl.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/sysfs.c | 2 ++
include/uapi/linux/btrfs.h | 12 ++++++++-
6 files changed, 145 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c
index f938997c6d93..b57de28bbf4e 100644
--- a/fs/btrfs/dedupe.c
+++ b/fs/btrfs/dedupe.c
@@ -41,6 +41,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 int init_dedupe_info(struct btrfs_dedupe_info **ret_info,
struct btrfs_ioctl_dedupe_args *dargs)
{
@@ -420,6 +449,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 3a15fc2069b9..07079238cb8c 100644
--- a/fs/btrfs/dedupe.h
+++ b/fs/btrfs/dedupe.h
@@ -109,6 +109,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.
@@ -120,12 +129,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 b99e9eb124ae..83a023ba5c11 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -50,6 +50,7 @@
#include "sysfs.h"
#include "qgroup.h"
#include "compression.h"
+#include "dedupe.h"
#ifdef CONFIG_X86
#include <asm/cpufeature.h>
@@ -3971,6 +3972,8 @@ void close_ctree(struct btrfs_fs_info *fs_info)
btrfs_free_qgroup_config(fs_info);
+ 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 8ca64a203fe5..3bc1d5985046 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3281,6 +3281,69 @@ ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen,
return olen;
}
+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,
@@ -5635,6 +5698,10 @@ 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);
+#ifdef CONFIG_BTRFS_DEBUG
+ case BTRFS_IOC_DEDUPE_CTL:
+ return btrfs_ioctl_dedupe_ctl(root, argp);
+#endif
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 1f157fba8940..cc408a5e58cf 100644
--- a/fs/btrfs/sysfs.c
+++ b/fs/btrfs/sysfs.c
@@ -206,6 +206,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),
@@ -218,6 +219,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 3dc237c589ed..b12c5658ab53 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -250,6 +250,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)
@@ -646,7 +647,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.
@@ -873,6 +881,8 @@ static inline char *btrfs_err_str(enum btrfs_err_code 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, \
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 15/16] btrfs: relocation: Enhance error handling to avoid BUG_ON
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (13 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 14/16] btrfs: dedupe: Add ioctl for inband dedupelication Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 16/16] btrfs: dedupe: Introduce new reconfigure ioctl Qu Wenruo
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs
Since the introduce 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] [<ffffffffa01f71d3>]
btrfs_relocate_block_group+0x1b3/0x290 [btrfs]
[ 2611.687706] [<ffffffffa01cc177>]
btrfs_relocate_chunk.isra.40+0x47/0xd0 [btrfs]
[ 2611.688385] [<ffffffffa01cdb12>] btrfs_balance+0xb22/0x11e0 [btrfs]
[ 2611.688966] [<ffffffffa01d9611>] btrfs_ioctl_balance+0x391/0x3a0
[btrfs]
[ 2611.689587] [<ffffffffa01ddaf0>] btrfs_ioctl+0x1650/0x2290 [btrfs]
[ 2611.690145] [<ffffffff81171cda>] ? lru_cache_add+0x3a/0x80
[ 2611.690647] [<ffffffff81171e4c>] ?
lru_cache_add_active_or_unevictable+0x4c/0xc0
[ 2611.691310] [<ffffffff81193f04>] ? handle_mm_fault+0xcd4/0x17f0
[ 2611.691842] [<ffffffff811da423>] ? cp_new_stat+0x153/0x180
[ 2611.692342] [<ffffffff8119913d>] ? __vma_link_rb+0xfd/0x110
[ 2611.692842] [<ffffffff81199209>] ? vma_link+0xb9/0xc0
[ 2611.693303] [<ffffffff811e7e81>] do_vfs_ioctl+0xa1/0x5a0
[ 2611.693781] [<ffffffff8104e024>] ? __do_page_fault+0x1b4/0x400
[ 2611.694310] [<ffffffff811e83c1>] SyS_ioctl+0x41/0x70
[ 2611.694758] [<ffffffff816dfc6e>] 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 [<ffffffffa01f6fc1>]
relocate_block_group+0x741/0x7a0 [btrfs]
[ 2611.698818] RSP <ffff88002a81fb30>
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 <takeuchi_satoru@jp.fujitsu.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
---
fs/btrfs/relocation.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 01320ada307b..e24eeb991e02 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -889,6 +889,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;
}
@@ -3051,14 +3058,21 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans,
}
rb_node = rb_first(blocks);
- while (rb_node) {
+ for (rb_node = rb_first(blocks); rb_node; rb_node = rb_next(rb_node)) {
block = rb_entry(rb_node, struct tree_block, rb_node);
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,
@@ -3066,11 +3080,9 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans,
if (ret < 0) {
if (ret != -EAGAIN || rb_node == rb_first(blocks))
err = ret;
- goto out;
+ break;
}
- rb_node = rb_next(rb_node);
}
-out:
err = finish_pending_nodes(trans, rc, path, err);
out_free_path:
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v14.2 16/16] btrfs: dedupe: Introduce new reconfigure ioctl
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
` (14 preceding siblings ...)
2017-03-16 7:58 ` [PATCH v14.2 15/16] btrfs: relocation: Enhance error handling to avoid BUG_ON Qu Wenruo
@ 2017-03-16 7:58 ` Qu Wenruo
15 siblings, 0 replies; 17+ messages in thread
From: Qu Wenruo @ 2017-03-16 7:58 UTC (permalink / raw)
To: linux-btrfs
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 disbale 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 <dsterba@suse.cz>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
---
fs/btrfs/dedupe.c | 131 ++++++++++++++++++++++++++++++++++++---------
fs/btrfs/dedupe.h | 13 +++++
fs/btrfs/ioctl.c | 13 +++++
include/uapi/linux/btrfs.h | 11 +++-
4 files changed, 143 insertions(+), 25 deletions(-)
diff --git a/fs/btrfs/dedupe.c b/fs/btrfs/dedupe.c
index b57de28bbf4e..442ded0457f1 100644
--- a/fs/btrfs/dedupe.c
+++ b/fs/btrfs/dedupe.c
@@ -41,6 +41,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)
{
@@ -57,15 +91,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));
}
@@ -114,17 +140,50 @@ static int init_dedupe_info(struct btrfs_dedupe_info **ret_info,
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 ||
@@ -145,7 +204,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;
@@ -178,18 +238,19 @@ 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;
+ int 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 ||
@@ -216,6 +277,28 @@ int btrfs_dedupe_enable(struct btrfs_fs_info *fs_info,
return ret;
}
+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,
struct inmem_hash *hash, int hash_len)
{
diff --git a/fs/btrfs/dedupe.h b/fs/btrfs/dedupe.h
index 07079238cb8c..678e0d3d2635 100644
--- a/fs/btrfs/dedupe.h
+++ b/fs/btrfs/dedupe.h
@@ -109,6 +109,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 3bc1d5985046..686342e8384a 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3325,6 +3325,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 b12c5658ab53..f0e0ec05e4b8 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -654,7 +654,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.
--
2.12.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
end of thread, other threads:[~2017-03-16 7:59 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-03-16 7:58 [PATCH v14.2 00/16] Btrfs In-band De-duplication Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 01/16] btrfs: improve inode's outstanding_extents computation Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 02/16] btrfs: introduce type based delalloc metadata reserve Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 03/16] btrfs: Introduce COMPRESS reserve type to fix false enospc for compression Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 04/16] btrfs: dedupe: Introduce dedupe framework and its header Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 05/16] btrfs: dedupe: Introduce function to initialize dedupe info Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 06/16] btrfs: dedupe: Introduce function to add hash into in-memory tree Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 07/16] btrfs: dedupe: Introduce function to remove hash from " Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 08/16] btrfs: delayed-ref: Add support for increasing data ref under spinlock Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 09/16] btrfs: dedupe: Introduce function to search for an existing hash Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 10/16] btrfs: dedupe: Implement btrfs_dedupe_calc_hash interface Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 11/16] btrfs: ordered-extent: Add support for dedupe Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 12/16] btrfs: dedupe: Inband in-memory only de-duplication implement Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 13/16] btrfs: Introduce DEDUPE reserve type to fix false enospc for in-band dedupe Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 14/16] btrfs: dedupe: Add ioctl for inband dedupelication Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 15/16] btrfs: relocation: Enhance error handling to avoid BUG_ON Qu Wenruo
2017-03-16 7:58 ` [PATCH v14.2 16/16] btrfs: dedupe: Introduce new reconfigure ioctl Qu Wenruo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).