Am 06.10.2016 um 04:51 schrieb Wang Xiaoguang:
> 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 plenty of metadata space, even for very small
> data range. In btrfs_delalloc_reserve_metadata(), the number of metadata bytes
> we try to reserve is calculated by the difference between outstanding_extents
> and reserved_extents. Please see below case for how ENOSPC occurs:
> 
>   1, Buffered write 128MB data in unit of 128KB, so finially we'll have inode
> outstanding extents be 1, and reserved_extents be 1024. Note it's
> btrfs_merge_extent_hook() that merges these 128KB units into one big
> outstanding extent, but do not change reserved_extents.
> 
>   2, When writing dirty pages, for compression, cow_file_range_async() will
> split above big extent in unit of 128KB(compression extent size is 128KB).
> When first split opeartion finishes, we'll have 2 outstanding extents and 1024
> reserved extents, and just right now the currently generated ordered extent is
> dispatched to run and complete, then btrfs_delalloc_release_metadata()(see
> btrfs_finish_ordered_io()) will be called to release metadata, after that we
> will have 1 outstanding extents and 1 reserved extents(also see logic in
> drop_outstanding_extent()). Later cow_file_range_async() continues to handles
> left data range[128KB, 128MB), and if no other ordered extent was dispatched
> to run, there will be 1023 outstanding extents and 1 reserved extent.
> 
>   3, Now if another bufferd write for this file enters, then
> btrfs_delalloc_reserve_metadata() will at least try to reserve metadata
> for 1023 outstanding extents' metadata, for 16KB node size, it'll be 
> 1023*16384*2*8,
> about 255MB, for 64K node size, it'll be 1023*65536*8*2, about 1GB metadata, 
> so
> obviously it's not sane and can easily result in enospc error.
> 
> The root cause is that for compression, its max extent size will no longer be
> BTRFS_MAX_EXTENT_SIZE(128MB), it'll be 128KB, so current metadata reservation
> method in btrfs is not appropriate or correct, here we introduce:
>       enum btrfs_metadata_reserve_type {
>               BTRFS_RESERVE_NORMAL,
>               BTRFS_RESERVE_COMPRESS,
>       };
> and expand btrfs_delalloc_reserve_metadata() and 
> btrfs_delalloc_reserve_space()
> by adding a new enum btrfs_metadata_reserve_type argument. When a data range 
> will
> go through compression, we use BTRFS_RESERVE_COMPRESS to reserve metatata.
> Meanwhile we introduce EXTENT_COMPRESS flag to mark a data range that will go
> through compression path.
> 
> With this patch, we can fix these false enospc error for compression.
> 
> Signed-off-by: Wang Xiaoguang <wangxg.f...@cn.fujitsu.com>

Tested-by: Stefan Priebe <s.pri...@profihost.ag>

Works fine since 8 days - no ENOSPC errors anymore.

Greets,
Stefan


