// SPDX-License-Identifier: GPL-2.0
#ifndef NO_BCACHEFS_FS

#include "bcachefs.h"

#include "alloc/foreground.h"

#include "data/read.h"
#include "data/write.h"

#include "vfs/fs.h"
#include "vfs/io.h"
#include "vfs/direct.h"
#include "vfs/pagecache.h"

#include "vendor/bio_iov_iter.h"

#include "util/enumerated_ref.h"

#include <linux/kthread.h>
#include <linux/pagemap.h>
#include <linux/prefetch.h>
#include <linux/task_io_accounting_ops.h>

/* O_DIRECT reads */

static void bio_check_or_release(struct bio *bio, bool check_dirty)
{
	if (check_dirty) {
		bch2_bio_check_pages_dirty(bio);
	} else {
		bio_release_pages(bio, false);
		bio_put(bio);
	}
}

static CLOSURE_CALLBACK(bch2_dio_read_complete)
{
	closure_type(dio, struct dio_read, cl);

	dio->req->ki_complete(dio->req, dio->ret);
	bio_check_or_release(&dio->rbio.bio, dio->should_dirty);
}

static void bch2_direct_IO_read_endio(struct bio *bio)
{
	struct bch_read_bio *rbio = to_rbio(bio);
	struct dio_read *dio = bio->bi_private;

	if (rbio->ret)
		dio->ret = bch2_err_class(rbio->ret);

	closure_put(&dio->cl);
}

static void bch2_direct_IO_read_split_endio(struct bio *bio)
{
	struct dio_read *dio = bio->bi_private;
	bool should_dirty = dio->should_dirty;

	bch2_direct_IO_read_endio(bio);
	bio_check_or_release(bio, should_dirty);
}

static int __bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter,
				enum bch_read_flags flags,
				struct bch_read_err_report *err_report)
{
	struct file *file = req->ki_filp;
	struct bch_inode_info *inode = file_bch_inode(file);
	struct bch_fs *c = inode->v.i_sb->s_fs_info;
	struct dio_read *dio;
	struct bio *bio;
	loff_t offset = req->ki_pos;
	bool sync = is_sync_kiocb(req);
	bool split = false;
	size_t shorten;
	ssize_t ret;

	struct bch_inode_opts opts;
	bch2_inode_opts_get_inode(c, &inode->ei_inode, &opts);

	/* bios must be 512 byte aligned: */
	if ((offset|iter->count) & (SECTOR_SIZE - 1))
		return bch_err_throw(c, EINVAL_unaligned_io);

	ret = min_t(loff_t, iter->count,
		    max_t(loff_t, 0, i_size_read(&inode->v) - offset));

	if (!ret)
		return ret;

	shorten = iov_iter_count(iter) - round_up(ret, block_bytes(c));
	if (shorten >= iter->count)
		shorten = 0;
	iter->count -= shorten;

	bio = bio_alloc_bioset(NULL,
			       bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),
			       REQ_OP_READ,
			       GFP_KERNEL,
			       &c->vfs.dio_read_bioset);

	dio = container_of(bio, struct dio_read, rbio.bio);
	closure_init(&dio->cl, NULL);

	/*
	 * this is a _really_ horrible hack just to avoid an atomic sub at the
	 * end:
	 */
	if (!sync) {
		set_closure_fn(&dio->cl, bch2_dio_read_complete, NULL);
		atomic_set(&dio->cl.remaining,
			   CLOSURE_REMAINING_INITIALIZER -
			   CLOSURE_RUNNING +
			   CLOSURE_DESTRUCTOR);
	} else {
		atomic_set(&dio->cl.remaining,
			   CLOSURE_REMAINING_INITIALIZER + 1);
	}

	dio->req	= req;
	dio->ret	= ret;
	/*
	 * This is one of the sketchier things I've encountered: we have to skip
	 * the dirtying of requests that are internal from the kernel (i.e. from
	 * loopback), because we'll deadlock on page_lock.
	 */
	dio->should_dirty = user_backed_iter(iter);

	goto start;
	while (iter->count) {
		split = true;

		bio = bio_alloc_bioset(NULL,
				       bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),
				       REQ_OP_READ,
				       GFP_KERNEL,
				       &c->bio_read);
