btrfs: fix integer overflow in calc_reclaim_items_nr
authorChris Mason <clm@fb.com>
Fri, 23 Jun 2017 16:48:21 +0000 (09:48 -0700)
committerDavid Sterba <dsterba@suse.com>
Thu, 29 Jun 2017 18:17:02 +0000 (20:17 +0200)
Dave Jones hit a WARN_ON(nr < 0) in btrfs_wait_ordered_roots() with
v4.12-rc6.  This was because commit 70e7af244 made it possible for
calc_reclaim_items_nr() to return a negative number.  It's not really a
bug in that commit, it just didn't go far enough down the stack to find
all the possible 64->32 bit overflows.

This switches calc_reclaim_items_nr() to return a u64 and changes everyone
that uses the results of that math to u64 as well.

Reported-by: Dave Jones <davej@codemonkey.org.uk>
Fixes: 70e7af2 ("Btrfs: fix delalloc accounting leak caused by u32 overflow")
Signed-off-by: Chris Mason <clm@fb.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/dev-replace.c
fs/btrfs/extent-tree.c
fs/btrfs/ioctl.c
fs/btrfs/ordered-data.c
fs/btrfs/ordered-data.h
fs/btrfs/qgroup.c
fs/btrfs/relocation.c
fs/btrfs/scrub.c
fs/btrfs/super.c
fs/btrfs/transaction.c

index 5fe1ca8abc70577fe28ee6fd69899d11b618d479..bee3edeea7a37404de4d428a57e90c6bbcac6bc1 100644 (file)
@@ -388,7 +388,7 @@ int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info,
        if (ret)
                btrfs_err(fs_info, "kobj add dev failed %d", ret);
 
        if (ret)
                btrfs_err(fs_info, "kobj add dev failed %d", ret);
 
-       btrfs_wait_ordered_roots(fs_info, -1, 0, (u64)-1);
+       btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1);
 
        /* force writing the updated state information to disk */
        trans = btrfs_start_transaction(root, 0);
 
        /* force writing the updated state information to disk */
        trans = btrfs_start_transaction(root, 0);
@@ -507,7 +507,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
                return ret;
        }
                mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
                return ret;
        }
-       btrfs_wait_ordered_roots(fs_info, -1, 0, (u64)-1);
+       btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1);
 
        trans = btrfs_start_transaction(root, 0);
        if (IS_ERR(trans)) {
 
        trans = btrfs_start_transaction(root, 0);
        if (IS_ERR(trans)) {
index a3339acec28c015c6d343234f89fc334868caa09..375f8c728d91888469a4820f1d669ea74c752b8c 100644 (file)
@@ -4288,7 +4288,7 @@ commit_trans:
 
                        if (need_commit > 0) {
                                btrfs_start_delalloc_roots(fs_info, 0, -1);
 
                        if (need_commit > 0) {
                                btrfs_start_delalloc_roots(fs_info, 0, -1);
-                               btrfs_wait_ordered_roots(fs_info, -1, 0,
+                               btrfs_wait_ordered_roots(fs_info, U64_MAX, 0,
                                                         (u64)-1);
                        }
 
                                                         (u64)-1);
                        }
 
@@ -4748,14 +4748,14 @@ static void btrfs_writeback_inodes_sb_nr(struct btrfs_fs_info *fs_info,
        }
 }
 
        }
 }
 
