diff options
author | Bob Peterson <rpeterso@redhat.com> | 2017-08-30 10:26:09 -0400 |
---|---|---|
committer | Bob Peterson <rpeterso@redhat.com> | 2017-08-30 14:29:22 -0400 |
commit | c4a9d1892f1ce6fe040b717b68bd21e689cc2410 (patch) | |
tree | 318a7c5a9566218c32e543a28b7e6e3d734f5fcf | |
parent | d296b15ed58231bd991c0fb0f3592d595539bcd1 (diff) |
GFS2: Fix non-recursive truncate bug
Before this patch if you truncated a file to a smaller size it
wasn't freeing all the blocks properly. There are two reasons.
First, the metapath comparison was not comparing previous heights.
I added a function, mp_eq_to_hgt, which checks the metapath at
all heights prior to the target height.
Second, in function find_nonnull_ptr, it needed to zero out all
pointers for heights following the target height. Translated into
decimal integer terms, this way a number like 299, when incremented,
becomes 300, not 399. The 2 gets incremented to 3, and the following
digits need to be reset.
These two things allow the truncate state machine to properly find
the blocks it needs to delete.
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
-rw-r--r-- | fs/gfs2/bmap.c | 19 |
1 files changed, 16 insertions, 3 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index fa3ea29f39cf..3dd0cceefa43 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c | |||
@@ -1104,8 +1104,15 @@ static bool find_nonnull_ptr(struct gfs2_sbd *sdp, struct metapath *mp, | |||
1104 | 1104 | ||
1105 | while (true) { | 1105 | while (true) { |
1106 | ptr = metapointer(h, mp); | 1106 | ptr = metapointer(h, mp); |
1107 | if (*ptr) /* if we have a non-null pointer */ | 1107 | if (*ptr) { /* if we have a non-null pointer */ |
1108 | /* Now zero the metapath after the current height. */ | ||
1109 | h++; | ||
1110 | if (h < GFS2_MAX_META_HEIGHT) | ||
1111 | memset(&mp->mp_list[h], 0, | ||
1112 | (GFS2_MAX_META_HEIGHT - h) * | ||
1113 | sizeof(mp->mp_list[0])); | ||
1108 | return true; | 1114 | return true; |
1115 | } | ||
1109 | 1116 | ||
1110 | if (mp->mp_list[h] < ptrs) | 1117 | if (mp->mp_list[h] < ptrs) |
1111 | mp->mp_list[h]++; | 1118 | mp->mp_list[h]++; |
@@ -1121,6 +1128,13 @@ enum dealloc_states { | |||
1121 | DEALLOC_DONE = 3, /* process complete */ | 1128 | DEALLOC_DONE = 3, /* process complete */ |
1122 | }; | 1129 | }; |
1123 | 1130 | ||
1131 | static bool mp_eq_to_hgt(struct metapath *mp, __u16 *nbof, unsigned int h) | ||
1132 | { | ||
1133 | if (memcmp(mp->mp_list, nbof, h * sizeof(mp->mp_list[0]))) | ||
1134 | return false; | ||
1135 | return true; | ||
1136 | } | ||
1137 | |||
1124 | /** | 1138 | /** |
1125 | * trunc_dealloc - truncate a file down to a desired size | 1139 | * trunc_dealloc - truncate a file down to a desired size |
1126 | * @ip: inode to truncate | 1140 | * @ip: inode to truncate |
@@ -1198,8 +1212,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) | |||
1198 | /* If we're truncating to a non-zero size and the mp is | 1212 | /* If we're truncating to a non-zero size and the mp is |
1199 | at the beginning of file for the strip height, we | 1213 | at the beginning of file for the strip height, we |
1200 | need to preserve the first metadata pointer. */ | 1214 | need to preserve the first metadata pointer. */ |
1201 | preserve1 = (newsize && | 1215 | preserve1 = (newsize && mp_eq_to_hgt(&mp, nbof, mp_h)); |
1202 | (mp.mp_list[mp_h] == nbof[mp_h])); | ||
1203 | bh = mp.mp_bh[mp_h]; | 1216 | bh = mp.mp_bh[mp_h]; |
1204 | gfs2_assert_withdraw(sdp, bh); | 1217 | gfs2_assert_withdraw(sdp, bh); |
1205 | if (gfs2_assert_withdraw(sdp, | 1218 | if (gfs2_assert_withdraw(sdp, |