start:
		struct bch_read_bio *rbio =
			rbio_init(bio,
				  c,
				  opts,
				  split
				  ? bch2_direct_IO_read_split_endio
				  : bch2_direct_IO_read_endio);

		BUG_ON(rbio->_state);
		rbio->err_report		= err_report;
		rbio->subvol			= inode_inum(inode).subvol;
		rbio->bio.bi_opf		= REQ_OP_READ|REQ_SYNC;
		rbio->bio.bi_iter.bi_sector	= offset >> 9;
		rbio->bio.bi_private		= dio;

		ret = bch2_bio_iov_iter_get_pages(&rbio->bio, iter, 0);
		if (ret < 0) {
			/* XXX: fault inject this path */
			rbio->ret = ret;
			bio_endio(&rbio->bio);
			break;
		}

		offset += rbio->bio.bi_iter.bi_size;

		if (dio->should_dirty)
			bch2_bio_set_pages_dirty(&rbio->bio);

		if (iter->count)
			closure_get(&dio->cl);

		CLASS(btree_trans, trans)(c);
		bch2_read(trans, rbio, rbio->bio.bi_iter, inode_inum(inode),
			  NULL, NULL,
			  BCH_READ_retry_if_stale|
			  BCH_READ_may_promote|
			  BCH_READ_user_mapped|
			  flags);
	}

	iter->count += shorten;

	if (sync) {
		closure_sync(&dio->cl);
		closure_debug_destroy(&dio->cl);
		ret = dio->ret;
		bio_check_or_release(&dio->rbio.bio, dio->should_dirty);
		return ret;
	} else {
		return -EIOCBQUEUED;
	}
}

int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter,
			enum bch_read_flags flags,
			struct bch_read_err_report *err_report)
{
	struct file *file = req->ki_filp;
	struct address_space *mapping = file->f_mapping;

	if (unlikely(mapping->nrpages)) {
		ssize_t ret = filemap_write_and_wait_range(mapping,
					req->ki_pos,
					req->ki_pos + iov_iter_count(iter) - 1);
		if (ret < 0)
			return ret;
	}

	file_accessed(file);

	struct blk_plug plug;
	blk_start_plug(&plug);
	ssize_t ret = __bch2_direct_IO_read(req, iter, flags, err_report);
	blk_finish_plug(&plug);

	if (ret >= 0)
		req->ki_pos += ret;
	return ret;
}

ssize_t bch2_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
	struct file *file = iocb->ki_filp;
	ssize_t ret = 0;

	if (!iov_iter_count(iter))
		return 0; /* skip atime */

	if (iocb->ki_flags & IOCB_DIRECT) {
		ret = bch2_direct_IO_read(iocb, iter, 0, NULL);
	} else {
		guard(bch2_pagecache_add)(file_bch_inode(file));
		ret = filemap_read(iocb, iter, ret);
	}

	return bch2_err_class(ret);
}

/* O_DIRECT writes */

static bool bch2_check_range_allocated(struct bch_fs *c, subvol_inum inum,
				       u64 offset, u64 size,
				       unsigned nr_replicas, bool compressed)
{
	CLASS(btree_trans, trans)(c);
	struct bkey_s_c k;
	u64 end = offset + size;
	u32 snapshot;
	int err;
retry:
	bch2_trans_begin(trans);

	err = bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot);
	if (err)
		goto err;

	for_each_btree_key_norestart(trans, iter, BTREE_ID_extents,
			   SPOS(inum.inum, offset, snapshot),
			   BTREE_ITER_slots, k, err) {
		offset = iter.pos.offset;

		if (bkey_ge(bkey_start_pos(k.k), POS(inum.inum, end)))
			break;

		if (k.k->p.snapshot != snapshot ||
		    nr_replicas > bch2_bkey_replicas(c, k) ||
		    (!compressed && bch2_bkey_sectors_compressed(c, k)))
			return false;
	}
