FreeBSD ZFS
The Zettabyte File System
|
00001 /* 00002 * CDDL HEADER START 00003 * 00004 * The contents of this file are subject to the terms of the 00005 * Common Development and Distribution License (the "License"). 00006 * You may not use this file except in compliance with the License. 00007 * 00008 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 00009 * or http://www.opensolaris.org/os/licensing. 00010 * See the License for the specific language governing permissions 00011 * and limitations under the License. 00012 * 00013 * When distributing Covered Code, include this CDDL HEADER in each 00014 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 00015 * If applicable, add the following below this CDDL HEADER, with the 00016 * fields enclosed by brackets "[]" replaced with your own identifying 00017 * information: Portions Copyright [yyyy] [name of copyright owner] 00018 * 00019 * CDDL HEADER END 00020 */ 00021 /* 00022 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 00023 * Copyright (c) 2012 by Delphix. All rights reserved. 00024 */ 00025 00026 #include <sys/zfs_context.h> 00027 #include <sys/dmu_objset.h> 00028 #include <sys/dmu_traverse.h> 00029 #include <sys/dsl_dataset.h> 00030 #include <sys/dsl_dir.h> 00031 #include <sys/dsl_pool.h> 00032 #include <sys/dnode.h> 00033 #include <sys/spa.h> 00034 #include <sys/zio.h> 00035 #include <sys/dmu_impl.h> 00036 #include <sys/sa.h> 00037 #include <sys/sa_impl.h> 00038 #include <sys/callb.h> 00039 00040 int zfs_pd_blks_max = 100; 00041 00042 typedef struct prefetch_data { 00043 kmutex_t pd_mtx; 00044 kcondvar_t pd_cv; 00045 int pd_blks_max; 00046 int pd_blks_fetched; 00047 int pd_flags; 00048 boolean_t pd_cancel; 00049 boolean_t pd_exited; 00050 } prefetch_data_t; 00051 00052 typedef struct traverse_data { 00053 spa_t *td_spa; 00054 uint64_t td_objset; 00055 blkptr_t *td_rootbp; 00056 uint64_t td_min_txg; 00057 zbookmark_t *td_resume; 00058 int td_flags; 00059 prefetch_data_t *td_pfd; 00060 blkptr_cb_t *td_func; 00061 void *td_arg; 00062 } traverse_data_t; 00063 00064 static int traverse_dnode(traverse_data_t *td, const dnode_phys_t *dnp, 00065 arc_buf_t *buf, uint64_t objset, uint64_t object); 00066 static void prefetch_dnode_metadata(traverse_data_t *td, const dnode_phys_t *, 00067 arc_buf_t *buf, uint64_t objset, uint64_t object); 00068 00069 static int 00070 traverse_zil_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg) 00071 { 00072 traverse_data_t *td = arg; 00073 zbookmark_t zb; 00074 00075 if (bp->blk_birth == 0) 00076 return (0); 00077 00078 if (claim_txg == 0 && bp->blk_birth >= spa_first_txg(td->td_spa)) 00079 return (0); 00080 00081 SET_BOOKMARK(&zb, td->td_objset, ZB_ZIL_OBJECT, ZB_ZIL_LEVEL, 00082 bp->blk_cksum.zc_word[ZIL_ZC_SEQ]); 00083 00084 (void) td->td_func(td->td_spa, zilog, bp, NULL, &zb, NULL, td->td_arg); 00085 00086 return (0); 00087 } 00088 00089 static int 00090 traverse_zil_record(zilog_t *zilog, lr_t *lrc, void *arg, uint64_t claim_txg) 00091 { 00092 traverse_data_t *td = arg; 00093 00094 if (lrc->lrc_txtype == TX_WRITE) { 00095 lr_write_t *lr = (lr_write_t *)lrc; 00096 blkptr_t *bp = &lr->lr_blkptr; 00097 zbookmark_t zb; 00098 00099 if (bp->blk_birth == 0) 00100 return (0); 00101 00102 if (claim_txg == 0 || bp->blk_birth < claim_txg) 00103 return (0); 00104 00105 SET_BOOKMARK(&zb, td->td_objset, lr->lr_foid, 00106 ZB_ZIL_LEVEL, lr->lr_offset / BP_GET_LSIZE(bp)); 00107 00108 (void) td->td_func(td->td_spa, zilog, bp, NULL, &zb, NULL, 00109 td->td_arg); 00110 } 00111 return (0); 00112 } 00113 00114 static void 00115 traverse_zil(traverse_data_t *td, zil_header_t *zh) 00116 { 00117 uint64_t claim_txg = zh->zh_claim_txg; 00118 zilog_t *zilog; 00119 00120 /* 00121 * We only want to visit blocks that have been claimed but not yet 00122 * replayed; plus, in read-only mode, blocks that are already stable. 00123 */ 00124 if (claim_txg == 0 && spa_writeable(td->td_spa)) 00125 return; 00126 00127 zilog = zil_alloc(spa_get_dsl(td->td_spa)->dp_meta_objset, zh); 00128 00129 (void) zil_parse(zilog, traverse_zil_block, traverse_zil_record, td, 00130 claim_txg); 00131 00132 zil_free(zilog); 00133 } 00134 00135 typedef enum resume_skip { 00136 RESUME_SKIP_ALL, 00137 RESUME_SKIP_NONE, 00138 RESUME_SKIP_CHILDREN 00139 } resume_skip_t; 00140 00152 static resume_skip_t 00153 resume_skip_check(traverse_data_t *td, const dnode_phys_t *dnp, 00154 const zbookmark_t *zb) 00155 { 00156 if (td->td_resume != NULL && !ZB_IS_ZERO(td->td_resume)) { 00157 /* 00158 * If we already visited this bp & everything below, 00159 * don't bother doing it again. 00160 */ 00161 if (zbookmark_is_before(dnp, zb, td->td_resume)) 00162 return (RESUME_SKIP_ALL); 00163 00164 /* 00165 * If we found the block we're trying to resume from, zero 00166 * the bookmark out to indicate that we have resumed. 00167 */ 00168 ASSERT3U(zb->zb_object, <=, td->td_resume->zb_object); 00169 if (bcmp(zb, td->td_resume, sizeof (*zb)) == 0) { 00170 bzero(td->td_resume, sizeof (*zb)); 00171 if (td->td_flags & TRAVERSE_POST) 00172 return (RESUME_SKIP_CHILDREN); 00173 } 00174 } 00175 return (RESUME_SKIP_NONE); 00176 } 00177 00178 static void 00179 traverse_pause(traverse_data_t *td, const zbookmark_t *zb) 00180 { 00181 ASSERT(td->td_resume != NULL); 00182 ASSERT0(zb->zb_level); 00183 bcopy(zb, td->td_resume, sizeof (*td->td_resume)); 00184 } 00185 00186 static void 00187 traverse_prefetch_metadata(traverse_data_t *td, 00188 arc_buf_t *pbuf, const blkptr_t *bp, const zbookmark_t *zb) 00189 { 00190 uint32_t flags = ARC_NOWAIT | ARC_PREFETCH; 00191 00192 if (!(td->td_flags & TRAVERSE_PREFETCH_METADATA)) 00193 return; 00194 /* 00195 * If we are in the process of resuming, don't prefetch, because 00196 * some children will not be needed (and in fact may have already 00197 * been freed). 00198 */ 00199 if (td->td_resume != NULL && !ZB_IS_ZERO(td->td_resume)) 00200 return; 00201 if (BP_IS_HOLE(bp) || bp->blk_birth <= td->td_min_txg) 00202 return; 00203 if (BP_GET_LEVEL(bp) == 0 && BP_GET_TYPE(bp) != DMU_OT_DNODE) 00204 return; 00205 00206 (void) arc_read(NULL, td->td_spa, bp, 00207 pbuf, NULL, NULL, ZIO_PRIORITY_ASYNC_READ, 00208 ZIO_FLAG_CANFAIL, &flags, zb); 00209 } 00210 00211 static int 00212 traverse_visitbp(traverse_data_t *td, const dnode_phys_t *dnp, 00213 arc_buf_t *pbuf, const blkptr_t *bp, const zbookmark_t *zb) 00214 { 00215 zbookmark_t czb; 00216 int err = 0, lasterr = 0; 00217 arc_buf_t *buf = NULL; 00218 prefetch_data_t *pd = td->td_pfd; 00219 boolean_t hard = td->td_flags & TRAVERSE_HARD; 00220 boolean_t pause = B_FALSE; 00221 00222 switch (resume_skip_check(td, dnp, zb)) { 00223 case RESUME_SKIP_ALL: 00224 return (0); 00225 case RESUME_SKIP_CHILDREN: 00226 goto post; 00227 case RESUME_SKIP_NONE: 00228 break; 00229 default: 00230 ASSERT(0); 00231 } 00232 00233 if (BP_IS_HOLE(bp)) { 00234 err = td->td_func(td->td_spa, NULL, NULL, pbuf, zb, dnp, 00235 td->td_arg); 00236 return (err); 00237 } 00238 00239 if (bp->blk_birth <= td->td_min_txg) 00240 return (0); 00241 00242 if (pd && !pd->pd_exited && 00243 ((pd->pd_flags & TRAVERSE_PREFETCH_DATA) || 00244 BP_GET_TYPE(bp) == DMU_OT_DNODE || BP_GET_LEVEL(bp) > 0)) { 00245 mutex_enter(&pd->pd_mtx); 00246 ASSERT(pd->pd_blks_fetched >= 0); 00247 while (pd->pd_blks_fetched == 0 && !pd->pd_exited) 00248 cv_wait(&pd->pd_cv, &pd->pd_mtx); 00249 pd->pd_blks_fetched--; 00250 cv_broadcast(&pd->pd_cv); 00251 mutex_exit(&pd->pd_mtx); 00252 } 00253 00254 if (td->td_flags & TRAVERSE_PRE) { 00255 err = td->td_func(td->td_spa, NULL, bp, pbuf, zb, dnp, 00256 td->td_arg); 00257 if (err == TRAVERSE_VISIT_NO_CHILDREN) 00258 return (0); 00259 if (err == ERESTART) 00260 pause = B_TRUE; /* handle pausing at a common point */ 00261 if (err != 0) 00262 goto post; 00263 } 00264 00265 if (BP_GET_LEVEL(bp) > 0) { 00266 uint32_t flags = ARC_WAIT; 00267 int i; 00268 blkptr_t *cbp; 00269 int epb = BP_GET_LSIZE(bp) >> SPA_BLKPTRSHIFT; 00270 00271 err = dsl_read(NULL, td->td_spa, bp, pbuf, 00272 arc_getbuf_func, &buf, 00273 ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb); 00274 if (err) 00275 return (err); 00276 cbp = buf->b_data; 00277 00278 for (i = 0; i < epb; i++) { 00279 SET_BOOKMARK(&czb, zb->zb_objset, zb->zb_object, 00280 zb->zb_level - 1, 00281 zb->zb_blkid * epb + i); 00282 traverse_prefetch_metadata(td, buf, &cbp[i], &czb); 00283 } 00284 00285 /* recursively visitbp() blocks below this */ 00286 for (i = 0; i < epb; i++) { 00287 SET_BOOKMARK(&czb, zb->zb_objset, zb->zb_object, 00288 zb->zb_level - 1, 00289 zb->zb_blkid * epb + i); 00290 err = traverse_visitbp(td, dnp, buf, &cbp[i], &czb); 00291 if (err) { 00292 if (!hard) 00293 break; 00294 lasterr = err; 00295 } 00296 } 00297 } else if (BP_GET_TYPE(bp) == DMU_OT_DNODE) { 00298 uint32_t flags = ARC_WAIT; 00299 int i; 00300 int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT; 00301 00302 err = dsl_read(NULL, td->td_spa, bp, pbuf, 00303 arc_getbuf_func, &buf, 00304 ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb); 00305 if (err) 00306 return (err); 00307 dnp = buf->b_data; 00308 00309 for (i = 0; i < epb; i++) { 00310 prefetch_dnode_metadata(td, &dnp[i], buf, zb->zb_objset, 00311 zb->zb_blkid * epb + i); 00312 } 00313 00314 /* recursively visitbp() blocks below this */ 00315 for (i = 0; i < epb; i++) { 00316 err = traverse_dnode(td, &dnp[i], buf, zb->zb_objset, 00317 zb->zb_blkid * epb + i); 00318 if (err) { 00319 if (!hard) 00320 break; 00321 lasterr = err; 00322 } 00323 } 00324 } else if (BP_GET_TYPE(bp) == DMU_OT_OBJSET) { 00325 uint32_t flags = ARC_WAIT; 00326 objset_phys_t *osp; 00327 dnode_phys_t *dnp; 00328 00329 err = dsl_read_nolock(NULL, td->td_spa, bp, 00330 arc_getbuf_func, &buf, 00331 ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb); 00332 if (err) 00333 return (err); 00334 00335 osp = buf->b_data; 00336 dnp = &osp->os_meta_dnode; 00337 prefetch_dnode_metadata(td, dnp, buf, zb->zb_objset, 00338 DMU_META_DNODE_OBJECT); 00339 if (arc_buf_size(buf) >= sizeof (objset_phys_t)) { 00340 prefetch_dnode_metadata(td, &osp->os_userused_dnode, 00341 buf, zb->zb_objset, DMU_USERUSED_OBJECT); 00342 prefetch_dnode_metadata(td, &osp->os_groupused_dnode, 00343 buf, zb->zb_objset, DMU_USERUSED_OBJECT); 00344 } 00345 00346 err = traverse_dnode(td, dnp, buf, zb->zb_objset, 00347 DMU_META_DNODE_OBJECT); 00348 if (err && hard) { 00349 lasterr = err; 00350 err = 0; 00351 } 00352 if (err == 0 && arc_buf_size(buf) >= sizeof (objset_phys_t)) { 00353 dnp = &osp->os_userused_dnode; 00354 err = traverse_dnode(td, dnp, buf, zb->zb_objset, 00355 DMU_USERUSED_OBJECT); 00356 } 00357 if (err && hard) { 00358 lasterr = err; 00359 err = 0; 00360 } 00361 if (err == 0 && arc_buf_size(buf) >= sizeof (objset_phys_t)) { 00362 dnp = &osp->os_groupused_dnode; 00363 err = traverse_dnode(td, dnp, buf, zb->zb_objset, 00364 DMU_GROUPUSED_OBJECT); 00365 } 00366 } 00367 00368 if (buf) 00369 (void) arc_buf_remove_ref(buf, &buf); 00370 00371 post: 00372 if (err == 0 && lasterr == 0 && (td->td_flags & TRAVERSE_POST)) { 00373 err = td->td_func(td->td_spa, NULL, bp, pbuf, zb, dnp, 00374 td->td_arg); 00375 if (err == ERESTART) 00376 pause = B_TRUE; 00377 } 00378 00379 if (pause && td->td_resume != NULL) { 00380 ASSERT3U(err, ==, ERESTART); 00381 ASSERT(!hard); 00382 traverse_pause(td, zb); 00383 } 00384 00385 return (err != 0 ? err : lasterr); 00386 } 00387 00388 static void 00389 prefetch_dnode_metadata(traverse_data_t *td, const dnode_phys_t *dnp, 00390 arc_buf_t *buf, uint64_t objset, uint64_t object) 00391 { 00392 int j; 00393 zbookmark_t czb; 00394 00395 for (j = 0; j < dnp->dn_nblkptr; j++) { 00396 SET_BOOKMARK(&czb, objset, object, dnp->dn_nlevels - 1, j); 00397 traverse_prefetch_metadata(td, buf, &dnp->dn_blkptr[j], &czb); 00398 } 00399 00400 if (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) { 00401 SET_BOOKMARK(&czb, objset, object, 0, DMU_SPILL_BLKID); 00402 traverse_prefetch_metadata(td, buf, &dnp->dn_spill, &czb); 00403 } 00404 } 00405 00406 static int 00407 traverse_dnode(traverse_data_t *td, const dnode_phys_t *dnp, 00408 arc_buf_t *buf, uint64_t objset, uint64_t object) 00409 { 00410 int j, err = 0, lasterr = 0; 00411 zbookmark_t czb; 00412 boolean_t hard = (td->td_flags & TRAVERSE_HARD); 00413 00414 for (j = 0; j < dnp->dn_nblkptr; j++) { 00415 SET_BOOKMARK(&czb, objset, object, dnp->dn_nlevels - 1, j); 00416 err = traverse_visitbp(td, dnp, buf, &dnp->dn_blkptr[j], &czb); 00417 if (err) { 00418 if (!hard) 00419 break; 00420 lasterr = err; 00421 } 00422 } 00423 00424 if (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) { 00425 SET_BOOKMARK(&czb, objset, object, 0, DMU_SPILL_BLKID); 00426 err = traverse_visitbp(td, dnp, buf, &dnp->dn_spill, &czb); 00427 if (err) { 00428 if (!hard) 00429 return (err); 00430 lasterr = err; 00431 } 00432 } 00433 return (err != 0 ? err : lasterr); 00434 } 00435 00436 /* ARGSUSED */ 00437 static int 00438 traverse_prefetcher(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 00439 arc_buf_t *pbuf, const zbookmark_t *zb, const dnode_phys_t *dnp, 00440 void *arg) 00441 { 00442 prefetch_data_t *pfd = arg; 00443 uint32_t aflags = ARC_NOWAIT | ARC_PREFETCH; 00444 00445 ASSERT(pfd->pd_blks_fetched >= 0); 00446 if (pfd->pd_cancel) 00447 return (EINTR); 00448 00449 if (bp == NULL || !((pfd->pd_flags & TRAVERSE_PREFETCH_DATA) || 00450 BP_GET_TYPE(bp) == DMU_OT_DNODE || BP_GET_LEVEL(bp) > 0) || 00451 BP_GET_TYPE(bp) == DMU_OT_INTENT_LOG) 00452 return (0); 00453 00454 mutex_enter(&pfd->pd_mtx); 00455 while (!pfd->pd_cancel && pfd->pd_blks_fetched >= pfd->pd_blks_max) 00456 cv_wait(&pfd->pd_cv, &pfd->pd_mtx); 00457 pfd->pd_blks_fetched++; 00458 cv_broadcast(&pfd->pd_cv); 00459 mutex_exit(&pfd->pd_mtx); 00460 00461 (void) dsl_read(NULL, spa, bp, pbuf, NULL, NULL, 00462 ZIO_PRIORITY_ASYNC_READ, 00463 ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE, 00464 &aflags, zb); 00465 00466 return (0); 00467 } 00468 00469 static void 00470 traverse_prefetch_thread(void *arg) 00471 { 00472 traverse_data_t *td_main = arg; 00473 traverse_data_t td = *td_main; 00474 zbookmark_t czb; 00475 00476 td.td_func = traverse_prefetcher; 00477 td.td_arg = td_main->td_pfd; 00478 td.td_pfd = NULL; 00479 00480 SET_BOOKMARK(&czb, td.td_objset, 00481 ZB_ROOT_OBJECT, ZB_ROOT_LEVEL, ZB_ROOT_BLKID); 00482 (void) traverse_visitbp(&td, NULL, NULL, td.td_rootbp, &czb); 00483 00484 mutex_enter(&td_main->td_pfd->pd_mtx); 00485 td_main->td_pfd->pd_exited = B_TRUE; 00486 cv_broadcast(&td_main->td_pfd->pd_cv); 00487 mutex_exit(&td_main->td_pfd->pd_mtx); 00488 } 00489 00494 static int 00495 traverse_impl(spa_t *spa, dsl_dataset_t *ds, uint64_t objset, blkptr_t *rootbp, 00496 uint64_t txg_start, zbookmark_t *resume, int flags, 00497 blkptr_cb_t func, void *arg) 00498 { 00499 traverse_data_t td; 00500 prefetch_data_t pd = { 0 }; 00501 zbookmark_t czb; 00502 int err; 00503 00504 ASSERT(ds == NULL || objset == ds->ds_object); 00505 ASSERT(!(flags & TRAVERSE_PRE) || !(flags & TRAVERSE_POST)); 00506 00507 /* 00508 * The data prefetching mechanism (the prefetch thread) is incompatible 00509 * with resuming from a bookmark. 00510 */ 00511 ASSERT(resume == NULL || !(flags & TRAVERSE_PREFETCH_DATA)); 00512 00513 td.td_spa = spa; 00514 td.td_objset = objset; 00515 td.td_rootbp = rootbp; 00516 td.td_min_txg = txg_start; 00517 td.td_resume = resume; 00518 td.td_func = func; 00519 td.td_arg = arg; 00520 td.td_pfd = &pd; 00521 td.td_flags = flags; 00522 00523 pd.pd_blks_max = zfs_pd_blks_max; 00524 pd.pd_flags = flags; 00525 mutex_init(&pd.pd_mtx, NULL, MUTEX_DEFAULT, NULL); 00526 cv_init(&pd.pd_cv, NULL, CV_DEFAULT, NULL); 00527 00528 /* See comment on ZIL traversal in dsl_scan_visitds. */ 00529 if (ds != NULL && !dsl_dataset_is_snapshot(ds)) { 00530 objset_t *os; 00531 00532 err = dmu_objset_from_ds(ds, &os); 00533 if (err) 00534 return (err); 00535 00536 traverse_zil(&td, &os->os_zil_header); 00537 } 00538 00539 if (!(flags & TRAVERSE_PREFETCH_DATA) || 00540 0 == taskq_dispatch(system_taskq, traverse_prefetch_thread, 00541 &td, TQ_NOQUEUE)) 00542 pd.pd_exited = B_TRUE; 00543 00544 SET_BOOKMARK(&czb, td.td_objset, 00545 ZB_ROOT_OBJECT, ZB_ROOT_LEVEL, ZB_ROOT_BLKID); 00546 err = traverse_visitbp(&td, NULL, NULL, rootbp, &czb); 00547 00548 mutex_enter(&pd.pd_mtx); 00549 pd.pd_cancel = B_TRUE; 00550 cv_broadcast(&pd.pd_cv); 00551 while (!pd.pd_exited) 00552 cv_wait(&pd.pd_cv, &pd.pd_mtx); 00553 mutex_exit(&pd.pd_mtx); 00554 00555 mutex_destroy(&pd.pd_mtx); 00556 cv_destroy(&pd.pd_cv); 00557 00558 return (err); 00559 } 00560 00565 int 00566 traverse_dataset(dsl_dataset_t *ds, uint64_t txg_start, int flags, 00567 blkptr_cb_t func, void *arg) 00568 { 00569 return (traverse_impl(ds->ds_dir->dd_pool->dp_spa, ds, ds->ds_object, 00570 &ds->ds_phys->ds_bp, txg_start, NULL, flags, func, arg)); 00571 } 00572 00573 int 00574 traverse_dataset_destroyed(spa_t *spa, blkptr_t *blkptr, 00575 uint64_t txg_start, zbookmark_t *resume, int flags, 00576 blkptr_cb_t func, void *arg) 00577 { 00578 return (traverse_impl(spa, NULL, ZB_DESTROYED_OBJSET, 00579 blkptr, txg_start, resume, flags, func, arg)); 00580 } 00581 00585 int 00586 traverse_pool(spa_t *spa, uint64_t txg_start, int flags, 00587 blkptr_cb_t func, void *arg) 00588 { 00589 int err, lasterr = 0; 00590 uint64_t obj; 00591 dsl_pool_t *dp = spa_get_dsl(spa); 00592 objset_t *mos = dp->dp_meta_objset; 00593 boolean_t hard = (flags & TRAVERSE_HARD); 00594 00595 /* visit the MOS */ 00596 err = traverse_impl(spa, NULL, 0, spa_get_rootblkptr(spa), 00597 txg_start, NULL, flags, func, arg); 00598 if (err) 00599 return (err); 00600 00601 /* visit each dataset */ 00602 for (obj = 1; err == 0 || (err != ESRCH && hard); 00603 err = dmu_object_next(mos, &obj, FALSE, txg_start)) { 00604 dmu_object_info_t doi; 00605 00606 err = dmu_object_info(mos, obj, &doi); 00607 if (err) { 00608 if (!hard) 00609 return (err); 00610 lasterr = err; 00611 continue; 00612 } 00613 00614 if (doi.doi_type == DMU_OT_DSL_DATASET) { 00615 dsl_dataset_t *ds; 00616 uint64_t txg = txg_start; 00617 00618 rw_enter(&dp->dp_config_rwlock, RW_READER); 00619 err = dsl_dataset_hold_obj(dp, obj, FTAG, &ds); 00620 rw_exit(&dp->dp_config_rwlock); 00621 if (err) { 00622 if (!hard) 00623 return (err); 00624 lasterr = err; 00625 continue; 00626 } 00627 if (ds->ds_phys->ds_prev_snap_txg > txg) 00628 txg = ds->ds_phys->ds_prev_snap_txg; 00629 err = traverse_dataset(ds, txg, flags, func, arg); 00630 dsl_dataset_rele(ds, FTAG); 00631 if (err) { 00632 if (!hard) 00633 return (err); 00634 lasterr = err; 00635 } 00636 } 00637 } 00638 if (err == ESRCH) 00639 err = 0; 00640 return (err != 0 ? err : lasterr); 00641 }