]> Git Repo - J-linux.git/blob - fs/bcachefs/thread_with_file.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.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_more_input(struct stdio_redirect *stdio, size_t seen)
71 {
72         return stdio->input.buf.nr > seen || stdio->done;
73 }
74
75 static bool stdio_redirect_has_input(struct stdio_redirect *stdio)
76 {
77         return stdio_redirect_has_more_input(stdio, 0);
78 }
79
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)
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
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
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 */
This page took 0.054912 seconds and 4 git commands to generate.