{
struct btrfs_fs_info *fs_info = trans->fs_info;
- spin_lock(&fs_info->unused_bgs_lock);
- /*
- * The block group might already be on the unused_bgs list, remove it
- * if it is. It'll get readded after the async discard worker finishes,
- * or in btrfs_handle_fully_remapped_bgs() if we're not using async
- * discard.
- */
- if (!list_empty(&bg->bg_list))
- list_del(&bg->bg_list);
- else
- btrfs_get_block_group(bg);
- list_add_tail(&bg->bg_list, &fs_info->fully_remapped_bgs);
- spin_unlock(&fs_info->unused_bgs_lock);
+ if (btrfs_test_opt(fs_info, DISCARD_ASYNC)) {
+ btrfs_discard_queue_work(&fs_info->discard_ctl, bg);
+ } else {
+ spin_lock(&fs_info->unused_bgs_lock);
+ /*
+ * The block group might already be on the unused_bgs list,
+ * remove it if it is. It'll get readded after
+ * btrfs_handle_fully_remapped_bgs() finishes.
+ */
+ if (!list_empty(&bg->bg_list))
+ list_del(&bg->bg_list);
+ else
+ btrfs_get_block_group(bg);
+
+ list_add_tail(&bg->bg_list, &fs_info->fully_remapped_bgs);
+ spin_unlock(&fs_info->unused_bgs_lock);
+ }
}
BTRFS_DISCARD_EXTENTS,
BTRFS_DISCARD_BITMAPS,
BTRFS_DISCARD_RESET_CURSOR,
+ BTRFS_DISCARD_FULLY_REMAPPED,
};
/*
return ret_block_group;
}
+/*
+ * Check whether a block group is empty.
+ *
+ * "Empty" here means that there are no extents physically located within the
+ * device extents corresponding to this block group.
+ *
+ * For a remapped block group, this means that all of its identity remaps have
+ * been removed. For a non-remapped block group, this means that no extents
+ * have an address within its range, and that nothing has been remapped to be
+ * within it.
+ */
+static bool block_group_is_empty(const struct btrfs_block_group *bg)
+{
+ if (bg->flags & BTRFS_BLOCK_GROUP_REMAPPED)
+ return bg->identity_remap_count == 0;
+
+ return bg->used == 0 && bg->remap_bytes == 0;
+}
+
/*
* Look up next block group and set it for use.
*
block_group = find_next_block_group(discard_ctl, now);
if (block_group && now >= block_group->discard_eligible_time) {
+ const bool empty = block_group_is_empty(block_group);
+
if (block_group->discard_index == BTRFS_DISCARD_INDEX_UNUSED &&
- block_group->used != 0) {
+ !empty) {
if (btrfs_is_block_group_data_only(block_group)) {
__add_to_discard_list(discard_ctl, block_group);
/*
}
if (block_group->discard_state == BTRFS_DISCARD_RESET_CURSOR) {
block_group->discard_cursor = block_group->start;
- block_group->discard_state = BTRFS_DISCARD_EXTENTS;
+
+ if (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED && empty) {
+ block_group->discard_state = BTRFS_DISCARD_FULLY_REMAPPED;
+ } else {
+ block_group->discard_state = BTRFS_DISCARD_EXTENTS;
+ }
}
}
if (block_group) {
if (!block_group || !btrfs_test_opt(block_group->fs_info, DISCARD_ASYNC))
return;
- if (block_group->used == 0 && block_group->remap_bytes == 0)
+ if (block_group_is_empty(block_group))
add_to_discard_unused_list(discard_ctl, block_group);
else
add_to_discard_list(discard_ctl, block_group);
{
remove_from_discard_list(discard_ctl, block_group);
- if (block_group->used == 0) {
+ if (block_group_is_empty(block_group)) {
if (btrfs_is_free_space_trimmed(block_group))
btrfs_mark_bg_unused(block_group);
else
/* Perform discarding */
minlen = discard_minlen[discard_index];
- if (discard_state == BTRFS_DISCARD_BITMAPS) {
+ switch (discard_state) {
+ case BTRFS_DISCARD_BITMAPS: {
u64 maxlen = 0;
/*
btrfs_block_group_end(block_group),
minlen, maxlen, true);
discard_ctl->discard_bitmap_bytes += trimmed;
- } else {
+
+ break;
+ }
+
+ case BTRFS_DISCARD_FULLY_REMAPPED:
+ btrfs_trim_fully_remapped_block_group(block_group);
+ break;
+
+ default:
btrfs_trim_block_group_extents(block_group, &trimmed,
block_group->discard_cursor,
btrfs_block_group_end(block_group),
minlen, true);
discard_ctl->discard_extent_bytes += trimmed;
+
+ break;
}
/* Determine next steps for a block_group */
if (block_group->discard_cursor >= btrfs_block_group_end(block_group)) {
- if (discard_state == BTRFS_DISCARD_BITMAPS) {
+ if (discard_state == BTRFS_DISCARD_BITMAPS ||
+ discard_state == BTRFS_DISCARD_FULLY_REMAPPED) {
btrfs_finish_discard_pass(discard_ctl, block_group);
} else {
block_group->discard_cursor = block_group->start;
list_del_init(&bg->bg_list);
spin_unlock(&fs_info->unused_bgs_lock);
+ btrfs_discard_extent(fs_info, bg->start, bg->length, NULL, false);
+
ret = btrfs_complete_bg_remapping(bg);
if (ret) {
btrfs_put_block_group(bg);
#include "file-item.h"
#include "file.h"
#include "super.h"
+#include "relocation.h"
#define BITS_PER_BITMAP (PAGE_SIZE * 8UL)
#define MAX_CACHE_BYTES_PER_GIG SZ_64K
struct rb_node *node;
bool ret = true;
+ if (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED &&
+ block_group->identity_remap_count == 0) {
+ return true;
+ }
+
spin_lock(&ctl->tree_lock);
node = rb_first(&ctl->free_space_offset);
return ret;
}
+void btrfs_trim_fully_remapped_block_group(struct btrfs_block_group *bg)
+{
+ struct btrfs_fs_info *fs_info = bg->fs_info;
+ struct btrfs_discard_ctl *discard_ctl = &fs_info->discard_ctl;
+ int ret = 0;
+ u64 bytes, trimmed;
+ const u64 max_discard_size = READ_ONCE(discard_ctl->max_discard_size);
+ u64 end = btrfs_block_group_end(bg);
+
+ bytes = end - bg->discard_cursor;
+
+ if (max_discard_size &&
+ bytes >= (max_discard_size + BTRFS_ASYNC_DISCARD_MIN_FILTER))
+ bytes = max_discard_size;
+
+ ret = btrfs_discard_extent(fs_info, bg->discard_cursor, bytes, &trimmed, false);
+ if (ret)
+ return;
+
+ bg->discard_cursor += trimmed;
+
+ if (bg->discard_cursor < end)
+ return;
+
+ btrfs_complete_bg_remapping(bg);
+}
+
/*
* If we break out of trimming a bitmap prematurely, we should reset the
* trimming bit. In a rather contrived case, it's possible to race here so
int btrfs_trim_block_group_bitmaps(struct btrfs_block_group *block_group,
u64 *trimmed, u64 start, u64 end, u64 minlen,
u64 maxlen, bool async);
+void btrfs_trim_fully_remapped_block_group(struct btrfs_block_group *bg);
bool btrfs_free_space_cache_v1_active(struct btrfs_fs_info *fs_info);
int btrfs_set_free_space_cache_v1_active(struct btrfs_fs_info *fs_info, bool active);