err:
	if (bch2_err_matches(err, BCH_ERR_transaction_restart))
		goto retry;

	return !err;
}

static noinline bool bch2_dio_write_check_allocated(struct dio_write *dio)
{
	struct bch_fs *c = dio->op.c;
	struct bch_inode_info *inode = dio->inode;
	struct bio *bio = &dio->op.wbio.bio;

	return bch2_check_range_allocated(c, inode_inum(inode),
				dio->op.pos.offset, bio_sectors(bio),
				dio->op.opts.data_replicas,
				dio->op.opts.compression != 0);
}

static void bch2_dio_write_loop_async(struct bch_write_op *);
static __always_inline long bch2_dio_write_done(struct dio_write *dio);

/*
 * We're going to return -EIOCBQUEUED, but we haven't finished consuming the
 * iov_iter yet, so we need to stash a copy of the iovec: it might be on the
 * caller's stack, we're not guaranteed that it will live for the duration of
 * the IO:
 */
static noinline int bch2_dio_write_copy_iov(struct dio_write *dio)
{
	struct iovec *iov = dio->inline_vecs;

	/*
	 * iov_iter has a single embedded iovec - nothing to do:
	 */
	if (iter_is_ubuf(&dio->iter))
		return 0;

	/*
	 * We don't currently handle non-iovec iov_iters here - return an error,
	 * and we'll fall back to doing the IO synchronously:
	 */
	if (!iter_is_iovec(&dio->iter))
		return -1;

	if (dio->iter.nr_segs > ARRAY_SIZE(dio->inline_vecs)) {
		dio->iov = iov = kmalloc_array(dio->iter.nr_segs, sizeof(*iov),
				    GFP_KERNEL);
		if (unlikely(!iov))
			return -ENOMEM;
	}

	memcpy(iov, dio->iter.__iov, dio->iter.nr_segs * sizeof(*iov));
	dio->iter.__iov = iov;
	return 0;
}

static __always_inline long bch2_dio_write_done(struct dio_write *dio)
{
	struct bch_fs *c = dio->op.c;
	struct kiocb *req = dio->req;
	struct bch_inode_info *inode = dio->inode;
	bool sync = dio->sync;

	bch2_pagecache_block_put(inode);

	kfree(dio->iov);

	long ret = dio->op.error ?: ((long) dio->written << 9);
	bio_put(&dio->op.wbio.bio);

	enumerated_ref_put(&c->writes, BCH_WRITE_REF_dio_write);

	/* inode->i_dio_count is our ref on inode and thus bch_fs */
	inode_dio_end(&inode->v);

	if (ret < 0)
		ret = bch2_err_class(ret);

	if (!sync) {
		req->ki_complete(req, ret);
		ret = -EIOCBQUEUED;
	}
	return ret;
}

static __always_inline void bch2_dio_write_end(struct dio_write *dio)
{
	struct bch_fs *c = dio->op.c;
	struct kiocb *req = dio->req;
	struct bch_inode_info *inode = dio->inode;
	struct bio *bio = &dio->op.wbio.bio;

	req->ki_pos	+= (u64) dio->op.written << 9;
	dio->written	+= dio->op.written;

	if (dio->extending) {
		guard(spinlock)(&inode->v.i_lock);
		if (req->ki_pos > inode->v.i_size)
			i_size_write(&inode->v, req->ki_pos);
	}

	if (dio->op.i_sectors_delta || dio->quota_res.sectors) {
		guard(mutex)(&inode->ei_quota_lock);
		__bch2_i_sectors_acct(c, inode, &dio->quota_res, dio->op.i_sectors_delta);
		__bch2_quota_reservation_put(c, inode, &dio->quota_res);
	}

	bio_release_pages(bio, false);

	if (unlikely(dio->op.error))
		set_bit(EI_INODE_ERROR, &inode->ei_flags);
}

