aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/indirect.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext4/indirect.c')
-rw-r--r--fs/ext4/indirect.c105
1 files changed, 71 insertions, 34 deletions
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
index 6b9878a24182..45fe924f82bc 100644
--- a/fs/ext4/indirect.c
+++ b/fs/ext4/indirect.c
@@ -1401,10 +1401,7 @@ end_range:
1401 * to free. Everything was covered by the start 1401 * to free. Everything was covered by the start
1402 * of the range. 1402 * of the range.
1403 */ 1403 */
1404 return 0; 1404 goto do_indirects;
1405 } else {
1406 /* Shared branch grows from an indirect block */
1407 partial2--;
1408 } 1405 }
1409 } else { 1406 } else {
1410 /* 1407 /*
@@ -1435,56 +1432,96 @@ end_range:
1435 /* Punch happened within the same level (n == n2) */ 1432 /* Punch happened within the same level (n == n2) */
1436 partial = ext4_find_shared(inode, n, offsets, chain, &nr); 1433 partial = ext4_find_shared(inode, n, offsets, chain, &nr);
1437 partial2 = ext4_find_shared(inode, n2, offsets2, chain2, &nr2); 1434 partial2 = ext4_find_shared(inode, n2, offsets2, chain2, &nr2);
1438 /* 1435
1439 * ext4_find_shared returns Indirect structure which 1436 /* Free top, but only if partial2 isn't its subtree. */
1440 * points to the last element which should not be 1437 if (nr) {
1441 * removed by truncate. But this is end of the range 1438 int level = min(partial - chain, partial2 - chain2);
1442 * in punch_hole so we need to point to the next element 1439 int i;
1443 */ 1440 int subtree = 1;
1444 partial2->p++; 1441
1445 while ((partial > chain) || (partial2 > chain2)) { 1442 for (i = 0; i <= level; i++) {
1446 /* We're at the same block, so we're almost finished */ 1443 if (offsets[i] != offsets2[i]) {
1447 if ((partial->bh && partial2->bh) && 1444 subtree = 0;
1448 (partial->bh->b_blocknr == partial2->bh->b_blocknr)) { 1445 break;
1449 if ((partial > chain) && (partial2 > chain2)) { 1446 }
1447 }
1448
1449 if (!subtree) {
1450 if (partial == chain) {
1451 /* Shared branch grows from the inode */
1452 ext4_free_branches(handle, inode, NULL,
1453 &nr, &nr+1,
1454 (chain+n-1) - partial);
1455 *partial->p = 0;
1456 } else {
1457 /* Shared branch grows from an indirect block */
1458 BUFFER_TRACE(partial->bh, "get_write_access");
1450 ext4_free_branches(handle, inode, partial->bh, 1459 ext4_free_branches(handle, inode, partial->bh,
1451 partial->p + 1, 1460 partial->p,
1452 partial2->p, 1461 partial->p+1,
1453 (chain+n-1) - partial); 1462 (chain+n-1) - partial);
1454 BUFFER_TRACE(partial->bh, "call brelse");
1455 brelse(partial->bh);
1456 BUFFER_TRACE(partial2->bh, "call brelse");
1457 brelse(partial2->bh);
1458 } 1463 }
1459 return 0;
1460 } 1464 }
1465 }
1466
1467 if (!nr2) {
1461 /* 1468 /*
1462 * Clear the ends of indirect blocks on the shared branch 1469 * ext4_find_shared returns Indirect structure which
1463 * at the start of the range 1470 * points to the last element which should not be
1471 * removed by truncate. But this is end of the range
1472 * in punch_hole so we need to point to the next element
1464 */ 1473 */
1465 if (partial > chain) { 1474 partial2->p++;
1475 }
1476
1477 while (partial > chain || partial2 > chain2) {
1478 int depth = (chain+n-1) - partial;
1479 int depth2 = (chain2+n2-1) - partial2;
1480
1481 if (partial > chain && partial2 > chain2 &&
1482 partial->bh->b_blocknr == partial2->bh->b_blocknr) {
1483 /*
1484 * We've converged on the same block. Clear the range,
1485 * then we're done.
1486 */
1466 ext4_free_branches(handle, inode, partial->bh, 1487 ext4_free_branches(handle, inode, partial->bh,
1467 partial->p + 1, 1488 partial->p + 1,
1468 (__le32 *)partial->bh->b_data+addr_per_block, 1489 partial2->p,
1469 (chain+n-1) - partial); 1490 (chain+n-1) - partial);
1470 BUFFER_TRACE(partial->bh, "call brelse"); 1491 BUFFER_TRACE(partial->bh, "call brelse");
1471 brelse(partial->bh); 1492 brelse(partial->bh);
1472 partial--; 1493 BUFFER_TRACE(partial2->bh, "call brelse");
1494 brelse(partial2->bh);
1495 return 0;
1473 } 1496 }
1497
1474 /* 1498 /*
1475 * Clear the ends of indirect blocks on the shared branch 1499 * The start and end partial branches may not be at the same
1476 * at the end of the range 1500 * level even though the punch happened within one level. So, we
1501 * give them a chance to arrive at the same level, then walk
1502 * them in step with each other until we converge on the same
1503 * block.
1477 */ 1504 */
1478 if (partial2 > chain2) { 1505 if (partial > chain && depth <= depth2) {
1506 ext4_free_branches(handle, inode, partial->bh,
1507 partial->p + 1,
1508 (__le32 *)partial->bh->b_data+addr_per_block,
1509 (chain+n-1) - partial);
1510 BUFFER_TRACE(partial->bh, "call brelse");
1511 brelse(partial->bh);
1512 partial--;
1513 }
1514 if (partial2 > chain2 && depth2 <= depth) {
1479 ext4_free_branches(handle, inode, partial2->bh, 1515 ext4_free_branches(handle, inode, partial2->bh,
1480 (__le32 *)partial2->bh->b_data, 1516 (__le32 *)partial2->bh->b_data,
1481 partial2->p, 1517 partial2->p,
1482 (chain2+n-1) - partial2); 1518 (chain2+n2-1) - partial2);
1483 BUFFER_TRACE(partial2->bh, "call brelse"); 1519 BUFFER_TRACE(partial2->bh, "call brelse");
1484 brelse(partial2->bh); 1520 brelse(partial2->bh);
1485 partial2--; 1521 partial2--;
1486 } 1522 }
1487 } 1523 }
1524 return 0;
1488 1525
1489do_indirects: 1526do_indirects:
1490 /* Kill the remaining (whole) subtrees */ 1527 /* Kill the remaining (whole) subtrees */