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
bch2_thread_with_file_exit(struct thread_with_file * thr)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
bch2_run_thread_with_file(struct thread_with_file * thr,const struct file_operations * fops,int (* fn)(void *))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
56 get_task_struct(thr->task);
57 wake_up_process(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(thr->task);
65 return ret;
66 }
67
68 /* stdio_redirect */
69
stdio_redirect_has_more_input(struct stdio_redirect * stdio,size_t seen)70 static bool stdio_redirect_has_more_input(struct stdio_redirect *stdio, size_t seen)
71 {
72 return stdio->input.buf.nr > seen || stdio->done;
73 }
74
stdio_redirect_has_input(struct stdio_redirect * stdio)75 static bool stdio_redirect_has_input(struct stdio_redirect *stdio)
76 {
77 return stdio_redirect_has_more_input(stdio, 0);
78 }
79
stdio_redirect_has_output(struct stdio_redirect * stdio)80 static bool stdio_redirect_has_output(struct stdio_redirect *stdio)
81 {
82 return stdio->output.buf.nr || stdio->done;
83 }
84
85 #define STDIO_REDIRECT_BUFSIZE 4096
86
stdio_redirect_has_input_space(struct stdio_redirect * stdio)87 static bool stdio_redirect_has_input_space(struct stdio_redirect *stdio)
88 {
89 return stdio->input.buf.nr < STDIO_REDIRECT_BUFSIZE || stdio->done;
90 }
91
stdio_redirect_has_output_space(struct stdio_redirect * stdio)92 static bool stdio_redirect_has_output_space(struct stdio_redirect *stdio)
93 {
94 return stdio->output.buf.nr < STDIO_REDIRECT_BUFSIZE || stdio->done;
95 }
96
stdio_buf_init(struct stdio_buf * buf)97 static void stdio_buf_init(struct stdio_buf *buf)
98 {
99 spin_lock_init(&buf->lock);
100 init_waitqueue_head(&buf->wait);
101 darray_init(&buf->buf);
102 }
103
104 /* thread_with_stdio */
105
thread_with_stdio_done(struct thread_with_stdio * thr)106 static void thread_with_stdio_done(struct thread_with_stdio *thr)
107 {
108 thr->thr.done = true;
109 thr->stdio.done = true;
110 wake_up(&thr->stdio.input.wait);
111 wake_up(&thr->stdio.output.wait);
112 }
113
thread_with_stdio_read(struct file * file,char __user * ubuf,size_t len,loff_t * ppos)114 static ssize_t thread_with_stdio_read(struct file *file, char __user *ubuf,
115 size_t len, loff_t *ppos)
116 {
117 struct thread_with_stdio *thr =
118 container_of(file->private_data, struct thread_with_stdio, thr);
119 struct stdio_buf *buf = &thr->stdio.output;
120 size_t copied = 0, b;
121 int ret = 0;
122
123 if (!(file->f_flags & O_NONBLOCK)) {
124 ret = wait_event_interruptible(buf->wait, stdio_redirect_has_output(&thr->stdio));
125 if (ret)
126 return ret;
127 } else if (!stdio_redirect_has_output(&thr->stdio))
128 return -EAGAIN;
129
130 while (len && buf->buf.nr) {
131 if (fault_in_writeable(ubuf, len) == len) {
132 ret = -EFAULT;
133 break;
134 }
135
136 spin_lock_irq(&buf->lock);
137 b = min_t(size_t, len, buf->buf.nr);
138
139 if (b && !copy_to_user_nofault(ubuf, buf->buf.data, b)) {
140 ubuf += b;
141 len -= b;
142 copied += b;
143 buf->buf.nr -= b;
144 memmove(buf->buf.data,
145 buf->buf.data + b,
146 buf->buf.nr);
147 }
148 spin_unlock_irq(&buf->lock);
149 }
150
151 return copied ?: ret;
152 }
153
thread_with_stdio_release(struct inode * inode,struct file * file)154 static int thread_with_stdio_release(struct inode *inode, struct file *file)
155 {
156 struct thread_with_stdio *thr =
157 container_of(file->private_data, struct thread_with_stdio, thr);
158
159 thread_with_stdio_done(thr);
160 bch2_thread_with_file_exit(&thr->thr);
161 darray_exit(&thr->stdio.input.buf);
162 darray_exit(&thr->stdio.output.buf);
163 thr->ops->exit(thr);
164 return 0;
165 }
166
thread_with_stdio_write(struct file * file,const char __user * ubuf,size_t len,loff_t * ppos)167 static ssize_t thread_with_stdio_write(struct file *file, const char __user *ubuf,
168 size_t len, loff_t *ppos)
169 {
170 struct thread_with_stdio *thr =
171 container_of(file->private_data, struct thread_with_stdio, thr);
172 struct stdio_buf *buf = &thr->stdio.input;
173 size_t copied = 0;
174 ssize_t ret = 0;
175
176 while (len) {
177 if (thr->thr.done) {
178 ret = -EPIPE;
179 break;
180 }
181
182 size_t b = len - fault_in_readable(ubuf, len);
183 if (!b) {
184 ret = -EFAULT;
185 break;
186 }
187
188 spin_lock(&buf->lock);
189 size_t makeroom = b;
190 if (!buf->waiting_for_line || memchr(buf->buf.data, '\n', buf->buf.nr))
191 makeroom = min_t(ssize_t, makeroom,
192 max_t(ssize_t, STDIO_REDIRECT_BUFSIZE - buf->buf.nr,
193 0));
194 darray_make_room_gfp(&buf->buf, makeroom, GFP_NOWAIT);
195
196 b = min(len, darray_room(buf->buf));
197
198 if (b && !copy_from_user_nofault(&darray_top(buf->buf), ubuf, b)) {
199 buf->buf.nr += b;
200 ubuf += b;
201 len -= b;
202 copied += b;
203 }
204 spin_unlock(&buf->lock);
205
206 if (b) {
207 wake_up(&buf->wait);
208 } else {
209 if ((file->f_flags & O_NONBLOCK)) {
210 ret = -EAGAIN;
211 break;
212 }
213
214 ret = wait_event_interruptible(buf->wait,
215 stdio_redirect_has_input_space(&thr->stdio));
216 if (ret)
217 break;
218 }
219 }
220
221 return copied ?: ret;
222 }
223
thread_with_stdio_poll(struct file * file,struct poll_table_struct * wait)224 static __poll_t thread_with_stdio_poll(struct file *file, struct poll_table_struct *wait)
225 {
226 struct thread_with_stdio *thr =
227 container_of(file->private_data, struct thread_with_stdio, thr);
228
229 poll_wait(file, &thr->stdio.output.wait, wait);
230 poll_wait(file, &thr->stdio.input.wait, wait);
231
232 __poll_t mask = 0;
233
234 if (stdio_redirect_has_output(&thr->stdio))
235 mask |= EPOLLIN;
236 if (stdio_redirect_has_input_space(&thr->stdio))
237 mask |= EPOLLOUT;
238 if (thr->thr.done)
239 mask |= EPOLLHUP|EPOLLERR;
240 return mask;
241 }
242
thread_with_stdout_poll(struct file * file,struct poll_table_struct * wait)243 static __poll_t thread_with_stdout_poll(struct file *file, struct poll_table_struct *wait)
244 {
245 struct thread_with_stdio *thr =
246 container_of(file->private_data, struct thread_with_stdio, thr);
247
248 poll_wait(file, &thr->stdio.output.wait, wait);
249
250 __poll_t mask = 0;
251
252 if (stdio_redirect_has_output(&thr->stdio))
253 mask |= EPOLLIN;
254 if (thr->thr.done)
255 mask |= EPOLLHUP|EPOLLERR;
256 return mask;
257 }
258
thread_with_stdio_flush(struct file * file,fl_owner_t id)259 static int thread_with_stdio_flush(struct file *file, fl_owner_t id)
260 {
261 struct thread_with_stdio *thr =
262 container_of(file->private_data, struct thread_with_stdio, thr);
263
264 return thr->thr.ret;
265 }
266
thread_with_stdio_ioctl(struct file * file,unsigned int cmd,unsigned long p)267 static long thread_with_stdio_ioctl(struct file *file, unsigned int cmd, unsigned long p)
268 {
269 struct thread_with_stdio *thr =
270 container_of(file->private_data, struct thread_with_stdio, thr);
271
272 if (thr->ops->unlocked_ioctl)
273 return thr->ops->unlocked_ioctl(thr, cmd, p);
274 return -ENOTTY;
275 }
276
277 static const struct file_operations thread_with_stdio_fops = {
278 .read = thread_with_stdio_read,
279 .write = thread_with_stdio_write,
280 .poll = thread_with_stdio_poll,
281 .flush = thread_with_stdio_flush,
282 .release = thread_with_stdio_release,
283 .unlocked_ioctl = thread_with_stdio_ioctl,
284 };
285
286 static const struct file_operations thread_with_stdout_fops = {
287 .read = thread_with_stdio_read,
288 .poll = thread_with_stdout_poll,
289 .flush = thread_with_stdio_flush,
290 .release = thread_with_stdio_release,
291 .unlocked_ioctl = thread_with_stdio_ioctl,
292 };
293
thread_with_stdio_fn(void * arg)294 static int thread_with_stdio_fn(void *arg)
295 {
296 struct thread_with_stdio *thr = arg;
297
298 thr->thr.ret = thr->ops->fn(thr);
299
300 thread_with_stdio_done(thr);
301 return 0;
302 }
303
bch2_thread_with_stdio_init(struct thread_with_stdio * thr,const struct thread_with_stdio_ops * ops)304 void bch2_thread_with_stdio_init(struct thread_with_stdio *thr,
305 const struct thread_with_stdio_ops *ops)
306 {
307 stdio_buf_init(&thr->stdio.input);
308 stdio_buf_init(&thr->stdio.output);
309 thr->ops = ops;
310 }
311
__bch2_run_thread_with_stdio(struct thread_with_stdio * thr)312 int __bch2_run_thread_with_stdio(struct thread_with_stdio *thr)
313 {
314 return bch2_run_thread_with_file(&thr->thr, &thread_with_stdio_fops, thread_with_stdio_fn);
315 }
316
bch2_run_thread_with_stdio(struct thread_with_stdio * thr,const struct thread_with_stdio_ops * ops)317 int bch2_run_thread_with_stdio(struct thread_with_stdio *thr,
318 const struct thread_with_stdio_ops *ops)
319 {
320 bch2_thread_with_stdio_init(thr, ops);
321
322 return __bch2_run_thread_with_stdio(thr);
323 }
324
bch2_run_thread_with_stdout(struct thread_with_stdio * thr,const struct thread_with_stdio_ops * ops)325 int bch2_run_thread_with_stdout(struct thread_with_stdio *thr,
326 const struct thread_with_stdio_ops *ops)
327 {
328 stdio_buf_init(&thr->stdio.input);
329 stdio_buf_init(&thr->stdio.output);
330 thr->ops = ops;
331
332 return bch2_run_thread_with_file(&thr->thr, &thread_with_stdout_fops, thread_with_stdio_fn);
333 }
334 EXPORT_SYMBOL_GPL(bch2_run_thread_with_stdout);
335
bch2_stdio_redirect_read(struct stdio_redirect * stdio,char * ubuf,size_t len)336 int bch2_stdio_redirect_read(struct stdio_redirect *stdio, char *ubuf, size_t len)
337 {
338 struct stdio_buf *buf = &stdio->input;
339
340 /*
341 * we're waiting on user input (or for the file descriptor to be
342 * closed), don't want a hung task warning:
343 */
344 do {
345 wait_event_timeout(buf->wait, stdio_redirect_has_input(stdio),
346 sysctl_hung_task_timeout_secs * HZ / 2);
347 } while (!stdio_redirect_has_input(stdio));
348
349 if (stdio->done)
350 return -1;
351
352 spin_lock(&buf->lock);
353 int ret = min(len, buf->buf.nr);
354 buf->buf.nr -= ret;
355 memcpy(ubuf, buf->buf.data, ret);
356 memmove(buf->buf.data,
357 buf->buf.data + ret,
358 buf->buf.nr);
359 spin_unlock(&buf->lock);
360
361 wake_up(&buf->wait);
362 return ret;
363 }
364
bch2_stdio_redirect_readline_timeout(struct stdio_redirect * stdio,darray_char * line,unsigned long timeout)365 int bch2_stdio_redirect_readline_timeout(struct stdio_redirect *stdio,
366 darray_char *line,
367 unsigned long timeout)
368 {
369 unsigned long until = jiffies + timeout, t;
370 struct stdio_buf *buf = &stdio->input;
371 size_t seen = 0;
372 again:
373 t = timeout != MAX_SCHEDULE_TIMEOUT
374 ? max_t(long, until - jiffies, 0)
375 : timeout;
376
377 t = min(t, sysctl_hung_task_timeout_secs * HZ / 2);
378
379 wait_event_timeout(buf->wait, stdio_redirect_has_more_input(stdio, seen), t);
380
381 if (stdio->done)
382 return -1;
383
384 spin_lock(&buf->lock);
385 seen = buf->buf.nr;
386 char *n = memchr(buf->buf.data, '\n', seen);
387
388 if (!n && timeout != MAX_SCHEDULE_TIMEOUT && time_after_eq(jiffies, until)) {
389 spin_unlock(&buf->lock);
390 return -ETIME;
391 }
392
393 if (!n) {
394 buf->waiting_for_line = true;
395 spin_unlock(&buf->lock);
396 goto again;
397 }
398
399 size_t b = n + 1 - buf->buf.data;
400 if (b > line->size) {
401 spin_unlock(&buf->lock);
402 int ret = darray_resize(line, b);
403 if (ret)
404 return ret;
405 seen = 0;
406 goto again;
407 }
408
409 buf->buf.nr -= b;
410 memcpy(line->data, buf->buf.data, b);
411 memmove(buf->buf.data,
412 buf->buf.data + b,
413 buf->buf.nr);
414 line->nr = b;
415
416 buf->waiting_for_line = false;
417 spin_unlock(&buf->lock);
418
419 wake_up(&buf->wait);
420 return 0;
421 }
422
bch2_stdio_redirect_readline(struct stdio_redirect * stdio,darray_char * line)423 int bch2_stdio_redirect_readline(struct stdio_redirect *stdio, darray_char *line)
424 {
425 return bch2_stdio_redirect_readline_timeout(stdio, line, MAX_SCHEDULE_TIMEOUT);
426 }
427
428 __printf(3, 0)
bch2_darray_vprintf(darray_char * out,gfp_t gfp,const char * fmt,va_list args)429 static ssize_t bch2_darray_vprintf(darray_char *out, gfp_t gfp, const char *fmt, va_list args)
430 {
431 ssize_t ret;
432
433 do {
434 va_list args2;
435 size_t len;
436
437 va_copy(args2, args);
438 len = vsnprintf(out->data + out->nr, darray_room(*out), fmt, args2);
439 va_end(args2);
440
441 if (len + 1 <= darray_room(*out)) {
442 out->nr += len;
443 return len;
444 }
445
446 ret = darray_make_room_gfp(out, len + 1, gfp);
447 } while (ret == 0);
448
449 return ret;
450 }
451
bch2_stdio_redirect_vprintf(struct stdio_redirect * stdio,bool nonblocking,const char * fmt,va_list args)452 ssize_t bch2_stdio_redirect_vprintf(struct stdio_redirect *stdio, bool nonblocking,
453 const char *fmt, va_list args)
454 {
455 struct stdio_buf *buf = &stdio->output;
456 unsigned long flags;
457 ssize_t ret;
458
459 again:
460 spin_lock_irqsave(&buf->lock, flags);
461 ret = bch2_darray_vprintf(&buf->buf, GFP_NOWAIT, fmt, args);
462 spin_unlock_irqrestore(&buf->lock, flags);
463
464 if (ret < 0) {
465 if (nonblocking)
466 return -EAGAIN;
467
468 ret = wait_event_interruptible(buf->wait,
469 stdio_redirect_has_output_space(stdio));
470 if (ret)
471 return ret;
472 goto again;
473 }
474
475 wake_up(&buf->wait);
476 return ret;
477 }
478
bch2_stdio_redirect_printf(struct stdio_redirect * stdio,bool nonblocking,const char * fmt,...)479 ssize_t bch2_stdio_redirect_printf(struct stdio_redirect *stdio, bool nonblocking,
480 const char *fmt, ...)
481 {
482 va_list args;
483 ssize_t ret;
484
485 va_start(args, fmt);
486 ret = bch2_stdio_redirect_vprintf(stdio, nonblocking, fmt, args);
487 va_end(args);
488
489 return ret;
490 }
491
492 #endif /* NO_BCACHEFS_FS */
493