Commit | Line | Data |
---|---|---|
96f37eab | 1 | // SPDX-License-Identifier: GPL-2.0 |
d641d4ca | 2 | #ifndef NO_BCACHEFS_FS |
96f37eab KO |
3 | |
4 | #include "bcachefs.h" | |
96f37eab KO |
5 | #include "thread_with_file.h" |
6 | ||
7 | #include <linux/anon_inodes.h> | |
8 | #include <linux/file.h> | |
9 | #include <linux/kthread.h> | |
10 | #include <linux/pagemap.h> | |
11 | #include <linux/poll.h> | |
a5a650d6 | 12 | #include <linux/sched/sysctl.h> |
96f37eab KO |
13 | |
14 | void bch2_thread_with_file_exit(struct thread_with_file *thr) | |
15 | { | |
16 | if (thr->task) { | |
17 | kthread_stop(thr->task); | |
18 | put_task_struct(thr->task); | |
19 | } | |
20 | } | |
21 | ||
22 | int bch2_run_thread_with_file(struct thread_with_file *thr, | |
23 | const struct file_operations *fops, | |
24 | int (*fn)(void *)) | |
25 | { | |
26 | struct file *file = NULL; | |
27 | int ret, fd = -1; | |
28 | unsigned fd_flags = O_CLOEXEC; | |
29 | ||
30 | if (fops->read && fops->write) | |
31 | fd_flags |= O_RDWR; | |
32 | else if (fops->read) | |
33 | fd_flags |= O_RDONLY; | |
34 | else if (fops->write) | |
35 | fd_flags |= O_WRONLY; | |
36 | ||
37 | char name[TASK_COMM_LEN]; | |
38 | get_task_comm(name, current); | |
39 | ||
40 | thr->ret = 0; | |
41 | thr->task = kthread_create(fn, thr, "%s", name); | |
42 | ret = PTR_ERR_OR_ZERO(thr->task); | |
43 | if (ret) | |
44 | return ret; | |
45 | ||
46 | ret = get_unused_fd_flags(fd_flags); | |
47 | if (ret < 0) | |
48 | goto err; | |
49 | fd = ret; | |
50 | ||
51 | file = anon_inode_getfile(name, fops, thr, fd_flags); | |
52 | ret = PTR_ERR_OR_ZERO(file); | |
53 | if (ret) | |
54 | goto err; | |
55 | ||
96f37eab KO |
56 | get_task_struct(thr->task); |
57 | wake_up_process(thr->task); | |
dd839f31 | 58 | fd_install(fd, file); |
96f37eab KO |
59 | return fd; |
60 | err: | |
61 | if (fd >= 0) | |
62 | put_unused_fd(fd); | |
63 | if (thr->task) | |
64 | kthread_stop(thr->task); | |
65 | return ret; | |
66 | } | |
67 | ||
60e1baa8 KO |
68 | /* stdio_redirect */ |
69 | ||
70 | static bool stdio_redirect_has_input(struct stdio_redirect *stdio) | |
71 | { | |
72 | return stdio->input.buf.nr || stdio->done; | |
73 | } | |
74 | ||
75 | static bool stdio_redirect_has_output(struct stdio_redirect *stdio) | |
96f37eab | 76 | { |
60e1baa8 | 77 | return stdio->output.buf.nr || stdio->done; |
96f37eab KO |
78 | } |
79 | ||
032b3fd0 | 80 | #define STDIO_REDIRECT_BUFSIZE 4096 |
60e1baa8 KO |
81 | |
82 | static bool stdio_redirect_has_input_space(struct stdio_redirect *stdio) | |
83 | { | |
032b3fd0 | 84 | return stdio->input.buf.nr < STDIO_REDIRECT_BUFSIZE || stdio->done; |
60e1baa8 KO |
85 | } |
86 | ||
87 | static bool stdio_redirect_has_output_space(struct stdio_redirect *stdio) | |
88 | { | |
032b3fd0 | 89 | return stdio->output.buf.nr < STDIO_REDIRECT_BUFSIZE || stdio->done; |
60e1baa8 KO |
90 | } |
91 | ||
92 | static void stdio_buf_init(struct stdio_buf *buf) | |
93 | { | |
94 | spin_lock_init(&buf->lock); | |
95 | init_waitqueue_head(&buf->wait); | |
96 | darray_init(&buf->buf); | |
97 | } | |
98 | ||
99 | /* thread_with_stdio */ | |
100 | ||
8f9320d3 KO |
101 | static void thread_with_stdio_done(struct thread_with_stdio *thr) |
102 | { | |
103 | thr->thr.done = true; | |
104 | thr->stdio.done = true; | |
105 | wake_up(&thr->stdio.input.wait); | |
106 | wake_up(&thr->stdio.output.wait); | |
107 | } | |
108 | ||
e017047f | 109 | static ssize_t thread_with_stdio_read(struct file *file, char __user *ubuf, |
96f37eab KO |
110 | size_t len, loff_t *ppos) |
111 | { | |
112 | struct thread_with_stdio *thr = | |
113 | container_of(file->private_data, struct thread_with_stdio, thr); | |
60e1baa8 | 114 | struct stdio_buf *buf = &thr->stdio.output; |
96f37eab KO |
115 | size_t copied = 0, b; |
116 | int ret = 0; | |
117 | ||
60e1baa8 KO |
118 | if (!(file->f_flags & O_NONBLOCK)) { |
119 | ret = wait_event_interruptible(buf->wait, stdio_redirect_has_output(&thr->stdio)); | |
120 | if (ret) | |
121 | return ret; | |
122 | } else if (!stdio_redirect_has_output(&thr->stdio)) | |
96f37eab KO |
123 | return -EAGAIN; |
124 | ||
60e1baa8 | 125 | while (len && buf->buf.nr) { |
e017047f | 126 | if (fault_in_writeable(ubuf, len) == len) { |
96f37eab KO |
127 | ret = -EFAULT; |
128 | break; | |
129 | } | |
130 | ||
60e1baa8 KO |
131 | spin_lock_irq(&buf->lock); |
132 | b = min_t(size_t, len, buf->buf.nr); | |
e017047f | 133 | |
60e1baa8 | 134 | if (b && !copy_to_user_nofault(ubuf, buf->buf.data, b)) { |
e017047f KO |
135 | ubuf += b; |
136 | len -= b; | |
137 | copied += b; | |
60e1baa8 KO |
138 | buf->buf.nr -= b; |
139 | memmove(buf->buf.data, | |
140 | buf->buf.data + b, | |
141 | buf->buf.nr); | |
e017047f | 142 | } |
60e1baa8 | 143 | spin_unlock_irq(&buf->lock); |
96f37eab KO |
144 | } |
145 | ||
146 | return copied ?: ret; | |
147 | } | |
148 | ||
149 | static int thread_with_stdio_release(struct inode *inode, struct file *file) | |
150 | { | |
151 | struct thread_with_stdio *thr = | |
152 | container_of(file->private_data, struct thread_with_stdio, thr); | |
153 | ||
8f9320d3 | 154 | thread_with_stdio_done(thr); |
96f37eab | 155 | bch2_thread_with_file_exit(&thr->thr); |
60e1baa8 KO |
156 | darray_exit(&thr->stdio.input.buf); |
157 | darray_exit(&thr->stdio.output.buf); | |
ab6752e2 | 158 | thr->ops->exit(thr); |
96f37eab KO |
159 | return 0; |
160 | } | |
161 | ||
96f37eab KO |
162 | static ssize_t thread_with_stdio_write(struct file *file, const char __user *ubuf, |
163 | size_t len, loff_t *ppos) | |
164 | { | |
165 | struct thread_with_stdio *thr = | |
166 | container_of(file->private_data, struct thread_with_stdio, thr); | |
60e1baa8 | 167 | struct stdio_buf *buf = &thr->stdio.input; |
96f37eab KO |
168 | size_t copied = 0; |
169 | ssize_t ret = 0; | |
170 | ||
171 | while (len) { | |
172 | if (thr->thr.done) { | |
173 | ret = -EPIPE; | |
174 | break; | |
175 | } | |
176 | ||
177 | size_t b = len - fault_in_readable(ubuf, len); | |
178 | if (!b) { | |
179 | ret = -EFAULT; | |
180 | break; | |
181 | } | |
182 | ||
60e1baa8 | 183 | spin_lock(&buf->lock); |
032b3fd0 KO |
184 | if (buf->buf.nr < STDIO_REDIRECT_BUFSIZE) |
185 | darray_make_room_gfp(&buf->buf, | |
186 | min(b, STDIO_REDIRECT_BUFSIZE - buf->buf.nr), GFP_NOWAIT); | |
60e1baa8 | 187 | b = min(len, darray_room(buf->buf)); |
96f37eab | 188 | |
032b3fd0 | 189 | if (b && !copy_from_user_nofault(&darray_top(buf->buf), ubuf, b)) { |
60e1baa8 KO |
190 | buf->buf.nr += b; |
191 | ubuf += b; | |
192 | len -= b; | |
193 | copied += b; | |
96f37eab | 194 | } |
60e1baa8 | 195 | spin_unlock(&buf->lock); |
96f37eab KO |
196 | |
197 | if (b) { | |
60e1baa8 | 198 | wake_up(&buf->wait); |
96f37eab KO |
199 | } else { |
200 | if ((file->f_flags & O_NONBLOCK)) { | |
201 | ret = -EAGAIN; | |
202 | break; | |
203 | } | |
204 | ||
60e1baa8 KO |
205 | ret = wait_event_interruptible(buf->wait, |
206 | stdio_redirect_has_input_space(&thr->stdio)); | |
96f37eab KO |
207 | if (ret) |
208 | break; | |
209 | } | |
210 | } | |
211 | ||
212 | return copied ?: ret; | |
213 | } | |
214 | ||
215 | static __poll_t thread_with_stdio_poll(struct file *file, struct poll_table_struct *wait) | |
216 | { | |
217 | struct thread_with_stdio *thr = | |
218 | container_of(file->private_data, struct thread_with_stdio, thr); | |
219 | ||
60e1baa8 KO |
220 | poll_wait(file, &thr->stdio.output.wait, wait); |
221 | poll_wait(file, &thr->stdio.input.wait, wait); | |
96f37eab KO |
222 | |
223 | __poll_t mask = 0; | |
224 | ||
60e1baa8 | 225 | if (stdio_redirect_has_output(&thr->stdio)) |
96f37eab | 226 | mask |= EPOLLIN; |
60e1baa8 | 227 | if (stdio_redirect_has_input_space(&thr->stdio)) |
96f37eab KO |
228 | mask |= EPOLLOUT; |
229 | if (thr->thr.done) | |
230 | mask |= EPOLLHUP|EPOLLERR; | |
231 | return mask; | |
232 | } | |
233 | ||
fcb1620e DW |
234 | static __poll_t thread_with_stdout_poll(struct file *file, struct poll_table_struct *wait) |
235 | { | |
236 | struct thread_with_stdio *thr = | |
237 | container_of(file->private_data, struct thread_with_stdio, thr); | |
238 | ||
239 | poll_wait(file, &thr->stdio.output.wait, wait); | |
240 | ||
241 | __poll_t mask = 0; | |
242 | ||
243 | if (stdio_redirect_has_output(&thr->stdio)) | |
244 | mask |= EPOLLIN; | |
245 | if (thr->thr.done) | |
246 | mask |= EPOLLHUP|EPOLLERR; | |
247 | return mask; | |
248 | } | |
249 | ||
da23795e KO |
250 | static int thread_with_stdio_flush(struct file *file, fl_owner_t id) |
251 | { | |
252 | struct thread_with_stdio *thr = | |
253 | container_of(file->private_data, struct thread_with_stdio, thr); | |
254 | ||
255 | return thr->thr.ret; | |
256 | } | |
257 | ||
658a1e42 DW |
258 | static long thread_with_stdio_ioctl(struct file *file, unsigned int cmd, unsigned long p) |
259 | { | |
260 | struct thread_with_stdio *thr = | |
261 | container_of(file->private_data, struct thread_with_stdio, thr); | |
262 | ||
263 | if (thr->ops->unlocked_ioctl) | |
264 | return thr->ops->unlocked_ioctl(thr, cmd, p); | |
265 | return -ENOTTY; | |
266 | } | |
267 | ||
96f37eab | 268 | static const struct file_operations thread_with_stdio_fops = { |
60e1baa8 | 269 | .llseek = no_llseek, |
96f37eab KO |
270 | .read = thread_with_stdio_read, |
271 | .write = thread_with_stdio_write, | |
272 | .poll = thread_with_stdio_poll, | |
da23795e | 273 | .flush = thread_with_stdio_flush, |
60e1baa8 | 274 | .release = thread_with_stdio_release, |
658a1e42 | 275 | .unlocked_ioctl = thread_with_stdio_ioctl, |
96f37eab KO |
276 | }; |
277 | ||
fcb1620e DW |
278 | static const struct file_operations thread_with_stdout_fops = { |
279 | .llseek = no_llseek, | |
280 | .read = thread_with_stdio_read, | |
281 | .poll = thread_with_stdout_poll, | |
da23795e | 282 | .flush = thread_with_stdio_flush, |
fcb1620e | 283 | .release = thread_with_stdio_release, |
658a1e42 | 284 | .unlocked_ioctl = thread_with_stdio_ioctl, |
fcb1620e DW |
285 | }; |
286 | ||
a6777ca4 KO |
287 | static int thread_with_stdio_fn(void *arg) |
288 | { | |
289 | struct thread_with_stdio *thr = arg; | |
290 | ||
da23795e | 291 | thr->thr.ret = thr->ops->fn(thr); |
a6777ca4 | 292 | |
8f9320d3 | 293 | thread_with_stdio_done(thr); |
a6777ca4 KO |
294 | return 0; |
295 | } | |
296 | ||
02bed83d KO |
297 | void bch2_thread_with_stdio_init(struct thread_with_stdio *thr, |
298 | const struct thread_with_stdio_ops *ops) | |
96f37eab | 299 | { |
60e1baa8 KO |
300 | stdio_buf_init(&thr->stdio.input); |
301 | stdio_buf_init(&thr->stdio.output); | |
ab6752e2 | 302 | thr->ops = ops; |
02bed83d | 303 | } |
96f37eab | 304 | |
02bed83d KO |
305 | int __bch2_run_thread_with_stdio(struct thread_with_stdio *thr) |
306 | { | |
a6777ca4 | 307 | return bch2_run_thread_with_file(&thr->thr, &thread_with_stdio_fops, thread_with_stdio_fn); |
96f37eab KO |
308 | } |
309 | ||
02bed83d KO |
310 | int bch2_run_thread_with_stdio(struct thread_with_stdio *thr, |
311 | const struct thread_with_stdio_ops *ops) | |
312 | { | |
313 | bch2_thread_with_stdio_init(thr, ops); | |
314 | ||
315 | return __bch2_run_thread_with_stdio(thr); | |
316 | } | |
317 | ||
fcb1620e | 318 | int bch2_run_thread_with_stdout(struct thread_with_stdio *thr, |
ab6752e2 | 319 | const struct thread_with_stdio_ops *ops) |
fcb1620e DW |
320 | { |
321 | stdio_buf_init(&thr->stdio.input); | |
322 | stdio_buf_init(&thr->stdio.output); | |
ab6752e2 | 323 | thr->ops = ops; |
fcb1620e DW |
324 | |
325 | return bch2_run_thread_with_file(&thr->thr, &thread_with_stdout_fops, thread_with_stdio_fn); | |
326 | } | |
327 | EXPORT_SYMBOL_GPL(bch2_run_thread_with_stdout); | |
328 | ||
60e1baa8 | 329 | int bch2_stdio_redirect_read(struct stdio_redirect *stdio, char *ubuf, size_t len) |
96f37eab | 330 | { |
60e1baa8 | 331 | struct stdio_buf *buf = &stdio->input; |
96f37eab | 332 | |
a5a650d6 KO |
333 | /* |
334 | * we're waiting on user input (or for the file descriptor to be | |
335 | * closed), don't want a hung task warning: | |
336 | */ | |
337 | do { | |
338 | wait_event_timeout(buf->wait, stdio_redirect_has_input(stdio), | |
339 | sysctl_hung_task_timeout_secs * HZ / 2); | |
340 | } while (!stdio_redirect_has_input(stdio)); | |
341 | ||
96f37eab KO |
342 | if (stdio->done) |
343 | return -1; | |
344 | ||
60e1baa8 KO |
345 | spin_lock(&buf->lock); |
346 | int ret = min(len, buf->buf.nr); | |
347 | buf->buf.nr -= ret; | |
348 | memcpy(ubuf, buf->buf.data, ret); | |
349 | memmove(buf->buf.data, | |
350 | buf->buf.data + ret, | |
351 | buf->buf.nr); | |
352 | spin_unlock(&buf->lock); | |
96f37eab | 353 | |
60e1baa8 | 354 | wake_up(&buf->wait); |
96f37eab KO |
355 | return ret; |
356 | } | |
357 | ||
60e1baa8 | 358 | int bch2_stdio_redirect_readline(struct stdio_redirect *stdio, char *ubuf, size_t len) |
96f37eab | 359 | { |
60e1baa8 | 360 | struct stdio_buf *buf = &stdio->input; |
f704f108 KO |
361 | size_t copied = 0; |
362 | ssize_t ret = 0; | |
363 | again: | |
a5a650d6 KO |
364 | do { |
365 | wait_event_timeout(buf->wait, stdio_redirect_has_input(stdio), | |
366 | sysctl_hung_task_timeout_secs * HZ / 2); | |
367 | } while (!stdio_redirect_has_input(stdio)); | |
368 | ||
f704f108 KO |
369 | if (stdio->done) { |
370 | ret = -1; | |
371 | goto out; | |
372 | } | |
96f37eab | 373 | |
60e1baa8 | 374 | spin_lock(&buf->lock); |
f704f108 KO |
375 | size_t b = min(len, buf->buf.nr); |
376 | char *n = memchr(buf->buf.data, '\n', b); | |
377 | if (n) | |
378 | b = min_t(size_t, b, n + 1 - buf->buf.data); | |
379 | buf->buf.nr -= b; | |
380 | memcpy(ubuf, buf->buf.data, b); | |
60e1baa8 | 381 | memmove(buf->buf.data, |
f704f108 | 382 | buf->buf.data + b, |
60e1baa8 | 383 | buf->buf.nr); |
f704f108 KO |
384 | ubuf += b; |
385 | len -= b; | |
386 | copied += b; | |
60e1baa8 KO |
387 | spin_unlock(&buf->lock); |
388 | ||
389 | wake_up(&buf->wait); | |
f704f108 KO |
390 | |
391 | if (!n && len) | |
392 | goto again; | |
393 | out: | |
394 | return copied ?: ret; | |
96f37eab | 395 | } |
d641d4ca | 396 | |
60e1baa8 | 397 | __printf(3, 0) |
1cbae651 | 398 | static ssize_t bch2_darray_vprintf(darray_char *out, gfp_t gfp, const char *fmt, va_list args) |
60e1baa8 | 399 | { |
1cbae651 | 400 | ssize_t ret; |
60e1baa8 KO |
401 | |
402 | do { | |
403 | va_list args2; | |
1cbae651 | 404 | size_t len; |
60e1baa8 | 405 | |
1cbae651 | 406 | va_copy(args2, args); |
60e1baa8 | 407 | len = vsnprintf(out->data + out->nr, darray_room(*out), fmt, args2); |
6b333129 KO |
408 | va_end(args2); |
409 | ||
1cbae651 DW |
410 | if (len + 1 <= darray_room(*out)) { |
411 | out->nr += len; | |
412 | return len; | |
413 | } | |
60e1baa8 | 414 | |
1cbae651 DW |
415 | ret = darray_make_room_gfp(out, len + 1, gfp); |
416 | } while (ret == 0); | |
417 | ||
418 | return ret; | |
60e1baa8 KO |
419 | } |
420 | ||
1cbae651 DW |
421 | ssize_t bch2_stdio_redirect_vprintf(struct stdio_redirect *stdio, bool nonblocking, |
422 | const char *fmt, va_list args) | |
60e1baa8 KO |
423 | { |
424 | struct stdio_buf *buf = &stdio->output; | |
425 | unsigned long flags; | |
1cbae651 | 426 | ssize_t ret; |
60e1baa8 | 427 | |
1cbae651 | 428 | again: |
60e1baa8 | 429 | spin_lock_irqsave(&buf->lock, flags); |
1cbae651 | 430 | ret = bch2_darray_vprintf(&buf->buf, GFP_NOWAIT, fmt, args); |
60e1baa8 KO |
431 | spin_unlock_irqrestore(&buf->lock, flags); |
432 | ||
1cbae651 DW |
433 | if (ret < 0) { |
434 | if (nonblocking) | |
435 | return -EAGAIN; | |
436 | ||
437 | ret = wait_event_interruptible(buf->wait, | |
438 | stdio_redirect_has_output_space(stdio)); | |
439 | if (ret) | |
440 | return ret; | |
441 | goto again; | |
442 | } | |
443 | ||
60e1baa8 | 444 | wake_up(&buf->wait); |
1cbae651 | 445 | return ret; |
60e1baa8 KO |
446 | } |
447 | ||
1cbae651 | 448 | ssize_t bch2_stdio_redirect_printf(struct stdio_redirect *stdio, bool nonblocking, |
60e1baa8 KO |
449 | const char *fmt, ...) |
450 | { | |
60e1baa8 | 451 | va_list args; |
1cbae651 DW |
452 | ssize_t ret; |
453 | ||
60e1baa8 | 454 | va_start(args, fmt); |
1cbae651 | 455 | ret = bch2_stdio_redirect_vprintf(stdio, nonblocking, fmt, args); |
60e1baa8 | 456 | va_end(args); |
1cbae651 DW |
457 | |
458 | return ret; | |
60e1baa8 KO |
459 | } |
460 | ||
d641d4ca | 461 | #endif /* NO_BCACHEFS_FS */ |