usr/src/uts/common/fs/zfs/dsl_dataset.c
changeset 12798 f6c8601080b4
parent 12711 7ba0ca9a0395
child 12839 1eab9192da8b
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c	Wed Jul 07 02:21:35 2010 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c	Wed Jul 07 15:04:13 2010 -0600
@@ -367,6 +367,7 @@
 	dmu_buf_t *dbuf;
 	dsl_dataset_t *ds;
 	int err;
+	dmu_object_info_t doi;
 
 	ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock) ||
 	    dsl_pool_sync_context(dp));
@@ -374,6 +375,12 @@
 	err = dmu_bonus_hold(mos, dsobj, tag, &dbuf);
 	if (err)
 		return (err);
+
+	/* Make sure dsobj has the correct object type. */
+	dmu_object_info_from_db(dbuf, &doi);
+	if (doi.doi_type != DMU_OT_DSL_DATASET)
+		return (EINVAL);
+
 	ds = dmu_buf_get_user(dbuf);
 	if (ds == NULL) {
 		dsl_dataset_t *winner;
@@ -3422,10 +3429,9 @@
 };
 
 typedef struct zfs_hold_cleanup_arg {
-	char dsname[MAXNAMELEN];
-	char snapname[MAXNAMELEN];
+	dsl_pool_t *dp;
+	uint64_t dsobj;
 	char htag[MAXNAMELEN];
-	boolean_t recursive;
 } zfs_hold_cleanup_arg_t;
 
 static void
@@ -3433,11 +3439,25 @@
 {
 	zfs_hold_cleanup_arg_t *ca = arg;
 
-	(void) dsl_dataset_user_release(ca->dsname, ca->snapname,
-	    ca->htag, ca->recursive);
+	(void) dsl_dataset_user_release_tmp(ca->dp, ca->dsobj, ca->htag,
+	    B_TRUE);
 	kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t));
 }
 
+void
+dsl_register_onexit_hold_cleanup(dsl_dataset_t *ds, const char *htag,
+    minor_t minor)
+{
+	zfs_hold_cleanup_arg_t *ca;
+
+	ca = kmem_alloc(sizeof (zfs_hold_cleanup_arg_t), KM_SLEEP);
+	ca->dp = ds->ds_dir->dd_pool;
+	ca->dsobj = ds->ds_object;
+	(void) strlcpy(ca->htag, htag, sizeof (ca->htag));
+	VERIFY3U(0, ==, zfs_onexit_add_cb(minor,
+	    dsl_dataset_user_release_onexit, ca, NULL));
+}
+
 /*
  * The max length of a temporary tag prefix is the number of hex digits
  * required to express UINT64_MAX plus one for the hyphen.
@@ -3541,6 +3561,24 @@
 }
 
 int
+dsl_dataset_user_hold_for_send(dsl_dataset_t *ds, char *htag,
+    boolean_t temphold)
+{
+	struct dsl_ds_holdarg *ha;
+	int error;
+
+	ha = kmem_zalloc(sizeof (struct dsl_ds_holdarg), KM_SLEEP);
+	ha->htag = htag;
+	ha->temphold = temphold;
+	error = dsl_sync_task_do(ds->ds_dir->dd_pool,
+	    dsl_dataset_user_hold_check, dsl_dataset_user_hold_sync,
+	    ds, ha, 0);
+	kmem_free(ha, sizeof (struct dsl_ds_holdarg));
+
+	return (error);
+}
+
+int
 dsl_dataset_user_hold(char *dsname, char *snapname, char *htag,
     boolean_t recursive, boolean_t temphold, int cleanup_fd)
 {
@@ -3548,6 +3586,16 @@
 	dsl_sync_task_t *dst;
 	spa_t *spa;
 	int error;
+	minor_t minor = 0;
+
+	if (cleanup_fd != -1) {
+		/* Currently we only support cleanup-on-exit of tempholds. */
+		if (!temphold)
+			return (EINVAL);
+		error = zfs_onexit_fd_hold(cleanup_fd, &minor);
+		if (error)
+			return (error);
+	}
 
 	ha = kmem_zalloc(sizeof (struct dsl_ds_holdarg), KM_SLEEP);
 
@@ -3556,6 +3604,8 @@
 	error = spa_open(dsname, &spa, FTAG);
 	if (error) {
 		kmem_free(ha, sizeof (struct dsl_ds_holdarg));
+		if (cleanup_fd != -1)
+			zfs_onexit_fd_rele(cleanup_fd);
 		return (error);
 	}
 
