Deleted Added
full compact
dsl_dataset.c (226707) dsl_dataset.c (228103)
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE

--- 9 unchanged lines hidden (view full) ---

18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2011 by Delphix. All rights reserved.
24 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
25 * All rights reserved.
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE

--- 9 unchanged lines hidden (view full) ---

18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2011 by Delphix. All rights reserved.
24 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
25 * All rights reserved.
26 * Portions Copyright 2011 Martin Matuska <mm@FreeBSd.org>
26 */
27
28#include <sys/dmu_objset.h>
29#include <sys/dsl_dataset.h>
30#include <sys/dsl_dir.h>
31#include <sys/dsl_prop.h>
32#include <sys/dsl_synctask.h>
33#include <sys/dmu_traverse.h>

--- 869 unchanged lines hidden (view full) ---

903 bzero(&os->os_zil_header, sizeof (os->os_zil_header));
904 dsl_dataset_dirty(ds, tx);
905 dsl_dataset_rele(ds, FTAG);
906 }
907
908 return (dsobj);
909}
910
27 */
28
29#include <sys/dmu_objset.h>
30#include <sys/dsl_dataset.h>
31#include <sys/dsl_dir.h>
32#include <sys/dsl_prop.h>
33#include <sys/dsl_synctask.h>
34#include <sys/dmu_traverse.h>

--- 869 unchanged lines hidden (view full) ---

