dmu_traverse.c revision f76886de6cd6914424d9f6c25bd9d93d87889269
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
*/
#include <sys/zfs_context.h>
#include <sys/dmu_objset.h>
#include <sys/dmu_traverse.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_pool.h>
#include <sys/dmu_impl.h>
#include <sys/zfeature.h>
typedef struct prefetch_data {
int pd_flags;
typedef struct traverse_data {
int td_flags;
void *td_arg;
static int
{
if (BP_IS_HOLE(bp))
return (0);
return (0);
return (0);
}
static int
{
if (BP_IS_HOLE(bp))
return (0);
return (0);
}
return (0);
}
static void
{
/*
* We only want to visit blocks that have been claimed but not yet
* replayed; plus, in read-only mode, blocks that are already stable.
*/
return;
}
typedef enum resume_skip {
/*
* Returns RESUME_SKIP_ALL if td indicates that we are resuming a traversal and
* the block indicated by zb does not need to be visited at all. Returns
* RESUME_SKIP_CHILDREN if we are resuming a post traversal and we reach the
* resume point. This indicates that this block should be visited but not its
* children (since they must have been visited in a previous traversal).
* Otherwise returns RESUME_SKIP_NONE.
*/
static resume_skip_t
const zbookmark_phys_t *zb)
{
/*
* If we already visited this bp & everything below,
* don't bother doing it again.
*/
return (RESUME_SKIP_ALL);
/*
* If we found the block we're trying to resume from, zero
* the bookmark out to indicate that we have resumed.
*/
return (RESUME_SKIP_CHILDREN);
}
}
return (RESUME_SKIP_NONE);
}
static void
{
return;
/*
* If we are in the process of resuming, don't prefetch, because
* some children will not be needed (and in fact may have already
* been freed).
*/
return;
return;
return;
}
static boolean_t
{
return (B_FALSE);
return (B_TRUE);
}
static int
{
int err = 0;
case RESUME_SKIP_ALL:
return (0);
case RESUME_SKIP_CHILDREN:
goto post;
case RESUME_SKIP_NONE:
break;
default:
ASSERT(0);
}
/*
* Since this block has a birth time of 0 it must be one of
* two things: a hole created before the
* SPA_FEATURE_HOLE_BIRTH feature was enabled, or a hole
* which has always been a hole in an object.
*
* If a file is written sparsely, then the unwritten parts of
* the file were "always holes" -- that is, they have been
* holes since this object was allocated. However, we (and
* our callers) can not necessarily tell when an object was
* allocated. Therefore, if it's possible that this object
* was freed and then its object number reused, we need to
* visit all the holes with birth==0.
*
* If it isn't possible that the object number was reused,
* then if SPA_FEATURE_HOLE_BIRTH was enabled before we wrote
* all the blocks we will visit as part of this traversal,
* then this hole must have always existed, so we can skip
* it. We visit blocks born after (exclusive) td_min_txg.
*
* Note that the meta-dnode cannot be reallocated.
*/
if (!send_holes_without_birth_time &&
(!td->td_realloc_possible ||
return (0);
return (0);
}
}
if (BP_IS_HOLE(bp)) {
if (err != 0)
goto post;
return (0);
}
if (err == TRAVERSE_VISIT_NO_CHILDREN)
return (0);
if (err != 0)
goto post;
}
if (BP_GET_LEVEL(bp) > 0) {
int i;
if (err != 0)
goto post;
for (i = 0; i < epb; i++) {
}
/* recursively visitbp() blocks below this */
for (i = 0; i < epb; i++) {
if (err != 0)
break;
}
int i;
if (err != 0)
goto post;
for (i = 0; i < epb; i++) {
}
/* recursively visitbp() blocks below this */
for (i = 0; i < epb; i++) {
if (err != 0)
break;
}
if (err != 0)
goto post;
/*
* See the block comment above for the goal of this variable.
* If the maxblkid of the meta-dnode is 0, then we know that
* we've never had more than DNODES_PER_BLOCK objects in the
* dataset, which means we can't have reused any object ids.
*/
}
}
}
}
if (buf)
post:
/*
* Ignore this disk error as requested by the HARD flag,
* and continue traversal.
*/
err = 0;
}
/*
* If we are stopping here, set td_resume.
*/
/*
* If we have stopped on an indirect block (e.g. due to
* i/o error), we have not visited anything below it.
* Set the bookmark to the first level-0 block that we need
* to visit. This way, the resuming code does not need to
* deal with resuming from indirect blocks.
*
* Note, if zb_level <= 0, dnp may be NULL, so we don't want
* to dereference it.
*/
}
}
return (err);
}
static void
{
int j;
for (j = 0; j < dnp->dn_nblkptr; j++) {
}
}
}
static int
{
int j, err = 0;
return (0);
if (err == TRAVERSE_VISIT_NO_CHILDREN)
return (0);
if (err != 0)
return (err);
}
for (j = 0; j < dnp->dn_nblkptr; j++) {
if (err != 0)
break;
}
}
if (err == TRAVERSE_VISIT_NO_CHILDREN)
return (0);
if (err != 0)
return (err);
}
return (err);
}
/* ARGSUSED */
static int
{
return (0);
return (0);
return (0);
}
static void
traverse_prefetch_thread(void *arg)
{
}
/*
* NB: dataset must not be changing on-disk (eg, is a snapshot or we are
* in syncing context).
*/
static int
{
prefetch_data_t pd = { 0 };
int err;
} else {
}
/* See comment on ZIL traversal in dsl_scan_visitds. */
if (err != 0)
return (err);
}
if (!(flags & TRAVERSE_PREFETCH_DATA) ||
&td, TQ_NOQUEUE))
return (err);
}
/*
* NB: dataset must not be changing on-disk (eg, is a snapshot or we are
* in syncing context).
*/
int
{
}
int
{
}
int
{
}
/*
* NB: pool must not be changing on-disk (eg, from zdb or sync context).
*/
int
{
int err;
/* visit the MOS */
if (err != 0)
return (err);
/* visit each dataset */
if (err != 0) {
if (hard)
continue;
break;
}
if (err != 0) {
if (hard)
continue;
break;
}
if (err != 0)
break;
}
}
err = 0;
return (err);
}