xref: /dragonfly/sys/vfs/fuse/fuse_ipc.c (revision 1a8e5e4cf0cfbe16676810f7edca73f98bb9b8f5)
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 #if 0
42 static int
43 fuse_block_sigs(sigset_t *oldset)
44 {
45           if (curproc) {
46                     sigset_t newset;
47                     int error;
48 
49                     SIGFILLSET(newset);
50                     SIGDELSET(newset, SIGKILL);
51 
52                     error = kern_sigprocmask(SIG_BLOCK, &newset, oldset);
53                     KKASSERT(!error);
54                     return error;
55           }
56 
57           return -1;
58 }
59 
60 static int
61 fuse_restore_sigs(sigset_t *oldset)
62 {
63           if (curproc) {
64                     int error = kern_sigprocmask(SIG_SETMASK, oldset, NULL);
65                     KKASSERT(!error);
66                     return error;
67           }
68 
69           return -1;
70 }
71 #endif
72 
73 void
fuse_buf_alloc(struct fuse_buf * fbp,size_t len)74 fuse_buf_alloc(struct fuse_buf *fbp, size_t len)
75 {
76           fbp->buf = kmalloc(len, M_FUSE_BUF, M_WAITOK | M_ZERO);
77           KKASSERT(fbp->buf);
78           fbp->len = len;
79 }
80 
81 void
fuse_buf_free(struct fuse_buf * fbp)82 fuse_buf_free(struct fuse_buf *fbp)
83 {
84           if (fbp->buf) {
85                     kfree(fbp->buf, M_FUSE_BUF);
86                     fbp->buf = NULL;
87           }
88           fbp->len = 0;
89 }
90 
91 struct fuse_ipc*
fuse_ipc_get(struct fuse_mount * fmp,size_t len)92 fuse_ipc_get(struct fuse_mount *fmp, size_t len)
93 {
94           struct fuse_ipc *fip;
95 
96           fip = objcache_get(fuse_ipc_objcache, M_WAITOK);
97           bzero(fip, sizeof(*fip));
98           refcount_init(&fip->refcnt, 1);
99           fip->fmp = fmp;
100           fip->unique = atomic_fetchadd_long(&fmp->unique, 1);
101           fip->done = 0;
102 
103           fuse_buf_alloc(&fip->request, sizeof(struct fuse_in_header) + len);
104           fip->reply.buf = NULL;
105 
106           return fip;
107 }
108 
109 void
fuse_ipc_put(struct fuse_ipc * fip)110 fuse_ipc_put(struct fuse_ipc *fip)
111 {
112           if (refcount_release(&fip->refcnt)) {
113                     fuse_buf_free(&fip->request);
114                     fuse_buf_free(&fip->reply);
115                     objcache_put(fuse_ipc_objcache, fip);
116           }
117 }
118 
119 static void
fuse_ipc_remove(struct fuse_ipc * fip)120 fuse_ipc_remove(struct fuse_ipc *fip)
121 {
122           struct fuse_mount *fmp = fip->fmp;
123           struct fuse_ipc *p;
124 
125           mtx_lock(&fmp->ipc_lock);
126           TAILQ_FOREACH(p, &fmp->request_head, request_entry) {
127                     if (fip == p) {
128                               TAILQ_REMOVE(&fmp->request_head, p, request_entry);
129                               if (atomic_swap_int(&fip->sent, 1) == -1)
130                                         wakeup(fip);
131                               break;
132                     }
133           }
134           TAILQ_FOREACH(p, &fmp->reply_head, reply_entry) {
135                     if (fip == p) {
136                               TAILQ_REMOVE(&fmp->reply_head, p, reply_entry);
137                               break;
138                     }
139           }
140           mtx_unlock(&fmp->ipc_lock);
141 }
142 
143 void*
fuse_ipc_fill(struct fuse_ipc * fip,int op,uint64_t ino,struct ucred * cred)144 fuse_ipc_fill(struct fuse_ipc *fip, int op, uint64_t ino, struct ucred *cred)
145 {
146           if (!cred)
147                     cred = curthread->td_ucred;
148 
149           fuse_fill_in_header(fuse_in(fip), fuse_in_size(fip), op, fip->unique,
150               ino, cred->cr_uid, cred->cr_rgid,
151               curthread->td_proc ? curthread->td_proc->p_pid : 0);
152 
153           fuse_dbgipc(fip, 0, "");
154 
155           return fuse_in_data(fip);
156 }
157 
158 static int
fuse_ipc_wait(struct fuse_ipc * fip)159 fuse_ipc_wait(struct fuse_ipc *fip)
160 {
161           int error, retry = 0;
162 
163           if (fuse_test_dead(fip->fmp)) {
164                     KKASSERT(!fuse_ipc_test_replied(fip));
165                     fuse_ipc_set_replied(fip);
166                     return ENOTCONN;
167           }
168 
169           if (fuse_ipc_test_replied(fip))
170                     return 0;
171 again:
172           tsleep_interlock(fip, 0);
173           if (fuse_ipc_test_replied(fip))
174                     return 0;
175           error = tsleep(fip, PINTERLOCKED, "ftxp", 5 * hz);
176           if (!error)
177                     KKASSERT(fuse_ipc_test_replied(fip));
178 
179           if (error == EWOULDBLOCK) {
180                     if (!fuse_ipc_test_replied(fip)) {
181                               if (!retry)
182                                         fuse_print("timeout/retry\n");
183                               if (retry++ < 6)
184                                         goto again;
185                               fuse_print("timeout\n");
186                               fuse_ipc_remove(fip);
187                               fuse_ipc_set_replied(fip);
188                               return ETIMEDOUT;
189                     } else
190                               fuse_dbg("EWOULDBLOCK lost race\n");
191           } else if (error) {
192                     fuse_print("error=%d\n", error);
193                     fuse_ipc_remove(fip);
194                     fuse_ipc_set_replied(fip);
195                     return error;
196           }
197 
198           if (fuse_test_dead(fip->fmp)) {
199                     KKASSERT(fuse_ipc_test_replied(fip));
200                     return ENOTCONN;
201           }
202 
203           return 0;
204 }
205 
206 static int
fuse_ipc_wait_sent(struct fuse_ipc * fip)207 fuse_ipc_wait_sent(struct fuse_ipc *fip)
208 {
209           int error, retry = 0;
210 
211           if (fuse_test_dead(fip->fmp)) {
212                     KKASSERT(!fuse_ipc_test_replied(fip));
213                     fuse_ipc_remove(fip);
214                     fuse_ipc_set_replied(fip);
215                     return ENOTCONN;
216           }
217 
218           error = 0;
219 
220           for (;;) {
221                     tsleep_interlock(fip, 0);
222                     if (atomic_swap_int(&fip->sent, -1) == 1) {
223                               error = 0;
224                               break;
225                     }
226                     error = tsleep(fip, PINTERLOCKED, "ftxp", 5 * hz);
227                     if (error == EWOULDBLOCK) {
228                               ++retry;
229                               if (retry == 6) {
230                                         fuse_print("timeout\n");
231                                         error = ETIMEDOUT;
232                                         break;
233                               }
234                               fuse_print("timeout/retry\n");
235                     }
236           }
237           if (fuse_test_dead(fip->fmp))
238                     error = ENOTCONN;
239           if (error) {
240                     fuse_ipc_remove(fip);
241                     fuse_ipc_set_replied(fip);
242           }
243           return error;
244 }
245 
246 int
fuse_ipc_tx(struct fuse_ipc * fip)247 fuse_ipc_tx(struct fuse_ipc *fip)
248 {
249           struct fuse_mount *fmp = fip->fmp;
250           struct fuse_out_header *ohd;
251           int error;
252 
253           if (fuse_test_dead(fmp)) {
254                     fuse_ipc_put(fip);
255                     return ENOTCONN;
256           }
257 
258           mtx_lock(&fmp->ipc_lock);
259           TAILQ_INSERT_TAIL(&fmp->reply_head, fip, reply_entry);
260           TAILQ_INSERT_TAIL(&fmp->request_head, fip, request_entry);
261           mtx_unlock(&fmp->ipc_lock);
262 
263           wakeup(fmp);
264           KNOTE(&fmp->kq.ki_note, 0);
265 
266           error = fuse_ipc_wait(fip);
267           KKASSERT(fuse_ipc_test_replied(fip));
268           if (error) {
269                     fuse_dbgipc(fip, error, "ipc_wait");
270                     fuse_ipc_put(fip);
271                     return error;
272           }
273 
274           ohd = fuse_out(fip);
275           KKASSERT(ohd);
276           error = ohd->error;
277           if (error) {
278                     fuse_dbgipc(fip, error, "ipc_error");
279                     fuse_ipc_put(fip);
280                     if (error < 0)
281                               error = -error;
282                     return error;
283           }
284           fuse_dbgipc(fip, 0, "done");
285 
286           return 0;
287 }
288 
289 int
fuse_ipc_tx_noreply(struct fuse_ipc * fip)290 fuse_ipc_tx_noreply(struct fuse_ipc *fip)
291 {
292           struct fuse_mount *fmp = fip->fmp;
293           int error;
294 
295           if (fuse_test_dead(fmp)) {
296                     fuse_ipc_put(fip);
297                     return ENOTCONN;
298           }
299 
300           mtx_lock(&fmp->ipc_lock);
301           TAILQ_INSERT_TAIL(&fmp->request_head, fip, request_entry);
302           mtx_unlock(&fmp->ipc_lock);
303 
304           wakeup(fmp);
305           KNOTE(&fmp->kq.ki_note, 0);
306 
307           error = fuse_ipc_wait_sent(fip);
308           if (error)
309                     fuse_ipc_put(fip);
310           return error;
311 }
312 
313 void
fuse_ipc_init(void)314 fuse_ipc_init(void)
315 {
316           fuse_ipc_objcache = objcache_create("fuse_ipc", 0, 0,
317               NULL, NULL, NULL,
318               objcache_malloc_alloc_zero, objcache_malloc_free, &fuse_ipc_args);
319 }
320 
321 void
fuse_ipc_cleanup(void)322 fuse_ipc_cleanup(void)
323 {
324           objcache_destroy(fuse_ipc_objcache);
325 }
326