aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruenba@redhat.com>2017-12-18 18:06:44 -0500
committerAndreas Gruenbacher <agruenba@redhat.com>2018-01-18 15:15:57 -0500
commit10d2cf94c23d48ef1b141084216e7580011e4790 (patch)
tree2b246046c8fba5415aba59f464426c507b4d8927
parent5cf26b1e88c9eef76a1e8bdedbad48db925bbdd5 (diff)
gfs2: Turn trunc_dealloc into punch_hole
Add an upper bound to the range of blocks to deallocate blocks to function trunc_dealloc so that this function can be used for truncating a file as well as for punching a hole into a file. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Bob Peterson <rpeterso@redhat.com>
-rw-r--r--fs/gfs2/bmap.c179
1 files changed, 120 insertions, 59 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 8fd42ae026dd..f6dbd2f400cc 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -461,13 +461,6 @@ enum alloc_state {
461 /* ALLOC_UNSTUFF = 3, TBD and rather complicated */ 461 /* ALLOC_UNSTUFF = 3, TBD and rather complicated */
462}; 462};
463 463
464static inline unsigned int hptrs(struct gfs2_sbd *sdp, const unsigned int hgt)
465{
466 if (hgt)
467 return sdp->sd_inptrs;
468 return sdp->sd_diptrs;
469}
470
471/** 464/**
472 * gfs2_bmap_alloc - Build a metadata tree of the requested height 465 * gfs2_bmap_alloc - Build a metadata tree of the requested height
473 * @inode: The GFS2 inode 466 * @inode: The GFS2 inode
@@ -1243,38 +1236,48 @@ out:
1243 return ret; 1236 return ret;
1244} 1237}
1245 1238
1239static bool mp_eq_to_hgt(struct metapath *mp, __u16 *list, unsigned int h)
1240{
1241 if (memcmp(mp->mp_list, list, h * sizeof(mp->mp_list[0])))
1242 return false;
1243 return true;
1244}
1245
1246/** 1246/**
1247 * find_nonnull_ptr - find a non-null pointer given a metapath and height 1247 * find_nonnull_ptr - find a non-null pointer given a metapath and height
1248 * assumes the metapath is valid (with buffers) out to height h
1249 * @mp: starting metapath 1248 * @mp: starting metapath
1250 * @h: desired height to search 1249 * @h: desired height to search
1251 * 1250 *
1251 * Assumes the metapath is valid (with buffers) out to height h.
1252 * Returns: true if a non-null pointer was found in the metapath buffer 1252 * Returns: true if a non-null pointer was found in the metapath buffer
1253 * false if all remaining pointers are NULL in the buffer 1253 * false if all remaining pointers are NULL in the buffer
1254 */ 1254 */
1255static bool find_nonnull_ptr(struct gfs2_sbd *sdp, struct metapath *mp, 1255static bool find_nonnull_ptr(struct gfs2_sbd *sdp, struct metapath *mp,
1256 unsigned int h) 1256 unsigned int h,
1257 __u16 *end_list, unsigned int end_aligned)
1257{ 1258{
1258 __be64 *ptr; 1259 struct buffer_head *bh = mp->mp_bh[h];
1259 unsigned int ptrs = hptrs(sdp, h) - 1; 1260 __be64 *first, *ptr, *end;
1261
1262 first = metaptr1(h, mp);
1263 ptr = first + mp->mp_list[h];
1264 end = (__be64 *)(bh->b_data + bh->b_size);
1265 if (end_list && mp_eq_to_hgt(mp, end_list, h)) {
1266 bool keep_end = h < end_aligned;
1267 end = first + end_list[h] + keep_end;
1268 }
1260 1269
1261 while (true) { 1270 while (ptr < end) {
1262 ptr = metapointer(h, mp);
1263 if (*ptr) { /* if we have a non-null pointer */ 1271 if (*ptr) { /* if we have a non-null pointer */
1264 /* Now zero the metapath after the current height. */ 1272 mp->mp_list[h] = ptr - first;
1265 h++; 1273 h++;
1266 if (h < GFS2_MAX_META_HEIGHT) 1274 if (h < GFS2_MAX_META_HEIGHT)
1267 memset(&mp->mp_list[h], 0, 1275 mp->mp_list[h] = 0;
1268 (GFS2_MAX_META_HEIGHT - h) *
1269 sizeof(mp->mp_list[0]));
1270 return true; 1276 return true;
1271 } 1277 }
1272 1278 ptr++;
1273 if (mp->mp_list[h] < ptrs)
1274 mp->mp_list[h]++;
1275 else
1276 return false; /* no more pointers in this buffer */
1277 } 1279 }
1280 return false;
1278} 1281}
1279 1282
1280enum dealloc_states { 1283enum dealloc_states {
@@ -1284,16 +1287,10 @@ enum dealloc_states {
1284 DEALLOC_DONE = 3, /* process complete */ 1287 DEALLOC_DONE = 3, /* process complete */
1285}; 1288};
1286 1289
1287static bool mp_eq_to_hgt(struct metapath *mp, __u16 *list, unsigned int h)
1288{
1289 if (memcmp(mp->mp_list, list, h * sizeof(mp->mp_list[0])))
1290 return false;
1291 return true;
1292}
1293
1294static inline void 1290static inline void
1295metapointer_range(struct metapath *mp, int height, 1291metapointer_range(struct metapath *mp, int height,
1296 __u16 *start_list, unsigned int start_aligned, 1292 __u16 *start_list, unsigned int start_aligned,
1293 __u16 *end_list, unsigned int end_aligned,
1297 __be64 **start, __be64 **end) 1294 __be64 **start, __be64 **end)
1298{ 1295{
1299 struct buffer_head *bh = mp->mp_bh[height]; 1296 struct buffer_head *bh = mp->mp_bh[height];
@@ -1306,29 +1303,55 @@ metapointer_range(struct metapath *mp, int height,
1306 *start = first + start_list[height] + keep_start; 1303 *start = first + start_list[height] + keep_start;
1307 } 1304 }
1308 *end = (__be64 *)(bh->b_data + bh->b_size); 1305 *end = (__be64 *)(bh->b_data + bh->b_size);
1306 if (end_list && mp_eq_to_hgt(mp, end_list, height)) {
1307 bool keep_end = height < end_aligned;
1308 *end = first + end_list[height] + keep_end;
1309 }
1310}
1311
1312static inline bool walk_done(struct gfs2_sbd *sdp,
1313 struct metapath *mp, int height,
1314 __u16 *end_list, unsigned int end_aligned)
1315{
1316 __u16 end;
1317
1318 if (end_list) {
1319 bool keep_end = height < end_aligned;
1320 if (!mp_eq_to_hgt(mp, end_list, height))
1321 return false;
1322 end = end_list[height] + keep_end;
1323 } else
1324 end = (height > 0) ? sdp->sd_inptrs : sdp->sd_diptrs;
1325 return mp->mp_list[height] >= end;
1309} 1326}
1310 1327
1311/** 1328/**
1312 * trunc_dealloc - truncate a file down to a desired size 1329 * punch_hole - deallocate blocks in a file
1313 * @ip: inode to truncate 1330 * @ip: inode to truncate
1314 * @newsize: The desired size of the file 1331 * @offset: the start of the hole
1332 * @length: the size of the hole (or 0 for truncate)
1333 *
1334 * Punch a hole into a file or truncate a file at a given position. This
1335 * function operates in whole blocks (@offset and @length are rounded
1336 * accordingly); partially filled blocks must be cleared otherwise.
1315 * 1337 *
1316 * This function truncates a file to newsize. It works from the 1338 * This function works from the bottom up, and from the right to the left. In
1317 * bottom up, and from the right to the left. In other words, it strips off 1339 * other words, it strips off the highest layer (data) before stripping any of
1318 * the highest layer (data) before stripping any of the metadata. Doing it 1340 * the metadata. Doing it this way is best in case the operation is interrupted
1319 * this way is best in case the operation is interrupted by power failure, etc. 1341 * by power failure, etc. The dinode is rewritten in every transaction to
1320 * The dinode is rewritten in every transaction to guarantee integrity. 1342 * guarantee integrity.
1321 */ 1343 */
1322static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) 1344static int punch_hole(struct gfs2_inode *ip, u64 offset, u64 length)
1323{ 1345{
1324 struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); 1346 struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
1325 struct metapath mp; 1347 struct metapath mp = {};
1326 struct buffer_head *dibh, *bh; 1348 struct buffer_head *dibh, *bh;
1327 struct gfs2_holder rd_gh; 1349 struct gfs2_holder rd_gh;
1328 unsigned int bsize_shift = sdp->sd_sb.sb_bsize_shift; 1350 unsigned int bsize_shift = sdp->sd_sb.sb_bsize_shift;
1329 u64 lblock = (newsize + (1 << bsize_shift) - 1) >> bsize_shift; 1351 u64 lblock = (offset + (1 << bsize_shift) - 1) >> bsize_shift;
1330 __u16 start_list[GFS2_MAX_META_HEIGHT]; /* new beginning of truncation */ 1352 __u16 start_list[GFS2_MAX_META_HEIGHT];
1331 unsigned int start_aligned; 1353 __u16 __end_list[GFS2_MAX_META_HEIGHT], *end_list = NULL;
1354 unsigned int start_aligned, end_aligned;
1332 unsigned int strip_h = ip->i_height - 1; 1355 unsigned int strip_h = ip->i_height - 1;
1333 u32 btotal = 0; 1356 u32 btotal = 0;
1334 int ret, state; 1357 int ret, state;
@@ -1336,19 +1359,49 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1336 u64 prev_bnr = 0; 1359 u64 prev_bnr = 0;
1337 __be64 *start, *end; 1360 __be64 *start, *end;
1338 1361
1339 memset(&mp, 0, sizeof(mp)); 1362 /*
1340 find_metapath(sdp, lblock, &mp, ip->i_height); 1363 * The start position of the hole is defined by lblock, start_list, and
1364 * start_aligned. The end position of the hole is defined by lend,
1365 * end_list, and end_aligned.
1366 *
1367 * start_aligned and end_aligned define down to which height the start
1368 * and end positions are aligned to the metadata tree (i.e., the
1369 * position is a multiple of the metadata granularity at the height
1370 * above). This determines at which heights additional meta pointers
1371 * needs to be preserved for the remaining data.
1372 */
1373
1374 if (length) {
1375 u64 maxsize = sdp->sd_heightsize[ip->i_height];
1376 u64 end_offset = offset + length;
1377 u64 lend;
1378
1379 /*
1380 * Clip the end at the maximum file size for the given height:
1381 * that's how far the metadata goes; files bigger than that
1382 * will have additional layers of indirection.
1383 */
1384 if (end_offset > maxsize)
1385 end_offset = maxsize;
1386 lend = end_offset >> bsize_shift;
1387
1388 if (lblock >= lend)
1389 return 0;
1341 1390
1391 find_metapath(sdp, lend, &mp, ip->i_height);
1392 end_list = __end_list;
1393 memcpy(end_list, mp.mp_list, sizeof(mp.mp_list));
1394
1395 for (mp_h = ip->i_height - 1; mp_h > 0; mp_h--) {
1396 if (end_list[mp_h])
1397 break;
1398 }
1399 end_aligned = mp_h;
1400 }
1401
1402 find_metapath(sdp, lblock, &mp, ip->i_height);
1342 memcpy(start_list, mp.mp_list, sizeof(start_list)); 1403 memcpy(start_list, mp.mp_list, sizeof(start_list));
1343 1404
1344 /*
1345 * Set start_aligned to the metadata height up to which the truncate
1346 * point is aligned to the metadata tree (i.e., the truncate point is a
1347 * multiple of the granularity at the height above). This determines
1348 * at which heights an additional meta pointer needs to be preserved:
1349 * an additional meta pointer is needed at a given height if
1350 * height < start_aligned.
1351 */
1352 for (mp_h = ip->i_height - 1; mp_h > 0; mp_h--) { 1405 for (mp_h = ip->i_height - 1; mp_h > 0; mp_h--) {
1353 if (start_list[mp_h]) 1406 if (start_list[mp_h])
1354 break; 1407 break;
@@ -1367,7 +1420,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1367 /* issue read-ahead on metadata */ 1420 /* issue read-ahead on metadata */
1368 for (mp_h = 0; mp_h < mp.mp_aheight - 1; mp_h++) { 1421 for (mp_h = 0; mp_h < mp.mp_aheight - 1; mp_h++) {
1369 metapointer_range(&mp, mp_h, start_list, start_aligned, 1422 metapointer_range(&mp, mp_h, start_list, start_aligned,
1370 &start, &end); 1423 end_list, end_aligned, &start, &end);
1371 gfs2_metapath_ra(ip->i_gl, start, end); 1424 gfs2_metapath_ra(ip->i_gl, start, end);
1372 } 1425 }
1373 1426
@@ -1411,7 +1464,14 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1411 goto out; 1464 goto out;
1412 } 1465 }
1413 1466
1467 /*
1468 * Below, passing end_aligned as 0 gives us the
1469 * metapointer range excluding the end point: the end
1470 * point is the first metapath we must not deallocate!
1471 */
1472
1414 metapointer_range(&mp, mp_h, start_list, start_aligned, 1473 metapointer_range(&mp, mp_h, start_list, start_aligned,
1474 end_list, 0 /* end_aligned */,
1415 &start, &end); 1475 &start, &end);
1416 ret = sweep_bh_for_rgrps(ip, &rd_gh, mp.mp_bh[mp_h], 1476 ret = sweep_bh_for_rgrps(ip, &rd_gh, mp.mp_bh[mp_h],
1417 start, end, 1477 start, end,
@@ -1448,13 +1508,13 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1448 } 1508 }
1449 mp.mp_list[mp_h] = 0; 1509 mp.mp_list[mp_h] = 0;
1450 mp_h--; /* search one metadata height down */ 1510 mp_h--; /* search one metadata height down */
1451 if (mp.mp_list[mp_h] >= hptrs(sdp, mp_h) - 1)
1452 break; /* loop around in the same state */
1453 mp.mp_list[mp_h]++; 1511 mp.mp_list[mp_h]++;
1512 if (walk_done(sdp, &mp, mp_h, end_list, end_aligned))
1513 break;
1454 /* Here we've found a part of the metapath that is not 1514 /* Here we've found a part of the metapath that is not
1455 * allocated. We need to search at that height for the 1515 * allocated. We need to search at that height for the
1456 * next non-null pointer. */ 1516 * next non-null pointer. */
1457 if (find_nonnull_ptr(sdp, &mp, mp_h)) { 1517 if (find_nonnull_ptr(sdp, &mp, mp_h, end_list, end_aligned)) {
1458 state = DEALLOC_FILL_MP; 1518 state = DEALLOC_FILL_MP;
1459 mp_h++; 1519 mp_h++;
1460 } 1520 }
@@ -1474,6 +1534,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1474 for (; ret > 1; ret--) { 1534 for (; ret > 1; ret--) {
1475 metapointer_range(&mp, mp.mp_aheight - ret, 1535 metapointer_range(&mp, mp.mp_aheight - ret,
1476 start_list, start_aligned, 1536 start_list, start_aligned,
1537 end_list, end_aligned,
1477 &start, &end); 1538 &start, &end);
1478 gfs2_metapath_ra(ip->i_gl, start, end); 1539 gfs2_metapath_ra(ip->i_gl, start, end);
1479 } 1540 }
@@ -1490,7 +1551,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1490 /* If we find a non-null block pointer, crawl a bit 1551 /* If we find a non-null block pointer, crawl a bit
1491 higher up in the metapath and try again, otherwise 1552 higher up in the metapath and try again, otherwise
1492 we need to look lower for a new starting point. */ 1553 we need to look lower for a new starting point. */
1493 if (find_nonnull_ptr(sdp, &mp, mp_h)) 1554 if (find_nonnull_ptr(sdp, &mp, mp_h, end_list, end_aligned))
1494 mp_h++; 1555 mp_h++;
1495 else 1556 else
1496 state = DEALLOC_MP_LOWER; 1557 state = DEALLOC_MP_LOWER;
@@ -1587,7 +1648,7 @@ static int do_shrink(struct inode *inode, u64 newsize)
1587 if (gfs2_is_stuffed(ip)) 1648 if (gfs2_is_stuffed(ip))
1588 return 0; 1649 return 0;
1589 1650
1590 error = trunc_dealloc(ip, newsize); 1651 error = punch_hole(ip, newsize, 0);
1591 if (error == 0) 1652 if (error == 0)
1592 error = trunc_end(ip); 1653 error = trunc_end(ip);
1593 1654
@@ -1719,7 +1780,7 @@ out:
1719int gfs2_truncatei_resume(struct gfs2_inode *ip) 1780int gfs2_truncatei_resume(struct gfs2_inode *ip)
1720{ 1781{
1721 int error; 1782 int error;
1722 error = trunc_dealloc(ip, i_size_read(&ip->i_inode)); 1783 error = punch_hole(ip, i_size_read(&ip->i_inode), 0);
1723 if (!error) 1784 if (!error)
1724 error = trunc_end(ip); 1785 error = trunc_end(ip);
1725 return error; 1786 return error;
@@ -1727,7 +1788,7 @@ int gfs2_truncatei_resume(struct gfs2_inode *ip)
1727 1788
1728int gfs2_file_dealloc(struct gfs2_inode *ip) 1789int gfs2_file_dealloc(struct gfs2_inode *ip)
1729{ 1790{
1730 return trunc_dealloc(ip, 0); 1791 return punch_hole(ip, 0, 0);
1731} 1792}
1732 1793
1733/** 1794/**