GNU Linux-libre 6.9-gnu
[releases.git] / fs / bcachefs / thread_with_file.c
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(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
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
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(&thr->stdio))
123                 return -EAGAIN;
124
125         while (len && buf->buf.nr) {
126                 if (fault_in_writeable(ubuf, len) == len) {
127                         ret = -EFAULT;
128                         break;
129                 }
130
131                 spin_lock_irq(&buf->lock);
132                 b = min_t(size_t, len, buf->buf.nr);
133
134                 if (b && !copy_to_user_nofault(ubuf, buf->buf.data, 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(&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);
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(ubuf, len);
178                 if (!b) {
179                         ret = -EFAULT;
180                         break;
181                 }
182
183                 spin_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(&darray_top(buf->buf), ubuf, b)) {
190                         buf->buf.nr += b;
191                         ubuf    += b;
192                         len     -= b;
193                         copied  += b;
194                 }
195                 spin_unlock(&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(file, &thr->stdio.output.wait, wait);
221         poll_wait(file, &thr->stdio.input.wait, wait);
222
223         __poll_t mask = 0;
224
225         if (stdio_redirect_has_output(&thr->stdio))
226                 mask |= EPOLLIN;
227         if (stdio_redirect_has_input_space(&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(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
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(&thr->stdio.input);
301         stdio_buf_init(&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, &thread_with_stdio_fops, 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(&thr->stdio.input);
322         stdio_buf_init(&thr->stdio.output);
323         thr->ops = ops;
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
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(&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);
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(&buf->lock);
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);
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(&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(out->data + out->nr, darray_room(*out), fmt, 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(&buf->buf, GFP_NOWAIT, fmt, args);
431         spin_unlock_irqrestore(&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 */