Commit 6374e57a authored by Chris Mason's avatar Chris Mason Committed by David Sterba

btrfs: fix integer overflow in calc_reclaim_items_nr

Dave Jones hit a WARN_ON(nr < 0) in btrfs_wait_ordered_roots() with
v4.12-rc6.  This was because commit 70e7af24 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: default avatarDave Jones <davej@codemonkey.org.uk>
Fixes: 70e7af24 ("Btrfs: fix delalloc accounting leak caused by u32 overflow")
Signed-off-by: default avatarChris Mason <clm@fb.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent ded56184
...@@ -388,7 +388,7 @@ int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, ...@@ -388,7 +388,7 @@ int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info,
if (ret) if (ret)
btrfs_err(fs_info, "kobj add dev failed %d", 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 */ /* force writing the updated state information to disk */
trans = btrfs_start_transaction(root, 0); trans = btrfs_start_transaction(root, 0);
...@@ -507,7 +507,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, ...@@ -507,7 +507,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
return ret; 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); trans = btrfs_start_transaction(root, 0);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
......
...@@ -4288,7 +4288,7 @@ int btrfs_alloc_data_chunk_ondemand(struct btrfs_inode *inode, u64 bytes) ...@@ -4288,7 +4288,7 @@ int btrfs_alloc_data_chunk_ondemand(struct btrfs_inode *inode, u64 bytes)
if (need_commit > 0) { if (need_commit > 0) {
btrfs_start_delalloc_roots(fs_info, 0, -1); 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, ...@@ -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 to_reclaim)
{ {
u64 bytes; 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) if (!nr)
nr = 1; nr = 1;
return nr; return nr;
...@@ -4774,15 +4774,15 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info, u64 to_reclaim, ...@@ -4774,15 +4774,15 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info, u64 to_reclaim,
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
u64 delalloc_bytes; u64 delalloc_bytes;
u64 max_reclaim; u64 max_reclaim;
u64 items;
long time_left; long time_left;
unsigned long nr_pages; unsigned long nr_pages;
int loops; int loops;
int items;
enum btrfs_reserve_flush_enum flush; enum btrfs_reserve_flush_enum flush;
/* Calc the number of the pages we need flush for space reservation */ /* Calc the number of the pages we need flush for space reservation */
items = calc_reclaim_items_nr(fs_info, to_reclaim); 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; trans = (struct btrfs_trans_handle *)current->journal_info;
block_rsv = &fs_info->delalloc_block_rsv; block_rsv = &fs_info->delalloc_block_rsv;
......
...@@ -689,7 +689,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, ...@@ -689,7 +689,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
if (ret) if (ret)
goto dec_and_free; 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_init_block_rsv(&pending_snapshot->block_rsv,
BTRFS_BLOCK_RSV_TEMP); BTRFS_BLOCK_RSV_TEMP);
......
...@@ -663,7 +663,7 @@ static void btrfs_run_ordered_extent_work(struct btrfs_work *work) ...@@ -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 * wait for all the ordered extents in a root. This is done when balancing
* space between drives. * 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) const u64 range_start, const u64 range_len)
{ {
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
...@@ -671,7 +671,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr, ...@@ -671,7 +671,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr,
LIST_HEAD(skipped); LIST_HEAD(skipped);
LIST_HEAD(works); LIST_HEAD(works);
struct btrfs_ordered_extent *ordered, *next; struct btrfs_ordered_extent *ordered, *next;
int count = 0; u64 count = 0;
const u64 range_end = range_start + range_len; const u64 range_end = range_start + range_len;
mutex_lock(&root->ordered_extent_mutex); mutex_lock(&root->ordered_extent_mutex);
...@@ -701,7 +701,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr, ...@@ -701,7 +701,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr,
cond_resched(); cond_resched();
spin_lock(&root->ordered_extent_lock); spin_lock(&root->ordered_extent_lock);
if (nr != -1) if (nr != U64_MAX)
nr--; nr--;
count++; count++;
} }
...@@ -720,13 +720,13 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr, ...@@ -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, u64 btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, u64 nr,
const u64 range_start, const u64 range_len) const u64 range_start, const u64 range_len)
{ {
struct btrfs_root *root; struct btrfs_root *root;
struct list_head splice; struct list_head splice;
int done; u64 total_done = 0;
int 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, ...@@ -748,9 +748,8 @@ int btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr,
total_done += done; total_done += done;
spin_lock(&fs_info->ordered_root_lock); 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);
......
...@@ -200,9 +200,9 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset, ...@@ -200,9 +200,9 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
struct btrfs_ordered_extent *ordered); struct btrfs_ordered_extent *ordered);
int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr, int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr,
u32 *sum, int len); 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); const u64 range_start, const u64 range_len);
void btrfs_get_logged_extents(struct btrfs_inode *inode, void btrfs_get_logged_extents(struct btrfs_inode *inode,
struct list_head *logged_list, struct list_head *logged_list,
......
...@@ -2405,7 +2405,7 @@ static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce) ...@@ -2405,7 +2405,7 @@ static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce)
ret = btrfs_start_delalloc_inodes(root, 0); ret = btrfs_start_delalloc_inodes(root, 0);
if (ret) if (ret)
return 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); trans = btrfs_join_transaction(root);
if (IS_ERR(trans)) if (IS_ERR(trans))
return PTR_ERR(trans); return PTR_ERR(trans);
......
...@@ -4373,7 +4373,7 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start) ...@@ -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_block_group_reservations(rc->block_group);
btrfs_wait_nocow_writers(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.objectid,
rc->block_group->key.offset); rc->block_group->key.offset);
......
...@@ -3836,7 +3836,7 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx, ...@@ -3836,7 +3836,7 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
*/ */
btrfs_wait_block_group_reservations(cache); btrfs_wait_block_group_reservations(cache);
btrfs_wait_nocow_writers(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.objectid,
cache->key.offset); cache->key.offset);
if (ret > 0) { if (ret > 0) {
......
...@@ -1177,7 +1177,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait) ...@@ -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); trans = btrfs_attach_transaction_barrier(root);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
......
...@@ -1923,7 +1923,7 @@ static inline int btrfs_start_delalloc_flush(struct btrfs_fs_info *fs_info) ...@@ -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) static inline void btrfs_wait_delalloc_flush(struct btrfs_fs_info *fs_info)
{ {
if (btrfs_test_opt(fs_info, FLUSHONCOMMIT)) 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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment