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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.