Message ID | 1434375680-4115-3-git-send-email-jeffm@suse.com (mailing list archive) |
---|---|
State | Accepted |
Headers | show |
On Mon, Jun 15, 2015 at 2:41 PM, <jeffm@suse.com> wrote: > From: Jeff Mahoney <jeffm@suse.com> > > Btrfs doesn't track superblocks with extent records so there is nothing > persistent on-disk to indicate that those blocks are in use. We track > the superblocks in memory to ensure they don't get used by removing them > from the free space cache when we load a block group from disk. Prior > to 47ab2a6c6a (Btrfs: remove empty block groups automatically), that > was fine since the block group would never be reclaimed so the superblock > was always safe. Once we started removing the empty block groups, we > were protected by the fact that discards weren't being properly issued > for unused space either via FITRIM or -odiscard. The block groups were > still being released, but the blocks remained on disk. > > In order to properly discard unused block groups, we need to filter out > the superblocks from the discard range. Superblocks are located at fixed > locations on each device, so it makes sense to filter them out in > btrfs_issue_discard, which is used by both -odiscard and FITRIM. > > Signed-off-by: Jeff Mahoney <jeffm@suse.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> Tested-by: Filipe Manana <fdmanana@suse.com> > --- > fs/btrfs/extent-tree.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++---- > 1 file changed, 55 insertions(+), 4 deletions(-) > > diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c > index cf9cefd..1e44b93 100644 > --- a/fs/btrfs/extent-tree.c > +++ b/fs/btrfs/extent-tree.c > @@ -1884,10 +1884,12 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans, > return ret; > } > > +#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) > { > - int ret = 0; > + int j, ret = 0; > + u64 bytes_left, end; > u64 aligned_start = ALIGN(start, 1 << 9); > > if (WARN_ON(start != aligned_start)) { > @@ -1897,11 +1899,60 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len, > } > > *discarded_bytes = 0; > - if (len) { > - ret = blkdev_issue_discard(bdev, start >> 9, len >> 9, > + > + 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 = len; > + *discarded_bytes += bytes_left; > } > return ret; > } > -- > 2.4.3 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index cf9cefd..1e44b93 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -1884,10 +1884,12 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans, return ret; } +#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) { - int ret = 0; + int j, ret = 0; + u64 bytes_left, end; u64 aligned_start = ALIGN(start, 1 << 9); if (WARN_ON(start != aligned_start)) { @@ -1897,11 +1899,60 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len, } *discarded_bytes = 0; - if (len) { - ret = blkdev_issue_discard(bdev, start >> 9, len >> 9, + + 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 = len; + *discarded_bytes += bytes_left; } return ret; }