diff options
author | mm <mm@FreeBSD.org> | 2010-06-03 11:08:46 +0000 |
---|---|---|
committer | mm <mm@FreeBSD.org> | 2010-06-03 11:08:46 +0000 |
commit | df50b677c079b9f0a5637f9e2c1ab9e07ed0823e (patch) | |
tree | 2697915a6d3ad962af8f9ac9c1b2e8857fbf65e4 /sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c | |
parent | 881c9b1a5c2a6aadd5230cc3aa0a496454289511 (diff) | |
download | FreeBSD-src-df50b677c079b9f0a5637f9e2c1ab9e07ed0823e.zip FreeBSD-src-df50b677c079b9f0a5637f9e2c1ab9e07ed0823e.tar.gz |
Fix freeing space after deleting large files with holes.
OpenSolaris onnv revision: 9950:78fc41aa9bc5
Approved by: pjd, delphij (mentor)
Obtained from: OpenSolaris (Bug ID 6792701)
MFC after: 3 days
Diffstat (limited to 'sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c')
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c | 36 |
1 files changed, 30 insertions, 6 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c index 731e064..5f64116 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c @@ -1239,6 +1239,22 @@ dnode_willuse_space(dnode_t *dn, int64_t space, dmu_tx_t *tx) dmu_tx_willuse_space(tx, space); } +/* + * This function scans a block at the indicated "level" looking for + * a hole or data (depending on 'flags'). If level > 0, then we are + * scanning an indirect block looking at its pointers. If level == 0, + * then we are looking at a block of dnodes. If we don't find what we + * are looking for in the block, we return ESRCH. Otherwise, return + * with *offset pointing to the beginning (if searching forwards) or + * end (if searching backwards) of the range covered by the block + * pointer we matched on (or dnode). + * + * The basic search algorithm used below by dnode_next_offset() is to + * use this function to search up the block tree (widen the search) until + * we find something (i.e., we don't return ESRCH) and then search back + * down the tree (narrow the search) until we reach our original search + * level. + */ static int dnode_next_offset_level(dnode_t *dn, int flags, uint64_t *offset, int lvl, uint64_t blkfill, uint64_t txg) @@ -1318,6 +1334,7 @@ dnode_next_offset_level(dnode_t *dn, int flags, uint64_t *offset, error = ESRCH; } else { blkptr_t *bp = data; + uint64_t start = *offset; span = (lvl - 1) * epbs + dn->dn_datablkshift; minfill = 0; maxfill = blkfill << ((lvl - 1) * epbs); @@ -1327,18 +1344,25 @@ dnode_next_offset_level(dnode_t *dn, int flags, uint64_t *offset, else minfill++; - for (i = (*offset >> span) & ((1ULL << epbs) - 1); + *offset = *offset >> span; + for (i = BF64_GET(*offset, 0, epbs); i >= 0 && i < epb; i += inc) { if (bp[i].blk_fill >= minfill && bp[i].blk_fill <= maxfill && (hole || bp[i].blk_birth > txg)) break; - if (inc < 0 && *offset < (1ULL << span)) - *offset = 0; - else - *offset += (1ULL << span) * inc; + if (inc > 0 || *offset > 0) + *offset += inc; + } + *offset = *offset << span; + if (inc < 0) { + /* traversing backwards; position offset at the end */ + ASSERT3U(*offset, <=, start); + *offset = MIN(*offset + (1ULL << span) - 1, start); + } else if (*offset < start) { + *offset = start; } - if (i < 0 || i == epb) + if (i < 0 || i >= epb) error = ESRCH; } |