bcachefs: thread_with_file: fix various printf problems
authorDarrick J. Wong <djwong@kernel.org>
Wed, 7 Feb 2024 19:39:03 +0000 (11:39 -0800)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 14 Mar 2024 01:22:10 +0000 (21:22 -0400)
Experimentally fix some problems with stdio_redirect_vprintf by creating
a MOO variant with which we can experiment.  We can't do a GFP_KERNEL
allocation while holding the spinlock, and I don't like how the printf
function can silently truncate the output if memory allocation fails.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/thread_with_file.c
fs/bcachefs/thread_with_file.h

index d1cd5f4ab06edeb8286ce6039e331b65e17a47a9..fc4f97c56021ac61b629b3019d68397a439b0e7c 100644 (file)
@@ -366,48 +366,65 @@ out:
 }
 
 __printf(3, 0)
-static void bch2_darray_vprintf(darray_char *out, gfp_t gfp, const char *fmt, va_list args)
+static ssize_t bch2_darray_vprintf(darray_char *out, gfp_t gfp, const char *fmt, va_list args)
 {
-       size_t len;
+       ssize_t ret;
 
        do {
                va_list args2;
-               va_copy(args2, args);
+               size_t len;
 
+               va_copy(args2, args);
                len = vsnprintf(out->data + out->nr, darray_room(*out), fmt, args2);
-       } while (len + 1 > darray_room(*out) && !darray_make_room_gfp(out, len + 1, gfp));
+               if (len + 1 <= darray_room(*out)) {
+                       out->nr += len;
+                       return len;
+               }
 
-       out->nr += min(len, darray_room(*out));
+               ret = darray_make_room_gfp(out, len + 1, gfp);
+       } while (ret == 0);
+
+       return ret;
 }
 
-void bch2_stdio_redirect_vprintf(struct stdio_redirect *stdio, bool nonblocking,
-                                const char *fmt, va_list args)
+ssize_t bch2_stdio_redirect_vprintf(struct stdio_redirect *stdio, bool nonblocking,
+                                   const char *fmt, va_list args)
 {
        struct stdio_buf *buf = &stdio->output;
        unsigned long flags;
+       ssize_t ret;
 
-       if (!nonblocking)
-               wait_event(buf->wait, stdio_redirect_has_output_space(stdio));
-       else if (!stdio_redirect_has_output_space(stdio))
-               return;
-       if (stdio->done)
-               return;
-
+again:
        spin_lock_irqsave(&buf->lock, flags);
-       bch2_darray_vprintf(&buf->buf, nonblocking ? GFP_NOWAIT : GFP_KERNEL, fmt, args);
+       ret = bch2_darray_vprintf(&buf->buf, GFP_NOWAIT, fmt, args);
        spin_unlock_irqrestore(&buf->lock, flags);
 
+       if (ret < 0) {
+               if (nonblocking)
+                       return -EAGAIN;
+
+               ret = wait_event_interruptible(buf->wait,
+                               stdio_redirect_has_output_space(stdio));
+               if (ret)
+                       return ret;
+               goto again;
+       }
+
        wake_up(&buf->wait);
+       return ret;
 }
 
-void bch2_stdio_redirect_printf(struct stdio_redirect *stdio, bool nonblocking,
+ssize_t bch2_stdio_redirect_printf(struct stdio_redirect *stdio, bool nonblocking,
                                const char *fmt, ...)
 {
-
        va_list args;
+       ssize_t ret;
+
        va_start(args, fmt);
-       bch2_stdio_redirect_vprintf(stdio, nonblocking, fmt, args);
+       ret = bch2_stdio_redirect_vprintf(stdio, nonblocking, fmt, args);
        va_end(args);
+
+       return ret;
 }
 
 #endif /* NO_BCACHEFS_FS */
index 2b687723d6b9b78f3d07e93c2042d2b213e5482a..e20f2d17ee59d9d5091120a9be019743e37332b1 100644 (file)
@@ -65,7 +65,7 @@ int bch2_run_thread_with_stdout(struct thread_with_stdio *,
 int bch2_stdio_redirect_read(struct stdio_redirect *, char *, size_t);
 int bch2_stdio_redirect_readline(struct stdio_redirect *, char *, size_t);
 
-__printf(3, 0) void bch2_stdio_redirect_vprintf(struct stdio_redirect *, bool, const char *, va_list);
-__printf(3, 4) void bch2_stdio_redirect_printf(struct stdio_redirect *, bool, const char *, ...);
+__printf(3, 0) ssize_t bch2_stdio_redirect_vprintf(struct stdio_redirect *, bool, const char *, va_list);
+__printf(3, 4) ssize_t bch2_stdio_redirect_printf(struct stdio_redirect *, bool, const char *, ...);
 
 #endif /* _BCACHEFS_THREAD_WITH_FILE_H */