-static inline int calc_reclaim_items_nr(struct btrfs_fs_info *fs_info,
+static inline u64 calc_reclaim_items_nr(struct btrfs_fs_info *fs_info,
                                        u64 to_reclaim)
 {
        u64 bytes;
                                        u64 to_reclaim)
 {
        u64 bytes;
-       int nr;
+       u64 nr;
 
        bytes = btrfs_calc_trans_metadata_size(fs_info, 1);
 
        bytes = btrfs_calc_trans_metadata_size(fs_info, 1);
-       nr = (int)div64_u64(to_reclaim, bytes);
+       nr = div64_u64(to_reclaim, bytes);
        if (!nr)
                nr = 1;
        return nr;
        if (!nr)
                nr = 1;
        return nr;
@@ -4774,15 +4774,15 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info, u64 to_reclaim,
        struct btrfs_trans_handle *trans;
        u64 delalloc_bytes;
        u64 max_reclaim;
        struct btrfs_trans_handle *trans;
        u64 delalloc_bytes;
        u64 max_reclaim;
+       u64 items;
        long time_left;
        unsigned long nr_pages;
        int loops;
        long time_left;
        unsigned long nr_pages;
        int loops;
-       int items;
        enum btrfs_reserve_flush_enum flush;
 
        /* Calc the number of the pages we need flush for space reservation */
        items = calc_reclaim_items_nr(fs_info, to_reclaim);
        enum btrfs_reserve_flush_enum flush;
 
        /* Calc the number of the pages we need flush for space reservation */
        items = calc_reclaim_items_nr(fs_info, to_reclaim);
-       to_reclaim = (u64)items * EXTENT_SIZE_PER_ITEM;
+       to_reclaim = items * EXTENT_SIZE_PER_ITEM;
 
        trans = (struct btrfs_trans_handle *)current->journal_info;
        block_rsv = &fs_info->delalloc_block_rsv;
 
        trans = (struct btrfs_trans_handle *)current->journal_info;
        block_rsv = &fs_info->delalloc_block_rsv;
index b4e9941efb6078b94c8e9203ede0ae19ff380ff4..fa1b78cf25f68083de3db86ead690a41563316ce 100644 (file)
@@ -689,7 +689,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
        if (ret)
                goto dec_and_free;
 
        if (ret)
                goto dec_and_free;
 
-       btrfs_wait_ordered_extents(root, -1, 0, (u64)-1);
+       btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1);
 
        btrfs_init_block_rsv(&pending_snapshot->block_rsv,
                             BTRFS_BLOCK_RSV_TEMP);
 
        btrfs_init_block_rsv(&pending_snapshot->block_rsv,
                             BTRFS_BLOCK_RSV_TEMP);
index 7b40e2e7292a41a047b0f8e300304822b7c63e74..a3aca495e33e2c97890551b409afd1ceb40b4f69 100644 (file)
@@ -663,7 +663,7 @@ static void btrfs_run_ordered_extent_work(struct btrfs_work *work)
  * wait for all the ordered extents in a root.  This is done when balancing
  * space between drives.
  */
  * wait for all the ordered extents in a root.  This is done when balancing
  * space between drives.
  */
-int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr,
+u64 btrfs_wait_ordered_extents(struct btrfs_root *root, u64 nr,
                               const u64 range_start, const u64 range_len)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
                               const u64 range_start, const u64 range_len)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -671,7 +671,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr,
        LIST_HEAD(skipped);
        LIST_HEAD(works);
        struct btrfs_ordered_extent *ordered, *next;
        LIST_HEAD(skipped);
        LIST_HEAD(works);
        struct btrfs_ordered_extent *ordered, *next;
-       int count = 0;
+       u64 count = 0;
        const u64 range_end = range_start + range_len;
 
        mutex_lock(&root->ordered_extent_mutex);
        const u64 range_end = range_start + range_len;
 
        mutex_lock(&root->ordered_extent_mutex);
@@ -701,7 +701,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr,
 
                cond_resched();
                spin_lock(&root->ordered_extent_lock);
 
                cond_resched();
                spin_lock(&root->ordered_extent_lock);
-               if (nr != -1)
+               if (nr != U64_MAX)
                        nr--;
                count++;
        }
                        nr--;
                count++;
        }
@@ -720,13 +720,13 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr,
        return count;
 }
 
        return count;
 }
 
-int btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr,
-                             const u64 range_start, const u64 range_len)
+u64 btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, u64 nr,
+                            const u64 range_start, const u64 range_len)
 {
        struct btrfs_root *root;
        struct list_head splice;
 {
        struct btrfs_root *root;
        struct list_head splice;
-       int done;
-       int total_done = 0;
+       u64 total_done = 0;
+       u64 done;
 
        INIT_LIST_HEAD(&splice);
 
 
        INIT_LIST_HEAD(&splice);
 
@@ -748,9 +748,8 @@ int btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr,
                total_done += done;
 
                spin_lock(&fs_info->ordered_root_lock);
                total_done += done;
 
                spin_lock(&fs_info->ordered_root_lock);
-               if (nr != -1) {
+               if (nr != U64_MAX) {
                        nr -= done;
                        nr -= done;
-                       WARN_ON(nr < 0);
                }
        }
        list_splice_tail(&splice, &fs_info->ordered_roots);
                }
        }
        list_splice_tail(&splice, &fs_info->ordered_roots);
index e0c1d5b8d859c95c9e870b8feb735c921a09d8f1..56c4c0ee6381f2a271216adbc7cbe278d48b98c0 100644 (file)
@@ -200,9 +200,9 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
                                struct btrfs_ordered_extent *ordered);
 int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr,
                           u32 *sum, int len);
                                struct btrfs_ordered_extent *ordered);
 int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr,
                           u32 *sum, int len);
