aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruenba@redhat.com>2017-12-04 10:06:17 -0500
committerBob Peterson <rpeterso@redhat.com>2018-01-17 08:35:52 -0500
commitcb7f0903efacb7d25b844b9d321b43f228c7a37a (patch)
tree8fcec6fb485a7e67af23b7f290ebc3192ed4c777
parentc3ce5aa9b02dd912e381237e7f98d591d7358cdc (diff)
gfs2: Improve non-recursive delete algorithm
In rare cases, the current non-recursive delete algorithm doesn't deallocate empty intermediary indirect blocks. This should have very little practical effect, but deallocating all blocks correctly should still be preferable as it is cleaner and easier to validate. The fix consists of using the first block to deallocate to compute the start marker of the truncate point instead of the last block that needs to be kept. With that change, computing which indirect blocks are still needed becomes relatively easy. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Bob Peterson <rpeterso@redhat.com>
-rw-r--r--fs/gfs2/bmap.c51
1 files changed, 31 insertions, 20 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index e983b5872679..1c964def34fd 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -1078,7 +1078,7 @@ out:
1078 * @mp: current metapath fully populated with buffers 1078 * @mp: current metapath fully populated with buffers
1079 * @btotal: place to keep count of total blocks freed 1079 * @btotal: place to keep count of total blocks freed
1080 * @hgt: height we're processing 1080 * @hgt: height we're processing
1081 * @first: true if this is the first call to this function for this height 1081 * @keep_start: preserve the first meta pointer
1082 * 1082 *
1083 * We sweep a metadata buffer (provided by the metapath) for blocks we need to 1083 * We sweep a metadata buffer (provided by the metapath) for blocks we need to
1084 * free, and free them all. However, we do it one rgrp at a time. If this 1084 * free, and free them all. However, we do it one rgrp at a time. If this
@@ -1094,7 +1094,7 @@ out:
1094 */ 1094 */
1095static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh, 1095static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
1096 const struct metapath *mp, u32 *btotal, int hgt, 1096 const struct metapath *mp, u32 *btotal, int hgt,
1097 bool preserve1) 1097 bool keep_start)
1098{ 1098{
1099 struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); 1099 struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
1100 struct gfs2_rgrpd *rgd; 1100 struct gfs2_rgrpd *rgd;
@@ -1119,7 +1119,7 @@ more_rgrps:
1119 top = metapointer(hgt, mp); /* first ptr from metapath */ 1119 top = metapointer(hgt, mp); /* first ptr from metapath */
1120 /* If we're keeping some data at the truncation point, we've got to 1120 /* If we're keeping some data at the truncation point, we've got to
1121 preserve the metadata tree by adding 1 to the starting metapath. */ 1121 preserve the metadata tree by adding 1 to the starting metapath. */
1122 if (preserve1) 1122 if (keep_start)
1123 top++; 1123 top++;
1124 1124
1125 bottom = (__be64 *)(bh->b_data + bh->b_size); 1125 bottom = (__be64 *)(bh->b_data + bh->b_size);
@@ -1286,9 +1286,9 @@ enum dealloc_states {
1286 DEALLOC_DONE = 3, /* process complete */ 1286 DEALLOC_DONE = 3, /* process complete */
1287}; 1287};
1288 1288
1289static bool mp_eq_to_hgt(struct metapath *mp, __u16 *nbof, unsigned int h) 1289static bool mp_eq_to_hgt(struct metapath *mp, __u16 *list, unsigned int h)
1290{ 1290{
1291 if (memcmp(mp->mp_list, nbof, h * sizeof(mp->mp_list[0]))) 1291 if (memcmp(mp->mp_list, list, h * sizeof(mp->mp_list[0])))
1292 return false; 1292 return false;
1293 return true; 1293 return true;
1294} 1294}
@@ -1310,24 +1310,35 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1310 struct metapath mp; 1310 struct metapath mp;
1311 struct buffer_head *dibh, *bh; 1311 struct buffer_head *dibh, *bh;
1312 struct gfs2_holder rd_gh; 1312 struct gfs2_holder rd_gh;
1313 u64 lblock; 1313 unsigned int bsize_shift = sdp->sd_sb.sb_bsize_shift;
1314 __u16 nbof[GFS2_MAX_META_HEIGHT]; /* new beginning of truncation */ 1314 u64 lblock = (newsize + (1 << bsize_shift) - 1) >> bsize_shift;
1315 __u16 start_list[GFS2_MAX_META_HEIGHT]; /* new beginning of truncation */
1316 unsigned int start_aligned;
1315 unsigned int strip_h = ip->i_height - 1; 1317 unsigned int strip_h = ip->i_height - 1;
1316 u32 btotal = 0; 1318 u32 btotal = 0;
1317 int ret, state; 1319 int ret, state;
1318 int mp_h; /* metapath buffers are read in to this height */ 1320 int mp_h; /* metapath buffers are read in to this height */
1319 u64 prev_bnr = 0; 1321 u64 prev_bnr = 0;
1320 bool preserve1; /* need to preserve the first meta pointer? */ 1322 bool keep_start; /* need to preserve the first meta pointer? */
1321
1322 if (!newsize)
1323 lblock = 0;
1324 else
1325 lblock = (newsize - 1) >> sdp->sd_sb.sb_bsize_shift;
1326 1323
1327 memset(&mp, 0, sizeof(mp)); 1324 memset(&mp, 0, sizeof(mp));
1328 find_metapath(sdp, lblock, &mp, ip->i_height); 1325 find_metapath(sdp, lblock, &mp, ip->i_height);
1329 1326
1330 memcpy(&nbof, &mp.mp_list, sizeof(nbof)); 1327 memcpy(start_list, mp.mp_list, sizeof(start_list));
1328
1329 /*
1330 * Set start_aligned to the metadata height up to which the truncate
1331 * point is aligned to the metadata tree (i.e., the truncate point is a
1332 * multiple of the granularity at the height above). This determines
1333 * at which heights an additional meta pointer needs to be preserved:
1334 * an additional meta pointer is needed at a given height if
1335 * height < start_aligned.
1336 */
1337 for (mp_h = ip->i_height - 1; mp_h > 0; mp_h--) {
1338 if (start_list[mp_h])
1339 break;
1340 }
1341 start_aligned = mp_h;
1331 1342
1332 ret = gfs2_meta_inode_buffer(ip, &dibh); 1343 ret = gfs2_meta_inode_buffer(ip, &dibh);
1333 if (ret) 1344 if (ret)
@@ -1363,10 +1374,6 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1363 /* Truncate a full metapath at the given strip height. 1374 /* Truncate a full metapath at the given strip height.
1364 * Note that strip_h == mp_h in order to be in this state. */ 1375 * Note that strip_h == mp_h in order to be in this state. */
1365 case DEALLOC_MP_FULL: 1376 case DEALLOC_MP_FULL:
1366 /* If we're truncating to a non-zero size and the mp is
1367 at the beginning of file for the strip height, we
1368 need to preserve the first metadata pointer. */
1369 preserve1 = (newsize && mp_eq_to_hgt(&mp, nbof, mp_h));
1370 bh = mp.mp_bh[mp_h]; 1377 bh = mp.mp_bh[mp_h];
1371 gfs2_assert_withdraw(sdp, bh); 1378 gfs2_assert_withdraw(sdp, bh);
1372 if (gfs2_assert_withdraw(sdp, 1379 if (gfs2_assert_withdraw(sdp,
@@ -1378,8 +1385,12 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1378 prev_bnr, ip->i_height, strip_h, mp_h); 1385 prev_bnr, ip->i_height, strip_h, mp_h);
1379 } 1386 }
1380 prev_bnr = bh->b_blocknr; 1387 prev_bnr = bh->b_blocknr;
1388
1389 keep_start = mp_h < start_aligned &&
1390 mp_eq_to_hgt(&mp, start_list, mp_h);
1391
1381 ret = sweep_bh_for_rgrps(ip, &rd_gh, &mp, &btotal, 1392 ret = sweep_bh_for_rgrps(ip, &rd_gh, &mp, &btotal,
1382 mp_h, preserve1); 1393 mp_h, keep_start);
1383 /* If we hit an error or just swept dinode buffer, 1394 /* If we hit an error or just swept dinode buffer,
1384 just exit. */ 1395 just exit. */
1385 if (ret || !mp_h) { 1396 if (ret || !mp_h) {
@@ -1403,7 +1414,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1403 stripping the previous level of metadata. */ 1414 stripping the previous level of metadata. */
1404 if (mp_h == 0) { 1415 if (mp_h == 0) {
1405 strip_h--; 1416 strip_h--;
1406 memcpy(&mp.mp_list, &nbof, sizeof(nbof)); 1417 memcpy(mp.mp_list, start_list, sizeof(start_list));
1407 mp_h = strip_h; 1418 mp_h = strip_h;
1408 state = DEALLOC_FILL_MP; 1419 state = DEALLOC_FILL_MP;
1409 break; 1420 break;