xref: /dragonfly/sys/vfs/fuse/fuse_ipc.c (revision 335b9e93)
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