static void bch2_dio_write_sync_done(struct bch_write_op *op)
{
	struct dio_write *dio = container_of(op, struct dio_write, op);

	dio->sync_done = true;
}

static __always_inline long bch2_dio_write_loop(struct dio_write *dio)
{
	struct bch_fs *c = dio->op.c;
	struct kiocb *req = dio->req;
	struct address_space *mapping = dio->mapping;
	struct bch_inode_info *inode = dio->inode;
	struct bch_inode_opts opts;
	struct bio *bio = &dio->op.wbio.bio;
	unsigned unaligned, iter_count;
	bool sync = dio->sync, dropped_locks;
	long ret;

	bch2_inode_opts_get_inode(c, &inode->ei_inode, &opts);

	while (1) {
		iter_count = dio->iter.count;

		EBUG_ON(faults_disabled_mapping(c));
		fdm_set(&c->fdm_table, mapping);

		ret = bch2_bio_iov_iter_get_pages(bio, &dio->iter, 0);

		dropped_locks = bch2_fdm_dropped_locks(c);

		fdm_clear(&c->fdm_table);

		/*
		 * If the fault handler returned an error but also signalled
		 * that it dropped & retook ei_pagecache_lock, we just need to
		 * re-shoot down the page cache and retry:
		 */
		if (dropped_locks && ret)
			ret = 0;

		if (unlikely(ret < 0))
			goto err;

		if (unlikely(dropped_locks)) {
			ret = bch2_write_invalidate_inode_pages_range(mapping,
					req->ki_pos,
					req->ki_pos + iter_count - 1);
			if (unlikely(ret))
				goto err;

			if (!bio->bi_iter.bi_size)
				continue;
		}

		unaligned = bio->bi_iter.bi_size & (block_bytes(c) - 1);
		bio->bi_iter.bi_size -= unaligned;
		iov_iter_revert(&dio->iter, unaligned);

		if (!bio->bi_iter.bi_size) {
			/*
			 * bio_iov_iter_get_pages was only able to get <
			 * blocksize worth of pages:
			 */
			ret = -EFAULT;
			goto err;
		}

		bch2_write_op_init(&dio->op, c, opts);
		dio->op.end_io		= sync
			? bch2_dio_write_sync_done
			: bch2_dio_write_loop_async;
		dio->op.target		= dio->op.opts.foreground_target;
		dio->op.write_point	= writepoint_hashed((unsigned long) current);
		dio->op.nr_replicas	= dio->op.opts.data_replicas;
		dio->op.subvol		= inode->ei_inum.subvol;
		dio->op.pos		= POS(inode->v.i_ino, (u64) req->ki_pos >> 9);
		dio->op.devs_need_flush	= &inode->ei_devs_need_flush;

		if (sync)
			dio->op.flags |= BCH_WRITE_sync;
		if (dio->flush)
			dio->op.flags |= BCH_WRITE_flush;
		dio->op.flags |= BCH_WRITE_check_enospc;

		ret = bch2_quota_reservation_add(c, inode, &dio->quota_res,
						 bio_sectors(bio), true);
		if (unlikely(ret))
			goto err;

		ret = bch2_disk_reservation_get(c, &dio->op.res, bio_sectors(bio),
						dio->op.opts.data_replicas, 0);
		if (unlikely(ret) &&
		    !bch2_dio_write_check_allocated(dio))
			goto err;

		task_io_account_write(bio->bi_iter.bi_size);

		if (unlikely(dio->iter.count) &&
		    !dio->sync &&
		    !dio->loop &&
		    bch2_dio_write_copy_iov(dio))
			dio->sync = sync = true;

		dio->loop = true;
		closure_call(&dio->op.cl, bch2_write, NULL, NULL);

		if (!sync)
			return -EIOCBQUEUED;

		BUG_ON(!dio->sync_done);
		dio->sync_done = false;

		bch2_dio_write_end(dio);

		if (likely(!dio->iter.count) || dio->op.error)
			break;

		bio_reset(bio, NULL, REQ_OP_WRITE | REQ_SYNC | REQ_IDLE);
	}
out:
	return bch2_dio_write_done(dio);
err:
	dio->op.error = ret;

	bio_release_pages(bio, false);

	bch2_quota_reservation_put(c, inode, &dio->quota_res);
	goto out;
}

