1*61145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2eda14cbcSMatt Macy /*
3eda14cbcSMatt Macy * CDDL HEADER START
4eda14cbcSMatt Macy *
5eda14cbcSMatt Macy * The contents of this file are subject to the terms of the
6eda14cbcSMatt Macy * Common Development and Distribution License (the "License").
7eda14cbcSMatt Macy * You may not use this file except in compliance with the License.
8eda14cbcSMatt Macy *
9eda14cbcSMatt Macy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10271171e0SMartin Matuska * or https://opensource.org/licenses/CDDL-1.0.
11eda14cbcSMatt Macy * See the License for the specific language governing permissions
12eda14cbcSMatt Macy * and limitations under the License.
13eda14cbcSMatt Macy *
14eda14cbcSMatt Macy * When distributing Covered Code, include this CDDL HEADER in each
15eda14cbcSMatt Macy * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16eda14cbcSMatt Macy * If applicable, add the following below this CDDL HEADER, with the
17eda14cbcSMatt Macy * fields enclosed by brackets "[]" replaced with your own identifying
18eda14cbcSMatt Macy * information: Portions Copyright [yyyy] [name of copyright owner]
19eda14cbcSMatt Macy *
20eda14cbcSMatt Macy * CDDL HEADER END
21eda14cbcSMatt Macy */
22eda14cbcSMatt Macy /*
23eda14cbcSMatt Macy * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24eda14cbcSMatt Macy * Copyright (c) 2013, 2020 by Delphix. All rights reserved.
25eda14cbcSMatt Macy */
26eda14cbcSMatt Macy
27eda14cbcSMatt Macy #include <sys/types.h>
28eda14cbcSMatt Macy #include <sys/param.h>
29eda14cbcSMatt Macy #include <sys/errno.h>
30eda14cbcSMatt Macy #include <sys/kmem.h>
31eda14cbcSMatt Macy #include <sys/sunddi.h>
32eda14cbcSMatt Macy #include <sys/zfs_ioctl.h>
33eda14cbcSMatt Macy #include <sys/zfs_onexit.h>
34eda14cbcSMatt Macy #include <sys/zvol.h>
35eda14cbcSMatt Macy
36eda14cbcSMatt Macy /*
37eda14cbcSMatt Macy * ZFS kernel routines may add/delete callback routines to be invoked
38eda14cbcSMatt Macy * upon process exit (triggered via the close operation from the /dev/zfs
39eda14cbcSMatt Macy * driver).
40eda14cbcSMatt Macy *
41eda14cbcSMatt Macy * These cleanup callbacks are intended to allow for the accumulation
42eda14cbcSMatt Macy * of kernel state across multiple ioctls. User processes participate
43eda14cbcSMatt Macy * simply by opening ZFS_DEV. This causes the ZFS driver to do create
44eda14cbcSMatt Macy * some private data for the file descriptor and generating a unique
45eda14cbcSMatt Macy * minor number. The process then passes along that file descriptor to
46eda14cbcSMatt Macy * each ioctl that might have a cleanup operation.
47eda14cbcSMatt Macy *
48eda14cbcSMatt Macy * Consumers of the onexit routines should call zfs_onexit_fd_hold() early
49eda14cbcSMatt Macy * on to validate the given fd and add a reference to its file table entry.
50eda14cbcSMatt Macy * This allows the consumer to do its work and then add a callback, knowing
51eda14cbcSMatt Macy * that zfs_onexit_add_cb() won't fail with EBADF. When finished, consumers
52eda14cbcSMatt Macy * should call zfs_onexit_fd_rele().
53eda14cbcSMatt Macy *
54eda14cbcSMatt Macy * A simple example is zfs_ioc_recv(), where we might create an AVL tree
55eda14cbcSMatt Macy * with dataset/GUID mappings and then reuse that tree on subsequent
56eda14cbcSMatt Macy * zfs_ioc_recv() calls.
57eda14cbcSMatt Macy *
58eda14cbcSMatt Macy * On the first zfs_ioc_recv() call, dmu_recv_stream() will kmem_alloc()
59eda14cbcSMatt Macy * the AVL tree and pass it along with a callback function to
60eda14cbcSMatt Macy * zfs_onexit_add_cb(). The zfs_onexit_add_cb() routine will register the
61eda14cbcSMatt Macy * callback and return an action handle.
62eda14cbcSMatt Macy *
63eda14cbcSMatt Macy * The action handle is then passed from user space to subsequent
64eda14cbcSMatt Macy * zfs_ioc_recv() calls, so that dmu_recv_stream() can fetch its AVL tree
65eda14cbcSMatt Macy * by calling zfs_onexit_cb_data() with the device minor number and
66eda14cbcSMatt Macy * action handle.
67eda14cbcSMatt Macy *
68eda14cbcSMatt Macy * If the user process exits abnormally, the callback is invoked implicitly
69eda14cbcSMatt Macy * as part of the driver close operation. Once the user space process is
70eda14cbcSMatt Macy * finished with the accumulated kernel state, it can also just call close(2)
71eda14cbcSMatt Macy * on the cleanup fd to trigger the cleanup callback.
72eda14cbcSMatt Macy */
73eda14cbcSMatt Macy
74eda14cbcSMatt Macy void
zfs_onexit_init(zfs_onexit_t ** zop)75eda14cbcSMatt Macy zfs_onexit_init(zfs_onexit_t **zop)
76eda14cbcSMatt Macy {
77eda14cbcSMatt Macy zfs_onexit_t *zo;
78eda14cbcSMatt Macy
79eda14cbcSMatt Macy zo = *zop = kmem_zalloc(sizeof (zfs_onexit_t), KM_SLEEP);
80eda14cbcSMatt Macy mutex_init(&zo->zo_lock, NULL, MUTEX_DEFAULT, NULL);
81eda14cbcSMatt Macy list_create(&zo->zo_actions, sizeof (zfs_onexit_action_node_t),
82eda14cbcSMatt Macy offsetof(zfs_onexit_action_node_t, za_link));
83eda14cbcSMatt Macy }
84eda14cbcSMatt Macy
85eda14cbcSMatt Macy void
zfs_onexit_destroy(zfs_onexit_t * zo)86eda14cbcSMatt Macy zfs_onexit_destroy(zfs_onexit_t *zo)
87eda14cbcSMatt Macy {
88eda14cbcSMatt Macy zfs_onexit_action_node_t *ap;
89eda14cbcSMatt Macy
90eda14cbcSMatt Macy mutex_enter(&zo->zo_lock);
914e8d558cSMartin Matuska while ((ap = list_remove_head(&zo->zo_actions)) != NULL) {
92eda14cbcSMatt Macy mutex_exit(&zo->zo_lock);
93eda14cbcSMatt Macy ap->za_func(ap->za_data);
94eda14cbcSMatt Macy kmem_free(ap, sizeof (zfs_onexit_action_node_t));
95eda14cbcSMatt Macy mutex_enter(&zo->zo_lock);
96eda14cbcSMatt Macy }
97eda14cbcSMatt Macy mutex_exit(&zo->zo_lock);
98eda14cbcSMatt Macy
99eda14cbcSMatt Macy list_destroy(&zo->zo_actions);
100eda14cbcSMatt Macy mutex_destroy(&zo->zo_lock);
101eda14cbcSMatt Macy kmem_free(zo, sizeof (zfs_onexit_t));
102eda14cbcSMatt Macy }
103eda14cbcSMatt Macy
104eda14cbcSMatt Macy /*
105eda14cbcSMatt Macy * Consumers might need to operate by minor number instead of fd, since
106eda14cbcSMatt Macy * they might be running in another thread (e.g. txg_sync_thread). Callers
107eda14cbcSMatt Macy * of this function must call zfs_onexit_fd_rele() when they're finished
108eda14cbcSMatt Macy * using the minor number.
109eda14cbcSMatt Macy */
1105eb61f6cSMartin Matuska zfs_file_t *
zfs_onexit_fd_hold(int fd,minor_t * minorp)111eda14cbcSMatt Macy zfs_onexit_fd_hold(int fd, minor_t *minorp)
112eda14cbcSMatt Macy {
113eda14cbcSMatt Macy zfs_onexit_t *zo = NULL;
114eda14cbcSMatt Macy
1155eb61f6cSMartin Matuska zfs_file_t *fp = zfs_file_get(fd);
1165eb61f6cSMartin Matuska if (fp == NULL)
1175eb61f6cSMartin Matuska return (NULL);
1185eb61f6cSMartin Matuska
1195eb61f6cSMartin Matuska int error = zfsdev_getminor(fp, minorp);
120eda14cbcSMatt Macy if (error) {
1215eb61f6cSMartin Matuska zfs_onexit_fd_rele(fp);
1225eb61f6cSMartin Matuska return (NULL);
123eda14cbcSMatt Macy }
124eda14cbcSMatt Macy
125eda14cbcSMatt Macy zo = zfsdev_get_state(*minorp, ZST_ONEXIT);
126eda14cbcSMatt Macy if (zo == NULL) {
1275eb61f6cSMartin Matuska zfs_onexit_fd_rele(fp);
1285eb61f6cSMartin Matuska return (NULL);
129eda14cbcSMatt Macy }
1305eb61f6cSMartin Matuska return (fp);
131eda14cbcSMatt Macy }
132eda14cbcSMatt Macy
133eda14cbcSMatt Macy void
zfs_onexit_fd_rele(zfs_file_t * fp)1345eb61f6cSMartin Matuska zfs_onexit_fd_rele(zfs_file_t *fp)
135eda14cbcSMatt Macy {
1365eb61f6cSMartin Matuska zfs_file_put(fp);
137eda14cbcSMatt Macy }
138eda14cbcSMatt Macy
139eda14cbcSMatt Macy static int
zfs_onexit_minor_to_state(minor_t minor,zfs_onexit_t ** zo)140eda14cbcSMatt Macy zfs_onexit_minor_to_state(minor_t minor, zfs_onexit_t **zo)
141eda14cbcSMatt Macy {
142eda14cbcSMatt Macy *zo = zfsdev_get_state(minor, ZST_ONEXIT);
143eda14cbcSMatt Macy if (*zo == NULL)
144eda14cbcSMatt Macy return (SET_ERROR(EBADF));
145eda14cbcSMatt Macy
146eda14cbcSMatt Macy return (0);
147eda14cbcSMatt Macy }
148eda14cbcSMatt Macy
149eda14cbcSMatt Macy /*
150eda14cbcSMatt Macy * Add a callback to be invoked when the calling process exits.
151eda14cbcSMatt Macy */
152eda14cbcSMatt Macy int
zfs_onexit_add_cb(minor_t minor,void (* func)(void *),void * data,uintptr_t * action_handle)153eda14cbcSMatt Macy zfs_onexit_add_cb(minor_t minor, void (*func)(void *), void *data,
154dbd5678dSMartin Matuska uintptr_t *action_handle)
155eda14cbcSMatt Macy {
156eda14cbcSMatt Macy zfs_onexit_t *zo;
157eda14cbcSMatt Macy zfs_onexit_action_node_t *ap;
158eda14cbcSMatt Macy int error;
159eda14cbcSMatt Macy
160eda14cbcSMatt Macy error = zfs_onexit_minor_to_state(minor, &zo);
161eda14cbcSMatt Macy if (error)
162eda14cbcSMatt Macy return (error);
163eda14cbcSMatt Macy
164eda14cbcSMatt Macy ap = kmem_alloc(sizeof (zfs_onexit_action_node_t), KM_SLEEP);
165eda14cbcSMatt Macy list_link_init(&ap->za_link);
166eda14cbcSMatt Macy ap->za_func = func;
167eda14cbcSMatt Macy ap->za_data = data;
168eda14cbcSMatt Macy
169eda14cbcSMatt Macy mutex_enter(&zo->zo_lock);
170eda14cbcSMatt Macy list_insert_tail(&zo->zo_actions, ap);
171eda14cbcSMatt Macy mutex_exit(&zo->zo_lock);
172eda14cbcSMatt Macy if (action_handle)
173dbd5678dSMartin Matuska *action_handle = (uintptr_t)ap;
174eda14cbcSMatt Macy
175eda14cbcSMatt Macy return (0);
176eda14cbcSMatt Macy }
177