[PATCH] ext4: issue aligned discards

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Reiser Filesystem Development]     [Ceph FS]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite National Park]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Media]

  Powered by Linux