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