diff options
author | LOLi <[email protected]> | 2017-01-14 00:47:34 +0100 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2017-01-13 15:47:34 -0800 |
commit | 08f0510d87186575db00269fff17a3409de5ceb6 (patch) | |
tree | 21938c4d405161079020ef9d1a47c1b55d677d7b /module | |
parent | 5043684ae51195fc8b7ce7ff15241f17fe23d993 (diff) |
Fix unallocated object detection for large_dnode datasets
Fix dmu_object_next() to correctly handle unallocated objects on
large_dnode datasets.
We implement this by scanning the dnode block until we find the correct
offset to be used in dnode_next_offset(). This is necessary because we
can't assume *objectp is a hole even if dmu_object_info() returns
ENOENT.
This fixes a couple of issues with zfs receive on large_dnode datasets.
Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: Ned Bass <[email protected]>
Signed-off-by: loli10K <[email protected]>
Closes #5027
Closes #5532
Diffstat (limited to 'module')
-rw-r--r-- | module/zfs/dmu_object.c | 43 | ||||
-rw-r--r-- | module/zfs/dnode.c | 1 |
2 files changed, 28 insertions, 16 deletions
diff --git a/module/zfs/dmu_object.c b/module/zfs/dmu_object.c index 488ca2155..3e3efe0c1 100644 --- a/module/zfs/dmu_object.c +++ b/module/zfs/dmu_object.c @@ -237,28 +237,39 @@ int dmu_object_next(objset_t *os, uint64_t *objectp, boolean_t hole, uint64_t txg) { uint64_t offset; - dmu_object_info_t doi; + uint64_t start_obj; struct dsl_dataset *ds = os->os_dsl_dataset; - int dnodesize; int error; - /* - * Avoid expensive dnode hold if this dataset doesn't use large dnodes. - */ - if (ds && ds->ds_feature_inuse[SPA_FEATURE_LARGE_DNODE]) { - error = dmu_object_info(os, *objectp, &doi); - if (error && !(error == EINVAL && *objectp == 0)) - return (SET_ERROR(error)); - else - dnodesize = doi.doi_dnodesize; + if (*objectp == 0) { + start_obj = 1; + } else if (ds && ds->ds_feature_inuse[SPA_FEATURE_LARGE_DNODE]) { + /* + * For large_dnode datasets, scan from the beginning of the + * dnode block to find the starting offset. This is needed + * because objectp could be part of a large dnode so we can't + * assume it's a hole even if dmu_object_info() returns ENOENT. + */ + int epb = DNODE_BLOCK_SIZE >> DNODE_SHIFT; + int skip; + uint64_t i; + + for (i = *objectp & ~(epb - 1); i <= *objectp; i += skip) { + dmu_object_info_t doi; + + error = dmu_object_info(os, i, &doi); + if (error) + skip = 1; + else + skip = doi.doi_dnodesize >> DNODE_SHIFT; + } + + start_obj = i; } else { - dnodesize = DNODE_MIN_SIZE; + start_obj = *objectp + 1; } - if (*objectp == 0) - offset = 1 << DNODE_SHIFT; - else - offset = (*objectp << DNODE_SHIFT) + dnodesize; + offset = start_obj << DNODE_SHIFT; error = dnode_next_offset(DMU_META_DNODE(os), (hole ? DNODE_FIND_HOLE : 0), &offset, 0, DNODES_PER_BLOCK, txg); diff --git a/module/zfs/dnode.c b/module/zfs/dnode.c index 45bb958cd..cbd54be04 100644 --- a/module/zfs/dnode.c +++ b/module/zfs/dnode.c @@ -1184,6 +1184,7 @@ dnode_is_free(dmu_buf_impl_t *db, int idx, int slots) * errors: * EINVAL - invalid object number. * ENOSPC - hole too small to fulfill "slots" request + * ENOENT - the requested dnode is not allocated * EIO - i/o error. * succeeds even for free dnodes. */ |