Ext4 before this patch can issue discards without respecting block device's discard alignment. Such a discard results in EIO and kernel logs. Verified that there were no regressions in xfstests smoke tests. Signed-off-by: Harshad Shirwadkar <harshadshirwadkar@xxxxxxxxx> --- fs/ext4/mballoc.c | 32 +++++++++++++++++++++----------- include/linux/blkdev.h | 7 +++++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 30d5d97548c4..a591a3ab93d3 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2786,20 +2786,30 @@ static inline int ext4_issue_discard(struct super_block *sb, ext4_group_t block_group, ext4_grpblk_t cluster, int count, struct bio **biop) { - ext4_fsblk_t discard_block; - - discard_block = (EXT4_C2B(EXT4_SB(sb), cluster) + - ext4_group_first_block_no(sb, block_group)); - count = EXT4_C2B(EXT4_SB(sb), count); + unsigned long long discard_start, discard_len, alignment, granularity, + aligned_discard_start; + + granularity = max(bdev_discard_granularity(sb->s_bdev), 1 << 9); + alignment = max_t(int, bdev_logical_block_size(sb->s_bdev), + bdev_discard_alignment(sb->s_bdev)); + discard_start = (EXT4_C2B(EXT4_SB(sb), cluster) + + ext4_group_first_block_no(sb, block_group)) << + sb->s_blocksize_bits; + discard_len = EXT4_C2B(EXT4_SB(sb), count) << sb->s_blocksize_bits; + aligned_discard_start = round_up(discard_start, alignment); + discard_len -= min(discard_len, aligned_discard_start - discard_start); + discard_len = round_down(discard_len, granularity); + if (discard_len >> 9 == 0) + return 0; trace_ext4_discard_blocks(sb, - (unsigned long long) discard_block, count); - if (biop) { + aligned_discard_start >> sb->s_blocksize_bits, + discard_len >> (sb->s_blocksize_bits)); + if (biop) return __blkdev_issue_discard(sb->s_bdev, - (sector_t)discard_block << (sb->s_blocksize_bits - 9), - (sector_t)count << (sb->s_blocksize_bits - 9), + aligned_discard_start >> 9, discard_len >> 9, GFP_NOFS, 0, biop); - } else - return sb_issue_discard(sb, discard_block, count, GFP_NOFS, 0); + return sb_issue_discard(sb, aligned_discard_start, discard_len, + GFP_NOFS, 0); } static void ext4_free_data_in_buddy(struct super_block *sb, diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 8fd900998b4e..f448b3498336 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1431,6 +1431,13 @@ static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector return offset << SECTOR_SHIFT; } +static inline int bdev_discard_granularity(struct block_device *bdev) +{ + struct request_queue *q = bdev_get_queue(bdev); + + return q ? q->limits.discard_granularity : 0; +} + static inline int bdev_discard_alignment(struct block_device *bdev) { struct request_queue *q = bdev_get_queue(bdev); -- 2.27.0.278.ge193c7cf3a9-goog