1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #ifndef NO_BCACHEFS_FS |
3 | |
4 | #include "bcachefs.h" |
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> |
12 | #include <linux/sched/sysctl.h> |
13 | |
14 | void bch2_thread_with_file_exit(struct thread_with_file *thr) |
15 | { |
16 | if (thr->task) { |
17 | kthread_stop(k: thr->task); |
18 | put_task_struct(t: 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(ptr: thr->task); |
43 | if (ret) |
44 | return ret; |
45 | |
46 | ret = get_unused_fd_flags(flags: fd_flags); |
47 | if (ret < 0) |
48 | goto err; |
49 | fd = ret; |
50 | |
51 | file = anon_inode_getfile(name, fops, priv: thr, flags: fd_flags); |
52 | ret = PTR_ERR_OR_ZERO(ptr: file); |
53 | if (ret) |
54 | goto err; |
55 | |
56 | get_task_struct(t: thr->task); |
57 | wake_up_process(tsk: thr->task); |
58 | fd_install(fd, file); |
59 | return fd; |
60 | err: |
61 | if (fd >= 0) |
62 | put_unused_fd(fd); |
63 | if (thr->task) |
64 | kthread_stop(k: thr->task); |
65 | return ret; |
66 | } |
67 | |
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) |
76 | { |
77 | return stdio->output.buf.nr || stdio->done; |
78 | } |
79 | |
80 | #define STDIO_REDIRECT_BUFSIZE 4096 |
81 | |
82 | static bool stdio_redirect_has_input_space(struct stdio_redirect *stdio) |
83 | { |
84 | return stdio->input.buf.nr < STDIO_REDIRECT_BUFSIZE || stdio->done; |
85 | } |
86 | |
87 | static bool stdio_redirect_has_output_space(struct stdio_redirect *stdio) |
88 | { |
89 | return stdio->output.buf.nr < STDIO_REDIRECT_BUFSIZE || stdio->done; |
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 | |
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 | |
109 | static ssize_t thread_with_stdio_read(struct file *file, char __user *ubuf, |
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); |
114 | struct stdio_buf *buf = &thr->stdio.output; |
115 | size_t copied = 0, b; |
116 | int ret = 0; |
117 | |
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(stdio: &thr->stdio)) |
123 | return -EAGAIN; |
124 | |
125 | while (len && buf->buf.nr) { |
126 | if (fault_in_writeable(uaddr: ubuf, size: len) == len) { |
127 | ret = -EFAULT; |
128 | break; |
129 | } |
130 | |
131 | spin_lock_irq(lock: &buf->lock); |
132 | b = min_t(size_t, len, buf->buf.nr); |
133 | |
134 | if (b && !copy_to_user_nofault(dst: ubuf, src: buf->buf.data, size: b)) { |
135 | ubuf += b; |
136 | len -= b; |
137 | copied += b; |
138 | buf->buf.nr -= b; |
139 | memmove(buf->buf.data, |
140 | buf->buf.data + b, |
141 | buf->buf.nr); |
142 | } |
143 | spin_unlock_irq(lock: &buf->lock); |
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 | |
154 | thread_with_stdio_done(thr); |
155 | bch2_thread_with_file_exit(thr: &thr->thr); |
156 | darray_exit(&thr->stdio.input.buf); |
157 | darray_exit(&thr->stdio.output.buf); |
158 | thr->ops->exit(thr); |
159 | return 0; |
160 | } |
161 | |
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); |
167 | struct stdio_buf *buf = &thr->stdio.input; |
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(uaddr: ubuf, size: len); |
178 | if (!b) { |
179 | ret = -EFAULT; |
180 | break; |
181 | } |
182 | |
183 | spin_lock(lock: &buf->lock); |
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); |
187 | b = min(len, darray_room(buf->buf)); |
188 | |
189 | if (b && !copy_from_user_nofault(dst: &darray_top(buf->buf), src: ubuf, size: b)) { |
190 | buf->buf.nr += b; |
191 | ubuf += b; |
192 | len -= b; |
193 | copied += b; |
194 | } |
195 | spin_unlock(lock: &buf->lock); |
196 | |
197 | if (b) { |
198 | wake_up(&buf->wait); |
199 | } else { |
200 | if ((file->f_flags & O_NONBLOCK)) { |
201 | ret = -EAGAIN; |
202 | break; |
203 | } |
204 | |
205 | ret = wait_event_interruptible(buf->wait, |
206 | stdio_redirect_has_input_space(&thr->stdio)); |
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 | |
220 | poll_wait(filp: file, wait_address: &thr->stdio.output.wait, p: wait); |
221 | poll_wait(filp: file, wait_address: &thr->stdio.input.wait, p: wait); |
222 | |
223 | __poll_t mask = 0; |
224 | |
225 | if (stdio_redirect_has_output(stdio: &thr->stdio)) |
226 | mask |= EPOLLIN; |
227 | if (stdio_redirect_has_input_space(stdio: &thr->stdio)) |
228 | mask |= EPOLLOUT; |
229 | if (thr->thr.done) |
230 | mask |= EPOLLHUP|EPOLLERR; |
231 | return mask; |
232 | } |
233 | |
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(filp: file, wait_address: &thr->stdio.output.wait, p: wait); |
240 | |
241 | __poll_t mask = 0; |
242 | |
243 | if (stdio_redirect_has_output(stdio: &thr->stdio)) |
244 | mask |= EPOLLIN; |
245 | if (thr->thr.done) |
246 | mask |= EPOLLHUP|EPOLLERR; |
247 | return mask; |
248 | } |
249 | |
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 | |
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 | |
268 | static const struct file_operations thread_with_stdio_fops = { |
269 | .llseek = no_llseek, |
270 | .read = thread_with_stdio_read, |
271 | .write = thread_with_stdio_write, |
272 | .poll = thread_with_stdio_poll, |
273 | .flush = thread_with_stdio_flush, |
274 | .release = thread_with_stdio_release, |
275 | .unlocked_ioctl = thread_with_stdio_ioctl, |
276 | }; |
277 | |
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, |
282 | .flush = thread_with_stdio_flush, |
283 | .release = thread_with_stdio_release, |
284 | .unlocked_ioctl = thread_with_stdio_ioctl, |
285 | }; |
286 | |
287 | static int thread_with_stdio_fn(void *arg) |
288 | { |
289 | struct thread_with_stdio *thr = arg; |
290 | |
291 | thr->thr.ret = thr->ops->fn(thr); |
292 | |
293 | thread_with_stdio_done(thr); |
294 | return 0; |
295 | } |
296 | |
297 | void bch2_thread_with_stdio_init(struct thread_with_stdio *thr, |
298 | const struct thread_with_stdio_ops *ops) |
299 | { |
300 | stdio_buf_init(buf: &thr->stdio.input); |
301 | stdio_buf_init(buf: &thr->stdio.output); |
302 | thr->ops = ops; |
303 | } |
304 | |
305 | int __bch2_run_thread_with_stdio(struct thread_with_stdio *thr) |
306 | { |
307 | return bch2_run_thread_with_file(thr: &thr->thr, fops: &thread_with_stdio_fops, fn: thread_with_stdio_fn); |
308 | } |
309 | |
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 | |
318 | int bch2_run_thread_with_stdout(struct thread_with_stdio *thr, |
319 | const struct thread_with_stdio_ops *ops) |
320 | { |
321 | stdio_buf_init(buf: &thr->stdio.input); |
322 | stdio_buf_init(buf: &thr->stdio.output); |
323 | thr->ops = ops; |
324 | |
325 | return bch2_run_thread_with_file(thr: &thr->thr, fops: &thread_with_stdout_fops, fn: thread_with_stdio_fn); |
326 | } |
327 | EXPORT_SYMBOL_GPL(bch2_run_thread_with_stdout); |
328 | |
329 | int bch2_stdio_redirect_read(struct stdio_redirect *stdio, char *ubuf, size_t len) |
330 | { |
331 | struct stdio_buf *buf = &stdio->input; |
332 | |
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 | |
342 | if (stdio->done) |
343 | return -1; |
344 | |
345 | spin_lock(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(lock: &buf->lock); |
353 | |
354 | wake_up(&buf->wait); |
355 | return ret; |
356 | } |
357 | |
358 | int bch2_stdio_redirect_readline(struct stdio_redirect *stdio, char *ubuf, size_t len) |
359 | { |
360 | struct stdio_buf *buf = &stdio->input; |
361 | size_t copied = 0; |
362 | ssize_t ret = 0; |
363 | again: |
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 | |
369 | if (stdio->done) { |
370 | ret = -1; |
371 | goto out; |
372 | } |
373 | |
374 | spin_lock(lock: &buf->lock); |
375 | size_t b = min(len, buf->buf.nr); |
376 | char *n = memchr(p: buf->buf.data, c: '\n', size: 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); |
381 | memmove(buf->buf.data, |
382 | buf->buf.data + b, |
383 | buf->buf.nr); |
384 | ubuf += b; |
385 | len -= b; |
386 | copied += b; |
387 | spin_unlock(lock: &buf->lock); |
388 | |
389 | wake_up(&buf->wait); |
390 | |
391 | if (!n && len) |
392 | goto again; |
393 | out: |
394 | return copied ?: ret; |
395 | } |
396 | |
397 | __printf(3, 0) |
398 | static ssize_t bch2_darray_vprintf(darray_char *out, gfp_t gfp, const char *fmt, va_list args) |
399 | { |
400 | ssize_t ret; |
401 | |
402 | do { |
403 | va_list args2; |
404 | size_t len; |
405 | |
406 | va_copy(args2, args); |
407 | len = vsnprintf(buf: out->data + out->nr, darray_room(*out), fmt, args: args2); |
408 | va_end(args2); |
409 | |
410 | if (len + 1 <= darray_room(*out)) { |
411 | out->nr += len; |
412 | return len; |
413 | } |
414 | |
415 | ret = darray_make_room_gfp(out, len + 1, gfp); |
416 | } while (ret == 0); |
417 | |
418 | return ret; |
419 | } |
420 | |
421 | ssize_t bch2_stdio_redirect_vprintf(struct stdio_redirect *stdio, bool nonblocking, |
422 | const char *fmt, va_list args) |
423 | { |
424 | struct stdio_buf *buf = &stdio->output; |
425 | unsigned long flags; |
426 | ssize_t ret; |
427 | |
428 | again: |
429 | spin_lock_irqsave(&buf->lock, flags); |
430 | ret = bch2_darray_vprintf(out: &buf->buf, GFP_NOWAIT, fmt, args); |
431 | spin_unlock_irqrestore(lock: &buf->lock, flags); |
432 | |
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 | |
444 | wake_up(&buf->wait); |
445 | return ret; |
446 | } |
447 | |
448 | ssize_t bch2_stdio_redirect_printf(struct stdio_redirect *stdio, bool nonblocking, |
449 | const char *fmt, ...) |
450 | { |
451 | va_list args; |
452 | ssize_t ret; |
453 | |
454 | va_start(args, fmt); |
455 | ret = bch2_stdio_redirect_vprintf(stdio, nonblocking, fmt, args); |
456 | va_end(args); |
457 | |
458 | return ret; |
459 | } |
460 | |
461 | #endif /* NO_BCACHEFS_FS */ |
462 | |