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