Loading fs/btrfs/ctree.h +3 −0 Original line number Diff line number Diff line Loading @@ -3431,6 +3431,8 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 group_start, struct extent_map *em); void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info); void btrfs_get_block_group_trimming(struct btrfs_block_group_cache *cache); void btrfs_put_block_group_trimming(struct btrfs_block_group_cache *cache); void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans, struct btrfs_root *root); u64 btrfs_get_alloc_profile(struct btrfs_root *root, int data); Loading Loading @@ -4067,6 +4069,7 @@ __cold void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, unsigned int line, int errno, const char *fmt, ...); const char *btrfs_decode_error(int errno); __cold void __btrfs_abort_transaction(struct btrfs_trans_handle *trans, Loading fs/btrfs/disk-io.c +9 −0 Original line number Diff line number Diff line Loading @@ -3761,6 +3761,15 @@ void close_ctree(struct btrfs_root *root) cancel_work_sync(&fs_info->async_reclaim_work); if (!(fs_info->sb->s_flags & MS_RDONLY)) { /* * If the cleaner thread is stopped and there are * block groups queued for removal, the deletion will be * skipped when we quit the cleaner thread. */ mutex_lock(&root->fs_info->cleaner_mutex); btrfs_delete_unused_bgs(root->fs_info); mutex_unlock(&root->fs_info->cleaner_mutex); ret = btrfs_commit_super(root); if (ret) btrfs_err(fs_info, "commit super ret %d", ret); Loading fs/btrfs/extent-tree.c +239 −9 Original line number Diff line number Diff line Loading @@ -1882,10 +1882,77 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans, return ret; } static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len) #define in_range(b, first, len) ((b) >= (first) && (b) < (first) + (len)) static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len, u64 *discarded_bytes) { return blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_NOFS, 0); int j, ret = 0; u64 bytes_left, end; u64 aligned_start = ALIGN(start, 1 << 9); if (WARN_ON(start != aligned_start)) { len -= aligned_start - start; len = round_down(len, 1 << 9); start = aligned_start; } *discarded_bytes = 0; if (!len) return 0; end = start + len; bytes_left = len; /* Skip any superblocks on this device. */ for (j = 0; j < BTRFS_SUPER_MIRROR_MAX; j++) { u64 sb_start = btrfs_sb_offset(j); u64 sb_end = sb_start + BTRFS_SUPER_INFO_SIZE; u64 size = sb_start - start; if (!in_range(sb_start, start, bytes_left) && !in_range(sb_end, start, bytes_left) && !in_range(start, sb_start, BTRFS_SUPER_INFO_SIZE)) continue; /* * Superblock spans beginning of range. Adjust start and * try again. */ if (sb_start <= start) { start += sb_end - start; if (start > end) { bytes_left = 0; break; } bytes_left = end - start; continue; } if (size) { ret = blkdev_issue_discard(bdev, start >> 9, size >> 9, GFP_NOFS, 0); if (!ret) *discarded_bytes += size; else if (ret != -EOPNOTSUPP) return ret; } start = sb_end; if (start > end) { bytes_left = 0; break; } bytes_left = end - start; } if (bytes_left) { ret = blkdev_issue_discard(bdev, start >> 9, bytes_left >> 9, GFP_NOFS, 0); if (!ret) *discarded_bytes += bytes_left; } return ret; } int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr, Loading @@ -1906,14 +1973,16 @@ int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr, for (i = 0; i < bbio->num_stripes; i++, stripe++) { u64 bytes; if (!stripe->dev->can_discard) continue; ret = btrfs_issue_discard(stripe->dev->bdev, stripe->physical, stripe->length); stripe->length, &bytes); if (!ret) discarded_bytes += stripe->length; discarded_bytes += bytes; else if (ret != -EOPNOTSUPP) break; /* Logic errors or -ENOMEM, or -EIO but I don't know how that could happen JDM */ Loading Loading @@ -6061,20 +6130,19 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct btrfs_root *root) { struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_block_group_cache *block_group, *tmp; struct list_head *deleted_bgs; struct extent_io_tree *unpin; u64 start; u64 end; int ret; if (trans->aborted) return 0; if (fs_info->pinned_extents == &fs_info->freed_extents[0]) unpin = &fs_info->freed_extents[1]; else unpin = &fs_info->freed_extents[0]; while (1) { while (!trans->aborted) { mutex_lock(&fs_info->unused_bg_unpin_mutex); ret = find_first_extent_bit(unpin, 0, &start, &end, EXTENT_DIRTY, NULL); Loading @@ -6093,6 +6161,34 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, cond_resched(); } /* * Transaction is finished. We don't need the lock anymore. We * do need to clean up the block groups in case of a transaction * abort. */ deleted_bgs = &trans->transaction->deleted_bgs; list_for_each_entry_safe(block_group, tmp, deleted_bgs, bg_list) { u64 trimmed = 0; ret = -EROFS; if (!trans->aborted) ret = btrfs_discard_extent(root, block_group->key.objectid, block_group->key.offset, &trimmed); list_del_init(&block_group->bg_list); btrfs_put_block_group_trimming(block_group); btrfs_put_block_group(block_group); if (ret) { const char *errstr = btrfs_decode_error(ret); btrfs_warn(fs_info, "Discard failed while removing blockgroup: errno=%d %s\n", ret, errstr); } } return 0; } Loading Loading @@ -9830,6 +9926,11 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, * currently running transaction might finish and a new one start, * allowing for new block groups to be created that can reuse the same * physical device locations unless we take this special care. * * There may also be an implicit trim operation if the file system * is mounted with -odiscard. The same protections must remain * in place until the extents have been discarded completely when * the transaction commit has completed. */ remove_em = (atomic_read(&block_group->trimming) == 0); /* Loading Loading @@ -9904,6 +10005,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) spin_lock(&fs_info->unused_bgs_lock); while (!list_empty(&fs_info->unused_bgs)) { u64 start, end; int trimming; block_group = list_first_entry(&fs_info->unused_bgs, struct btrfs_block_group_cache, Loading Loading @@ -10003,12 +10105,39 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) spin_unlock(&block_group->lock); spin_unlock(&space_info->lock); /* DISCARD can flip during remount */ trimming = btrfs_test_opt(root, DISCARD); /* Implicit trim during transaction commit. */ if (trimming) btrfs_get_block_group_trimming(block_group); /* * Btrfs_remove_chunk will abort the transaction if things go * horribly wrong. */ ret = btrfs_remove_chunk(trans, root, block_group->key.objectid); if (ret) { if (trimming) btrfs_put_block_group_trimming(block_group); goto end_trans; } /* * If we're not mounted with -odiscard, we can just forget * about this block group. Otherwise we'll need to wait * until transaction commit to do the actual discard. */ if (trimming) { WARN_ON(!list_empty(&block_group->bg_list)); spin_lock(&trans->transaction->deleted_bgs_lock); list_move(&block_group->bg_list, &trans->transaction->deleted_bgs); spin_unlock(&trans->transaction->deleted_bgs_lock); btrfs_get_block_group(block_group); } end_trans: btrfs_end_transaction(trans, root); next: Loading Loading @@ -10062,10 +10191,99 @@ int btrfs_error_unpin_extent_range(struct btrfs_root *root, u64 start, u64 end) return unpin_extent_range(root, start, end, false); } /* * It used to be that old block groups would be left around forever. * Iterating over them would be enough to trim unused space. Since we * now automatically remove them, we also need to iterate over unallocated * space. * * We don't want a transaction for this since the discard may take a * substantial amount of time. We don't require that a transaction be * running, but we do need to take a running transaction into account * to ensure that we're not discarding chunks that were released in * the current transaction. * * Holding the chunks lock will prevent other threads from allocating * or releasing chunks, but it won't prevent a running transaction * from committing and releasing the memory that the pending chunks * list head uses. For that, we need to take a reference to the * transaction. */ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 minlen, u64 *trimmed) { u64 start = 0, len = 0; int ret; *trimmed = 0; /* Not writeable = nothing to do. */ if (!device->writeable) return 0; /* No free space = nothing to do. */ if (device->total_bytes <= device->bytes_used) return 0; ret = 0; while (1) { struct btrfs_fs_info *fs_info = device->dev_root->fs_info; struct btrfs_transaction *trans; u64 bytes; ret = mutex_lock_interruptible(&fs_info->chunk_mutex); if (ret) return ret; down_read(&fs_info->commit_root_sem); spin_lock(&fs_info->trans_lock); trans = fs_info->running_transaction; if (trans) atomic_inc(&trans->use_count); spin_unlock(&fs_info->trans_lock); ret = find_free_dev_extent_start(trans, device, minlen, start, &start, &len); if (trans) btrfs_put_transaction(trans); if (ret) { up_read(&fs_info->commit_root_sem); mutex_unlock(&fs_info->chunk_mutex); if (ret == -ENOSPC) ret = 0; break; } ret = btrfs_issue_discard(device->bdev, start, len, &bytes); up_read(&fs_info->commit_root_sem); mutex_unlock(&fs_info->chunk_mutex); if (ret) break; start += len; *trimmed += bytes; if (fatal_signal_pending(current)) { ret = -ERESTARTSYS; break; } cond_resched(); } return ret; } int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range) { struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_block_group_cache *cache = NULL; struct btrfs_device *device; struct list_head *devices; u64 group_trimmed; u64 start; u64 end; Loading Loading @@ -10120,6 +10338,18 @@ int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range) cache = next_block_group(fs_info->tree_root, cache); } mutex_lock(&root->fs_info->fs_devices->device_list_mutex); devices = &root->fs_info->fs_devices->alloc_list; list_for_each_entry(device, devices, dev_alloc_list) { ret = btrfs_trim_free_extents(device, range->minlen, &group_trimmed); if (ret) break; trimmed += group_trimmed; } mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); range->len = trimmed; return ret; } Loading fs/btrfs/free-space-cache.c +33 −24 Original line number Diff line number Diff line Loading @@ -3272,35 +3272,23 @@ static int trim_bitmaps(struct btrfs_block_group_cache *block_group, return ret; } int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, u64 *trimmed, u64 start, u64 end, u64 minlen) void btrfs_get_block_group_trimming(struct btrfs_block_group_cache *cache) { int ret; *trimmed = 0; spin_lock(&block_group->lock); if (block_group->removed) { spin_unlock(&block_group->lock); return 0; atomic_inc(&cache->trimming); } atomic_inc(&block_group->trimming); spin_unlock(&block_group->lock); ret = trim_no_bitmap(block_group, trimmed, start, end, minlen); if (ret) goto out; ret = trim_bitmaps(block_group, trimmed, start, end, minlen); out: spin_lock(&block_group->lock); if (atomic_dec_and_test(&block_group->trimming) && block_group->removed) { void btrfs_put_block_group_trimming(struct btrfs_block_group_cache *block_group) { struct extent_map_tree *em_tree; struct extent_map *em; bool cleanup; spin_lock(&block_group->lock); cleanup = (atomic_dec_and_test(&block_group->trimming) && block_group->removed); spin_unlock(&block_group->lock); if (cleanup) { lock_chunks(block_group->fs_info->chunk_root); em_tree = &block_group->fs_info->mapping_tree.map_tree; write_lock(&em_tree->lock); Loading @@ -3324,10 +3312,31 @@ int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, * this block group have left 1 entry each one. Free them. */ __btrfs_remove_free_space_cache(block_group->free_space_ctl); } else { } } int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, u64 *trimmed, u64 start, u64 end, u64 minlen) { int ret; *trimmed = 0; spin_lock(&block_group->lock); if (block_group->removed) { spin_unlock(&block_group->lock); return 0; } btrfs_get_block_group_trimming(block_group); spin_unlock(&block_group->lock); ret = trim_no_bitmap(block_group, trimmed, start, end, minlen); if (ret) goto out; ret = trim_bitmaps(block_group, trimmed, start, end, minlen); out: btrfs_put_block_group_trimming(block_group); return ret; } Loading fs/btrfs/super.c +12 −1 Original line number Diff line number Diff line Loading @@ -69,7 +69,7 @@ static struct file_system_type btrfs_fs_type; static int btrfs_remount(struct super_block *sb, int *flags, char *data); static const char *btrfs_decode_error(int errno) const char *btrfs_decode_error(int errno) { char *errstr = "unknown"; Loading Loading @@ -1651,6 +1651,17 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) sb->s_flags |= MS_RDONLY; /* * Setting MS_RDONLY will put the cleaner thread to * sleep at the next loop if it's already active. * If it's already asleep, we'll leave unused block * groups on disk until we're mounted read-write again * unless we clean them up here. */ mutex_lock(&root->fs_info->cleaner_mutex); btrfs_delete_unused_bgs(fs_info); mutex_unlock(&root->fs_info->cleaner_mutex); btrfs_dev_replace_suspend_for_unmount(fs_info); btrfs_scrub_cancel(fs_info); btrfs_pause_balance(fs_info); Loading Loading
fs/btrfs/ctree.h +3 −0 Original line number Diff line number Diff line Loading @@ -3431,6 +3431,8 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 group_start, struct extent_map *em); void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info); void btrfs_get_block_group_trimming(struct btrfs_block_group_cache *cache); void btrfs_put_block_group_trimming(struct btrfs_block_group_cache *cache); void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans, struct btrfs_root *root); u64 btrfs_get_alloc_profile(struct btrfs_root *root, int data); Loading Loading @@ -4067,6 +4069,7 @@ __cold void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, unsigned int line, int errno, const char *fmt, ...); const char *btrfs_decode_error(int errno); __cold void __btrfs_abort_transaction(struct btrfs_trans_handle *trans, Loading
fs/btrfs/disk-io.c +9 −0 Original line number Diff line number Diff line Loading @@ -3761,6 +3761,15 @@ void close_ctree(struct btrfs_root *root) cancel_work_sync(&fs_info->async_reclaim_work); if (!(fs_info->sb->s_flags & MS_RDONLY)) { /* * If the cleaner thread is stopped and there are * block groups queued for removal, the deletion will be * skipped when we quit the cleaner thread. */ mutex_lock(&root->fs_info->cleaner_mutex); btrfs_delete_unused_bgs(root->fs_info); mutex_unlock(&root->fs_info->cleaner_mutex); ret = btrfs_commit_super(root); if (ret) btrfs_err(fs_info, "commit super ret %d", ret); Loading
fs/btrfs/extent-tree.c +239 −9 Original line number Diff line number Diff line Loading @@ -1882,10 +1882,77 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans, return ret; } static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len) #define in_range(b, first, len) ((b) >= (first) && (b) < (first) + (len)) static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len, u64 *discarded_bytes) { return blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_NOFS, 0); int j, ret = 0; u64 bytes_left, end; u64 aligned_start = ALIGN(start, 1 << 9); if (WARN_ON(start != aligned_start)) { len -= aligned_start - start; len = round_down(len, 1 << 9); start = aligned_start; } *discarded_bytes = 0; if (!len) return 0; end = start + len; bytes_left = len; /* Skip any superblocks on this device. */ for (j = 0; j < BTRFS_SUPER_MIRROR_MAX; j++) { u64 sb_start = btrfs_sb_offset(j); u64 sb_end = sb_start + BTRFS_SUPER_INFO_SIZE; u64 size = sb_start - start; if (!in_range(sb_start, start, bytes_left) && !in_range(sb_end, start, bytes_left) && !in_range(start, sb_start, BTRFS_SUPER_INFO_SIZE)) continue; /* * Superblock spans beginning of range. Adjust start and * try again. */ if (sb_start <= start) { start += sb_end - start; if (start > end) { bytes_left = 0; break; } bytes_left = end - start; continue; } if (size) { ret = blkdev_issue_discard(bdev, start >> 9, size >> 9, GFP_NOFS, 0); if (!ret) *discarded_bytes += size; else if (ret != -EOPNOTSUPP) return ret; } start = sb_end; if (start > end) { bytes_left = 0; break; } bytes_left = end - start; } if (bytes_left) { ret = blkdev_issue_discard(bdev, start >> 9, bytes_left >> 9, GFP_NOFS, 0); if (!ret) *discarded_bytes += bytes_left; } return ret; } int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr, Loading @@ -1906,14 +1973,16 @@ int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr, for (i = 0; i < bbio->num_stripes; i++, stripe++) { u64 bytes; if (!stripe->dev->can_discard) continue; ret = btrfs_issue_discard(stripe->dev->bdev, stripe->physical, stripe->length); stripe->length, &bytes); if (!ret) discarded_bytes += stripe->length; discarded_bytes += bytes; else if (ret != -EOPNOTSUPP) break; /* Logic errors or -ENOMEM, or -EIO but I don't know how that could happen JDM */ Loading Loading @@ -6061,20 +6130,19 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct btrfs_root *root) { struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_block_group_cache *block_group, *tmp; struct list_head *deleted_bgs; struct extent_io_tree *unpin; u64 start; u64 end; int ret; if (trans->aborted) return 0; if (fs_info->pinned_extents == &fs_info->freed_extents[0]) unpin = &fs_info->freed_extents[1]; else unpin = &fs_info->freed_extents[0]; while (1) { while (!trans->aborted) { mutex_lock(&fs_info->unused_bg_unpin_mutex); ret = find_first_extent_bit(unpin, 0, &start, &end, EXTENT_DIRTY, NULL); Loading @@ -6093,6 +6161,34 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, cond_resched(); } /* * Transaction is finished. We don't need the lock anymore. We * do need to clean up the block groups in case of a transaction * abort. */ deleted_bgs = &trans->transaction->deleted_bgs; list_for_each_entry_safe(block_group, tmp, deleted_bgs, bg_list) { u64 trimmed = 0; ret = -EROFS; if (!trans->aborted) ret = btrfs_discard_extent(root, block_group->key.objectid, block_group->key.offset, &trimmed); list_del_init(&block_group->bg_list); btrfs_put_block_group_trimming(block_group); btrfs_put_block_group(block_group); if (ret) { const char *errstr = btrfs_decode_error(ret); btrfs_warn(fs_info, "Discard failed while removing blockgroup: errno=%d %s\n", ret, errstr); } } return 0; } Loading Loading @@ -9830,6 +9926,11 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, * currently running transaction might finish and a new one start, * allowing for new block groups to be created that can reuse the same * physical device locations unless we take this special care. * * There may also be an implicit trim operation if the file system * is mounted with -odiscard. The same protections must remain * in place until the extents have been discarded completely when * the transaction commit has completed. */ remove_em = (atomic_read(&block_group->trimming) == 0); /* Loading Loading @@ -9904,6 +10005,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) spin_lock(&fs_info->unused_bgs_lock); while (!list_empty(&fs_info->unused_bgs)) { u64 start, end; int trimming; block_group = list_first_entry(&fs_info->unused_bgs, struct btrfs_block_group_cache, Loading Loading @@ -10003,12 +10105,39 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) spin_unlock(&block_group->lock); spin_unlock(&space_info->lock); /* DISCARD can flip during remount */ trimming = btrfs_test_opt(root, DISCARD); /* Implicit trim during transaction commit. */ if (trimming) btrfs_get_block_group_trimming(block_group); /* * Btrfs_remove_chunk will abort the transaction if things go * horribly wrong. */ ret = btrfs_remove_chunk(trans, root, block_group->key.objectid); if (ret) { if (trimming) btrfs_put_block_group_trimming(block_group); goto end_trans; } /* * If we're not mounted with -odiscard, we can just forget * about this block group. Otherwise we'll need to wait * until transaction commit to do the actual discard. */ if (trimming) { WARN_ON(!list_empty(&block_group->bg_list)); spin_lock(&trans->transaction->deleted_bgs_lock); list_move(&block_group->bg_list, &trans->transaction->deleted_bgs); spin_unlock(&trans->transaction->deleted_bgs_lock); btrfs_get_block_group(block_group); } end_trans: btrfs_end_transaction(trans, root); next: Loading Loading @@ -10062,10 +10191,99 @@ int btrfs_error_unpin_extent_range(struct btrfs_root *root, u64 start, u64 end) return unpin_extent_range(root, start, end, false); } /* * It used to be that old block groups would be left around forever. * Iterating over them would be enough to trim unused space. Since we * now automatically remove them, we also need to iterate over unallocated * space. * * We don't want a transaction for this since the discard may take a * substantial amount of time. We don't require that a transaction be * running, but we do need to take a running transaction into account * to ensure that we're not discarding chunks that were released in * the current transaction. * * Holding the chunks lock will prevent other threads from allocating * or releasing chunks, but it won't prevent a running transaction * from committing and releasing the memory that the pending chunks * list head uses. For that, we need to take a reference to the * transaction. */ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 minlen, u64 *trimmed) { u64 start = 0, len = 0; int ret; *trimmed = 0; /* Not writeable = nothing to do. */ if (!device->writeable) return 0; /* No free space = nothing to do. */ if (device->total_bytes <= device->bytes_used) return 0; ret = 0; while (1) { struct btrfs_fs_info *fs_info = device->dev_root->fs_info; struct btrfs_transaction *trans; u64 bytes; ret = mutex_lock_interruptible(&fs_info->chunk_mutex); if (ret) return ret; down_read(&fs_info->commit_root_sem); spin_lock(&fs_info->trans_lock); trans = fs_info->running_transaction; if (trans) atomic_inc(&trans->use_count); spin_unlock(&fs_info->trans_lock); ret = find_free_dev_extent_start(trans, device, minlen, start, &start, &len); if (trans) btrfs_put_transaction(trans); if (ret) { up_read(&fs_info->commit_root_sem); mutex_unlock(&fs_info->chunk_mutex); if (ret == -ENOSPC) ret = 0; break; } ret = btrfs_issue_discard(device->bdev, start, len, &bytes); up_read(&fs_info->commit_root_sem); mutex_unlock(&fs_info->chunk_mutex); if (ret) break; start += len; *trimmed += bytes; if (fatal_signal_pending(current)) { ret = -ERESTARTSYS; break; } cond_resched(); } return ret; } int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range) { struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_block_group_cache *cache = NULL; struct btrfs_device *device; struct list_head *devices; u64 group_trimmed; u64 start; u64 end; Loading Loading @@ -10120,6 +10338,18 @@ int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range) cache = next_block_group(fs_info->tree_root, cache); } mutex_lock(&root->fs_info->fs_devices->device_list_mutex); devices = &root->fs_info->fs_devices->alloc_list; list_for_each_entry(device, devices, dev_alloc_list) { ret = btrfs_trim_free_extents(device, range->minlen, &group_trimmed); if (ret) break; trimmed += group_trimmed; } mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); range->len = trimmed; return ret; } Loading
fs/btrfs/free-space-cache.c +33 −24 Original line number Diff line number Diff line Loading @@ -3272,35 +3272,23 @@ static int trim_bitmaps(struct btrfs_block_group_cache *block_group, return ret; } int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, u64 *trimmed, u64 start, u64 end, u64 minlen) void btrfs_get_block_group_trimming(struct btrfs_block_group_cache *cache) { int ret; *trimmed = 0; spin_lock(&block_group->lock); if (block_group->removed) { spin_unlock(&block_group->lock); return 0; atomic_inc(&cache->trimming); } atomic_inc(&block_group->trimming); spin_unlock(&block_group->lock); ret = trim_no_bitmap(block_group, trimmed, start, end, minlen); if (ret) goto out; ret = trim_bitmaps(block_group, trimmed, start, end, minlen); out: spin_lock(&block_group->lock); if (atomic_dec_and_test(&block_group->trimming) && block_group->removed) { void btrfs_put_block_group_trimming(struct btrfs_block_group_cache *block_group) { struct extent_map_tree *em_tree; struct extent_map *em; bool cleanup; spin_lock(&block_group->lock); cleanup = (atomic_dec_and_test(&block_group->trimming) && block_group->removed); spin_unlock(&block_group->lock); if (cleanup) { lock_chunks(block_group->fs_info->chunk_root); em_tree = &block_group->fs_info->mapping_tree.map_tree; write_lock(&em_tree->lock); Loading @@ -3324,10 +3312,31 @@ int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, * this block group have left 1 entry each one. Free them. */ __btrfs_remove_free_space_cache(block_group->free_space_ctl); } else { } } int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, u64 *trimmed, u64 start, u64 end, u64 minlen) { int ret; *trimmed = 0; spin_lock(&block_group->lock); if (block_group->removed) { spin_unlock(&block_group->lock); return 0; } btrfs_get_block_group_trimming(block_group); spin_unlock(&block_group->lock); ret = trim_no_bitmap(block_group, trimmed, start, end, minlen); if (ret) goto out; ret = trim_bitmaps(block_group, trimmed, start, end, minlen); out: btrfs_put_block_group_trimming(block_group); return ret; } Loading
fs/btrfs/super.c +12 −1 Original line number Diff line number Diff line Loading @@ -69,7 +69,7 @@ static struct file_system_type btrfs_fs_type; static int btrfs_remount(struct super_block *sb, int *flags, char *data); static const char *btrfs_decode_error(int errno) const char *btrfs_decode_error(int errno) { char *errstr = "unknown"; Loading Loading @@ -1651,6 +1651,17 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) sb->s_flags |= MS_RDONLY; /* * Setting MS_RDONLY will put the cleaner thread to * sleep at the next loop if it's already active. * If it's already asleep, we'll leave unused block * groups on disk until we're mounted read-write again * unless we clean them up here. */ mutex_lock(&root->fs_info->cleaner_mutex); btrfs_delete_unused_bgs(fs_info); mutex_unlock(&root->fs_info->cleaner_mutex); btrfs_dev_replace_suspend_for_unmount(fs_info); btrfs_scrub_cancel(fs_info); btrfs_pause_balance(fs_info); Loading