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