diff options
Diffstat (limited to 'fs/ext4/indirect.c')
-rw-r--r-- | fs/ext4/indirect.c | 105 |
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 | ||
1489 | do_indirects: | 1526 | do_indirects: |
1490 | /* Kill the remaining (whole) subtrees */ | 1527 | /* Kill the remaining (whole) subtrees */ |