aboutsummaryrefslogtreecommitdiffstats
path: root/module/zfs
diff options
context:
space:
mode:
authorLOLi <[email protected]>2017-01-14 00:47:34 +0100
committerBrian Behlendorf <[email protected]>2017-01-13 15:47:34 -0800
commit08f0510d87186575db00269fff17a3409de5ceb6 (patch)
tree21938c4d405161079020ef9d1a47c1b55d677d7b /module/zfs
parent5043684ae51195fc8b7ce7ff15241f17fe23d993 (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/zfs')
-rw-r--r--module/zfs/dmu_object.c43
-rw-r--r--module/zfs/dnode.c1
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.
*/