From patchwork Fri Aug 25 01:05:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lee Trager X-Patchwork-Id: 13364906 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9B514C88CB9 for ; Fri, 25 Aug 2023 01:07:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233526AbjHYBGb (ORCPT ); Thu, 24 Aug 2023 21:06:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35554 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235607AbjHYBF7 (ORCPT ); Thu, 24 Aug 2023 21:05:59 -0400 Received: from trager.us (trager.us [52.5.81.116]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7B311E77 for ; Thu, 24 Aug 2023 18:05:56 -0700 (PDT) Received: from [163.114.132.5] (helo=localhost) by trager.us with esmtpsa (TLSv1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.92.3) (envelope-from ) id 1qZLGt-0006NU-6D; Fri, 25 Aug 2023 01:05:55 +0000 From: Lee Trager To: linux-btrfs@vger.kernel.org Cc: Lee Trager Subject: [PATCH] btrfs: Allow online resize to use "min" shortcut Date: Thu, 24 Aug 2023 18:05:42 -0700 Message-Id: <20230825010542.4158944-1-lee@trager.us> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org btrfs already supports online resize and has a "max" shortcut to expand to all available space on the disk. This creates a "min" shortcut which shrinks the filesystem to allocated space. Signed-off-by: Lee Trager --- fs/btrfs/ioctl.c | 10 ++-- fs/btrfs/volumes.c | 123 ++++++++++++++++++++++++++++++++++++--------- fs/btrfs/volumes.h | 3 +- 3 files changed, 109 insertions(+), 27 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a895d105464b..c2bb6e18d80f 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1108,6 +1108,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, int ret = 0; int mod = 0; bool cancel; + bool to_min = false; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1165,9 +1166,12 @@ static noinline int btrfs_ioctl_resize(struct file *file, goto out_finish; } - if (!strcmp(sizestr, "max")) + if (!strcmp(sizestr, "max")) { new_size = bdev_nr_bytes(device->bdev); - else { + } else if (!strcmp(sizestr, "min")) { + to_min = true; + new_size = SZ_256M; + } else { if (sizestr[0] == '-') { mod = -1; sizestr++; @@ -1223,7 +1227,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, ret = btrfs_grow_device(trans, device, new_size); btrfs_commit_transaction(trans); } else if (new_size < old_size) { - ret = btrfs_shrink_device(device, new_size); + ret = btrfs_shrink_device(device, &new_size, to_min); } /* equal, nothing need to do */ if (ret == 0 && new_size != old_size) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 733842136163..257e7f004ce7 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2145,7 +2145,8 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, mutex_unlock(&fs_info->chunk_mutex); } - ret = btrfs_shrink_device(device, 0); + u64 size = 0; + ret = btrfs_shrink_device(device, &size, false); if (ret) goto error_undo; @@ -4816,12 +4817,74 @@ int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info) return 0; } +u64 btrfs_get_allocated_space(struct btrfs_fs_info *fs_info) +{ + u64 ret = 0; + u64 metadata_ratio = 1; + static const u64 types[] = { + BTRFS_BLOCK_GROUP_DATA, + BTRFS_BLOCK_GROUP_SYSTEM, + BTRFS_BLOCK_GROUP_METADATA, + BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA + }; + + for (int i = 0; i < 4; i++) { + struct btrfs_space_info *tmp = NULL; + struct btrfs_space_info *info = NULL; + + list_for_each_entry(tmp, &fs_info->space_info, list) { + if (tmp->flags == types[i]) { + info = tmp; + break; + } + } + + if (!info) + continue; + + down_read(&info->groups_sem); + for (int c = 0; c < BTRFS_NR_RAID_TYPES; c++) { + if (list_empty(&info->block_groups[c])) + continue; + + struct btrfs_block_group *bg; + list_for_each_entry(bg, &info->block_groups[c], list) { + enum btrfs_raid_types i; + u64 ratio; + i = btrfs_bg_flags_to_raid_index(bg->flags); + ratio = btrfs_raid_array[i].ncopies; + if (bg->flags & BTRFS_BLOCK_GROUP_DATA) + ret += bg->length * ratio; + if (bg->flags & BTRFS_BLOCK_GROUP_METADATA) { + metadata_ratio = max(metadata_ratio, ratio); + ret += bg->length * ratio; + } + if (bg->flags & BTRFS_BLOCK_GROUP_SYSTEM) + ret += bg->length * ratio; + } + } + up_read(&info->groups_sem); + } + /* + * btrfs_shrink_device relocates chunks to shrink the filesystem. In + * order to do so extra space must be reserved for metadata operations + * which require 3 + num of devices with a minimum of metadata + * duplication(default 2) as discovered above. See comment in + * btrfs_trans_handle for a full explination. + */ + ret += btrfs_calc_insert_metadata_size( + fs_info, + 3 + max(metadata_ratio, fs_info->fs_devices->total_devices)); + + return ret; +} + /* * shrinking a device means finding all of the device extents past * the new size, and then following the back refs to the chunks. * The chunk relocation code actually frees the device extent */ -int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) +int btrfs_shrink_device(struct btrfs_device *device, u64 *new_size, bool to_min) { struct btrfs_fs_info *fs_info = device->fs_info; struct btrfs_root *root = fs_info->dev_root; @@ -4842,10 +4905,6 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) u64 diff; u64 start; - new_size = round_down(new_size, fs_info->sectorsize); - start = new_size; - diff = round_down(old_size - new_size, fs_info->sectorsize); - if (test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) return -EINVAL; @@ -4855,6 +4914,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) path->reada = READA_BACK; +again: trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { btrfs_free_path(path); @@ -4863,28 +4923,45 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) mutex_lock(&fs_info->chunk_mutex); - btrfs_device_set_total_bytes(device, new_size); - if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { - device->fs_devices->total_rw_bytes -= diff; - atomic64_sub(diff, &fs_info->free_chunk_space); - } - /* - * Once the device's size has been set to the new size, ensure all - * in-memory chunks are synced to disk so that the loop below sees them - * and relocates them accordingly. + * Ensure all in-memory chunks are synced to disk before calculating the + * new size to ensure the loop below can relocate them. */ - if (contains_pending_extent(device, &start, diff)) { + start = 0; + if (contains_pending_extent(device, &start, old_size)) { mutex_unlock(&fs_info->chunk_mutex); ret = btrfs_commit_transaction(trans); - if (ret) - goto done; + if (ret) { + btrfs_free_path(path); + return PTR_ERR(trans); + } + mutex_lock(&fs_info->chunk_mutex); } else { - mutex_unlock(&fs_info->chunk_mutex); btrfs_end_transaction(trans); } -again: + if (to_min) + *new_size = btrfs_get_allocated_space(fs_info); + + *new_size = round_down(*new_size, fs_info->sectorsize); + diff = round_down(old_size - *new_size, fs_info->sectorsize); + + trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) { + mutex_unlock(&fs_info->chunk_mutex); + btrfs_free_path(path); + return PTR_ERR(trans); + } + + btrfs_device_set_total_bytes(device, *new_size); + if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { + device->fs_devices->total_rw_bytes -= diff; + atomic64_sub(diff, &fs_info->free_chunk_space); + } + + btrfs_end_transaction(trans); + mutex_unlock(&fs_info->chunk_mutex); + key.objectid = device->devid; key.offset = (u64)-1; key.type = BTRFS_DEV_EXTENT_KEY; @@ -4920,7 +4997,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) dev_extent = btrfs_item_ptr(l, slot, struct btrfs_dev_extent); length = btrfs_dev_extent_length(l, dev_extent); - if (key.offset + length <= new_size) { + if (key.offset + length <= *new_size) { mutex_unlock(&fs_info->reclaim_bgs_lock); btrfs_release_path(path); break; @@ -4973,10 +5050,10 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) mutex_lock(&fs_info->chunk_mutex); /* Clear all state bits beyond the shrunk device size */ - clear_extent_bits(&device->alloc_state, new_size, (u64)-1, + clear_extent_bits(&device->alloc_state, *new_size, (u64)-1, CHUNK_STATE_MASK); - btrfs_device_set_disk_total_bytes(device, new_size); + btrfs_device_set_disk_total_bytes(device, *new_size); if (list_empty(&device->post_commit_list)) list_add_tail(&device->post_commit_list, &trans->transaction->dev_update_list); diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 2128a032c3b7..db8000b4cf94 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -636,7 +636,7 @@ int btrfs_grow_device(struct btrfs_trans_handle *trans, struct btrfs_device *device, u64 new_size); struct btrfs_device *btrfs_find_device(const struct btrfs_fs_devices *fs_devices, const struct btrfs_dev_lookup_args *args); -int btrfs_shrink_device(struct btrfs_device *device, u64 new_size); +int btrfs_shrink_device(struct btrfs_device *device, u64 *new_size, bool to_min); int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *path); int btrfs_balance(struct btrfs_fs_info *fs_info, struct btrfs_balance_control *bctl, @@ -648,6 +648,7 @@ int btrfs_pause_balance(struct btrfs_fs_info *fs_info); int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset); int btrfs_cancel_balance(struct btrfs_fs_info *fs_info); int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info); +u64 btrfs_get_allocated_space(struct btrfs_fs_info *fs_info); int btrfs_uuid_scan_kthread(void *data); bool btrfs_chunk_writeable(struct btrfs_fs_info *fs_info, u64 chunk_offset); void btrfs_dev_stat_inc_and_print(struct btrfs_device *dev, int index);