904 bzero(&os->os_zil_header, sizeof (os->os_zil_header));
905 dsl_dataset_dirty(ds, tx);
906 dsl_dataset_rele(ds, FTAG);
907 }
908
909 return (dsobj);
910}
911
912#ifdef __FreeBSD__
913/* FreeBSD ioctl compat begin */
911struct destroyarg {
914struct destroyarg {
912 dsl_sync_task_group_t *dstg;
913 char *snapname;
914 char *failed;
915 boolean_t defer;
915 nvlist_t *nvl;
916 const char *snapname;
916};
917
918static int
917};
918
919static int
919dsl_snapshot_destroy_one(const char *name, void *arg)
920dsl_check_snap_cb(const char *name, void *arg)
920{
921 struct destroyarg *da = arg;
922 dsl_dataset_t *ds;
921{
922 struct destroyarg *da = arg;
923 dsl_dataset_t *ds;
923 int err;
924 char *dsname;
925
926 dsname = kmem_asprintf("%s@%s", name, da->snapname);
924 char *dsname;
925
926 dsname = kmem_asprintf("%s@%s", name, da->snapname);
927 err = dsl_dataset_own(dsname, B_TRUE, da->dstg, &ds);
928 strfree(dsname);
929 if (err == 0) {
930 struct dsl_ds_destroyarg *dsda;
927 VERIFY(nvlist_add_boolean(da->nvl, dsname) == 0);
931
928
932 dsl_dataset_make_exclusive(ds, da->dstg);
933 dsda = kmem_zalloc(sizeof (struct dsl_ds_destroyarg), KM_SLEEP);
934 dsda->ds = ds;
935 dsda->defer = da->defer;
936 dsl_sync_task_create(da->dstg, dsl_dataset_destroy_check,
937 dsl_dataset_destroy_sync, dsda, da->dstg, 0);
938 } else if (err == ENOENT) {
939 err = 0;
940 } else {
941 (void) strcpy(da->failed, name);
942 }
929 return (0);
930}
931
932int
933dmu_get_recursive_snaps_nvl(const char *fsname, const char *snapname,
934 nvlist_t *snaps)
935{
936 struct destroyarg *da;
937 int err;
938
939 da = kmem_zalloc(sizeof (struct destroyarg), KM_SLEEP);
940 da->nvl = snaps;
941 da->snapname = snapname;
942 err = dmu_objset_find(fsname, dsl_check_snap_cb, da,
943 DS_FIND_CHILDREN);
944 kmem_free(da, sizeof (struct destroyarg));
945
943 return (err);
944}
946 return (err);
947}
948/* FreeBSD ioctl compat end */
949#endif /* __FreeBSD__ */
945
946/*
950
951/*
947 * Destroy 'snapname' in all descendants of 'fsname'.
952 * The snapshots must all be in the same pool.
948 */
953 */
949#pragma weak dmu_snapshots_destroy = dsl_snapshots_destroy
950int
954int
951dsl_snapshots_destroy(char *fsname, char *snapname, boolean_t defer)
955dmu_snapshots_destroy_nvl(nvlist_t *snaps, boolean_t defer, char *failed)
952{
953 int err;
956{
957 int err;
954 struct destroyarg da;
955 dsl_sync_task_t *dst;
956 spa_t *spa;
958 dsl_sync_task_t *dst;
959 spa_t *spa;
960 nvpair_t *pair;
961 dsl_sync_task_group_t *dstg;
957
962
958 err = spa_open(fsname, &spa, FTAG);
963 pair = nvlist_next_nvpair(snaps, NULL);
964 if (pair == NULL)
965 return (0);
966
967 err = spa_open(nvpair_name(pair), &spa, FTAG);
959 if (err)
960 return (err);
968 if (err)
969 return (err);
961 da.dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
962 da.snapname = snapname;
963 da.failed = fsname;
964 da.defer = defer;
970 dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
965
971
966 err = dmu_objset_find(fsname,
967 dsl_snapshot_destroy_one, &da, DS_FIND_CHILDREN);
972 for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
973 pair = nvlist_next_nvpair(snaps, pair)) {
974 dsl_dataset_t *ds;
975 int err;
968
976
977 err = dsl_dataset_own(nvpair_name(pair), B_TRUE, dstg, &ds);
978 if (err == 0) {
979 struct dsl_ds_destroyarg *dsda;
980
981 dsl_dataset_make_exclusive(ds, dstg);
982 dsda = kmem_zalloc(sizeof (struct dsl_ds_destroyarg),
983 KM_SLEEP);
984 dsda->ds = ds;
985 dsda->defer = defer;
986 dsl_sync_task_create(dstg, dsl_dataset_destroy_check,
987 dsl_dataset_destroy_sync, dsda, dstg, 0);
988 } else if (err == ENOENT) {
989 err = 0;
990 } else {
991 (void) strcpy(failed, nvpair_name(pair));
992 break;
993 }
994 }
995
969 if (err == 0)
996 if (err == 0)
970 err = dsl_sync_task_group_wait(da.dstg);
997 err = dsl_sync_task_group_wait(dstg);
971
998
972 for (dst = list_head(&da.dstg->dstg_tasks); dst;
973 dst = list_next(&da.dstg->dstg_tasks, dst)) {
999 for (dst = list_head(&dstg->dstg_tasks); dst;
1000 dst = list_next(&dstg->dstg_tasks, dst)) {
974 struct dsl_ds_destroyarg *dsda = dst->dst_arg1;
975 dsl_dataset_t *ds = dsda->ds;
976
977 /*
978 * Return the file system name that triggered the error
979 */
980 if (dst->dst_err) {
1001 struct dsl_ds_destroyarg *dsda = dst->dst_arg1;
1002 dsl_dataset_t *ds = dsda->ds;
1003
1004 /*
1005 * Return the file system name that triggered the error
1006 */
1007 if (dst->dst_err) {
981 dsl_dataset_name(ds, fsname);
982 *strchr(fsname, '@') = '\0';
1008 dsl_dataset_name(ds, failed);
983 }
984 ASSERT3P(dsda->rm_origin, ==, NULL);
1009 }
1010 ASSERT3P(dsda->rm_origin, ==, NULL);
985 dsl_dataset_disown(ds, da.dstg);
1011 dsl_dataset_disown(ds, dstg);
986 kmem_free(dsda, sizeof (struct dsl_ds_destroyarg));
987 }
988
1012 kmem_free(dsda, sizeof (struct dsl_ds_destroyarg));
1013 }
1014
989 dsl_sync_task_group_destroy(da.dstg);
1015 dsl_sync_task_group_destroy(dstg);
990 spa_close(spa, FTAG);
991 return (err);
1016 spa_close(spa, FTAG);
1017 return (err);
1018
992}
993
994static boolean_t
995dsl_dataset_might_destroy_origin(dsl_dataset_t *ds)
996{
997 boolean_t might_destroy = B_FALSE;
998
999 mutex_enter(&ds->ds_lock);

--- 1145 unchanged lines hidden (view full) ---

2145 */
2146 dmu_buf_will_dirty(ds->ds_dbuf, tx);
2147 ds->ds_phys->ds_fsid_guid = ds->ds_fsid_guid;
2148
2149 dsl_dir_dirty(ds->ds_dir, tx);
2150 dmu_objset_sync(ds->ds_objset, zio, tx);
2151}
2152
1019}
1020
1021static boolean_t
1022dsl_dataset_might_destroy_origin(dsl_dataset_t *ds)
1023{
1024 boolean_t might_destroy = B_FALSE;
1025
1026 mutex_enter(&ds->ds_lock);

--- 1145 unchanged lines hidden (view full) ---

2172 */
2173 dmu_buf_will_dirty(ds->ds_dbuf, tx);
2174 ds->ds_phys->ds_fsid_guid = ds->ds_fsid_guid;
2175
2176 dsl_dir_dirty(ds->ds_dir, tx);
2177 dmu_objset_sync(ds->ds_objset, zio, tx);
2178}
2179
2180static void
2181get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv)
2182{
2183 uint64_t count = 0;
2184 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
2185 zap_cursor_t zc;
2186 zap_attribute_t za;
2187 nvlist_t *propval;
2188 nvlist_t *val;
2189
2190 rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
2191 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
2192 VERIFY(nvlist_alloc(&val, NV_UNIQUE_NAME, KM_SLEEP) == 0);
2193
2194 /*
2195 * There may me missing entries in ds_next_clones_obj
2196 * due to a bug in a previous version of the code.
2197 * Only trust it if it has the right number of entries.
2198 */
2199 if (ds->ds_phys->ds_next_clones_obj != 0) {
2200 ASSERT3U(0, ==, zap_count(mos, ds->ds_phys->ds_next_clones_obj,
2201 &count));
2202 }
2203 if (count != ds->ds_phys->ds_num_children - 1) {
2204 goto fail;
2205 }
2206 for (zap_cursor_init(&zc, mos, ds->ds_phys->ds_next_clones_obj);
2207 zap_cursor_retrieve(&zc, &za) == 0;
2208 zap_cursor_advance(&zc)) {
2209 dsl_dataset_t *clone;
2210 char buf[ZFS_MAXNAMELEN];
2211 if (dsl_dataset_hold_obj(ds->ds_dir->dd_pool,
2212 za.za_first_integer, FTAG, &clone) != 0) {
2213 goto fail;
2214 }
2215 dsl_dir_name(clone->ds_dir, buf);
2216 VERIFY(nvlist_add_boolean(val, buf) == 0);
2217 dsl_dataset_rele(clone, FTAG);
2218 }
2219 zap_cursor_fini(&zc);
2220 VERIFY(nvlist_add_nvlist(propval, ZPROP_VALUE, val) == 0);
2221 VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(ZFS_PROP_CLONES),
2222 propval) == 0);
2223fail:
2224 nvlist_free(val);
2225 nvlist_free(propval);
2226 rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
2227}
2228
2153void
2154dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv)
2155{
2156 uint64_t refd, avail, uobjs, aobjs, ratio;
2157
2158 dsl_dir_stats(ds->ds_dir, nv);
2159
2160 dsl_dataset_space(ds, &refd, &avail, &uobjs, &aobjs);

--- 14 unchanged lines hidden (view full) ---

2175 ds->ds_phys->ds_unique_bytes);
2176 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_OBJSETID,
2177 ds->ds_object);
2178 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USERREFS,
2179 ds->ds_userrefs);
2180 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_DEFER_DESTROY,
2181 DS_IS_DEFER_DESTROY(ds) ? 1 : 0);
2182
2229void
2230dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv)
2231{
2232 uint64_t refd, avail, uobjs, aobjs, ratio;
2233
2234 dsl_dir_stats(ds->ds_dir, nv);
2235
2236 dsl_dataset_space(ds, &refd, &avail, &uobjs, &aobjs);

--- 14 unchanged lines hidden (view full) ---

2251 ds->ds_phys->ds_unique_bytes);
2252 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_OBJSETID,
2253 ds->ds_object);
2254 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USERREFS,
2255 ds->ds_userrefs);
2256 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_DEFER_DESTROY,
2257 DS_IS_DEFER_DESTROY(ds) ? 1 : 0);
2258
2259 if (ds->ds_phys->ds_prev_snap_obj != 0) {
2260 uint64_t written, comp, uncomp;
2261 dsl_pool_t *dp = ds->ds_dir->dd_pool;
2262 dsl_dataset_t *prev;
2263
2264 rw_enter(&dp->dp_config_rwlock, RW_READER);
2265 int err = dsl_dataset_hold_obj(dp,
2266 ds->ds_phys->ds_prev_snap_obj, FTAG, &prev);
2267 rw_exit(&dp->dp_config_rwlock);
2268 if (err == 0) {
2269 err = dsl_dataset_space_written(prev, ds, &written,
2270 &comp, &uncomp);
2271 dsl_dataset_rele(prev, FTAG);
2272 if (err == 0) {
2273 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_WRITTEN,
2274 written);
2275 }
2276 }
2277 }
2278
2183 ratio = ds->ds_phys->ds_compressed_bytes == 0 ? 100 :
2184 (ds->ds_phys->ds_uncompressed_bytes * 100 /
2185 ds->ds_phys->ds_compressed_bytes);
2186 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRATIO, ratio);
2187
2188 if (ds->ds_phys->ds_next_snap_obj) {
2189 /*
2190 * This is a snapshot; override the dd's space used with
2191 * our unique space and compression ratio.
2192 */
2193 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED,
2194 ds->ds_phys->ds_unique_bytes);
2195 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, ratio);
2279 ratio = ds->ds_phys->ds_compressed_bytes == 0 ? 100 :
2280 (ds->ds_phys->ds_uncompressed_bytes * 100 /
2281 ds->ds_phys->ds_compressed_bytes);
2282 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRATIO, ratio);
2283
2284 if (ds->ds_phys->ds_next_snap_obj) {
2285 /*
2286 * This is a snapshot; override the dd's space used with
2287 * our unique space and compression ratio.
2288 */
2289 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED,
2290 ds->ds_phys->ds_unique_bytes);
2291 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, ratio);
2292
2293 get_clones_stat(ds, nv);
2196 }
2197}
2198
2199void
2200dsl_dataset_fast_stat(dsl_dataset_t *ds, dmu_objset_stats_t *stat)
2201{
2202 stat->dds_creation_txg = ds->ds_phys->ds_creation_txg;
2203 stat->dds_inconsistent = ds->ds_phys->ds_flags & DS_FLAG_INCONSISTENT;

--- 1816 unchanged lines hidden (view full) ---

4020 zap_cursor_fini(&zc);
4021 kmem_free(za, sizeof (zap_attribute_t));
4022 }
4023 dsl_dataset_rele(ds, FTAG);
4024 return (0);
4025}
4026
4027/*
2294 }
2295}
2296
2297void
2298dsl_dataset_fast_stat(dsl_dataset_t *ds, dmu_objset_stats_t *stat)
2299{
2300 stat->dds_creation_txg = ds->ds_phys->ds_creation_txg;
2301 stat->dds_inconsistent = ds->ds_phys->ds_flags & DS_FLAG_INCONSISTENT;

--- 1816 unchanged lines hidden (view full) ---

4118 zap_cursor_fini(&zc);
4119 kmem_free(za, sizeof (zap_attribute_t));
4120 }
4121 dsl_dataset_rele(ds, FTAG);
4122 return (0);
4123}
4124
4125/*
4028 * Note, this fuction is used as the callback for dmu_objset_find(). We
4126 * Note, this function is used as the callback for dmu_objset_find(). We
4029 * always return 0 so that we will continue to find and process
4030 * inconsistent datasets, even if we encounter an error trying to
4031 * process one of them.
4032 */
4033/* ARGSUSED */
4034int
4035dsl_destroy_inconsistent(const char *dsname, void *arg)
4036{
4037 dsl_dataset_t *ds;
4038
4039 if (dsl_dataset_own(dsname, B_TRUE, FTAG, &ds) == 0) {
4040 if (DS_IS_INCONSISTENT(ds))
4041 (void) dsl_dataset_destroy(ds, FTAG, B_FALSE);
4042 else
4043 dsl_dataset_disown(ds, FTAG);
4044 }
4045 return (0);
4046}
4127 * always return 0 so that we will continue to find and process
4128 * inconsistent datasets, even if we encounter an error trying to
4129 * process one of them.
4130 */
4131/* ARGSUSED */
4132int
4133dsl_destroy_inconsistent(const char *dsname, void *arg)
4134{
4135 dsl_dataset_t *ds;
4136
4137 if (dsl_dataset_own(dsname, B_TRUE, FTAG, &ds) == 0) {
4138 if (DS_IS_INCONSISTENT(ds))
4139 (void) dsl_dataset_destroy(ds, FTAG, B_FALSE);
4140 else
4141 dsl_dataset_disown(ds, FTAG);
4142 }
4143 return (0);
4144}
4145
4146/*
4147 * Return (in *usedp) the amount of space written in new that is not
4148 * present in oldsnap. New may be a snapshot or the head. Old must be
4149 * a snapshot before new, in new's filesystem (or its origin). If not then
4150 * fail and return EINVAL.
4151 *
4152 * The written space is calculated by considering two components: First, we
4153 * ignore any freed space, and calculate the written as new's used space
4154 * minus old's used space. Next, we add in the amount of space that was freed
4155 * between the two snapshots, thus reducing new's used space relative to old's.
4156 * Specifically, this is the space that was born before old->ds_creation_txg,
4157 * and freed before new (ie. on new's deadlist or a previous deadlist).
4158 *
4159 * space freed [---------------------]
4160 * snapshots ---O-------O--------O-------O------
4161 * oldsnap new
4162 */
4163int
4164dsl_dataset_space_written(dsl_dataset_t *oldsnap, dsl_dataset_t *new,
4165 uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
4166{
4167 int err = 0;
4168 uint64_t snapobj;
4169 dsl_pool_t *dp = new->ds_dir->dd_pool;
4170
4171 *usedp = 0;
4172 *usedp += new->ds_phys->ds_used_bytes;
4173 *usedp -= oldsnap->ds_phys->ds_used_bytes;
4174
4175 *compp = 0;
4176 *compp += new->ds_phys->ds_compressed_bytes;
4177 *compp -= oldsnap->ds_phys->ds_compressed_bytes;
4178
4179 *uncompp = 0;
4180 *uncompp += new->ds_phys->ds_uncompressed_bytes;
4181 *uncompp -= oldsnap->ds_phys->ds_uncompressed_bytes;
4182
4183 rw_enter(&dp->dp_config_rwlock, RW_READER);
4184 snapobj = new->ds_object;
4185 while (snapobj != oldsnap->ds_object) {
4186 dsl_dataset_t *snap;
4187 uint64_t used, comp, uncomp;
4188
4189 err = dsl_dataset_hold_obj(dp, snapobj, FTAG, &snap);
4190 if (err != 0)
4191 break;
4192
4193 if (snap->ds_phys->ds_prev_snap_txg ==
4194 oldsnap->ds_phys->ds_creation_txg) {
4195 /*
4196 * The blocks in the deadlist can not be born after
4197 * ds_prev_snap_txg, so get the whole deadlist space,
4198 * which is more efficient (especially for old-format
4199 * deadlists). Unfortunately the deadlist code
4200 * doesn't have enough information to make this
4201 * optimization itself.
4202 */
4203 dsl_deadlist_space(&snap->ds_deadlist,
4204 &used, &comp, &uncomp);
4205 } else {
4206 dsl_deadlist_space_range(&snap->ds_deadlist,
4207 0, oldsnap->ds_phys->ds_creation_txg,
4208 &used, &comp, &uncomp);
4209 }
4210 *usedp += used;
4211 *compp += comp;
4212 *uncompp += uncomp;
4213
4214 /*
4215 * If we get to the beginning of the chain of snapshots
4216 * (ds_prev_snap_obj == 0) before oldsnap, then oldsnap
4217 * was not a snapshot of/before new.
4218 */
4219 snapobj = snap->ds_phys->ds_prev_snap_obj;
4220 dsl_dataset_rele(snap, FTAG);
4221 if (snapobj == 0) {
4222 err = EINVAL;
4223 break;
4224 }
4225
4226 }
4227 rw_exit(&dp->dp_config_rwlock);
4228 return (err);
4229}
4230
4231/*
4232 * Return (in *usedp) the amount of space that will be reclaimed if firstsnap,
4233 * lastsnap, and all snapshots in between are deleted.
4234 *
4235 * blocks that would be freed [---------------------------]
4236 * snapshots ---O-------O--------O-------O--------O
4237 * firstsnap lastsnap
4238 *
4239 * This is the set of blocks that were born after the snap before firstsnap,
4240 * (birth > firstsnap->prev_snap_txg) and died before the snap after the
4241 * last snap (ie, is on lastsnap->ds_next->ds_deadlist or an earlier deadlist).
4242 * We calculate this by iterating over the relevant deadlists (from the snap
4243 * after lastsnap, backward to the snap after firstsnap), summing up the
4244 * space on the deadlist that was born after the snap before firstsnap.
4245 */
4246int
4247dsl_dataset_space_wouldfree(dsl_dataset_t *firstsnap,
4248 dsl_dataset_t *lastsnap,
4249 uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
4250{
4251 int err = 0;
4252 uint64_t snapobj;
4253 dsl_pool_t *dp = firstsnap->ds_dir->dd_pool;
4254
4255 ASSERT(dsl_dataset_is_snapshot(firstsnap));
4256 ASSERT(dsl_dataset_is_snapshot(lastsnap));
4257
4258 /*
4259 * Check that the snapshots are in the same dsl_dir, and firstsnap
4260 * is before lastsnap.
4261 */
4262 if (firstsnap->ds_dir != lastsnap->ds_dir ||
4263 firstsnap->ds_phys->ds_creation_txg >
4264 lastsnap->ds_phys->ds_creation_txg)
4265 return (EINVAL);
4266
4267 *usedp = *compp = *uncompp = 0;
4268
4269 rw_enter(&dp->dp_config_rwlock, RW_READER);
4270 snapobj = lastsnap->ds_phys->ds_next_snap_obj;
4271 while (snapobj != firstsnap->ds_object) {
4272 dsl_dataset_t *ds;
4273 uint64_t used, comp, uncomp;
4274
4275 err = dsl_dataset_hold_obj(dp, snapobj, FTAG, &ds);
4276 if (err != 0)
4277 break;
4278
4279 dsl_deadlist_space_range(&ds->ds_deadlist,
4280 firstsnap->ds_phys->ds_prev_snap_txg, UINT64_MAX,
4281 &used, &comp, &uncomp);
4282 *usedp += used;
4283 *compp += comp;
4284 *uncompp += uncomp;
4285
4286 snapobj = ds->ds_phys->ds_prev_snap_obj;
4287 ASSERT3U(snapobj, !=, 0);
4288 dsl_dataset_rele(ds, FTAG);
4289 }
4290 rw_exit(&dp->dp_config_rwlock);
4291 return (err);
4292}