~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/bcachefs/thread_with_file.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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         .llseek         = no_llseek,
279         .read           = thread_with_stdio_read,
280         .write          = thread_with_stdio_write,
281         .poll           = thread_with_stdio_poll,
282         .flush          = thread_with_stdio_flush,
283         .release        = thread_with_stdio_release,
284         .unlocked_ioctl = thread_with_stdio_ioctl,
285 };
286 
287 static const struct file_operations thread_with_stdout_fops = {
288         .llseek         = no_llseek,
289         .read           = thread_with_stdio_read,
290         .poll           = thread_with_stdout_poll,
291         .flush          = thread_with_stdio_flush,
292         .release        = thread_with_stdio_release,
293         .unlocked_ioctl = thread_with_stdio_ioctl,
294 };
295 
296 static int thread_with_stdio_fn(void *arg)
297 {
298         struct thread_with_stdio *thr = arg;
299 
300         thr->thr.ret = thr->ops->fn(thr);
301 
302         thread_with_stdio_done(thr);
303         return 0;
304 }
305 
306 void bch2_thread_with_stdio_init(struct thread_with_stdio *thr,
307                                  const struct thread_with_stdio_ops *ops)
308 {
309         stdio_buf_init(&thr->stdio.input);
310         stdio_buf_init(&thr->stdio.output);
311         thr->ops = ops;
312 }
313 
314 int __bch2_run_thread_with_stdio(struct thread_with_stdio *thr)
315 {
316         return bch2_run_thread_with_file(&thr->thr, &thread_with_stdio_fops, thread_with_stdio_fn);
317 }
318 
319 int bch2_run_thread_with_stdio(struct thread_with_stdio *thr,
320                                const struct thread_with_stdio_ops *ops)
321 {
322         bch2_thread_with_stdio_init(thr, ops);
323 
324         return __bch2_run_thread_with_stdio(thr);
325 }
326 
327 int bch2_run_thread_with_stdout(struct thread_with_stdio *thr,
328                                 const struct thread_with_stdio_ops *ops)
329 {
330         stdio_buf_init(&thr->stdio.input);
331         stdio_buf_init(&thr->stdio.output);
332         thr->ops = ops;
333 
334         return bch2_run_thread_with_file(&thr->thr, &thread_with_stdout_fops, thread_with_stdio_fn);
335 }
336 EXPORT_SYMBOL_GPL(bch2_run_thread_with_stdout);
337 
338 int bch2_stdio_redirect_read(struct stdio_redirect *stdio, char *ubuf, size_t len)
339 {
340         struct stdio_buf *buf = &stdio->input;
341 
342         /*
343          * we're waiting on user input (or for the file descriptor to be
344          * closed), don't want a hung task warning:
345          */
346         do {
347                 wait_event_timeout(buf->wait, stdio_redirect_has_input(stdio),
348                                    sysctl_hung_task_timeout_secs * HZ / 2);
349         } while (!stdio_redirect_has_input(stdio));
350 
351         if (stdio->done)
352                 return -1;
353 
354         spin_lock(&buf->lock);
355         int ret = min(len, buf->buf.nr);
356         buf->buf.nr -= ret;
357         memcpy(ubuf, buf->buf.data, ret);
358         memmove(buf->buf.data,
359                 buf->buf.data + ret,
360                 buf->buf.nr);
361         spin_unlock(&buf->lock);
362 
363         wake_up(&buf->wait);
364         return ret;
365 }
366 
367 int bch2_stdio_redirect_readline_timeout(struct stdio_redirect *stdio,
368                                          darray_char *line,
369                                          unsigned long timeout)
370 {
371         unsigned long until = jiffies + timeout, t;
372         struct stdio_buf *buf = &stdio->input;
373         size_t seen = 0;
374 again:
375         t = timeout != MAX_SCHEDULE_TIMEOUT
376                 ? max_t(long, until - jiffies, 0)
377                 : timeout;
378 
379         t = min(t, sysctl_hung_task_timeout_secs * HZ / 2);
380 
381         wait_event_timeout(buf->wait, stdio_redirect_has_more_input(stdio, seen), t);
382 
383         if (stdio->done)
384                 return -1;
385 
386         spin_lock(&buf->lock);
387         seen = buf->buf.nr;
388         char *n = memchr(buf->buf.data, '\n', seen);
389 
390         if (!n && timeout != MAX_SCHEDULE_TIMEOUT && jiffies >= until) {
391                 spin_unlock(&buf->lock);
392                 return -ETIME;
393         }
394 
395         if (!n) {
396                 buf->waiting_for_line = true;
397                 spin_unlock(&buf->lock);
398                 goto again;
399         }
400 
401         size_t b = n + 1 - buf->buf.data;
402         if (b > line->size) {
403                 spin_unlock(&buf->lock);
404                 int ret = darray_resize(line, b);
405                 if (ret)
406                         return ret;
407                 seen = 0;
408                 goto again;
409         }
410 
411         buf->buf.nr -= b;
412         memcpy(line->data, buf->buf.data, b);
413         memmove(buf->buf.data,
414                 buf->buf.data + b,
415                 buf->buf.nr);
416         line->nr = b;
417 
418         buf->waiting_for_line = false;
419         spin_unlock(&buf->lock);
420 
421         wake_up(&buf->wait);
422         return 0;
423 }
424 
425 int bch2_stdio_redirect_readline(struct stdio_redirect *stdio, darray_char *line)
426 {
427         return bch2_stdio_redirect_readline_timeout(stdio, line, MAX_SCHEDULE_TIMEOUT);
428 }
429 
430 __printf(3, 0)
431 static ssize_t bch2_darray_vprintf(darray_char *out, gfp_t gfp, const char *fmt, va_list args)
432 {
433         ssize_t ret;
434 
435         do {
436                 va_list args2;
437                 size_t len;
438 
439                 va_copy(args2, args);
440                 len = vsnprintf(out->data + out->nr, darray_room(*out), fmt, args2);
441                 va_end(args2);
442 
443                 if (len + 1 <= darray_room(*out)) {
444                         out->nr += len;
445                         return len;
446                 }
447 
448                 ret = darray_make_room_gfp(out, len + 1, gfp);
449         } while (ret == 0);
450 
451         return ret;
452 }
453 
454 ssize_t bch2_stdio_redirect_vprintf(struct stdio_redirect *stdio, bool nonblocking,
455                                     const char *fmt, va_list args)
456 {
457         struct stdio_buf *buf = &stdio->output;
458         unsigned long flags;
459         ssize_t ret;
460 
461 again:
462         spin_lock_irqsave(&buf->lock, flags);
463         ret = bch2_darray_vprintf(&buf->buf, GFP_NOWAIT, fmt, args);
464         spin_unlock_irqrestore(&buf->lock, flags);
465 
466         if (ret < 0) {
467                 if (nonblocking)
468                         return -EAGAIN;
469 
470                 ret = wait_event_interruptible(buf->wait,
471                                 stdio_redirect_has_output_space(stdio));
472                 if (ret)
473                         return ret;
474                 goto again;
475         }
476 
477         wake_up(&buf->wait);
478         return ret;
479 }
480 
481 ssize_t bch2_stdio_redirect_printf(struct stdio_redirect *stdio, bool nonblocking,
482                                 const char *fmt, ...)
483 {
484         va_list args;
485         ssize_t ret;
486 
487         va_start(args, fmt);
488         ret = bch2_stdio_redirect_vprintf(stdio, nonblocking, fmt, args);
489         va_end(args);
490 
491         return ret;
492 }
493 
494 #endif /* NO_BCACHEFS_FS */
495 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php