diff options
author | Andreas Gruenbacher <agruenba@redhat.com> | 2017-12-18 18:06:44 -0500 |
---|---|---|
committer | Andreas Gruenbacher <agruenba@redhat.com> | 2018-01-18 15:15:57 -0500 |
commit | 10d2cf94c23d48ef1b141084216e7580011e4790 (patch) | |
tree | 2b246046c8fba5415aba59f464426c507b4d8927 | |
parent | 5cf26b1e88c9eef76a1e8bdedbad48db925bbdd5 (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.c | 179 |
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 | ||
464 | static 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 | ||
1239 | static 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 | */ |
1255 | static bool find_nonnull_ptr(struct gfs2_sbd *sdp, struct metapath *mp, | 1255 | static 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 | ||
1280 | enum dealloc_states { | 1283 | enum 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 | ||
1287 | static 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 | |||
1294 | static inline void | 1290 | static inline void |
1295 | metapointer_range(struct metapath *mp, int height, | 1291 | metapointer_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 | |||
1312 | static 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 | */ |
1322 | static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) | 1344 | static 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: | |||
1719 | int gfs2_truncatei_resume(struct gfs2_inode *ip) | 1780 | int 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 | ||
1728 | int gfs2_file_dealloc(struct gfs2_inode *ip) | 1789 | int 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 | /** |