xref: /onnv-gate/usr/src/uts/common/fs/zfs/zfs_onexit.c (revision 12786:f6c8601080b4)
112527SChris.Kirby@oracle.com /*
212527SChris.Kirby@oracle.com  * CDDL HEADER START
312527SChris.Kirby@oracle.com  *
412527SChris.Kirby@oracle.com  * The contents of this file are subject to the terms of the
512527SChris.Kirby@oracle.com  * Common Development and Distribution License (the "License").
612527SChris.Kirby@oracle.com  * You may not use this file except in compliance with the License.
712527SChris.Kirby@oracle.com  *
812527SChris.Kirby@oracle.com  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
912527SChris.Kirby@oracle.com  * or http://www.opensolaris.org/os/licensing.
1012527SChris.Kirby@oracle.com  * See the License for the specific language governing permissions
1112527SChris.Kirby@oracle.com  * and limitations under the License.
1212527SChris.Kirby@oracle.com  *
1312527SChris.Kirby@oracle.com  * When distributing Covered Code, include this CDDL HEADER in each
1412527SChris.Kirby@oracle.com  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1512527SChris.Kirby@oracle.com  * If applicable, add the following below this CDDL HEADER, with the
1612527SChris.Kirby@oracle.com  * fields enclosed by brackets "[]" replaced with your own identifying
1712527SChris.Kirby@oracle.com  * information: Portions Copyright [yyyy] [name of copyright owner]
1812527SChris.Kirby@oracle.com  *
1912527SChris.Kirby@oracle.com  * CDDL HEADER END
2012527SChris.Kirby@oracle.com  */
2112527SChris.Kirby@oracle.com /*
2212527SChris.Kirby@oracle.com  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2312527SChris.Kirby@oracle.com  */
2412527SChris.Kirby@oracle.com 
2512527SChris.Kirby@oracle.com #include <sys/types.h>
2612527SChris.Kirby@oracle.com #include <sys/param.h>
2712527SChris.Kirby@oracle.com #include <sys/errno.h>
2812527SChris.Kirby@oracle.com #include <sys/open.h>
2912527SChris.Kirby@oracle.com #include <sys/kmem.h>
3012527SChris.Kirby@oracle.com #include <sys/conf.h>
3112527SChris.Kirby@oracle.com #include <sys/ddi.h>
3212527SChris.Kirby@oracle.com #include <sys/sunddi.h>
3312527SChris.Kirby@oracle.com #include <sys/zfs_ioctl.h>
3412527SChris.Kirby@oracle.com #include <sys/mkdev.h>
3512527SChris.Kirby@oracle.com #include <sys/zfs_onexit.h>
3612527SChris.Kirby@oracle.com #include <sys/zvol.h>
3712527SChris.Kirby@oracle.com 
3812527SChris.Kirby@oracle.com /*
3912527SChris.Kirby@oracle.com  * ZFS kernel routines may add/delete callback routines to be invoked
4012527SChris.Kirby@oracle.com  * upon process exit (triggered via the close operation from the /dev/zfs
4112527SChris.Kirby@oracle.com  * driver).
4212527SChris.Kirby@oracle.com  *
4312527SChris.Kirby@oracle.com  * These cleanup callbacks are intended to allow for the accumulation
4412527SChris.Kirby@oracle.com  * of kernel state across multiple ioctls.  User processes participate
4512527SChris.Kirby@oracle.com  * by opening ZFS_DEV with O_EXCL. This causes the ZFS driver to do a
4612527SChris.Kirby@oracle.com  * clone-open, generating a unique minor number. The process then passes
4712527SChris.Kirby@oracle.com  * along that file descriptor to each ioctl that might have a cleanup operation.
4812527SChris.Kirby@oracle.com  *
49*12786SChris.Kirby@oracle.com  * Consumers of the onexit routines should call zfs_onexit_fd_hold() early
50*12786SChris.Kirby@oracle.com  * on to validate the given fd and add a reference to its file table entry.
51*12786SChris.Kirby@oracle.com  * This allows the consumer to do its work and then add a callback, knowing
52*12786SChris.Kirby@oracle.com  * that zfs_onexit_add_cb() won't fail with EBADF.  When finished, consumers
53*12786SChris.Kirby@oracle.com  * should call zfs_onexit_fd_rele().
54*12786SChris.Kirby@oracle.com  *
5512527SChris.Kirby@oracle.com  * A simple example is zfs_ioc_recv(), where we might create an AVL tree
5612527SChris.Kirby@oracle.com  * with dataset/GUID mappings and then reuse that tree on subsequent
5712527SChris.Kirby@oracle.com  * zfs_ioc_recv() calls.
5812527SChris.Kirby@oracle.com  *
5912527SChris.Kirby@oracle.com  * On the first zfs_ioc_recv() call, dmu_recv_stream() will kmem_alloc()
6012527SChris.Kirby@oracle.com  * the AVL tree and pass it along with a callback function to
6112527SChris.Kirby@oracle.com  * zfs_onexit_add_cb(). The zfs_onexit_add_cb() routine will register the
6212527SChris.Kirby@oracle.com  * callback and return an action handle.
6312527SChris.Kirby@oracle.com  *
6412527SChris.Kirby@oracle.com  * The action handle is then passed from user space to subsequent
6512527SChris.Kirby@oracle.com  * zfs_ioc_recv() calls, so that dmu_recv_stream() can fetch its AVL tree
66*12786SChris.Kirby@oracle.com  * by calling zfs_onexit_cb_data() with the device minor number and
67*12786SChris.Kirby@oracle.com  * action handle.
6812527SChris.Kirby@oracle.com  *
6912527SChris.Kirby@oracle.com  * If the user process exits abnormally, the callback is invoked implicitly
7012527SChris.Kirby@oracle.com  * as part of the driver close operation.  Once the user space process is
7112527SChris.Kirby@oracle.com  * finished with the accumulated kernel state, it can also just call close(2)
7212527SChris.Kirby@oracle.com  * on the cleanup fd to trigger the cleanup callback.
7312527SChris.Kirby@oracle.com  */
7412527SChris.Kirby@oracle.com 
7512527SChris.Kirby@oracle.com void
zfs_onexit_init(zfs_onexit_t ** zop)7612527SChris.Kirby@oracle.com zfs_onexit_init(zfs_onexit_t **zop)
7712527SChris.Kirby@oracle.com {
7812527SChris.Kirby@oracle.com 	zfs_onexit_t *zo;
7912527SChris.Kirby@oracle.com 
8012527SChris.Kirby@oracle.com 	zo = *zop = kmem_zalloc(sizeof (zfs_onexit_t), KM_SLEEP);
8112527SChris.Kirby@oracle.com 	mutex_init(&zo->zo_lock, NULL, MUTEX_DEFAULT, NULL);
8212527SChris.Kirby@oracle.com 	list_create(&zo->zo_actions, sizeof (zfs_onexit_action_node_t),
8312527SChris.Kirby@oracle.com 	    offsetof(zfs_onexit_action_node_t, za_link));
8412527SChris.Kirby@oracle.com }
8512527SChris.Kirby@oracle.com 
8612527SChris.Kirby@oracle.com void
zfs_onexit_destroy(zfs_onexit_t * zo)8712527SChris.Kirby@oracle.com zfs_onexit_destroy(zfs_onexit_t *zo)
8812527SChris.Kirby@oracle.com {
8912527SChris.Kirby@oracle.com 	zfs_onexit_action_node_t *ap;
9012527SChris.Kirby@oracle.com 
9112527SChris.Kirby@oracle.com 	mutex_enter(&zo->zo_lock);
9212527SChris.Kirby@oracle.com 	while ((ap = list_head(&zo->zo_actions)) != NULL) {
9312527SChris.Kirby@oracle.com 		list_remove(&zo->zo_actions, ap);
9412527SChris.Kirby@oracle.com 		mutex_exit(&zo->zo_lock);
9512527SChris.Kirby@oracle.com 		ap->za_func(ap->za_data);
9612527SChris.Kirby@oracle.com 		kmem_free(ap, sizeof (zfs_onexit_action_node_t));
9712527SChris.Kirby@oracle.com 		mutex_enter(&zo->zo_lock);
9812527SChris.Kirby@oracle.com 	}
9912527SChris.Kirby@oracle.com 	mutex_exit(&zo->zo_lock);
10012527SChris.Kirby@oracle.com 
10112527SChris.Kirby@oracle.com 	list_destroy(&zo->zo_actions);
10212527SChris.Kirby@oracle.com 	mutex_destroy(&zo->zo_lock);
10312527SChris.Kirby@oracle.com 	kmem_free(zo, sizeof (zfs_onexit_t));
10412527SChris.Kirby@oracle.com }
10512527SChris.Kirby@oracle.com 
10612527SChris.Kirby@oracle.com static int
zfs_onexit_minor_to_state(minor_t minor,zfs_onexit_t ** zo)107*12786SChris.Kirby@oracle.com zfs_onexit_minor_to_state(minor_t minor, zfs_onexit_t **zo)
108*12786SChris.Kirby@oracle.com {
109*12786SChris.Kirby@oracle.com 	*zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV);
110*12786SChris.Kirby@oracle.com 	if (*zo == NULL)
111*12786SChris.Kirby@oracle.com 		return (EBADF);
112*12786SChris.Kirby@oracle.com 
113*12786SChris.Kirby@oracle.com 	return (0);
114*12786SChris.Kirby@oracle.com }
115*12786SChris.Kirby@oracle.com 
116*12786SChris.Kirby@oracle.com /*
117*12786SChris.Kirby@oracle.com  * Consumers might need to operate by minor number instead of fd, since
118*12786SChris.Kirby@oracle.com  * they might be running in another thread (e.g. txg_sync_thread). Callers
119*12786SChris.Kirby@oracle.com  * of this function must call zfs_onexit_fd_rele() when they're finished
120*12786SChris.Kirby@oracle.com  * using the minor number.
121*12786SChris.Kirby@oracle.com  */
122*12786SChris.Kirby@oracle.com int
zfs_onexit_fd_hold(int fd,minor_t * minorp)123*12786SChris.Kirby@oracle.com zfs_onexit_fd_hold(int fd, minor_t *minorp)
12412527SChris.Kirby@oracle.com {
12512527SChris.Kirby@oracle.com 	file_t *fp;
126*12786SChris.Kirby@oracle.com 	zfs_onexit_t *zo;
12712527SChris.Kirby@oracle.com 
12812527SChris.Kirby@oracle.com 	fp = getf(fd);
12912527SChris.Kirby@oracle.com 	if (fp == NULL)
13012527SChris.Kirby@oracle.com 		return (EBADF);
13112527SChris.Kirby@oracle.com 
132*12786SChris.Kirby@oracle.com 	*minorp = getminor(fp->f_vnode->v_rdev);
133*12786SChris.Kirby@oracle.com 	return (zfs_onexit_minor_to_state(*minorp, &zo));
134*12786SChris.Kirby@oracle.com }
13512527SChris.Kirby@oracle.com 
136*12786SChris.Kirby@oracle.com void
zfs_onexit_fd_rele(int fd)137*12786SChris.Kirby@oracle.com zfs_onexit_fd_rele(int fd)
138*12786SChris.Kirby@oracle.com {
139*12786SChris.Kirby@oracle.com 	releasef(fd);
14012527SChris.Kirby@oracle.com }
14112527SChris.Kirby@oracle.com 
14212527SChris.Kirby@oracle.com /*
14312527SChris.Kirby@oracle.com  * Add a callback to be invoked when the calling process exits.
14412527SChris.Kirby@oracle.com  */
14512527SChris.Kirby@oracle.com int
zfs_onexit_add_cb(minor_t minor,void (* func)(void *),void * data,uint64_t * action_handle)146*12786SChris.Kirby@oracle.com zfs_onexit_add_cb(minor_t minor, void (*func)(void *), void *data,
14712527SChris.Kirby@oracle.com     uint64_t *action_handle)
14812527SChris.Kirby@oracle.com {
14912527SChris.Kirby@oracle.com 	zfs_onexit_t *zo;
15012527SChris.Kirby@oracle.com 	zfs_onexit_action_node_t *ap;
15112527SChris.Kirby@oracle.com 	int error;
15212527SChris.Kirby@oracle.com 
153*12786SChris.Kirby@oracle.com 	error = zfs_onexit_minor_to_state(minor, &zo);
15412527SChris.Kirby@oracle.com 	if (error)
15512527SChris.Kirby@oracle.com 		return (error);
15612527SChris.Kirby@oracle.com 
15712527SChris.Kirby@oracle.com 	ap = kmem_alloc(sizeof (zfs_onexit_action_node_t), KM_SLEEP);
15812527SChris.Kirby@oracle.com 	list_link_init(&ap->za_link);
15912527SChris.Kirby@oracle.com 	ap->za_func = func;
16012527SChris.Kirby@oracle.com 	ap->za_data = data;
16112527SChris.Kirby@oracle.com 
16212527SChris.Kirby@oracle.com 	mutex_enter(&zo->zo_lock);
16312527SChris.Kirby@oracle.com 	list_insert_tail(&zo->zo_actions, ap);
16412527SChris.Kirby@oracle.com 	mutex_exit(&zo->zo_lock);
165*12786SChris.Kirby@oracle.com 	if (action_handle)
166*12786SChris.Kirby@oracle.com 		*action_handle = (uint64_t)(uintptr_t)ap;
16712527SChris.Kirby@oracle.com 
16812527SChris.Kirby@oracle.com 	return (0);
16912527SChris.Kirby@oracle.com }
17012527SChris.Kirby@oracle.com 
17112527SChris.Kirby@oracle.com static zfs_onexit_action_node_t *
zfs_onexit_find_cb(zfs_onexit_t * zo,uint64_t action_handle)17212527SChris.Kirby@oracle.com zfs_onexit_find_cb(zfs_onexit_t *zo, uint64_t action_handle)
17312527SChris.Kirby@oracle.com {
17412527SChris.Kirby@oracle.com 	zfs_onexit_action_node_t *match;
17512527SChris.Kirby@oracle.com 	zfs_onexit_action_node_t *ap;
17612527SChris.Kirby@oracle.com 	list_t *l;
17712527SChris.Kirby@oracle.com 
17812527SChris.Kirby@oracle.com 	ASSERT(MUTEX_HELD(&zo->zo_lock));
17912527SChris.Kirby@oracle.com 
18012527SChris.Kirby@oracle.com 	match = (zfs_onexit_action_node_t *)(uintptr_t)action_handle;
18112527SChris.Kirby@oracle.com 	l = &zo->zo_actions;
18212527SChris.Kirby@oracle.com 	for (ap = list_head(l); ap != NULL; ap = list_next(l, ap)) {
18312527SChris.Kirby@oracle.com 		if (match == ap)
18412527SChris.Kirby@oracle.com 			break;
18512527SChris.Kirby@oracle.com 	}
18612527SChris.Kirby@oracle.com 	return (ap);
18712527SChris.Kirby@oracle.com }
18812527SChris.Kirby@oracle.com 
18912527SChris.Kirby@oracle.com /*
19012527SChris.Kirby@oracle.com  * Delete the callback, triggering it first if 'fire' is set.
19112527SChris.Kirby@oracle.com  */
19212527SChris.Kirby@oracle.com int
zfs_onexit_del_cb(minor_t minor,uint64_t action_handle,boolean_t fire)193*12786SChris.Kirby@oracle.com zfs_onexit_del_cb(minor_t minor, uint64_t action_handle, boolean_t fire)
19412527SChris.Kirby@oracle.com {
19512527SChris.Kirby@oracle.com 	zfs_onexit_t *zo;
19612527SChris.Kirby@oracle.com 	zfs_onexit_action_node_t *ap;
19712527SChris.Kirby@oracle.com 	int error;
19812527SChris.Kirby@oracle.com 
199*12786SChris.Kirby@oracle.com 	error = zfs_onexit_minor_to_state(minor, &zo);
20012527SChris.Kirby@oracle.com 	if (error)
20112527SChris.Kirby@oracle.com 		return (error);
20212527SChris.Kirby@oracle.com 
20312527SChris.Kirby@oracle.com 	mutex_enter(&zo->zo_lock);
20412527SChris.Kirby@oracle.com 	ap = zfs_onexit_find_cb(zo, action_handle);
20512527SChris.Kirby@oracle.com 	if (ap != NULL) {
20612527SChris.Kirby@oracle.com 		list_remove(&zo->zo_actions, ap);
20712527SChris.Kirby@oracle.com 		mutex_exit(&zo->zo_lock);
20812527SChris.Kirby@oracle.com 		if (fire)
20912527SChris.Kirby@oracle.com 			ap->za_func(ap->za_data);
21012527SChris.Kirby@oracle.com 		kmem_free(ap, sizeof (zfs_onexit_action_node_t));
21112527SChris.Kirby@oracle.com 	} else {
21212527SChris.Kirby@oracle.com 		mutex_exit(&zo->zo_lock);
21312527SChris.Kirby@oracle.com 		error = ENOENT;
21412527SChris.Kirby@oracle.com 	}
21512527SChris.Kirby@oracle.com 
21612527SChris.Kirby@oracle.com 	return (error);
21712527SChris.Kirby@oracle.com }
21812527SChris.Kirby@oracle.com 
21912527SChris.Kirby@oracle.com /*
22012527SChris.Kirby@oracle.com  * Return the data associated with this callback.  This allows consumers
22112527SChris.Kirby@oracle.com  * of the cleanup-on-exit interfaces to stash kernel data across system
22212527SChris.Kirby@oracle.com  * calls, knowing that it will be cleaned up if the calling process exits.
22312527SChris.Kirby@oracle.com  */
22412527SChris.Kirby@oracle.com int
zfs_onexit_cb_data(minor_t minor,uint64_t action_handle,void ** data)225*12786SChris.Kirby@oracle.com zfs_onexit_cb_data(minor_t minor, uint64_t action_handle, void **data)
22612527SChris.Kirby@oracle.com {
22712527SChris.Kirby@oracle.com 	zfs_onexit_t *zo;
22812527SChris.Kirby@oracle.com 	zfs_onexit_action_node_t *ap;
22912527SChris.Kirby@oracle.com 	int error;
23012527SChris.Kirby@oracle.com 
23112527SChris.Kirby@oracle.com 	*data = NULL;
23212527SChris.Kirby@oracle.com 
233*12786SChris.Kirby@oracle.com 	error = zfs_onexit_minor_to_state(minor, &zo);
23412527SChris.Kirby@oracle.com 	if (error)
23512527SChris.Kirby@oracle.com 		return (error);
23612527SChris.Kirby@oracle.com 
23712527SChris.Kirby@oracle.com 	mutex_enter(&zo->zo_lock);
23812527SChris.Kirby@oracle.com 	ap = zfs_onexit_find_cb(zo, action_handle);
23912527SChris.Kirby@oracle.com 	if (ap != NULL)
24012527SChris.Kirby@oracle.com 		*data = ap->za_data;
24112527SChris.Kirby@oracle.com 	else
24212527SChris.Kirby@oracle.com 		error = ENOENT;
24312527SChris.Kirby@oracle.com 	mutex_exit(&zo->zo_lock);
24412527SChris.Kirby@oracle.com 
24512527SChris.Kirby@oracle.com 	return (error);
24612527SChris.Kirby@oracle.com }
247