brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · dea73bc Raw
493 lines · c
1// SPDX-License-Identifier: GPL-2.02#ifndef NO_BCACHEFS_FS3 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 14void 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 22int 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;60err: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 70static 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 75static bool stdio_redirect_has_input(struct stdio_redirect *stdio)76{77	return stdio_redirect_has_more_input(stdio, 0);78}79 80static 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		409686 87static bool stdio_redirect_has_input_space(struct stdio_redirect *stdio)88{89	return stdio->input.buf.nr < STDIO_REDIRECT_BUFSIZE || stdio->done;90}91 92static bool stdio_redirect_has_output_space(struct stdio_redirect *stdio)93{94	return stdio->output.buf.nr < STDIO_REDIRECT_BUFSIZE || stdio->done;95}96 97static 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 106static 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 114static 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 154static 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 167static 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 224static __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 243static __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 259static 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 267static 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 277static 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 286static 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 294static 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 304void 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 312int __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 317int 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 325int 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}334EXPORT_SYMBOL_GPL(bch2_run_thread_with_stdout);335 336int 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 be342	 * 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 365int 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;372again:373	t = timeout != MAX_SCHEDULE_TIMEOUT374		? 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 423int 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)429static 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 452ssize_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 459again: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 479ssize_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 */493