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 9 * or http://www.opensolaris.org/os/licensing. 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. 23 */ 24 25 #include <sys/types.h> 26 #include <sys/param.h> 27 #include <sys/errno.h> 28 #include <sys/open.h> 29 #include <sys/kmem.h> 30 #include <sys/conf.h> 31 #include <sys/ddi.h> 32 #include <sys/sunddi.h> 33 #include <sys/zfs_ioctl.h> 34 #include <sys/mkdev.h> 35 #include <sys/zfs_onexit.h> 36 #include <sys/zvol.h> 37 38 /* 39 * ZFS kernel routines may add/delete callback routines to be invoked 40 * upon process exit (triggered via the close operation from the /dev/zfs 41 * driver). 42 * 43 * These cleanup callbacks are intended to allow for the accumulation 44 * of kernel state across multiple ioctls. User processes participate 45 * by opening ZFS_DEV with O_EXCL. This causes the ZFS driver to do a 46 * clone-open, generating a unique minor number. The process then passes 47 * along that file descriptor to each ioctl that might have a cleanup operation. 48 * 49 * A simple example is zfs_ioc_recv(), where we might create an AVL tree 50 * with dataset/GUID mappings and then reuse that tree on subsequent 51 * zfs_ioc_recv() calls. 52 * 53 * On the first zfs_ioc_recv() call, dmu_recv_stream() will kmem_alloc() 54 * the AVL tree and pass it along with a callback function to 55 * zfs_onexit_add_cb(). The zfs_onexit_add_cb() routine will register the 56 * callback and return an action handle. 57 * 58 * The action handle is then passed from user space to subsequent 59 * zfs_ioc_recv() calls, so that dmu_recv_stream() can fetch its AVL tree 60 * by calling zfs_onexit_cb_data() with the cleanup fd and action handle. 61 * 62 * If the user process exits abnormally, the callback is invoked implicitly 63 * as part of the driver close operation. Once the user space process is 64 * finished with the accumulated kernel state, it can also just call close(2) 65 * on the cleanup fd to trigger the cleanup callback. 66 */ 67 68 void 69 zfs_onexit_init(zfs_onexit_t **zop) 70 { 71 zfs_onexit_t *zo; 72 73 zo = *zop = kmem_zalloc(sizeof (zfs_onexit_t), KM_SLEEP); 74 mutex_init(&zo->zo_lock, NULL, MUTEX_DEFAULT, NULL); 75 list_create(&zo->zo_actions, sizeof (zfs_onexit_action_node_t), 76 offsetof(zfs_onexit_action_node_t, za_link)); 77 } 78 79 void 80 zfs_onexit_destroy(zfs_onexit_t *zo) 81 { 82 zfs_onexit_action_node_t *ap; 83 84 mutex_enter(&zo->zo_lock); 85 while ((ap = list_head(&zo->zo_actions)) != NULL) { 86 list_remove(&zo->zo_actions, ap); 87 mutex_exit(&zo->zo_lock); 88 ap->za_func(ap->za_data); 89 kmem_free(ap, sizeof (zfs_onexit_action_node_t)); 90 mutex_enter(&zo->zo_lock); 91 } 92 mutex_exit(&zo->zo_lock); 93 94 list_destroy(&zo->zo_actions); 95 mutex_destroy(&zo->zo_lock); 96 kmem_free(zo, sizeof (zfs_onexit_t)); 97 } 98 99 static int 100 zfs_onexit_fd_to_state(int fd, zfs_onexit_t **zo) 101 { 102 file_t *fp; 103 dev_t rdev; 104 105 fp = getf(fd); 106 if (fp == NULL) 107 return (EBADF); 108 109 rdev = fp->f_vnode->v_rdev; 110 *zo = zfsdev_get_soft_state(getminor(rdev), ZSST_CTLDEV); 111 if (*zo == NULL) { 112 releasef(fd); 113 return (EBADF); 114 } 115 116 return (0); 117 } 118 119 /* 120 * Add a callback to be invoked when the calling process exits. 121 */ 122 int 123 zfs_onexit_add_cb(int fd, void (*func)(void *), void *data, 124 uint64_t *action_handle) 125 { 126 zfs_onexit_t *zo; 127 zfs_onexit_action_node_t *ap; 128 int error; 129 130 error = zfs_onexit_fd_to_state(fd, &zo); 131 if (error) 132 return (error); 133 134 ap = kmem_alloc(sizeof (zfs_onexit_action_node_t), KM_SLEEP); 135 list_link_init(&ap->za_link); 136 ap->za_func = func; 137 ap->za_data = data; 138 139 mutex_enter(&zo->zo_lock); 140 list_insert_tail(&zo->zo_actions, ap); 141 mutex_exit(&zo->zo_lock); 142 *action_handle = (uint64_t)(uintptr_t)ap; 143 releasef(fd); 144 145 return (0); 146 } 147 148 static zfs_onexit_action_node_t * 149 zfs_onexit_find_cb(zfs_onexit_t *zo, uint64_t action_handle) 150 { 151 zfs_onexit_action_node_t *match; 152 zfs_onexit_action_node_t *ap; 153 list_t *l; 154 155 ASSERT(MUTEX_HELD(&zo->zo_lock)); 156 157 match = (zfs_onexit_action_node_t *)(uintptr_t)action_handle; 158 l = &zo->zo_actions; 159 for (ap = list_head(l); ap != NULL; ap = list_next(l, ap)) { 160 if (match == ap) 161 break; 162 } 163 return (ap); 164 } 165 166 /* 167 * Delete the callback, triggering it first if 'fire' is set. 168 */ 169 int 170 zfs_onexit_del_cb(int fd, uint64_t action_handle, boolean_t fire) 171 { 172 zfs_onexit_t *zo; 173 zfs_onexit_action_node_t *ap; 174 int error; 175 176 error = zfs_onexit_fd_to_state(fd, &zo); 177 if (error) 178 return (error); 179 180 mutex_enter(&zo->zo_lock); 181 ap = zfs_onexit_find_cb(zo, action_handle); 182 if (ap != NULL) { 183 list_remove(&zo->zo_actions, ap); 184 mutex_exit(&zo->zo_lock); 185 if (fire) 186 ap->za_func(ap->za_data); 187 kmem_free(ap, sizeof (zfs_onexit_action_node_t)); 188 } else { 189 mutex_exit(&zo->zo_lock); 190 error = ENOENT; 191 } 192 releasef(fd); 193 194 return (error); 195 } 196 197 /* 198 * Return the data associated with this callback. This allows consumers 199 * of the cleanup-on-exit interfaces to stash kernel data across system 200 * calls, knowing that it will be cleaned up if the calling process exits. 201 */ 202 int 203 zfs_onexit_cb_data(int fd, uint64_t action_handle, void **data) 204 { 205 zfs_onexit_t *zo; 206 zfs_onexit_action_node_t *ap; 207 int error; 208 209 *data = NULL; 210 211 error = zfs_onexit_fd_to_state(fd, &zo); 212 if (error) 213 return (error); 214 215 mutex_enter(&zo->zo_lock); 216 ap = zfs_onexit_find_cb(zo, action_handle); 217 if (ap != NULL) 218 *data = ap->za_data; 219 else 220 error = ENOENT; 221 mutex_exit(&zo->zo_lock); 222 releasef(fd); 223 224 return (error); 225 } 226