static noinline __cold void bch2_dio_write_continue(struct dio_write *dio)
{
	struct mm_struct *mm = dio->mm;

	bio_reset(&dio->op.wbio.bio, NULL, REQ_OP_WRITE);

	if (mm)
		kthread_use_mm(mm);
	bch2_dio_write_loop(dio);
	if (mm)
		kthread_unuse_mm(mm);
}

static void bch2_dio_write_loop_async(struct bch_write_op *op)
{
	struct dio_write *dio = container_of(op, struct dio_write, op);

	bch2_dio_write_end(dio);

	if (likely(!dio->iter.count) || dio->op.error)
		bch2_dio_write_done(dio);
	else
		bch2_dio_write_continue(dio);
}

ssize_t bch2_direct_write(struct kiocb *req, struct iov_iter *iter)
{
	struct file *file = req->ki_filp;
	struct address_space *mapping = file->f_mapping;
	struct bch_inode_info *inode = file_bch_inode(file);
	struct bch_fs *c = inode->v.i_sb->s_fs_info;
	struct dio_write *dio;
	struct bio *bio;
	bool locked = true, extending;
	ssize_t ret;

	prefetch(&c->opts);
	prefetch((void *) &c->opts + 64);
	prefetch(&inode->ei_inode);
	prefetch((void *) &inode->ei_inode + 64);

	if (!enumerated_ref_tryget(&c->writes, BCH_WRITE_REF_dio_write))
		return -EROFS;

	inode_lock(&inode->v);

	ret = generic_write_checks(req, iter);
	if (unlikely(ret <= 0))
		goto err_put_write_ref;

	ret = file_remove_privs(file);
	if (unlikely(ret))
		goto err_put_write_ref;

	ret = file_update_time(file);
	if (unlikely(ret))
		goto err_put_write_ref;

	if (unlikely((req->ki_pos|iter->count) & (block_bytes(c) - 1))) {
		ret = bch_err_throw(c, EINVAL_unaligned_io);
		goto err_put_write_ref;
	}

	inode_dio_begin(&inode->v);
	bch2_pagecache_block_get(inode);

	extending = req->ki_pos + iter->count > inode->v.i_size;
	if (!extending) {
		inode_unlock(&inode->v);
		locked = false;
	}

	bio = bio_alloc_bioset(NULL,
			       bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),
			       REQ_OP_WRITE | REQ_SYNC | REQ_IDLE,
			       GFP_KERNEL,
			       &c->vfs.dio_write_bioset);
	dio = container_of(bio, struct dio_write, op.wbio.bio);
	dio->req		= req;
	dio->mapping		= mapping;
	dio->inode		= inode;
	dio->mm			= current->mm;
	dio->iov		= NULL;
	dio->loop		= false;
	dio->extending		= extending;
	dio->sync		= is_sync_kiocb(req) || extending;
	dio->flush		= iocb_is_dsync(req) && !c->opts.journal_flush_disabled;
	dio->quota_res.sectors	= 0;
	dio->written		= 0;
	dio->iter		= *iter;
	dio->op.c		= c;

	if (unlikely(mapping->nrpages)) {
		ret = bch2_write_invalidate_inode_pages_range(mapping,
						req->ki_pos,
						req->ki_pos + iter->count - 1);
		if (unlikely(ret))
			goto err_put_bio;
	}

	ret = bch2_dio_write_loop(dio);
out:
	if (locked)
		inode_unlock(&inode->v);
	return ret;
err_put_bio:
	bch2_pagecache_block_put(inode);
	bio_put(bio);
	inode_dio_end(&inode->v);
err_put_write_ref:
	enumerated_ref_put(&c->writes, BCH_WRITE_REF_dio_write);
	goto out;
}

#endif /* NO_BCACHEFS_FS */
