ocfs2: fstrim: Fix start offset of first cluster group during fstrim
authorAshish Samant <ashish.samant@oracle.com>
Thu, 2 Nov 2017 22:59:37 +0000 (15:59 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 3 Nov 2017 14:39:19 +0000 (07:39 -0700)
The first cluster group descriptor is not stored at the start of the
group but at an offset from the start.  We need to take this into
account while doing fstrim on the first cluster group.  Otherwise we
will wrongly start fstrim a few blocks after the desired start block and
the range can cross over into the next cluster group and zero out the
group descriptor there.  This can cause filesytem corruption that cannot
be fixed by fsck.

Link: http://lkml.kernel.org/r/1507835579-7308-1-git-send-email-ashish.samant@oracle.com
Signed-off-by: Ashish Samant <ashish.samant@oracle.com>
Reviewed-by: Junxiao Bi <junxiao.bi@oracle.com>
Reviewed-by: Joseph Qi <jiangqi903@gmail.com>
Cc: Mark Fasheh <mfasheh@versity.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/ocfs2/alloc.c

index a177eae3aa1ad593adc62349d6e734cc026717b4..addd7c5f2d3e522a7a65bd51b0164ef705402605 100644 (file)
@@ -7304,13 +7304,24 @@ out:
 
 static int ocfs2_trim_extent(struct super_block *sb,
                             struct ocfs2_group_desc *gd,
 
 static int ocfs2_trim_extent(struct super_block *sb,
                             struct ocfs2_group_desc *gd,
-                            u32 start, u32 count)
+                            u64 group, u32 start, u32 count)
 {
        u64 discard, bcount;
 {
        u64 discard, bcount;
+       struct ocfs2_super *osb = OCFS2_SB(sb);
 
        bcount = ocfs2_clusters_to_blocks(sb, count);
 
        bcount = ocfs2_clusters_to_blocks(sb, count);
-       discard = le64_to_cpu(gd->bg_blkno) +
-                       ocfs2_clusters_to_blocks(sb, start);
+       discard = ocfs2_clusters_to_blocks(sb, start);
+
+       /*
+        * For the first cluster group, the gd->bg_blkno is not at the start
+        * of the group, but at an offset from the start. If we add it while
+        * calculating discard for first group, we will wrongly start fstrim a
+        * few blocks after the desried start block and the range can cross
+        * over into the next cluster group. So, add it only if this is not
+        * the first cluster group.
+        */
+       if (group != osb->first_cluster_group_blkno)
+               discard += le64_to_cpu(gd->bg_blkno);
 
        trace_ocfs2_trim_extent(sb, (unsigned long long)discard, bcount);
 
 
        trace_ocfs2_trim_extent(sb, (unsigned long long)discard, bcount);
 
@@ -7318,7 +7329,7 @@ static int ocfs2_trim_extent(struct super_block *sb,
 }
 
 static int ocfs2_trim_group(struct super_block *sb,
 }
 
 static int ocfs2_trim_group(struct super_block *sb,
-                           struct ocfs2_group_desc *gd,
+                           struct ocfs2_group_desc *gd, u64 group,
                            u32 start, u32 max, u32 minbits)
 {
        int ret = 0, count = 0, next;
                            u32 start, u32 max, u32 minbits)
 {
        int ret = 0, count = 0, next;
@@ -7337,7 +7348,7 @@ static int ocfs2_trim_group(struct super_block *sb,
                next = ocfs2_find_next_bit(bitmap, max, start);
 
                if ((next - start) >= minbits) {
                next = ocfs2_find_next_bit(bitmap, max, start);
 
                if ((next - start) >= minbits) {
-                       ret = ocfs2_trim_extent(sb, gd,
+                       ret = ocfs2_trim_extent(sb, gd, group,
                                                start, next - start);
                        if (ret < 0) {
                                mlog_errno(ret);
                                                start, next - start);
                        if (ret < 0) {
                                mlog_errno(ret);
@@ -7435,7 +7446,8 @@ int ocfs2_trim_fs(struct super_block *sb, struct fstrim_range *range)
                }
 
                gd = (struct ocfs2_group_desc *)gd_bh->b_data;
                }
 
                gd = (struct ocfs2_group_desc *)gd_bh->b_data;
-               cnt = ocfs2_trim_group(sb, gd, first_bit, last_bit, minlen);
+               cnt = ocfs2_trim_group(sb, gd, group,
+                                      first_bit, last_bit, minlen);
                brelse(gd_bh);
                gd_bh = NULL;
                if (cnt < 0) {
                brelse(gd_bh);
                gd_bh = NULL;
                if (cnt < 0) {