1 /*- 2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org> 3 * Copyright (c) 2019 The DragonFly Project 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include "fuse.h" 29 30 #include <sys/signalvar.h> 31 #include <sys/kern_syscall.h> 32 33 static MALLOC_DEFINE(M_FUSE_BUF, "fuse_buf", "FUSE buf"); 34 static MALLOC_DEFINE(M_FUSE_IPC, "fuse_ipc", "FUSE ipc"); 35 36 static struct objcache *fuse_ipc_objcache = NULL; 37 static struct objcache_malloc_args fuse_ipc_args = { 38 sizeof(struct fuse_ipc), M_FUSE_IPC, 39 }; 40 41 static int 42 fuse_block_sigs(sigset_t *oldset) 43 { 44 if (curproc) { 45 sigset_t newset; 46 int error; 47 48 SIGFILLSET(newset); 49 SIGDELSET(newset, SIGKILL); 50 51 error = kern_sigprocmask(SIG_BLOCK, &newset, oldset); 52 KKASSERT(!error); 53 return error; 54 } 55 56 return -1; 57 } 58 59 static int 60 fuse_restore_sigs(sigset_t *oldset) 61 { 62 if (curproc) { 63 int error = kern_sigprocmask(SIG_SETMASK, oldset, NULL); 64 KKASSERT(!error); 65 return error; 66 } 67 68 return -1; 69 } 70 71 void 72 fuse_buf_alloc(struct fuse_buf *fbp, size_t len) 73 { 74 fbp->buf = kmalloc(len, M_FUSE_BUF, M_WAITOK | M_ZERO); 75 KKASSERT(fbp->buf); 76 fbp->len = len; 77 } 78 79 void 80 fuse_buf_free(struct fuse_buf *fbp) 81 { 82 if (fbp->buf) { 83 kfree(fbp->buf, M_FUSE_BUF); 84 fbp->buf = NULL; 85 } 86 fbp->len = 0; 87 } 88 89 struct fuse_ipc* 90 fuse_ipc_get(struct fuse_mount *fmp, size_t len) 91 { 92 struct fuse_ipc *fip; 93 94 fip = objcache_get(fuse_ipc_objcache, M_WAITOK); 95 refcount_init(&fip->refcnt, 1); 96 fip->fmp = fmp; 97 fip->unique = atomic_fetchadd_long(&fmp->unique, 1); 98 fip->done = 0; 99 100 fuse_buf_alloc(&fip->request, sizeof(struct fuse_in_header) + len); 101 fip->reply.buf = NULL; 102 103 return fip; 104 } 105 106 void 107 fuse_ipc_put(struct fuse_ipc *fip) 108 { 109 if (refcount_release(&fip->refcnt)) { 110 fuse_buf_free(&fip->request); 111 fuse_buf_free(&fip->reply); 112 objcache_put(fuse_ipc_objcache, fip); 113 } 114 } 115 116 static void 117 fuse_ipc_remove(struct fuse_ipc *fip) 118 { 119 struct fuse_mount *fmp = fip->fmp; 120 struct fuse_ipc *p; 121 122 mtx_lock(&fmp->ipc_lock); 123 TAILQ_FOREACH(p, &fmp->request_head, request_entry) { 124 if (fip == p) { 125 TAILQ_REMOVE(&fmp->request_head, p, request_entry); 126 break; 127 } 128 } 129 TAILQ_FOREACH(p, &fmp->reply_head, reply_entry) { 130 if (fip == p) { 131 TAILQ_REMOVE(&fmp->reply_head, p, reply_entry); 132 break; 133 } 134 } 135 mtx_unlock(&fmp->ipc_lock); 136 } 137 138 void* 139 fuse_ipc_fill(struct fuse_ipc *fip, int op, uint64_t ino, struct ucred *cred) 140 { 141 if (!cred) 142 cred = curthread->td_ucred; 143 144 fuse_fill_in_header(fuse_in(fip), fuse_in_size(fip), op, fip->unique, 145 ino, cred->cr_uid, cred->cr_rgid, 146 curthread->td_proc ? curthread->td_proc->p_pid : 0); 147 148 fuse_dbgipc(fip, 0, ""); 149 150 return fuse_in_data(fip); 151 } 152 153 static int 154 fuse_ipc_wait(struct fuse_ipc *fip) 155 { 156 sigset_t oldset; 157 int error, retry = 0; 158 159 if (fuse_test_dead(fip->fmp)) { 160 KKASSERT(!fuse_ipc_test_replied(fip)); 161 fuse_ipc_set_replied(fip); 162 return ENOTCONN; 163 } 164 165 if (fuse_ipc_test_replied(fip)) 166 return 0; 167 again: 168 fuse_block_sigs(&oldset); 169 error = tsleep(fip, 0, "ftxp", 5 * hz); 170 fuse_restore_sigs(&oldset); 171 if (!error) 172 KKASSERT(fuse_ipc_test_replied(fip)); 173 174 if (error == EWOULDBLOCK) { 175 if (!fuse_ipc_test_replied(fip)) { 176 if (!retry) 177 fuse_print("timeout/retry\n"); 178 if (retry++ < 6) 179 goto again; 180 fuse_print("timeout\n"); 181 fuse_ipc_remove(fip); 182 fuse_ipc_set_replied(fip); 183 return ETIMEDOUT; 184 } else 185 fuse_dbg("EWOULDBLOCK lost race\n"); 186 } else if (error) { 187 fuse_print("error=%d\n", error); 188 fuse_ipc_remove(fip); 189 fuse_ipc_set_replied(fip); 190 return error; 191 } 192 193 if (fuse_test_dead(fip->fmp)) { 194 KKASSERT(fuse_ipc_test_replied(fip)); 195 return ENOTCONN; 196 } 197 198 return 0; 199 } 200 201 int 202 fuse_ipc_tx(struct fuse_ipc *fip) 203 { 204 struct fuse_mount *fmp = fip->fmp; 205 struct fuse_out_header *ohd; 206 int error; 207 208 if (fuse_test_dead(fmp)) { 209 fuse_ipc_put(fip); 210 return ENOTCONN; 211 } 212 213 mtx_lock(&fmp->mnt_lock); 214 215 mtx_lock(&fmp->ipc_lock); 216 TAILQ_INSERT_TAIL(&fmp->reply_head, fip, reply_entry); 217 TAILQ_INSERT_TAIL(&fmp->request_head, fip, request_entry); 218 mtx_unlock(&fmp->ipc_lock); 219 220 wakeup(fmp); 221 KNOTE(&fmp->kq.ki_note, 0); 222 mtx_unlock(&fmp->mnt_lock); 223 224 error = fuse_ipc_wait(fip); 225 KKASSERT(fuse_ipc_test_replied(fip)); 226 if (error) { 227 fuse_dbgipc(fip, error, "ipc_wait"); 228 fuse_ipc_put(fip); 229 return error; 230 } 231 232 ohd = fuse_out(fip); 233 KKASSERT(ohd); 234 error = ohd->error; 235 if (error) { 236 fuse_dbgipc(fip, error, "ipc_error"); 237 fuse_ipc_put(fip); 238 if (error < 0) 239 error = -error; 240 return error; 241 } 242 fuse_dbgipc(fip, 0, "done"); 243 244 return 0; 245 } 246 247 void 248 fuse_ipc_init(void) 249 { 250 fuse_ipc_objcache = objcache_create("fuse_ipc", 0, 0, 251 NULL, NULL, NULL, 252 objcache_malloc_alloc_zero, objcache_malloc_free, &fuse_ipc_args); 253 } 254 255 void 256 fuse_ipc_cleanup(void) 257 { 258 objcache_destroy(fuse_ipc_objcache); 259 } 260