diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-11-09 11:51:37 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-11-09 11:51:37 -0500 |
commit | 00aff6836241ae5654895dcea10e6d4fc5878ca6 (patch) | |
tree | d28b74f641c5e3f380f664acee8a7996fd2e07e8 /fs | |
parent | 4aba1a7ed563d1c0b153377f57f4d213776ea573 (diff) | |
parent | a5009d3a318e9f02ddc9aa3d55e2c64d6285c4b9 (diff) |
Merge tag 'for-5.4-rc6-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux
Pull btrfs fixes from David Sterba:
"A few regressions and fixes for stable.
Regressions:
- fix a race leading to metadata space leak after task received a
signal
- un-deprecate 2 ioctls, marked as deprecated by mistake
Fixes:
- fix limit check for number of devices during chunk allocation
- fix a race due to double evaluation of i_size_read inside max()
macro, can cause a crash
- remove wrong device id check in tree-checker"
* tag 'for-5.4-rc6-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
btrfs: un-deprecate ioctls START_SYNC and WAIT_SYNC
btrfs: save i_size to avoid double evaluation of i_size_read in compress_file_range
Btrfs: fix race leading to metadata space leak after task received signal
btrfs: tree-checker: Fix wrong check on max devid
btrfs: Consider system chunk array size for new SYSTEM chunks
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/inode.c | 15 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 6 | ||||
-rw-r--r-- | fs/btrfs/space-info.c | 21 | ||||
-rw-r--r-- | fs/btrfs/tree-checker.c | 8 | ||||
-rw-r--r-- | fs/btrfs/volumes.c | 1 |
5 files changed, 36 insertions, 15 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c3f386b7cc0b..c6dc4dd16cf7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -474,6 +474,7 @@ static noinline int compress_file_range(struct async_chunk *async_chunk) | |||
474 | u64 start = async_chunk->start; | 474 | u64 start = async_chunk->start; |
475 | u64 end = async_chunk->end; | 475 | u64 end = async_chunk->end; |
476 | u64 actual_end; | 476 | u64 actual_end; |
477 | u64 i_size; | ||
477 | int ret = 0; | 478 | int ret = 0; |
478 | struct page **pages = NULL; | 479 | struct page **pages = NULL; |
479 | unsigned long nr_pages; | 480 | unsigned long nr_pages; |
@@ -488,7 +489,19 @@ static noinline int compress_file_range(struct async_chunk *async_chunk) | |||
488 | inode_should_defrag(BTRFS_I(inode), start, end, end - start + 1, | 489 | inode_should_defrag(BTRFS_I(inode), start, end, end - start + 1, |
489 | SZ_16K); | 490 | SZ_16K); |
490 | 491 | ||
491 | actual_end = min_t(u64, i_size_read(inode), end + 1); | 492 | /* |
493 | * We need to save i_size before now because it could change in between | ||
494 | * us evaluating the size and assigning it. This is because we lock and | ||
495 | * unlock the page in truncate and fallocate, and then modify the i_size | ||
496 | * later on. | ||
497 | * | ||
498 | * The barriers are to emulate READ_ONCE, remove that once i_size_read | ||
499 | * does that for us. | ||
500 | */ | ||
501 | barrier(); | ||
502 | i_size = i_size_read(inode); | ||
503 | barrier(); | ||
504 | actual_end = min_t(u64, i_size, end + 1); | ||
492 | again: | 505 | again: |
493 | will_compress = 0; | 506 | will_compress = 0; |
494 | nr_pages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; | 507 | nr_pages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 7c145a41decd..23272d9154f3 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -4195,9 +4195,6 @@ static noinline long btrfs_ioctl_start_sync(struct btrfs_root *root, | |||
4195 | u64 transid; | 4195 | u64 transid; |
4196 | int ret; | 4196 | int ret; |
4197 | 4197 | ||
4198 | btrfs_warn(root->fs_info, | ||
4199 | "START_SYNC ioctl is deprecated and will be removed in kernel 5.7"); | ||
4200 | |||
4201 | trans = btrfs_attach_transaction_barrier(root); | 4198 | trans = btrfs_attach_transaction_barrier(root); |
4202 | if (IS_ERR(trans)) { | 4199 | if (IS_ERR(trans)) { |
4203 | if (PTR_ERR(trans) != -ENOENT) | 4200 | if (PTR_ERR(trans) != -ENOENT) |
@@ -4225,9 +4222,6 @@ static noinline long btrfs_ioctl_wait_sync(struct btrfs_fs_info *fs_info, | |||
4225 | { | 4222 | { |
4226 | u64 transid; | 4223 | u64 transid; |
4227 | 4224 | ||
4228 | btrfs_warn(fs_info, | ||
4229 | "WAIT_SYNC ioctl is deprecated and will be removed in kernel 5.7"); | ||
4230 | |||
4231 | if (argp) { | 4225 | if (argp) { |
4232 | if (copy_from_user(&transid, argp, sizeof(transid))) | 4226 | if (copy_from_user(&transid, argp, sizeof(transid))) |
4233 | return -EFAULT; | 4227 | return -EFAULT; |
diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index 98dc092a905e..e8a4b0ebe97f 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c | |||
@@ -893,6 +893,15 @@ static void wait_reserve_ticket(struct btrfs_fs_info *fs_info, | |||
893 | while (ticket->bytes > 0 && ticket->error == 0) { | 893 | while (ticket->bytes > 0 && ticket->error == 0) { |
894 | ret = prepare_to_wait_event(&ticket->wait, &wait, TASK_KILLABLE); | 894 | ret = prepare_to_wait_event(&ticket->wait, &wait, TASK_KILLABLE); |
895 | if (ret) { | 895 | if (ret) { |
896 | /* | ||
897 | * Delete us from the list. After we unlock the space | ||
898 | * info, we don't want the async reclaim job to reserve | ||
899 | * space for this ticket. If that would happen, then the | ||
900 | * ticket's task would not known that space was reserved | ||
901 | * despite getting an error, resulting in a space leak | ||
902 | * (bytes_may_use counter of our space_info). | ||
903 | */ | ||
904 | list_del_init(&ticket->list); | ||
896 | ticket->error = -EINTR; | 905 | ticket->error = -EINTR; |
897 | break; | 906 | break; |
898 | } | 907 | } |
@@ -945,12 +954,24 @@ static int handle_reserve_ticket(struct btrfs_fs_info *fs_info, | |||
945 | spin_lock(&space_info->lock); | 954 | spin_lock(&space_info->lock); |
946 | ret = ticket->error; | 955 | ret = ticket->error; |
947 | if (ticket->bytes || ticket->error) { | 956 | if (ticket->bytes || ticket->error) { |
957 | /* | ||
958 | * Need to delete here for priority tickets. For regular tickets | ||
959 | * either the async reclaim job deletes the ticket from the list | ||
960 | * or we delete it ourselves at wait_reserve_ticket(). | ||
961 | */ | ||
948 | list_del_init(&ticket->list); | 962 | list_del_init(&ticket->list); |
949 | if (!ret) | 963 | if (!ret) |
950 | ret = -ENOSPC; | 964 | ret = -ENOSPC; |
951 | } | 965 | } |
952 | spin_unlock(&space_info->lock); | 966 | spin_unlock(&space_info->lock); |
953 | ASSERT(list_empty(&ticket->list)); | 967 | ASSERT(list_empty(&ticket->list)); |
968 | /* | ||
969 | * Check that we can't have an error set if the reservation succeeded, | ||
970 | * as that would confuse tasks and lead them to error out without | ||
971 | * releasing reserved space (if an error happens the expectation is that | ||
972 | * space wasn't reserved at all). | ||
973 | */ | ||
974 | ASSERT(!(ticket->bytes == 0 && ticket->error)); | ||
954 | return ret; | 975 | return ret; |
955 | } | 976 | } |
956 | 977 | ||
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 43e488f5d063..076d5b8014fb 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c | |||
@@ -686,9 +686,7 @@ static void dev_item_err(const struct extent_buffer *eb, int slot, | |||
686 | static int check_dev_item(struct extent_buffer *leaf, | 686 | static int check_dev_item(struct extent_buffer *leaf, |
687 | struct btrfs_key *key, int slot) | 687 | struct btrfs_key *key, int slot) |
688 | { | 688 | { |
689 | struct btrfs_fs_info *fs_info = leaf->fs_info; | ||
690 | struct btrfs_dev_item *ditem; | 689 | struct btrfs_dev_item *ditem; |
691 | u64 max_devid = max(BTRFS_MAX_DEVS(fs_info), BTRFS_MAX_DEVS_SYS_CHUNK); | ||
692 | 690 | ||
693 | if (key->objectid != BTRFS_DEV_ITEMS_OBJECTID) { | 691 | if (key->objectid != BTRFS_DEV_ITEMS_OBJECTID) { |
694 | dev_item_err(leaf, slot, | 692 | dev_item_err(leaf, slot, |
@@ -696,12 +694,6 @@ static int check_dev_item(struct extent_buffer *leaf, | |||
696 | key->objectid, BTRFS_DEV_ITEMS_OBJECTID); | 694 | key->objectid, BTRFS_DEV_ITEMS_OBJECTID); |
697 | return -EUCLEAN; | 695 | return -EUCLEAN; |
698 | } | 696 | } |
699 | if (key->offset > max_devid) { | ||
700 | dev_item_err(leaf, slot, | ||
701 | "invalid devid: has=%llu expect=[0, %llu]", | ||
702 | key->offset, max_devid); | ||
703 | return -EUCLEAN; | ||
704 | } | ||
705 | ditem = btrfs_item_ptr(leaf, slot, struct btrfs_dev_item); | 697 | ditem = btrfs_item_ptr(leaf, slot, struct btrfs_dev_item); |
706 | if (btrfs_device_id(leaf, ditem) != key->offset) { | 698 | if (btrfs_device_id(leaf, ditem) != key->offset) { |
707 | dev_item_err(leaf, slot, | 699 | dev_item_err(leaf, slot, |
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index bdfe4493e43a..e04409f85063 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -4967,6 +4967,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, | |||
4967 | } else if (type & BTRFS_BLOCK_GROUP_SYSTEM) { | 4967 | } else if (type & BTRFS_BLOCK_GROUP_SYSTEM) { |
4968 | max_stripe_size = SZ_32M; | 4968 | max_stripe_size = SZ_32M; |
4969 | max_chunk_size = 2 * max_stripe_size; | 4969 | max_chunk_size = 2 * max_stripe_size; |
4970 | devs_max = min_t(int, devs_max, BTRFS_MAX_DEVS_SYS_CHUNK); | ||
4970 | } else { | 4971 | } else { |
4971 | btrfs_err(info, "invalid chunk type 0x%llx requested", | 4972 | btrfs_err(info, "invalid chunk type 0x%llx requested", |
4972 | type); | 4973 | type); |