> ---
>  fs/btrfs/ctree.h             |  31 ++++++--
>  fs/btrfs/extent-tree.c       |  55 +++++++++----
>  fs/btrfs/extent_io.c         |  59 +++++++++++++-
>  fs/btrfs/extent_io.h         |   2 +
>  fs/btrfs/file.c              |  26 +++++--
>  fs/btrfs/free-space-cache.c  |   6 +-
>  fs/btrfs/inode-map.c         |   5 +-
>  fs/btrfs/inode.c             | 181 
> ++++++++++++++++++++++++++++++++-----------
>  fs/btrfs/ioctl.c             |  12 ++-
>  fs/btrfs/relocation.c        |  14 +++-
>  fs/btrfs/tests/inode-tests.c |  15 ++--
>  11 files changed, 309 insertions(+), 97 deletions(-)
> 
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 16885f6..fa6a19a 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -97,6 +97,19 @@ static const int btrfs_csum_sizes[] = { 4 };
>  
>  #define BTRFS_DIRTY_METADATA_THRESH  SZ_32M
>  
> +/*
> + * for compression, max file extent size would be limited to 128K, so when
> + * reserving metadata for such delalloc writes, pass BTRFS_RESERVE_COMPRESS 
> to
> + * btrfs_delalloc_reserve_metadata() or btrfs_delalloc_reserve_space() to
> + * calculate metadata, for none-compression, use BTRFS_RESERVE_NORMAL.
> + */
> +enum btrfs_metadata_reserve_type {
> +     BTRFS_RESERVE_NORMAL,
> +     BTRFS_RESERVE_COMPRESS,
> +};
> +int inode_need_compress(struct inode *inode);
> +u64 btrfs_max_extent_size(enum btrfs_metadata_reserve_type reserve_type);
> +
>  #define BTRFS_MAX_EXTENT_SIZE SZ_128M
>  
>  struct btrfs_mapping_tree {
> @@ -2677,10 +2690,14 @@ int btrfs_subvolume_reserve_metadata(struct 
> btrfs_root *root,
>  void btrfs_subvolume_release_metadata(struct btrfs_root *root,
>                                     struct btrfs_block_rsv *rsv,
>                                     u64 qgroup_reserved);
> -int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes);
> -void btrfs_delalloc_release_metadata(struct 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 inode *inode, u64 num_bytes,
> +             enum btrfs_metadata_reserve_type reserve_type);
> +void btrfs_delalloc_release_metadata(struct 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_root *root,
>                                             unsigned short type);
> @@ -3118,9 +3135,9 @@ 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);
> +                           struct extent_state **cached_state, int flag);
>  int btrfs_set_extent_defrag(struct inode *inode, u64 start, u64 end,
> -                         struct extent_state **cached_state);
> +                         struct extent_state **cached_state, int flag);
>  int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
>                            struct btrfs_root *new_root,
>                            struct btrfs_root *parent_root,
> @@ -3213,7 +3230,7 @@ int btrfs_release_file(struct inode *inode, struct file 
> *file);
>  int btrfs_dirty_pages(struct btrfs_root *root, 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, int flag);
>  int btrfs_fdatawrite_range(struct inode *inode, loff_t start, loff_t end);
>  ssize_t btrfs_copy_file_range(struct file *file_in, loff_t pos_in,
>                             struct file *file_out, loff_t pos_out,
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index 665da8f..9cfd1d0 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -5836,15 +5836,16 @@ void btrfs_subvolume_release_metadata(struct 
> btrfs_root *root,
>   * reserved extents that need to be freed.  This must be called with
>   * BTRFS_I(inode)->lock held.
>   */
> -static unsigned drop_outstanding_extent(struct inode *inode, u64 num_bytes)
> +static unsigned drop_outstanding_extent(struct inode *inode, u64 num_bytes,
> +                     enum btrfs_metadata_reserve_type reserve_type)
>  {
>       unsigned drop_inode_space = 0;
>       unsigned dropped_extents = 0;
>       unsigned num_extents = 0;
> +     u64 max_extent_size = btrfs_max_extent_size(reserve_type);
>  
> -     num_extents = (unsigned)div64_u64(num_bytes +
> -                                       BTRFS_MAX_EXTENT_SIZE - 1,
> -                                       BTRFS_MAX_EXTENT_SIZE);
> +     num_extents = (unsigned)div64_u64(num_bytes + max_extent_size - 1,
> +                                       max_extent_size);
>       ASSERT(num_extents);
>       ASSERT(BTRFS_I(inode)->outstanding_extents >= num_extents);
>       BTRFS_I(inode)->outstanding_extents -= num_extents;
> @@ -5914,7 +5915,21 @@ static u64 calc_csum_metadata_size(struct inode 
> *inode, u64 num_bytes,
>       return btrfs_calc_trans_metadata_size(root, old_csums - num_csums);
>  }
>  
> -int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
> +u64 btrfs_max_extent_size(enum btrfs_metadata_reserve_type reserve_type)
> +{
> +     if (reserve_type == BTRFS_RESERVE_COMPRESS)
> +             return SZ_128K;
> +
> +     return BTRFS_MAX_EXTENT_SIZE;
> +}
> +
> +/*
> + * @reserve_type: normally reserve_type should be BTRFS_RESERVE_NORMAL, but 
> for
> + * compression path, its max extent size is limited to 128KB, not 128MB, when
> + * reserving metadata, we should set reserve_type to BTRFS_RESERVE_COMPRESS.
> + */
> +int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes,
> +             enum btrfs_metadata_reserve_type reserve_type)
>  {
>       struct btrfs_root *root = BTRFS_I(inode)->root;
>       struct btrfs_block_rsv *block_rsv = &root->fs_info->delalloc_block_rsv;
> @@ -5927,6 +5942,7 @@ int btrfs_delalloc_reserve_metadata(struct inode 
> *inode, u64 num_bytes)
>       u64 to_free = 0;
>       unsigned dropped;
>       bool release_extra = false;
> +     u64 max_extent_size = btrfs_max_extent_size(reserve_type);
>  
>       /* If we are a free space inode we need to not flush since we will be in
>        * the middle of a transaction commit.  We also don't need the delalloc
> @@ -5953,9 +5969,8 @@ int btrfs_delalloc_reserve_metadata(struct inode 
> *inode, u64 num_bytes)
>       num_bytes = ALIGN(num_bytes, root->sectorsize);
>  
>       spin_lock(&BTRFS_I(inode)->lock);
> -     nr_extents = (unsigned)div64_u64(num_bytes +
> -                                      BTRFS_MAX_EXTENT_SIZE - 1,
> -                                      BTRFS_MAX_EXTENT_SIZE);
> +     nr_extents = (unsigned)div64_u64(num_bytes + max_extent_size - 1,
> +                                      max_extent_size);
>       BTRFS_I(inode)->outstanding_extents += nr_extents;
>  
>       nr_extents = 0;
> @@ -6006,7 +6021,7 @@ int btrfs_delalloc_reserve_metadata(struct inode 
> *inode, u64 num_bytes)
>  
>  out_fail:
>       spin_lock(&BTRFS_I(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
> @@ -6072,12 +6087,15 @@ out_fail:
>   * 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: this value must be same to the value passing 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 inode *inode, u64 num_bytes)
> +void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes,
> +             enum btrfs_metadata_reserve_type reserve_type)
>  {
>       struct btrfs_root *root = BTRFS_I(inode)->root;
>       u64 to_free = 0;
> @@ -6085,7 +6103,7 @@ void btrfs_delalloc_release_metadata(struct inode 
> *inode, u64 num_bytes)
>  
>       num_bytes = ALIGN(num_bytes, root->sectorsize);
>       spin_lock(&BTRFS_I(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);
> @@ -6109,6 +6127,9 @@ void btrfs_delalloc_release_metadata(struct 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: normally reserve_type should be BTRFS_RESERVE_NORMAL, but 
> for
> + * compression path, its max extent size is limited to 128KB, not 128MB, when
> + * reserving metadata, we should set reserve_type to BTRFS_RESERVE_COMPRESS.
>   *
>   * TODO: This function will finally replace old 
> btrfs_delalloc_reserve_space()
>   *
> @@ -6128,14 +6149,15 @@ void btrfs_delalloc_release_metadata(struct 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(inode, len);
> +     ret = btrfs_delalloc_reserve_metadata(inode, len, reserve_type);
>       if (ret < 0)
>               btrfs_free_reserved_data_space(inode, start, len);
>       return ret;
> @@ -6146,6 +6168,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: this value must be same to the value passing 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
> @@ -6156,9 +6180,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(inode, len);
> +     btrfs_delalloc_release_metadata(inode, len, reserve_type);
>       btrfs_free_reserved_data_space(inode, start, len);
>  }
>  
> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
> index 44fe66b..884da9e 100644
> --- a/fs/btrfs/extent_io.c
> +++ b/fs/btrfs/extent_io.c
> @@ -605,7 +605,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;
> @@ -744,6 +744,58 @@ out:
>  
>  }
>  
> +static void adjust_one_outstanding_extent(struct inode *inode, u64 len)
> +{
> +     unsigned old_extents, new_extents;
> +
> +     old_extents = div64_u64(len + SZ_128K - 1, SZ_128K);
> +     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)
> +{
> +     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);
> +             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)
> @@ -1506,6 +1558,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);
>  
> @@ -1523,7 +1576,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)) {
> @@ -1539,6 +1593,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 28cd88f..2940d41 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)
>  
> @@ -225,6 +226,7 @@ int clear_record_extent_bits(struct extent_io_tree *tree, 
> u64 start, u64 end,
>  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);
> +void adjust_outstanding_extents(struct inode *inode, u64 start, u64 end);
>  
>  static inline int unlock_extent(struct extent_io_tree *tree, u64 start, u64 
> end)
>  {
> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> index fea31a4..ab387d4 100644
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -484,11 +484,13 @@ static void btrfs_drop_pages(struct page **pages, 
> size_t num_pages)
>   *
>   * this also makes the decision about creating an inline extent vs
>   * doing real data extents, marking pages dirty and delalloc as required.
> + *
> + * if flag is 1, mark a data range that will go through compress path.
>   */
>  int btrfs_dirty_pages(struct btrfs_root *root, 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, int flag)
>  {
>       int err = 0;
>       int i;
> @@ -503,7 +505,7 @@ int btrfs_dirty_pages(struct btrfs_root *root, struct 
> inode *inode,
>  
>       end_of_last_block = start_pos + num_bytes - 1;
>       err = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block,
> -                                     cached);
> +                                     cached, flag);
>       if (err)
>               return err;
>  
> @@ -1496,6 +1498,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 *)));
> @@ -1505,6 +1508,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;
> @@ -1558,7 +1564,8 @@ static noinline ssize_t __btrfs_buffered_write(struct 
> file *file,
>                       }
>               }
>  
> -             ret = btrfs_delalloc_reserve_metadata(inode, reserve_bytes);
> +             ret = btrfs_delalloc_reserve_metadata(inode, reserve_bytes,
> +                                                   reserve_type);
>               if (ret) {
>                       if (!only_release_metadata)
>                               btrfs_free_reserved_data_space(inode, pos,
> @@ -1641,14 +1648,16 @@ again:
>                       }
>                       if (only_release_metadata) {
>                               btrfs_delalloc_release_metadata(inode,
> -                                                             release_bytes);
> +                                                             release_bytes,
> +                                                             reserve_type);
>                       } else {
>                               u64 __pos;
>  
>                               __pos = round_down(pos, root->sectorsize) +
>                                       (dirty_pages << PAGE_SHIFT);
>                               btrfs_delalloc_release_space(inode, __pos,
> -                                                          release_bytes);
> +                                                          release_bytes,
> +                                                          reserve_type);
>                       }
>               }
>  
> @@ -1658,7 +1667,7 @@ again:
>               if (copied > 0)
>                       ret = btrfs_dirty_pages(root, inode, pages,
>                                               dirty_pages, pos, copied,
> -                                             NULL);
> +                                             NULL, reserve_type);
>               if (need_unlock)
>                       unlock_extent_cached(&BTRFS_I(inode)->io_tree,
>                                            lockstart, lockend, &cached_state,
> @@ -1699,11 +1708,12 @@ again:
>       if (release_bytes) {
>               if (only_release_metadata) {
>                       btrfs_end_write_no_snapshoting(root);
> -                     btrfs_delalloc_release_metadata(inode, release_bytes);
> +                     btrfs_delalloc_release_metadata(inode, release_bytes,
> +                                                     reserve_type);
>               } else {
>                       btrfs_delalloc_release_space(inode,
>                                               round_down(pos, 
> root->sectorsize),
> -                                             release_bytes);
> +                                             release_bytes, reserve_type);
>               }
>       }
>  
> diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
> index d571bd2..620c853 100644
> --- a/fs/btrfs/free-space-cache.c
> +++ b/fs/btrfs/free-space-cache.c
> @@ -1296,7 +1296,7 @@ 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(root, inode, io_ctl->pages, io_ctl->num_pages,
> -                             0, i_size_read(inode), &cached_state);
> +                             0, i_size_read(inode), &cached_state, 0);
>       if (ret)
>               goto out_nospc;
>  
> @@ -3513,6 +3513,7 @@ int btrfs_write_out_ino_cache(struct btrfs_root *root,
>       int ret;
>       struct btrfs_io_ctl io_ctl;
>       bool release_metadata = true;
> +     enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
>  
>       if (!btrfs_test_opt(root->fs_info, INODE_MAP_CACHE))
>               return 0;
> @@ -3533,7 +3534,8 @@ int btrfs_write_out_ino_cache(struct btrfs_root *root,
>  
>       if (ret) {
>               if (release_metadata)
> -                     btrfs_delalloc_release_metadata(inode, inode->i_size);
> +                     btrfs_delalloc_release_metadata(inode, inode->i_size,
> +                                                     reserve_type);
>  #ifdef DEBUG
>               btrfs_err(root->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 359ee86..eb21f67 100644
> --- a/fs/btrfs/inode-map.c
> +++ b/fs/btrfs/inode-map.c
> @@ -401,6 +401,7 @@ int btrfs_save_ino_cache(struct btrfs_root *root,
>       int ret;
>       int prealloc;
>       bool retry = false;
> +     enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
>  
>       /* only fs tree and subvol/snap needs ino cache */
>       if (root->root_key.objectid != BTRFS_FS_TREE_OBJECTID &&
> @@ -488,14 +489,14 @@ again:
>       /* 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, reserve_type);
>       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(inode, prealloc);
> +             btrfs_delalloc_release_metadata(inode, prealloc, reserve_type);
>               goto out_put;
>       }
>  
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index a7193b1..ea15520 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -315,7 +315,7 @@ 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(inode, end + 1 - start);
> +     btrfs_delalloc_release_metadata(inode, end + 1 - start, 0);
>       btrfs_drop_extent_cache(inode, start, aligned_end - 1, 0);
>  out:
>       /*
> @@ -371,7 +371,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_root *root = BTRFS_I(inode)->root;
>  
> @@ -709,6 +709,16 @@ retry:
>                                        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);
>                       /* allocate blocks */
>                       ret = cow_file_range(inode, async_cow->locked_page,
>                                            async_extent->start,
> @@ -1562,14 +1572,24 @@ 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;
>  
> +     need_compress = test_range_bit(io_tree, start, end,
> +                                    EXTENT_COMPRESS, 1, NULL);
>       if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW && !force_cow) {
> +             if (need_compress)
> +                     adjust_outstanding_extents(inode, start, end);
> +
>               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);
> +
>               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 {
> @@ -1585,6 +1605,7 @@ static void btrfs_split_extent_hook(struct inode *inode,
>                                   struct extent_state *orig, u64 split)
>  {
>       u64 size;
> +     u64 max_extent_size = BTRFS_MAX_EXTENT_SIZE;
>  
>       /* not delalloc, ignore it */
>       if (!(orig->state & EXTENT_DELALLOC))
> @@ -1593,8 +1614,11 @@ static void btrfs_split_extent_hook(struct inode 
> *inode,
>       if (btrfs_is_free_space_inode(inode))
>               return;
>  
> +     if (orig->state & EXTENT_COMPRESS)
> +             max_extent_size = SZ_128K;
> +
>       size = orig->end - orig->start + 1;
> -     if (size > BTRFS_MAX_EXTENT_SIZE) {
> +     if (size > max_extent_size) {
>               u64 num_extents;
>               u64 new_size;
>  
> @@ -1603,13 +1627,13 @@ static void btrfs_split_extent_hook(struct inode 
> *inode,
>                * applies here, just in reverse.
>                */
>               new_size = orig->end - split + 1;
> -             num_extents = div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
> -                                     BTRFS_MAX_EXTENT_SIZE);
> +             num_extents = div64_u64(new_size + max_extent_size - 1,
> +                                     max_extent_size);
>               new_size = split - orig->start;
> -             num_extents += div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
> -                                     BTRFS_MAX_EXTENT_SIZE);
> -             if (div64_u64(size + BTRFS_MAX_EXTENT_SIZE - 1,
> -                           BTRFS_MAX_EXTENT_SIZE) >= num_extents)
> +             num_extents += div64_u64(new_size + max_extent_size - 1,
> +                                      max_extent_size);
> +             if (div64_u64(size + max_extent_size - 1,
> +                           max_extent_size) >= num_extents)
>                       return;
>       }
>  
> @@ -1630,6 +1654,7 @@ static void btrfs_merge_extent_hook(struct inode *inode,
>  {
>       u64 new_size, old_size;
>       u64 num_extents;
> +     u64 max_extent_size = BTRFS_MAX_EXTENT_SIZE;
>  
>       /* not delalloc, ignore it */
>       if (!(other->state & EXTENT_DELALLOC))
> @@ -1638,13 +1663,16 @@ static void btrfs_merge_extent_hook(struct inode 
> *inode,
>       if (btrfs_is_free_space_inode(inode))
>               return;
>  
> +     if (other->state & EXTENT_COMPRESS)
> +             max_extent_size = SZ_128K;
> +
>       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);
> @@ -1670,14 +1698,14 @@ static void btrfs_merge_extent_hook(struct inode 
> *inode,
>        * this case.
>        */
>       old_size = other->end - other->start + 1;
> -     num_extents = div64_u64(old_size + BTRFS_MAX_EXTENT_SIZE - 1,
> -                             BTRFS_MAX_EXTENT_SIZE);
> +     num_extents = div64_u64(old_size + max_extent_size - 1,
> +                             max_extent_size);
>       old_size = new->end - new->start + 1;
> -     num_extents += div64_u64(old_size + BTRFS_MAX_EXTENT_SIZE - 1,
> -                              BTRFS_MAX_EXTENT_SIZE);
> +     num_extents += div64_u64(old_size + max_extent_size - 1,
> +                              max_extent_size);
>  
> -     if (div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
> -                   BTRFS_MAX_EXTENT_SIZE) >= num_extents)
> +     if (div64_u64(new_size + max_extent_size - 1,
> +                   max_extent_size) >= num_extents)
>               return;
>  
>       spin_lock(&BTRFS_I(inode)->lock);
> @@ -1743,10 +1771,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 = BTRFS_MAX_EXTENT_SIZE;
> +             u64 num_extents;
>               bool do_list = !btrfs_is_free_space_inode(inode);
>  
> +             if (*bits & EXTENT_COMPRESS)
> +                     max_extent_size = SZ_128K;
> +             num_extents = div64_u64(len + max_extent_size - 1,
> +                                     max_extent_size);
> +
>               if (*bits & EXTENT_FIRST_DELALLOC)
>                       *bits &= ~EXTENT_FIRST_DELALLOC;
>  
> @@ -1781,8 +1814,9 @@ static void btrfs_clear_bit_hook(struct inode *inode,
>                                unsigned *bits)
>  {
>       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 = BTRFS_MAX_EXTENT_SIZE;
> +     u64 num_extents;
> +     enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
>  
>       spin_lock(&BTRFS_I(inode)->lock);
>       if ((state->state & EXTENT_DEFRAG) && (*bits & EXTENT_DEFRAG))
> @@ -1798,6 +1832,14 @@ static void btrfs_clear_bit_hook(struct inode *inode,
>               struct btrfs_root *root = BTRFS_I(inode)->root;
>               bool do_list = !btrfs_is_free_space_inode(inode);
>  
> +             if (state->state & EXTENT_COMPRESS) {
> +                     max_extent_size = SZ_128K;
> +                     reserve_type = BTRFS_RESERVE_COMPRESS;
> +             }
> +
> +             num_extents = div64_u64(len + max_extent_size - 1,
> +                                     max_extent_size);
> +
>               if (*bits & EXTENT_FIRST_DELALLOC) {
>                       *bits &= ~EXTENT_FIRST_DELALLOC;
>               } else if (!(*bits & EXTENT_DO_ACCOUNTING) && do_list) {
> @@ -1813,7 +1855,8 @@ static void btrfs_clear_bit_hook(struct inode *inode,
>                */
>               if (*bits & EXTENT_DO_ACCOUNTING &&
>                   root != 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(root->fs_info))
> @@ -1996,15 +2039,28 @@ 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)
> +                           struct extent_state **cached_state, int flag)
>  {
>       int ret;
> -     u64 num_extents = div64_u64(end - start + BTRFS_MAX_EXTENT_SIZE,
> -                                 BTRFS_MAX_EXTENT_SIZE);
> +     unsigned bits;
> +     u64 max_extent_size = BTRFS_MAX_EXTENT_SIZE;
> +     u64 num_extents;
> +
> +     if (flag == 1)
> +             max_extent_size = SZ_128K;
> +
> +     num_extents = div64_u64(end - start + max_extent_size,
> +                                 max_extent_size);
> +
> +     /* compression path */
> +     if (flag == 1)
> +             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
> @@ -2027,16 +2083,28 @@ 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, int flag)
>  {
>       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;
> +     u64 num_extents;
> +     unsigned bits;
> +
> +     if (flag == 1)
> +             max_extent_size = SZ_128K;
> +
> +     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 (flag == 1)
> +             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(inode)) {
>               spin_lock(&BTRFS_I(inode)->lock);
>               BTRFS_I(inode)->outstanding_extents -= num_extents;
> @@ -2062,6 +2130,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;
> @@ -2094,8 +2163,10 @@ again:
>               goto again;
>       }
>  
> +     if (inode_need_compress(inode))
> +             reserve_type = BTRFS_RESERVE_COMPRESS;
>       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);
> @@ -2103,7 +2174,8 @@ again:
>               goto out;
>        }
>  
> -     btrfs_set_extent_delalloc(inode, page_start, page_end, &cached_state);
> +     btrfs_set_extent_delalloc(inode, page_start, page_end, &cached_state,
> +                               reserve_type);
>       ClearPageChecked(page);
>       set_page_dirty(page);
>  out:
> @@ -2913,6 +2985,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(inode);
>  
> @@ -2990,8 +3063,11 @@ static int btrfs_finish_ordered_io(struct 
> btrfs_ordered_extent *ordered_extent)
>  
>       trans->block_rsv = &root->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, inode,
> @@ -3036,7 +3112,8 @@ out_unlock:
>                            ordered_extent->len - 1, &cached_state, GFP_NOFS);
>  out:
>       if (root != root->fs_info->tree_root)
> -             btrfs_delalloc_release_metadata(inode, ordered_extent->len);
> +             btrfs_delalloc_release_metadata(inode, ordered_extent->len,
> +                                             reserve_type);
>       if (trans)
>               btrfs_end_transaction(trans, root);
>  
> @@ -4750,13 +4827,17 @@ 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 (inode_need_compress(inode))
> +             reserve_type = BTRFS_RESERVE_COMPRESS;
>  
>       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;
>  
> @@ -4765,7 +4846,7 @@ again:
>       if (!page) {
>               btrfs_delalloc_release_space(inode,
>                               round_down(from, blocksize),
> -                             blocksize);
> +                             blocksize, reserve_type);
>               ret = -ENOMEM;
>               goto out;
>       }
> @@ -4808,7 +4889,7 @@ again:
>                         0, 0, &cached_state, GFP_NOFS);
>  
>       ret = btrfs_set_extent_delalloc(inode, block_start, block_end,
> -                                     &cached_state);
> +                                     &cached_state, reserve_type);
>       if (ret) {
>               unlock_extent_cached(io_tree, block_start, block_end,
>                                    &cached_state, GFP_NOFS);
> @@ -4836,7 +4917,7 @@ again:
>  out_unlock:
>       if (ret)
>               btrfs_delalloc_release_space(inode, block_start,
> -                                          blocksize);
> +                                          blocksize, reserve_type);
>       unlock_page(page);
>       put_page(page);
>  out:
> @@ -8728,7 +8809,8 @@ 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 = div64_u64(count +
> @@ -8760,7 +8842,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
> @@ -8776,7 +8858,7 @@ 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)
> @@ -9019,6 +9101,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, 
> struct vm_fault *vmf)
>       u64 page_start;
>       u64 page_end;
>       u64 end;
> +     enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
>  
>       reserved_space = PAGE_SIZE;
>  
> @@ -9027,6 +9110,8 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, 
> 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
> @@ -9036,7 +9121,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, 
> 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(vma->vm_file);
>               reserved = 1;
> @@ -9088,7 +9173,8 @@ again:
>                       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);
>               }
>       }
>  
> @@ -9105,7 +9191,7 @@ again:
>                         0, 0, &cached_state, GFP_NOFS);
>  
>       ret = btrfs_set_extent_delalloc(inode, page_start, end,
> -                                     &cached_state);
> +                                     &cached_state, reserve_type);
>       if (ret) {
>               unlock_extent_cached(io_tree, page_start, page_end,
>                                    &cached_state, GFP_NOFS);
> @@ -9143,7 +9229,8 @@ out_unlock:
>       }
>       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 6a19bea..81912e7 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -1132,6 +1132,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
>       struct extent_state *cached_state = NULL;
>       struct extent_io_tree *tree;
>       gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping);
> +     enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
>  
>       file_end = (isize - 1) >> PAGE_SHIFT;
>       if (!isize || start_index > file_end)
> @@ -1139,9 +1140,11 @@ 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);
> +                     page_cnt << PAGE_SHIFT, reserve_type);
>       if (ret)
>               return ret;
>       i_done = 0;
> @@ -1232,11 +1235,12 @@ again:
>               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);
> @@ -1257,7 +1261,7 @@ out:
>       }
>       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 c0c13dc..5c1f1cb 100644
> --- a/fs/btrfs/relocation.c
> +++ b/fs/btrfs/relocation.c
> @@ -3128,10 +3128,14 @@ static int relocate_file_extent_cluster(struct inode 
> *inode,
>       gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping);
>       int nr = 0;
>       int ret = 0;
> +     enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
>  
>       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;
> @@ -3150,7 +3154,8 @@ static int relocate_file_extent_cluster(struct inode 
> *inode,
>       index = (cluster->start - offset) >> PAGE_SHIFT;
>       last_index = (cluster->end - offset) >> PAGE_SHIFT;
>       while (index <= last_index) {
> -             ret = btrfs_delalloc_reserve_metadata(inode, PAGE_SIZE);
> +             ret = btrfs_delalloc_reserve_metadata(inode, PAGE_SIZE,
> +                                                   reserve_type);
>               if (ret)
>                       goto out;
>  
> @@ -3163,7 +3168,7 @@ static int relocate_file_extent_cluster(struct inode 
> *inode,
>                                                  mask);
>                       if (!page) {
>                               btrfs_delalloc_release_metadata(inode,
> -                                                     PAGE_SIZE);
> +                                             PAGE_SIZE, reserve_type);
>                               ret = -ENOMEM;
>                               goto out;
>                       }
> @@ -3182,7 +3187,7 @@ static int relocate_file_extent_cluster(struct inode 
> *inode,
>                               unlock_page(page);
>                               put_page(page);
>                               btrfs_delalloc_release_metadata(inode,
> -                                                     PAGE_SIZE);
> +                                             PAGE_SIZE, reserve_type);
>                               ret = -EIO;
>                               goto out;
>                       }
> @@ -3203,7 +3208,8 @@ static int relocate_file_extent_cluster(struct inode 
> *inode,
>                       nr++;
>               }
>  
> -             btrfs_set_extent_delalloc(inode, page_start, page_end, NULL);
> +             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 9f72aed..9a1a01d 100644
> --- a/fs/btrfs/tests/inode-tests.c
> +++ b/fs/btrfs/tests/inode-tests.c
> @@ -943,6 +943,7 @@ static int test_extent_accounting(u32 sectorsize, u32 
> nodesize)
>       struct inode *inode = NULL;
>       struct btrfs_root *root = NULL;
>       int ret = -ENOMEM;
> +     enum btrfs_metadata_reserve_type reserve_type = BTRFS_RESERVE_NORMAL;
>  
>       inode = btrfs_new_test_inode();
>       if (!inode) {
> @@ -968,7 +969,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);
> +                                     NULL, reserve_type);
>       if (ret) {
>               test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
>               goto out;
> @@ -984,7 +985,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);
> +                                     NULL, reserve_type);
>       if (ret) {
>               test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
>               goto out;
> @@ -1019,7 +1020,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);
> +                                     NULL, reserve_type);
>       if (ret) {
>               test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
>               goto out;
> @@ -1042,7 +1043,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);
> +                     NULL, reserve_type);
>       if (ret) {
>               test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
>               goto out;
> @@ -1060,7 +1061,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);
> +                     BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1,
> +                     NULL, reserve_type);
>       if (ret) {
>               test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
>               goto out;
> @@ -1097,7 +1099,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);
> +                     BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1,
> +                     NULL, reserve_type);
>       if (ret) {
>               test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
>               goto out;
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to