aboutsummaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2024-05-29 20:34:48 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2024-07-14 19:00:14 -0400
commit0c97c437e362fb825b7501bd5da801bac77981b4 (patch)
tree043eb2b235194e021568ffab24536473d197e71b /fs
parent36008d5d01ad155e14fd9df876d4356433613088 (diff)
bcachefs: twf: convert bch2_stdio_redirect_readline() to darray
We now read the line from the buffer atomically, which means we have to allow the buffer to grow past STDIO_REDIRECT_BUFSIZE if we're waiting for a full line - this behaviour is necessary for stdio_redirect_readline_timeout() in the next patch. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'fs')
-rw-r--r--fs/bcachefs/error.c15
-rw-r--r--fs/bcachefs/thread_with_file.c70
-rw-r--r--fs/bcachefs/thread_with_file.h3
-rw-r--r--fs/bcachefs/thread_with_file_types.h1
4 files changed, 54 insertions, 35 deletions
diff --git a/fs/bcachefs/error.c b/fs/bcachefs/error.c
index 3a9d0a03fecf..cfe791215915 100644
--- a/fs/bcachefs/error.c
+++ b/fs/bcachefs/error.c
@@ -109,18 +109,21 @@ static enum ask_yn bch2_fsck_ask_yn(struct bch_fs *c)
if (!stdio)
return YN_NO;
- char buf[100];
+ darray_char line = {};
int ret;
do {
bch2_print(c, " (y,n, or Y,N for all errors of this type) ");
- int r = bch2_stdio_redirect_readline(stdio, buf, sizeof(buf) - 1);
- if (r < 0)
- return YN_NO;
- buf[r] = '\0';
- } while ((ret = parse_yn_response(buf)) < 0);
+ int r = bch2_stdio_redirect_readline(stdio, &line);
+ if (r < 0) {
+ ret = YN_NO;
+ break;
+ }
+ darray_last(line) = '\0';
+ } while ((ret = parse_yn_response(line.data)) < 0);
+ darray_exit(&line);
return ret;
}
#else
diff --git a/fs/bcachefs/thread_with_file.c b/fs/bcachefs/thread_with_file.c
index b1af7ac430f6..080afa7eff25 100644
--- a/fs/bcachefs/thread_with_file.c
+++ b/fs/bcachefs/thread_with_file.c
@@ -67,9 +67,14 @@ err:
/* stdio_redirect */
+static bool stdio_redirect_has_more_input(struct stdio_redirect *stdio, size_t seen)
+{
+ return stdio->input.buf.nr > seen || stdio->done;
+}
+
static bool stdio_redirect_has_input(struct stdio_redirect *stdio)
{
- return stdio->input.buf.nr || stdio->done;
+ return stdio_redirect_has_more_input(stdio, 0);
}
static bool stdio_redirect_has_output(struct stdio_redirect *stdio)
@@ -181,9 +186,13 @@ static ssize_t thread_with_stdio_write(struct file *file, const char __user *ubu
}
spin_lock(&buf->lock);
- if (buf->buf.nr < STDIO_REDIRECT_BUFSIZE)
- darray_make_room_gfp(&buf->buf,
- min(b, STDIO_REDIRECT_BUFSIZE - buf->buf.nr), GFP_NOWAIT);
+ size_t makeroom = b;
+ if (!buf->waiting_for_line || memchr(buf->buf.data, '\n', buf->buf.nr))
+ makeroom = min_t(ssize_t, makeroom,
+ max_t(ssize_t, STDIO_REDIRECT_BUFSIZE - buf->buf.nr,
+ 0));
+ darray_make_room_gfp(&buf->buf, makeroom, GFP_NOWAIT);
+
b = min(len, darray_room(buf->buf));
if (b && !copy_from_user_nofault(&darray_top(buf->buf), ubuf, b)) {
@@ -355,43 +364,48 @@ int bch2_stdio_redirect_read(struct stdio_redirect *stdio, char *ubuf, size_t le
return ret;
}
-int bch2_stdio_redirect_readline(struct stdio_redirect *stdio, char *ubuf, size_t len)
+int bch2_stdio_redirect_readline(struct stdio_redirect *stdio, darray_char *line)
{
struct stdio_buf *buf = &stdio->input;
- size_t copied = 0;
- ssize_t ret = 0;
+ size_t seen = 0;
again:
- do {
- wait_event_timeout(buf->wait, stdio_redirect_has_input(stdio),
- sysctl_hung_task_timeout_secs * HZ / 2);
- } while (!stdio_redirect_has_input(stdio));
+ wait_event_timeout(buf->wait, stdio_redirect_has_more_input(stdio, seen),
+ sysctl_hung_task_timeout_secs * HZ / 2);
- if (stdio->done) {
- ret = -1;
- goto out;
- }
+ if (stdio->done)
+ return -1;
spin_lock(&buf->lock);
- size_t b = min(len, buf->buf.nr);
- char *n = memchr(buf->buf.data, '\n', b);
- if (n)
- b = min_t(size_t, b, n + 1 - buf->buf.data);
+ seen = buf->buf.nr;
+ char *n = memchr(buf->buf.data, '\n', seen);
+ if (!n) {
+ buf->waiting_for_line = true;
+ spin_unlock(&buf->lock);
+ goto again;
+ }
+
+ size_t b = n + 1 - buf->buf.data;
+ if (b > line->size) {
+ spin_unlock(&buf->lock);
+ int ret = darray_resize(line, b);
+ if (ret)
+ return ret;
+ seen = 0;
+ goto again;
+ }
+
buf->buf.nr -= b;
- memcpy(ubuf, buf->buf.data, b);
+ memcpy(line->data, buf->buf.data, b);
memmove(buf->buf.data,
buf->buf.data + b,
buf->buf.nr);
- ubuf += b;
- len -= b;
- copied += b;
+ line->nr = b;
+
+ buf->waiting_for_line = false;
spin_unlock(&buf->lock);
wake_up(&buf->wait);
-
- if (!n && len)
- goto again;
-out:
- return copied ?: ret;
+ return 0;
}
__printf(3, 0)
diff --git a/fs/bcachefs/thread_with_file.h b/fs/bcachefs/thread_with_file.h
index 1d63d14d7dca..e415dc2e2fb1 100644
--- a/fs/bcachefs/thread_with_file.h
+++ b/fs/bcachefs/thread_with_file.h
@@ -71,7 +71,8 @@ int bch2_run_thread_with_stdio(struct thread_with_stdio *,
int bch2_run_thread_with_stdout(struct thread_with_stdio *,
const struct thread_with_stdio_ops *);
int bch2_stdio_redirect_read(struct stdio_redirect *, char *, size_t);
-int bch2_stdio_redirect_readline(struct stdio_redirect *, char *, size_t);
+
+int bch2_stdio_redirect_readline(struct stdio_redirect *, darray_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 *, ...);
diff --git a/fs/bcachefs/thread_with_file_types.h b/fs/bcachefs/thread_with_file_types.h
index e0daf4eec341..12668ff3d65d 100644
--- a/fs/bcachefs/thread_with_file_types.h
+++ b/fs/bcachefs/thread_with_file_types.h
@@ -8,6 +8,7 @@ struct stdio_buf {
spinlock_t lock;
wait_queue_head_t wait;
darray_char buf;
+ bool waiting_for_line;
};
struct stdio_redirect {