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 https://opensource.org/licenses/CDDL-1.0. 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 * Copyright (c) 2013, 2020 by Delphix. All rights reserved. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/param.h> 28 #include <sys/errno.h> 29 #include <sys/kmem.h> 30 #include <sys/sunddi.h> 31 #include <sys/zfs_ioctl.h> 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 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. 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); 90 while ((ap = list_remove_head(&zo->zo_actions)) != NULL) { 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 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 */ 109 zfs_file_t * 110 zfs_onexit_fd_hold(int fd, minor_t *minorp) 111 { 112 zfs_onexit_t *zo = NULL; 113 114 zfs_file_t *fp = zfs_file_get(fd); 115 if (fp == NULL) 116 return (NULL); 117 118 int error = zfsdev_getminor(fp, minorp); 119 if (error) { 120 zfs_onexit_fd_rele(fp); 121 return (NULL); 122 } 123 124 zo = zfsdev_get_state(*minorp, ZST_ONEXIT); 125 if (zo == NULL) { 126 zfs_onexit_fd_rele(fp); 127 return (NULL); 128 } 129 return (fp); 130 } 131 132 void 133 zfs_onexit_fd_rele(zfs_file_t *fp) 134 { 135 zfs_file_put(fp); 136 } 137 138 static int 139 zfs_onexit_minor_to_state(minor_t minor, zfs_onexit_t **zo) 140 { 141 *zo = zfsdev_get_state(minor, ZST_ONEXIT); 142 if (*zo == NULL) 143 return (SET_ERROR(EBADF)); 144 145 return (0); 146 } 147 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, 153 uintptr_t *action_handle) 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 163 ap = kmem_alloc(sizeof (zfs_onexit_action_node_t), KM_SLEEP); 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) 172 *action_handle = (uintptr_t)ap; 173 174 return (0); 175 } 176