Merge branch 'vendor/DHCPCD'
[dragonfly.git] / sys / vfs / fuse / fuse_ipc.c
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 }