On Mon, Feb 21, 2011 at 04:52:27PM +0800, Li Dongyang wrote: > Here is batched discard support for btrfs, several changes were made: > > btrfs_test_opt(root, DISCARD) is moved from btrfs_discard_extent > to callers, as we still want to trim the fs even it's not mounted > with -o discard. > btrfs_discard_extent now reports errors and actual bytes trimmed to > callers, for EOPNOTSUPP, we will try other stripes as an extent > could span SSD and other drives, and we won't return error to > callers unless we failed with all stripes. > > And btrfs_discard_extent calls btrfs_map_block with READ, this means > we won't get all stripes mapped for RAID1/DUP/RAID10, I think this > should be fixed, Thanks. > > Signed-off-by: Li Dongyang <lidongy...@novell.com> > --- > fs/btrfs/ctree.h | 3 +- > fs/btrfs/disk-io.c | 5 ++- > fs/btrfs/extent-tree.c | 81 > ++++++++++++++++++++++++++++++++++++------- > fs/btrfs/free-space-cache.c | 79 +++++++++++++++++++++++++++++++++++++++++ > fs/btrfs/free-space-cache.h | 2 + > fs/btrfs/ioctl.c | 24 +++++++++++++ > 6 files changed, 179 insertions(+), 15 deletions(-) > > diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h > index 2c98b3a..4486349 100644 > --- a/fs/btrfs/ctree.h > +++ b/fs/btrfs/ctree.h > @@ -2217,7 +2217,8 @@ u64 btrfs_account_ro_block_groups_free_space(struct > btrfs_space_info *sinfo); > int btrfs_error_unpin_extent_range(struct btrfs_root *root, > u64 start, u64 end); > int btrfs_error_discard_extent(struct btrfs_root *root, u64 bytenr, > - u64 num_bytes); > + u64 num_bytes, u64 *actual_bytes); > +int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range); > > /* ctree.c */ > int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key, > diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c > index e1aa8d6..bcb9451 100644 > --- a/fs/btrfs/disk-io.c > +++ b/fs/btrfs/disk-io.c > @@ -2947,7 +2947,10 @@ static int btrfs_destroy_pinned_extent(struct > btrfs_root *root, > break; > > /* opt_discard */ > - ret = btrfs_error_discard_extent(root, start, end + 1 - start); > + if (btrfs_test_opt(root, DISCARD)) > + ret = btrfs_error_discard_extent(root, start, > + end + 1 - start, > + NULL); > > clear_extent_dirty(unpin, start, end, GFP_NOFS); > btrfs_error_unpin_extent_range(root, start, end); > diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c > index f3c96fc..7bed32a 100644 > --- a/fs/btrfs/extent-tree.c > +++ b/fs/btrfs/extent-tree.c > @@ -1740,22 +1740,20 @@ static int remove_extent_backref(struct > btrfs_trans_handle *trans, > return ret; > } > > -static void btrfs_issue_discard(struct block_device *bdev, > +static int btrfs_issue_discard(struct block_device *bdev, > u64 start, u64 len) > { > - blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL, 0); > + return blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL, 0); > } > > static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr, > - u64 num_bytes) > + u64 num_bytes, u64 *actual_bytes) > { > int ret; > u64 map_length = num_bytes; > + u64 discarded_bytes = 0; > struct btrfs_multi_bio *multi = NULL; > > - if (!btrfs_test_opt(root, DISCARD)) > - return 0; > - > /* Tell the block device(s) that the sectors can be discarded */ > ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, > bytenr, &map_length, &multi, 0); > @@ -1767,13 +1765,25 @@ static int btrfs_discard_extent(struct btrfs_root > *root, u64 bytenr, > map_length = num_bytes; > > for (i = 0; i < multi->num_stripes; i++, stripe++) { > - btrfs_issue_discard(stripe->dev->bdev, > - stripe->physical, > - map_length); > + ret = btrfs_issue_discard(stripe->dev->bdev, > + stripe->physical, > + map_length); > + if (!ret) > + discarded_bytes += map_length; > + else if (ret == -EOPNOTSUPP) > + continue; > + else > + break; > } > kfree(multi); > } > > + if (discarded_bytes) > + ret = 0; > + > + if (actual_bytes) > + *actual_bytes = discarded_bytes; > + > return ret; > } > > @@ -4353,7 +4363,8 @@ int btrfs_finish_extent_commit(struct > btrfs_trans_handle *trans, > if (ret) > break; > > - ret = btrfs_discard_extent(root, start, end + 1 - start); > + if (btrfs_test_opt(root, DISCARD)) > + ret = btrfs_discard_extent(root, start, end + 1 - > start, NULL); > > clear_extent_dirty(unpin, start, end, GFP_NOFS); > unpin_extent_range(root, start, end); > @@ -5401,7 +5412,8 @@ int btrfs_free_reserved_extent(struct btrfs_root *root, > u64 start, u64 len) > return -ENOSPC; > } > > - ret = btrfs_discard_extent(root, start, len); > + if (btrfs_test_opt(root, DISCARD)) > + ret = btrfs_discard_extent(root, start, len, NULL); > > btrfs_add_free_space(cache, start, len); > update_reserved_bytes(cache, len, 0, 1); > @@ -8712,7 +8724,50 @@ int btrfs_error_unpin_extent_range(struct btrfs_root > *root, u64 start, u64 end) > } > > int btrfs_error_discard_extent(struct btrfs_root *root, u64 bytenr, > - u64 num_bytes) > + u64 num_bytes, u64 *actual_bytes) > +{ > + return btrfs_discard_extent(root, bytenr, num_bytes, actual_bytes); > +} > + > +int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range) > { > - return btrfs_discard_extent(root, bytenr, num_bytes); > + struct btrfs_fs_info *fs_info = root->fs_info; > + struct btrfs_block_group_cache *cache = NULL; > + u64 cnt; > + u64 start; > + u64 end; > + u64 trimmed = 0; > + int ret = 0; > + > + cache = btrfs_lookup_block_group(fs_info, range->start); > + > + while (cache) { > + if (cache->key.objectid >= (range->start + range->len)) { > + btrfs_put_block_group(cache); > + break; > + } > + > + start = max(range->start, cache->key.objectid); > + end = min(range->start + range->len, > + cache->key.objectid + cache->key.offset); > + > + if (end - start >= range->minlen) { > + ret = btrfs_trim_block_group(cache, > + &cnt, > + start, > + end, > + range->minlen); > + > + trimmed += cnt; > + if (ret < 0) { > + btrfs_put_block_group(cache); > + break; > + } > + } > + > + cache = next_block_group(fs_info->tree_root, cache); > + } > + > + range->len = trimmed; > + return ret; > } > diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c > index a039065..a274df5 100644 > --- a/fs/btrfs/free-space-cache.c > +++ b/fs/btrfs/free-space-cache.c > @@ -2154,3 +2154,82 @@ void btrfs_init_free_cluster(struct btrfs_free_cluster > *cluster) > cluster->block_group = NULL; > } > > +int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, > + u64 *trimmed, u64 start, u64 end, u64 minlen) > +{ > + struct btrfs_free_space *entry = NULL; > + struct btrfs_fs_info *fs_info = block_group->fs_info; > + u64 bytes = 0; > + u64 actually_trimmed; > + int ret = 0; > + > + *trimmed = 0; > + > + while (start < end) { > + spin_lock(&block_group->tree_lock); > + if (block_group->free_space < minlen) { > + spin_unlock(&block_group->tree_lock); > + break; > + } > + > + entry = tree_search_offset(block_group, start, 0, 1); > + if (!entry) > + entry = tree_search_offset(block_group, > + offset_to_bitmap(block_group, > + start), > + 1, 1); > + > + if (!entry || entry->offset >= end) { > + spin_unlock(&block_group->tree_lock); > + break; > + } > + > + if (entry->bitmap) { > + ret = search_bitmap(block_group, entry, &start, &bytes); > + if (!ret) { > + if (start >= end ) { > + spin_unlock(&block_group->tree_lock); > + break; > + } > + bytes = min(bytes, end - start); > + } else { > + start = entry->offset + BITS_PER_BITMAP * > + block_group->sectorsize; > + spin_unlock(&block_group->tree_lock); > + continue; > + } > + } else { > + start = entry->offset; > + bytes = min(entry->bytes, end - start); > + } > + > + spin_unlock(&block_group->tree_lock); > + > + if (bytes >= minlen && !btrfs_remove_free_space(block_group, > + start, > + bytes)) {
So you have just done 2 searches for the same thing. Instead of doing btrfs_remove_free_space here, put bitmap_clear_bits() after the search_bitmap, and do a unlink_free_space() if it's not a bitmap, that way we don't race with the allocator (even though this isn't a problem, it just sucks) and we don't do the search twice. Other than that it looks great, thank you, Josef -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html