dsl_dataset.c revision 31fd60d36d9ae794bbedd5e834b8be6d412a853f
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/dmu_objset.h>
#include <sys/dsl_dataset.h>
#include <sys/dmu_traverse.h>
#include <sys/zfs_context.h>
#define BP_GET_UCSIZE(bp) \
/*
* We use weighted reference counts to express the various forms of exclusion
* between different open modes. A STANDARD open is 1 point, an EXCLUSIVE open
* is DOS_REF_MAX, and a PRIMARY open is little more than half of an EXCLUSIVE.
* This makes the exclusion logic simple: the total refcnt for all opens cannot
* exceed DOS_REF_MAX. For example, EXCLUSIVE opens are exclusive because their
* weight (DOS_REF_MAX) consumes the entire refcnt space. PRIMARY opens consume
* just over half of the refcnt space, so there can't be more than one, but it
* can peacefully coexist with any number of STANDARD opens.
*/
0, /* DOS_MODE_NONE - invalid */
1, /* DOS_MODE_STANDARD - unlimited number */
DOS_REF_MAX /* DOS_MODE_EXCLUSIVE - no other opens */
};
void
{
/* It could have been compressed away to nothing */
if (BP_IS_HOLE(bp))
return;
/*
* Account for the meta-objset space in its placeholder
* dsl_dir.
*/
return;
}
}
void
{
if (BP_IS_HOLE(bp))
return;
/*
* Account for the meta-objset space in its placeholder
* dataset.
*/
/* XXX this can fail, what do we do when it does? */
return;
}
/* XXX check return code? */
/* XXX unique_bytes is not accurate for head datasets */
/* ASSERT3U(ds->ds_phys->ds_unique_bytes, >=, used); */
} else {
/* if (bp->blk_birth > prev prev snap txg) prev unique += bs */
used;
}
}
}
}
{
return (0);
/*
* The snapshot creation could fail, but that would cause an
* incorrect FALSE return, which would only result in an
* overestimation of the amount of space that an operation would
* consume, which is OK.
*
* There's also a small window where we could miss a pending
* snapshot, because we could set the sync task in the quiescing
* phase. So this should only be used as a guess.
*/
else
return (txg);
}
int
{
}
/* ARGSUSED */
static void
{
/* open_refcount == DOS_REF_MAX when deleting */
}
}
static int
{
int err;
if (ds->ds_snapname[0])
return (0);
return (0);
if (err)
return (err);
return (err);
}
int
{
int err;
if (err)
return (err);
if (err == 0) {
}
if (err) {
/*
* we don't really need to close the blist if we
* just opened it.
*/
return (err);
}
}
} else {
if (snapname) {
#ifdef ZFS_DEBUG
if (err == 0) {
&foundobj);
}
#endif
} else if (zfs_flags & ZFS_DEBUG_SNAPNAMES) {
}
}
if (err == 0) {
}
DS_MODE_NONE, ds);
}
if (err) {
return (err);
}
} else {
/* XXX it won't necessarily be synced... */
}
}
}
return (EBUSY);
}
return (0);
}
int
{
dsl_pool_t *dp;
const char *tail;
int err = 0;
if (err)
return (err);
if (obj == 0) {
/* A dataset with no associated objset */
goto out;
}
if (err)
goto out;
if (tail[0] != '@') {
goto out;
}
tail++;
/* Look for a snapshot */
if (!DS_MODE_IS_READONLY(mode)) {
goto out;
}
if (err)
goto out;
}
out:
/* ASSERT(ds == NULL || strcmp(name, ds->ds_name) == 0); */
return (err);
}
int
{
}
void
{
} else {
if (ds->ds_snapname[0]) {
/*
* We use a "recursive" mutex so that we
* can call dprintf_ds() with ds_lock held.
*/
} else {
}
}
}
}
void
{
}
void
{
VERIFY(0 ==
}
int
{
int err;
if (clone_parent != NULL) {
/*
* You can't clone across pools.
*/
return (EXDEV);
/*
* You can only clone snapshots, not the head datasets.
*/
return (EINVAL);
}
if (err)
return (err);
/* This is the point of no (unsuccessful) return */
if (clone_parent) {
}
return (0);
}
int
dsl_dataset_destroy(const char *name)
{
int err;
dsl_pool_t *dp;
const char *tail;
if (err)
return (err);
if (tail[0] != '@') {
return (ENOENT);
}
tail++;
/* Just blow away the snapshot */
do {
txg_wait_synced(dp, 0);
dsl_dataset_destroy_sync, (void*)tail, 0);
} else {
char buf[MAXNAMELEN];
char *cp;
return (EINVAL);
}
if (err) {
return (err);
}
/*
* Check for errors and mark this ds as inconsistent, in
* case we crash while freeing the objects.
*/
if (err) {
return (err);
}
/*
* remove the objects in open context, so that we won't
* have too much to do in syncing context.
*/
if (err) {
/*
* Perhaps there is not enough disk
* space. Just deal with it from
* dsl_dataset_destroy_sync().
*/
continue;
}
}
/* Make sure it's not dirty before we finish destroying it. */
return (err);
}
/*
* Blow away the dsl_dir + head dataset.
* dsl_dir_destroy_sync() will call
* dsl_dataset_destroy_sync() to destroy the head dataset.
*/
if (err)
return (err);
do {
txg_wait_synced(dp, 0);
dsl_dir_destroy_sync, cp, 0);
}
return (err);
}
int
dsl_dataset_rollback(const char *name)
{
int err;
const char *tail;
if (err)
return (err);
return (EINVAL);
}
do {
return (err);
}
void *
void *p, dsl_dataset_evict_func_t func)
{
void *old;
ds->ds_user_ptr = p;
}
return (old);
}
void *
{
return (ds->ds_user_ptr);
}
void
{
}
void
{
/* If it's the meta-objset, set dp_meta_rootbp */
} else {
}
}
spa_t *
{
}
void
{
dsl_pool_t *dp;
return;
/* up the hold count until we can be written out */
}
}
struct killarg {
};
static int
{
/*
* Since this callback is not called concurrently, no lock is
* needed on the accounting values.
*/
/* XXX check for EIO? */
return (0);
}
/* ARGSUSED */
int
{
int err;
return (EINVAL);
if (err)
return (err);
/*
* There's no previous snapshot. I suppose we could
* roll it back to being empty (and re-initialize the
* upper (ZPL) layer). But for now there's no way to do
* this via the user interface.
*/
return (EINVAL);
}
if (ds->ds_open_refcount > 0) {
return (EBUSY);
}
/*
* If we made changes this txg, traverse_dsl_dataset won't find
* them. Try again.
*/
return (EAGAIN);
}
/* THE POINT OF NO (unsuccessful) RETURN */
/* Zero out the deadlist. */
{
/* Free blkptrs that we gave birth to */
}
/* Change our contents to that of the prev snapshot (finally!) */
ds->ds_open_refcount = 0;
return (0);
}
/* ARGSUSED */
static int
{
/*
* Can't delete a head dataset if there are snapshots of it.
* (Except if the only snapshots are from the branch we cloned
* from.)
*/
return (EINVAL);
/* Mark it as inconsistent on-disk, in case we crash */
return (0);
}
int
{
int err;
int after_branch_point = FALSE;
return (EINVAL);
}
if (err == 0) {
}
}
if (err) {
if (drop_lock)
return (err);
}
/* Can't delete a branch point. */
if (drop_lock)
return (EINVAL);
}
/*
* Can't delete a head dataset if there are snapshots of it.
* (Except if the only snapshots are from the branch we cloned
* from.)
*/
if (drop_lock)
return (EINVAL);
}
/*
* If we made changes this txg, traverse_dsl_dataset won't find
* them. Try again.
*/
if (drop_lock)
return (EAGAIN);
}
} else {
if (err) {
if (drop_lock)
return (err);
}
}
if (after_branch_point &&
/* This clone is toast. */
} else if (!after_branch_point) {
}
}
/* THE POINT OF NO (unsuccessful) RETURN */
/*
* Transfer to our deadlist (which will become next's
* new deadlist) any entries from next's current
* deadlist which were born before prev, and free the
* other entries.
*
* XXX we're doing this long task with the config lock held
*/
&bp) == 0) {
if (ds_prev && !after_branch_point &&
BP_GET_ASIZE(&bp);
}
} else {
/* XXX check return value? */
}
}
/* free next's deadlist */
/* set next's deadlist to our deadlist */
/*
* Update next's unique to include blocks which
* were previously shared by only this snapshot
* and it. Those blocks will be born after the
* prev snap and before this snap, and will have
* died after the next snap and before the one
* after that (ie. be on the snap after next's
* deadlist).
*
* XXX we're doing this long task with the
* config lock held
*/
itor = 0;
BP_GET_ASIZE(&bp);
}
}
} else {
/*
* It would be nice to update the head dataset's
* unique. To do so we would have to traverse
* it for blocks born after ds_prev, which is
* pretty expensive just to maintain something
* for debugging purposes.
*/
ds_next);
if (ds_prev) {
} else {
}
}
/*
* NB: unique_bytes is not accurate for head objsets
* because we don't update it when we delete the most
* recent snapshot -- see above comment.
*/
} else {
/*
* There's no next snapshot, so this is a head dataset.
* Destroy the deadlist. Unless it's a clone, the
* deadlist should be empty. (If it's a clone, it's
* safe to ignore the deadlist contents.)
*/
/*
* Free everything that we point to (that's born after
* the previous snapshot, if we are a clone)
*
* XXX we're doing this long task with the config lock held
*/
}
}
/* Erase the link in the dataset */
/*
* dsl_dir_sync_destroy() called us, they'll destroy
* the dataset.
*/
} else {
/* remove from snapshot namespace */
#ifdef ZFS_DEBUG
{
}
#endif
}
/*
* Close the objset with mode NONE, thus leaving it with
* DOS_REF_MAX set, so that noone can access it.
*/
if (drop_lock)
return (0);
}
int
{
int err;
return (EINVAL);
if (err)
return (err);
if (err == 0) {
return (EEXIST);
}
/* The point of no (unsuccessful) return */
}
} else {
}
return (0);
}
void
{
}
void
{
/* fill in properties crap */
}
/* We override the dataset's creation time... they should be the same */
/*
* This is a snapshot; override the dd's space used with
* our unique space
*/
}
}
{
}
struct osrenamearg {
const char *oldname;
const char *newname;
};
static int
{
const char *tail;
int err;
if (err)
return (err);
return (EINVAL);
}
/* better be changing a snapshot */
return (EINVAL);
}
/* new fs better exist */
if (err) {
return (err);
}
/* new name better be in same fs */
return (EINVAL);
}
/* new name better be a snapshot */
return (EINVAL);
}
tail++;
if (err) {
return (err);
}
/* new name better not be in use */
if (err == 0)
return (EEXIST);
}
/* The point of no (unsuccessful) return */
return (0);
}
int
{
const char *tail;
struct osrenamearg ora;
int err;
if (err)
return (err);
return (err);
}
if (tail[0] != '@') {
/* the name ended in a nonexistant component */
return (ENOENT);
}
return (err);
}