|
@@ -1401,10 +1401,7 @@ end_range:
|
|
|
* to free. Everything was covered by the start
|
|
|
* of the range.
|
|
|
*/
|
|
|
- return 0;
|
|
|
- } else {
|
|
|
- /* Shared branch grows from an indirect block */
|
|
|
- partial2--;
|
|
|
+ goto do_indirects;
|
|
|
}
|
|
|
} else {
|
|
|
/*
|
|
@@ -1435,56 +1432,96 @@ end_range:
|
|
|
/* Punch happened within the same level (n == n2) */
|
|
|
partial = ext4_find_shared(inode, n, offsets, chain, &nr);
|
|
|
partial2 = ext4_find_shared(inode, n2, offsets2, chain2, &nr2);
|
|
|
- /*
|
|
|
- * ext4_find_shared returns Indirect structure which
|
|
|
- * points to the last element which should not be
|
|
|
- * removed by truncate. But this is end of the range
|
|
|
- * in punch_hole so we need to point to the next element
|
|
|
- */
|
|
|
- partial2->p++;
|
|
|
- while ((partial > chain) || (partial2 > chain2)) {
|
|
|
- /* We're at the same block, so we're almost finished */
|
|
|
- if ((partial->bh && partial2->bh) &&
|
|
|
- (partial->bh->b_blocknr == partial2->bh->b_blocknr)) {
|
|
|
- if ((partial > chain) && (partial2 > chain2)) {
|
|
|
+
|
|
|
+ /* Free top, but only if partial2 isn't its subtree. */
|
|
|
+ if (nr) {
|
|
|
+ int level = min(partial - chain, partial2 - chain2);
|
|
|
+ int i;
|
|
|
+ int subtree = 1;
|
|
|
+
|
|
|
+ for (i = 0; i <= level; i++) {
|
|
|
+ if (offsets[i] != offsets2[i]) {
|
|
|
+ subtree = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!subtree) {
|
|
|
+ if (partial == chain) {
|
|
|
+ /* Shared branch grows from the inode */
|
|
|
+ ext4_free_branches(handle, inode, NULL,
|
|
|
+ &nr, &nr+1,
|
|
|
+ (chain+n-1) - partial);
|
|
|
+ *partial->p = 0;
|
|
|
+ } else {
|
|
|
+ /* Shared branch grows from an indirect block */
|
|
|
+ BUFFER_TRACE(partial->bh, "get_write_access");
|
|
|
ext4_free_branches(handle, inode, partial->bh,
|
|
|
- partial->p + 1,
|
|
|
- partial2->p,
|
|
|
+ partial->p,
|
|
|
+ partial->p+1,
|
|
|
(chain+n-1) - partial);
|
|
|
- BUFFER_TRACE(partial->bh, "call brelse");
|
|
|
- brelse(partial->bh);
|
|
|
- BUFFER_TRACE(partial2->bh, "call brelse");
|
|
|
- brelse(partial2->bh);
|
|
|
}
|
|
|
- return 0;
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!nr2) {
|
|
|
/*
|
|
|
- * Clear the ends of indirect blocks on the shared branch
|
|
|
- * at the start of the range
|
|
|
+ * ext4_find_shared returns Indirect structure which
|
|
|
+ * points to the last element which should not be
|
|
|
+ * removed by truncate. But this is end of the range
|
|
|
+ * in punch_hole so we need to point to the next element
|
|
|
*/
|
|
|
- if (partial > chain) {
|
|
|
+ partial2->p++;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (partial > chain || partial2 > chain2) {
|
|
|
+ int depth = (chain+n-1) - partial;
|
|
|
+ int depth2 = (chain2+n2-1) - partial2;
|
|
|
+
|
|
|
+ if (partial > chain && partial2 > chain2 &&
|
|
|
+ partial->bh->b_blocknr == partial2->bh->b_blocknr) {
|
|
|
+ /*
|
|
|
+ * We've converged on the same block. Clear the range,
|
|
|
+ * then we're done.
|
|
|
+ */
|
|
|
ext4_free_branches(handle, inode, partial->bh,
|
|
|
- partial->p + 1,
|
|
|
- (__le32 *)partial->bh->b_data+addr_per_block,
|
|
|
- (chain+n-1) - partial);
|
|
|
+ partial->p + 1,
|
|
|
+ partial2->p,
|
|
|
+ (chain+n-1) - partial);
|
|
|
BUFFER_TRACE(partial->bh, "call brelse");
|
|
|
brelse(partial->bh);
|
|
|
- partial--;
|
|
|
+ BUFFER_TRACE(partial2->bh, "call brelse");
|
|
|
+ brelse(partial2->bh);
|
|
|
+ return 0;
|
|
|
}
|
|
|
+
|
|
|
/*
|
|
|
- * Clear the ends of indirect blocks on the shared branch
|
|
|
- * at the end of the range
|
|
|
+ * The start and end partial branches may not be at the same
|
|
|
+ * level even though the punch happened within one level. So, we
|
|
|
+ * give them a chance to arrive at the same level, then walk
|
|
|
+ * them in step with each other until we converge on the same
|
|
|
+ * block.
|
|
|
*/
|
|
|
- if (partial2 > chain2) {
|
|
|
+ if (partial > chain && depth <= depth2) {
|
|
|
+ ext4_free_branches(handle, inode, partial->bh,
|
|
|
+ partial->p + 1,
|
|
|
+ (__le32 *)partial->bh->b_data+addr_per_block,
|
|
|
+ (chain+n-1) - partial);
|
|
|
+ BUFFER_TRACE(partial->bh, "call brelse");
|
|
|
+ brelse(partial->bh);
|
|
|
+ partial--;
|
|
|
+ }
|
|
|
+ if (partial2 > chain2 && depth2 <= depth) {
|
|
|
ext4_free_branches(handle, inode, partial2->bh,
|
|
|
(__le32 *)partial2->bh->b_data,
|
|
|
partial2->p,
|
|
|
- (chain2+n-1) - partial2);
|
|
|
+ (chain2+n2-1) - partial2);
|
|
|
BUFFER_TRACE(partial2->bh, "call brelse");
|
|
|
brelse(partial2->bh);
|
|
|
partial2--;
|
|
|
}
|
|
|
}
|
|
|
+ return 0;
|
|
|
|
|
|
do_indirects:
|
|
|
/* Kill the remaining (whole) subtrees */
|