-int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr,
+u64 btrfs_wait_ordered_extents(struct btrfs_root *root, u64 nr,
                               const u64 range_start, const u64 range_len);
                               const u64 range_start, const u64 range_len);
-int btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr,
+u64 btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, u64 nr,
                              const u64 range_start, const u64 range_len);
 void btrfs_get_logged_extents(struct btrfs_inode *inode,
                              struct list_head *logged_list,
                              const u64 range_start, const u64 range_len);
 void btrfs_get_logged_extents(struct btrfs_inode *inode,
                              struct list_head *logged_list,
index fc9dffaa9524b31c96ee3e74deb42da6262372c2..4ce351efe2813e101c74658873a25cff434633d6 100644 (file)
@@ -2405,7 +2405,7 @@ retry:
                                ret = btrfs_start_delalloc_inodes(root, 0);
                                if (ret)
                                        return ret;
                                ret = btrfs_start_delalloc_inodes(root, 0);
                                if (ret)
                                        return ret;
-                               btrfs_wait_ordered_extents(root, -1, 0, (u64)-1);
+                               btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1);
                                trans = btrfs_join_transaction(root);
                                if (IS_ERR(trans))
                                        return PTR_ERR(trans);
                                trans = btrfs_join_transaction(root);
                                if (IS_ERR(trans))
                                        return PTR_ERR(trans);
index dc69b6ba29af1ac4b1e35cca125892f9b99e9f19..65661d1aae4e65c7314c98ad27d9139708e71103 100644 (file)
@@ -4373,7 +4373,7 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start)
 
        btrfs_wait_block_group_reservations(rc->block_group);
        btrfs_wait_nocow_writers(rc->block_group);
 
        btrfs_wait_block_group_reservations(rc->block_group);
        btrfs_wait_nocow_writers(rc->block_group);
-       btrfs_wait_ordered_roots(fs_info, -1,
+       btrfs_wait_ordered_roots(fs_info, U64_MAX,
                                 rc->block_group->key.objectid,
                                 rc->block_group->key.offset);
 
                                 rc->block_group->key.objectid,
                                 rc->block_group->key.offset);
 
index 738e784ba20d110af2309ce15b9a974f81c9d386..0cebeb5eb5d00d80ad6794873f6948380c5de68c 100644 (file)
@@ -3836,7 +3836,7 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
                         */
                        btrfs_wait_block_group_reservations(cache);
                        btrfs_wait_nocow_writers(cache);
                         */
                        btrfs_wait_block_group_reservations(cache);
                        btrfs_wait_nocow_writers(cache);
-                       ret = btrfs_wait_ordered_roots(fs_info, -1,
+                       ret = btrfs_wait_ordered_roots(fs_info, U64_MAX,
                                                       cache->key.objectid,
                                                       cache->key.offset);
                        if (ret > 0) {
                                                       cache->key.objectid,
                                                       cache->key.offset);
                        if (ret > 0) {
index ba2cb7f96986111c7ddd47015f5f05de5e516676..74e47794e63ffdda6921f8445bd4f576f22f1434 100644 (file)
@@ -1177,7 +1177,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait)
                return 0;
        }
 
                return 0;
        }
 
-       btrfs_wait_ordered_roots(fs_info, -1, 0, (u64)-1);
+       btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1);
 
        trans = btrfs_attach_transaction_barrier(root);
        if (IS_ERR(trans)) {
 
        trans = btrfs_attach_transaction_barrier(root);
        if (IS_ERR(trans)) {
index 309b73da756b8f07ede0f4d35afc388ba3800749..f615d59b0489e2af52a711b896f758e13d05bd46 100644 (file)
@@ -1923,7 +1923,7 @@ static inline int btrfs_start_delalloc_flush(struct btrfs_fs_info *fs_info)
 static inline void btrfs_wait_delalloc_flush(struct btrfs_fs_info *fs_info)
 {
        if (btrfs_test_opt(fs_info, FLUSHONCOMMIT))
 static inline void btrfs_wait_delalloc_flush(struct btrfs_fs_info *fs_info)
 {
        if (btrfs_test_opt(fs_info, FLUSHONCOMMIT))
-               btrfs_wait_ordered_roots(fs_info, -1, 0, (u64)-1);
+               btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1);
 }
 
 static inline void
 }
 
 static inline void