ext4: avoid trim error on fs with small groups
authorJan Kara <jack@suse.cz>
Fri, 12 Nov 2021 15:22:02 +0000 (16:22 +0100)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 10 Jan 2022 18:25:55 +0000 (13:25 -0500)
A user reported FITRIM ioctl failing for him on ext4 on some devices
without apparent reason.  After some debugging we've found out that
these devices (being LVM volumes) report rather large discard
granularity of 42MB and the filesystem had 1k blocksize and thus group
size of 8MB. Because ext4 FITRIM implementation puts discard
granularity into minlen, ext4_trim_fs() declared the trim request as
invalid. However just silently doing nothing seems to be a more
appropriate reaction to such combination of parameters since user did
not specify anything wrong.

CC: Lukas Czerner <lczerner@redhat.com>
Fixes: 5c2ed62fd447 ("ext4: Adjust minlen with discard_granularity in the FITRIM ioctl")
Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20211112152202.26614-1-jack@suse.cz
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/ioctl.c
fs/ext4/mballoc.c

index 1366afb59fba2ac24562ab43e2252e173ee9012a..798d9d82879560d3c50cd7e7a9a65ee2fd7f6017 100644 (file)
@@ -1114,8 +1114,6 @@ resizefs_out:
                    sizeof(range)))
                        return -EFAULT;
 
-               range.minlen = max((unsigned int)range.minlen,
-                                  q->limits.discard_granularity);
                ret = ext4_trim_fs(sb, &range);
                if (ret < 0)
                        return ret;
index 3dd9b9e2f9675aa0e8c36875193db6ba14cc8566..ea764137462efffd91321367b56ec3c5e298b5e5 100644 (file)
@@ -6400,6 +6400,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
  */
 int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
 {
+       struct request_queue *q = bdev_get_queue(sb->s_bdev);
        struct ext4_group_info *grp;
        ext4_group_t group, first_group, last_group;
        ext4_grpblk_t cnt = 0, first_cluster, last_cluster;
@@ -6418,6 +6419,13 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
            start >= max_blks ||
            range->len < sb->s_blocksize)
                return -EINVAL;
+       /* No point to try to trim less than discard granularity */
+       if (range->minlen < q->limits.discard_granularity) {
+               minlen = EXT4_NUM_B2C(EXT4_SB(sb),
+                       q->limits.discard_granularity >> sb->s_blocksize_bits);
+               if (minlen > EXT4_CLUSTERS_PER_GROUP(sb))
+                       goto out;
+       }
        if (end >= max_blks)
                end = max_blks - 1;
        if (end <= first_data_blk)