@@ -3581,6 +3631,12 @@
 		if (dst->dst_err) {
 			dsl_dataset_name(ds, ha->failed);
 			*strchr(ha->failed, '@') = '\0';
+		} else if (error == 0 && minor != 0 && temphold) {
+			/*
+			 * If this hold is to be released upon process exit,
+			 * register that action now.
+			 */
+			dsl_register_onexit_hold_cleanup(ds, htag, minor);
 		}
 		dsl_dataset_rele(ds, ha->dstg);
 	}
@@ -3593,25 +3649,10 @@
 
 	dsl_sync_task_group_destroy(ha->dstg);
 
-	/*
-	 * If this set of temporary holds is to be removed upon process exit,
-	 * register that action now.
-	 */
-	if (error == 0 && cleanup_fd != -1 && temphold) {
-		zfs_hold_cleanup_arg_t *ca;
-		uint64_t action_handle;
-
-		ca = kmem_alloc(sizeof (zfs_hold_cleanup_arg_t), KM_SLEEP);
-		(void) strlcpy(ca->dsname, dsname, sizeof (ca->dsname));
-		(void) strlcpy(ca->snapname, snapname, sizeof (ca->snapname));
-		(void) strlcpy(ca->htag, htag, sizeof (ca->htag));
-		ca->recursive = recursive;
-		(void) zfs_onexit_add_cb(cleanup_fd,
-		    dsl_dataset_user_release_onexit, ca, &action_handle);
-	}
-
 	kmem_free(ha, sizeof (struct dsl_ds_holdarg));
 	spa_close(spa, FTAG);
+	if (cleanup_fd != -1)
+		zfs_onexit_fd_rele(cleanup_fd);
 	return (error);
 }
 
@@ -3703,11 +3744,6 @@
 	uint64_t refs;
 	int error;
 
-	if (ds->ds_objset) {
-		dmu_objset_evict(ds->ds_objset);
-		ds->ds_objset = NULL;
-	}
-
 	mutex_enter(&ds->ds_lock);
 	ds->ds_userrefs--;
 	refs = ds->ds_userrefs;
@@ -3867,10 +3903,12 @@
 }
 
 /*
- * Called at spa_load time to release a stale temporary user hold.
+ * Called at spa_load time (with retry == B_FALSE) to release a stale
+ * temporary user hold. Also called by the onexit code (with retry == B_TRUE).
  */
 int
-dsl_dataset_user_release_tmp(dsl_pool_t *dp, uint64_t dsobj, char *htag)
+dsl_dataset_user_release_tmp(dsl_pool_t *dp, uint64_t dsobj, char *htag,
+    boolean_t retry)
 {
 	dsl_dataset_t *ds;
 	char *snap;
@@ -3878,20 +3916,36 @@
 	int namelen;
 	int error;
 
-	rw_enter(&dp->dp_config_rwlock, RW_READER);
-	error = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
-	rw_exit(&dp->dp_config_rwlock);
-	if (error)
-		return (error);
-	namelen = dsl_dataset_namelen(ds)+1;
-	name = kmem_alloc(namelen, KM_SLEEP);
-	dsl_dataset_name(ds, name);
-	dsl_dataset_rele(ds, FTAG);
-
-	snap = strchr(name, '@');
-	*snap = '\0';
-	++snap;
-	return (dsl_dataset_user_release(name, snap, htag, B_FALSE));
+	do {
+		rw_enter(&dp->dp_config_rwlock, RW_READER);
+		error = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
+		rw_exit(&dp->dp_config_rwlock);
+		if (error)
+			return (error);
+		namelen = dsl_dataset_namelen(ds)+1;
+		name = kmem_alloc(namelen, KM_SLEEP);
+		dsl_dataset_name(ds, name);
+		dsl_dataset_rele(ds, FTAG);
+
+		snap = strchr(name, '@');
+		*snap = '\0';
+		++snap;
+		error = dsl_dataset_user_release(name, snap, htag, B_FALSE);
+		kmem_free(name, namelen);
+
+		/*
+		 * The object can't have been destroyed because we have a hold,
+		 * but it might have been renamed, resulting in ENOENT.  Retry
+		 * if we've been requested to do so.
+		 *
+		 * It would be nice if we could use the dsobj all the way
+		 * through and avoid ENOENT entirely.  But we might need to
+		 * unmount the snapshot, and there's currently no way to lookup
+		 * a vfsp using a ZFS object id.
+		 */
+	} while ((error == ENOENT) && retry);
+
+	return (error);
 }
 
 int