162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This file contians vfs file ops for 9P2000.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
662306a36Sopenharmony_ci *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/errno.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/filelock.h>
1362306a36Sopenharmony_ci#include <linux/sched.h>
1462306a36Sopenharmony_ci#include <linux/file.h>
1562306a36Sopenharmony_ci#include <linux/stat.h>
1662306a36Sopenharmony_ci#include <linux/string.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/pagemap.h>
1962306a36Sopenharmony_ci#include <linux/utsname.h>
2062306a36Sopenharmony_ci#include <linux/uaccess.h>
2162306a36Sopenharmony_ci#include <linux/uio.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <net/9p/9p.h>
2462306a36Sopenharmony_ci#include <net/9p/client.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "v9fs.h"
2762306a36Sopenharmony_ci#include "v9fs_vfs.h"
2862306a36Sopenharmony_ci#include "fid.h"
2962306a36Sopenharmony_ci#include "cache.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const struct vm_operations_struct v9fs_mmap_file_vm_ops;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/**
3462306a36Sopenharmony_ci * v9fs_file_open - open a file (or directory)
3562306a36Sopenharmony_ci * @inode: inode to be opened
3662306a36Sopenharmony_ci * @file: file being opened
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciint v9fs_file_open(struct inode *inode, struct file *file)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int err;
4362306a36Sopenharmony_ci	struct v9fs_session_info *v9ses;
4462306a36Sopenharmony_ci	struct p9_fid *fid;
4562306a36Sopenharmony_ci	int omode;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file);
4862306a36Sopenharmony_ci	v9ses = v9fs_inode2v9ses(inode);
4962306a36Sopenharmony_ci	if (v9fs_proto_dotl(v9ses))
5062306a36Sopenharmony_ci		omode = v9fs_open_to_dotl_flags(file->f_flags);
5162306a36Sopenharmony_ci	else
5262306a36Sopenharmony_ci		omode = v9fs_uflags2omode(file->f_flags,
5362306a36Sopenharmony_ci					v9fs_proto_dotu(v9ses));
5462306a36Sopenharmony_ci	fid = file->private_data;
5562306a36Sopenharmony_ci	if (!fid) {
5662306a36Sopenharmony_ci		fid = v9fs_fid_clone(file_dentry(file));
5762306a36Sopenharmony_ci		if (IS_ERR(fid))
5862306a36Sopenharmony_ci			return PTR_ERR(fid);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		if ((v9ses->cache & CACHE_WRITEBACK) && (omode & P9_OWRITE)) {
6162306a36Sopenharmony_ci			int writeback_omode = (omode & ~P9_OWRITE) | P9_ORDWR;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci			p9_debug(P9_DEBUG_CACHE, "write-only file with writeback enabled, try opening O_RDWR\n");
6462306a36Sopenharmony_ci			err = p9_client_open(fid, writeback_omode);
6562306a36Sopenharmony_ci			if (err < 0) {
6662306a36Sopenharmony_ci				p9_debug(P9_DEBUG_CACHE, "could not open O_RDWR, disabling caches\n");
6762306a36Sopenharmony_ci				err = p9_client_open(fid, omode);
6862306a36Sopenharmony_ci				fid->mode |= P9L_DIRECT;
6962306a36Sopenharmony_ci			}
7062306a36Sopenharmony_ci		} else {
7162306a36Sopenharmony_ci			err = p9_client_open(fid, omode);
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci		if (err < 0) {
7462306a36Sopenharmony_ci			p9_fid_put(fid);
7562306a36Sopenharmony_ci			return err;
7662306a36Sopenharmony_ci		}
7762306a36Sopenharmony_ci		if ((file->f_flags & O_APPEND) &&
7862306a36Sopenharmony_ci			(!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)))
7962306a36Sopenharmony_ci			generic_file_llseek(file, 0, SEEK_END);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		file->private_data = fid;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE
8562306a36Sopenharmony_ci	if (v9ses->cache & CACHE_FSCACHE)
8662306a36Sopenharmony_ci		fscache_use_cookie(v9fs_inode_cookie(V9FS_I(inode)),
8762306a36Sopenharmony_ci				   file->f_mode & FMODE_WRITE);
8862306a36Sopenharmony_ci#endif
8962306a36Sopenharmony_ci	v9fs_fid_add_modes(fid, v9ses->flags, v9ses->cache, file->f_flags);
9062306a36Sopenharmony_ci	v9fs_open_fid_add(inode, &fid);
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/**
9562306a36Sopenharmony_ci * v9fs_file_lock - lock a file (or directory)
9662306a36Sopenharmony_ci * @filp: file to be locked
9762306a36Sopenharmony_ci * @cmd: lock command
9862306a36Sopenharmony_ci * @fl: file lock structure
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci * Bugs: this looks like a local only lock, we should extend into 9P
10162306a36Sopenharmony_ci *       by using open exclusive
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
11162306a36Sopenharmony_ci		filemap_write_and_wait(inode->i_mapping);
11262306a36Sopenharmony_ci		invalidate_mapping_pages(&inode->i_data, 0, -1);
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct p9_flock flock;
12162306a36Sopenharmony_ci	struct p9_fid *fid;
12262306a36Sopenharmony_ci	uint8_t status = P9_LOCK_ERROR;
12362306a36Sopenharmony_ci	int res = 0;
12462306a36Sopenharmony_ci	unsigned char fl_type;
12562306a36Sopenharmony_ci	struct v9fs_session_info *v9ses;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	fid = filp->private_data;
12862306a36Sopenharmony_ci	BUG_ON(fid == NULL);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	BUG_ON((fl->fl_flags & FL_POSIX) != FL_POSIX);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	res = locks_lock_file_wait(filp, fl);
13362306a36Sopenharmony_ci	if (res < 0)
13462306a36Sopenharmony_ci		goto out;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* convert posix lock to p9 tlock args */
13762306a36Sopenharmony_ci	memset(&flock, 0, sizeof(flock));
13862306a36Sopenharmony_ci	/* map the lock type */
13962306a36Sopenharmony_ci	switch (fl->fl_type) {
14062306a36Sopenharmony_ci	case F_RDLCK:
14162306a36Sopenharmony_ci		flock.type = P9_LOCK_TYPE_RDLCK;
14262306a36Sopenharmony_ci		break;
14362306a36Sopenharmony_ci	case F_WRLCK:
14462306a36Sopenharmony_ci		flock.type = P9_LOCK_TYPE_WRLCK;
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	case F_UNLCK:
14762306a36Sopenharmony_ci		flock.type = P9_LOCK_TYPE_UNLCK;
14862306a36Sopenharmony_ci		break;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	flock.start = fl->fl_start;
15162306a36Sopenharmony_ci	if (fl->fl_end == OFFSET_MAX)
15262306a36Sopenharmony_ci		flock.length = 0;
15362306a36Sopenharmony_ci	else
15462306a36Sopenharmony_ci		flock.length = fl->fl_end - fl->fl_start + 1;
15562306a36Sopenharmony_ci	flock.proc_id = fl->fl_pid;
15662306a36Sopenharmony_ci	flock.client_id = fid->clnt->name;
15762306a36Sopenharmony_ci	if (IS_SETLKW(cmd))
15862306a36Sopenharmony_ci		flock.flags = P9_LOCK_FLAGS_BLOCK;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	v9ses = v9fs_inode2v9ses(file_inode(filp));
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/*
16362306a36Sopenharmony_ci	 * if its a blocked request and we get P9_LOCK_BLOCKED as the status
16462306a36Sopenharmony_ci	 * for lock request, keep on trying
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	for (;;) {
16762306a36Sopenharmony_ci		res = p9_client_lock_dotl(fid, &flock, &status);
16862306a36Sopenharmony_ci		if (res < 0)
16962306a36Sopenharmony_ci			goto out_unlock;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		if (status != P9_LOCK_BLOCKED)
17262306a36Sopenharmony_ci			break;
17362306a36Sopenharmony_ci		if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd))
17462306a36Sopenharmony_ci			break;
17562306a36Sopenharmony_ci		if (schedule_timeout_interruptible(v9ses->session_lock_timeout)
17662306a36Sopenharmony_ci				!= 0)
17762306a36Sopenharmony_ci			break;
17862306a36Sopenharmony_ci		/*
17962306a36Sopenharmony_ci		 * p9_client_lock_dotl overwrites flock.client_id with the
18062306a36Sopenharmony_ci		 * server message, free and reuse the client name
18162306a36Sopenharmony_ci		 */
18262306a36Sopenharmony_ci		if (flock.client_id != fid->clnt->name) {
18362306a36Sopenharmony_ci			kfree(flock.client_id);
18462306a36Sopenharmony_ci			flock.client_id = fid->clnt->name;
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* map 9p status to VFS status */
18962306a36Sopenharmony_ci	switch (status) {
19062306a36Sopenharmony_ci	case P9_LOCK_SUCCESS:
19162306a36Sopenharmony_ci		res = 0;
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci	case P9_LOCK_BLOCKED:
19462306a36Sopenharmony_ci		res = -EAGAIN;
19562306a36Sopenharmony_ci		break;
19662306a36Sopenharmony_ci	default:
19762306a36Sopenharmony_ci		WARN_ONCE(1, "unknown lock status code: %d\n", status);
19862306a36Sopenharmony_ci		fallthrough;
19962306a36Sopenharmony_ci	case P9_LOCK_ERROR:
20062306a36Sopenharmony_ci	case P9_LOCK_GRACE:
20162306a36Sopenharmony_ci		res = -ENOLCK;
20262306a36Sopenharmony_ci		break;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ciout_unlock:
20662306a36Sopenharmony_ci	/*
20762306a36Sopenharmony_ci	 * incase server returned error for lock request, revert
20862306a36Sopenharmony_ci	 * it locally
20962306a36Sopenharmony_ci	 */
21062306a36Sopenharmony_ci	if (res < 0 && fl->fl_type != F_UNLCK) {
21162306a36Sopenharmony_ci		fl_type = fl->fl_type;
21262306a36Sopenharmony_ci		fl->fl_type = F_UNLCK;
21362306a36Sopenharmony_ci		/* Even if this fails we want to return the remote error */
21462306a36Sopenharmony_ci		locks_lock_file_wait(filp, fl);
21562306a36Sopenharmony_ci		fl->fl_type = fl_type;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci	if (flock.client_id != fid->clnt->name)
21862306a36Sopenharmony_ci		kfree(flock.client_id);
21962306a36Sopenharmony_ciout:
22062306a36Sopenharmony_ci	return res;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct p9_getlock glock;
22662306a36Sopenharmony_ci	struct p9_fid *fid;
22762306a36Sopenharmony_ci	int res = 0;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	fid = filp->private_data;
23062306a36Sopenharmony_ci	BUG_ON(fid == NULL);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	posix_test_lock(filp, fl);
23362306a36Sopenharmony_ci	/*
23462306a36Sopenharmony_ci	 * if we have a conflicting lock locally, no need to validate
23562306a36Sopenharmony_ci	 * with server
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci	if (fl->fl_type != F_UNLCK)
23862306a36Sopenharmony_ci		return res;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* convert posix lock to p9 tgetlock args */
24162306a36Sopenharmony_ci	memset(&glock, 0, sizeof(glock));
24262306a36Sopenharmony_ci	glock.type  = P9_LOCK_TYPE_UNLCK;
24362306a36Sopenharmony_ci	glock.start = fl->fl_start;
24462306a36Sopenharmony_ci	if (fl->fl_end == OFFSET_MAX)
24562306a36Sopenharmony_ci		glock.length = 0;
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		glock.length = fl->fl_end - fl->fl_start + 1;
24862306a36Sopenharmony_ci	glock.proc_id = fl->fl_pid;
24962306a36Sopenharmony_ci	glock.client_id = fid->clnt->name;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	res = p9_client_getlock_dotl(fid, &glock);
25262306a36Sopenharmony_ci	if (res < 0)
25362306a36Sopenharmony_ci		goto out;
25462306a36Sopenharmony_ci	/* map 9p lock type to os lock type */
25562306a36Sopenharmony_ci	switch (glock.type) {
25662306a36Sopenharmony_ci	case P9_LOCK_TYPE_RDLCK:
25762306a36Sopenharmony_ci		fl->fl_type = F_RDLCK;
25862306a36Sopenharmony_ci		break;
25962306a36Sopenharmony_ci	case P9_LOCK_TYPE_WRLCK:
26062306a36Sopenharmony_ci		fl->fl_type = F_WRLCK;
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci	case P9_LOCK_TYPE_UNLCK:
26362306a36Sopenharmony_ci		fl->fl_type = F_UNLCK;
26462306a36Sopenharmony_ci		break;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	if (glock.type != P9_LOCK_TYPE_UNLCK) {
26762306a36Sopenharmony_ci		fl->fl_start = glock.start;
26862306a36Sopenharmony_ci		if (glock.length == 0)
26962306a36Sopenharmony_ci			fl->fl_end = OFFSET_MAX;
27062306a36Sopenharmony_ci		else
27162306a36Sopenharmony_ci			fl->fl_end = glock.start + glock.length - 1;
27262306a36Sopenharmony_ci		fl->fl_pid = -glock.proc_id;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ciout:
27562306a36Sopenharmony_ci	if (glock.client_id != fid->clnt->name)
27662306a36Sopenharmony_ci		kfree(glock.client_id);
27762306a36Sopenharmony_ci	return res;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/**
28162306a36Sopenharmony_ci * v9fs_file_lock_dotl - lock a file (or directory)
28262306a36Sopenharmony_ci * @filp: file to be locked
28362306a36Sopenharmony_ci * @cmd: lock command
28462306a36Sopenharmony_ci * @fl: file lock structure
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci */
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
29162306a36Sopenharmony_ci	int ret = -ENOLCK;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n",
29462306a36Sopenharmony_ci		 filp, cmd, fl, filp);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
29762306a36Sopenharmony_ci		filemap_write_and_wait(inode->i_mapping);
29862306a36Sopenharmony_ci		invalidate_mapping_pages(&inode->i_data, 0, -1);
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (IS_SETLK(cmd) || IS_SETLKW(cmd))
30262306a36Sopenharmony_ci		ret = v9fs_file_do_lock(filp, cmd, fl);
30362306a36Sopenharmony_ci	else if (IS_GETLK(cmd))
30462306a36Sopenharmony_ci		ret = v9fs_file_getlock(filp, fl);
30562306a36Sopenharmony_ci	else
30662306a36Sopenharmony_ci		ret = -EINVAL;
30762306a36Sopenharmony_ci	return ret;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/**
31162306a36Sopenharmony_ci * v9fs_file_flock_dotl - lock a file
31262306a36Sopenharmony_ci * @filp: file to be locked
31362306a36Sopenharmony_ci * @cmd: lock command
31462306a36Sopenharmony_ci * @fl: file lock structure
31562306a36Sopenharmony_ci *
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int v9fs_file_flock_dotl(struct file *filp, int cmd,
31962306a36Sopenharmony_ci	struct file_lock *fl)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
32262306a36Sopenharmony_ci	int ret = -ENOLCK;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n",
32562306a36Sopenharmony_ci		 filp, cmd, fl, filp);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (!(fl->fl_flags & FL_FLOCK))
32862306a36Sopenharmony_ci		goto out_err;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
33162306a36Sopenharmony_ci		filemap_write_and_wait(inode->i_mapping);
33262306a36Sopenharmony_ci		invalidate_mapping_pages(&inode->i_data, 0, -1);
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci	/* Convert flock to posix lock */
33562306a36Sopenharmony_ci	fl->fl_flags |= FL_POSIX;
33662306a36Sopenharmony_ci	fl->fl_flags ^= FL_FLOCK;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (IS_SETLK(cmd) | IS_SETLKW(cmd))
33962306a36Sopenharmony_ci		ret = v9fs_file_do_lock(filp, cmd, fl);
34062306a36Sopenharmony_ci	else
34162306a36Sopenharmony_ci		ret = -EINVAL;
34262306a36Sopenharmony_ciout_err:
34362306a36Sopenharmony_ci	return ret;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/**
34762306a36Sopenharmony_ci * v9fs_file_read_iter - read from a file
34862306a36Sopenharmony_ci * @iocb: The operation parameters
34962306a36Sopenharmony_ci * @to: The buffer to read into
35062306a36Sopenharmony_ci *
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_cistatic ssize_t
35362306a36Sopenharmony_civ9fs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct p9_fid *fid = iocb->ki_filp->private_data;
35662306a36Sopenharmony_ci	int ret, err = 0;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "fid %d count %zu offset %lld\n",
35962306a36Sopenharmony_ci		 fid->fid, iov_iter_count(to), iocb->ki_pos);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (!(fid->mode & P9L_DIRECT)) {
36262306a36Sopenharmony_ci		p9_debug(P9_DEBUG_VFS, "(cached)\n");
36362306a36Sopenharmony_ci		return generic_file_read_iter(iocb, to);
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (iocb->ki_filp->f_flags & O_NONBLOCK)
36762306a36Sopenharmony_ci		ret = p9_client_read_once(fid, iocb->ki_pos, to, &err);
36862306a36Sopenharmony_ci	else
36962306a36Sopenharmony_ci		ret = p9_client_read(fid, iocb->ki_pos, to, &err);
37062306a36Sopenharmony_ci	if (!ret)
37162306a36Sopenharmony_ci		return err;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	iocb->ki_pos += ret;
37462306a36Sopenharmony_ci	return ret;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci/*
37862306a36Sopenharmony_ci * v9fs_file_splice_read - splice-read from a file
37962306a36Sopenharmony_ci * @in: The 9p file to read from
38062306a36Sopenharmony_ci * @ppos: Where to find/update the file position
38162306a36Sopenharmony_ci * @pipe: The pipe to splice into
38262306a36Sopenharmony_ci * @len: The maximum amount of data to splice
38362306a36Sopenharmony_ci * @flags: SPLICE_F_* flags
38462306a36Sopenharmony_ci */
38562306a36Sopenharmony_cistatic ssize_t v9fs_file_splice_read(struct file *in, loff_t *ppos,
38662306a36Sopenharmony_ci				     struct pipe_inode_info *pipe,
38762306a36Sopenharmony_ci				     size_t len, unsigned int flags)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct p9_fid *fid = in->private_data;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "fid %d count %zu offset %lld\n",
39262306a36Sopenharmony_ci		 fid->fid, len, *ppos);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (fid->mode & P9L_DIRECT)
39562306a36Sopenharmony_ci		return copy_splice_read(in, ppos, pipe, len, flags);
39662306a36Sopenharmony_ci	return filemap_splice_read(in, ppos, pipe, len, flags);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/**
40062306a36Sopenharmony_ci * v9fs_file_write_iter - write to a file
40162306a36Sopenharmony_ci * @iocb: The operation parameters
40262306a36Sopenharmony_ci * @from: The data to write
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci */
40562306a36Sopenharmony_cistatic ssize_t
40662306a36Sopenharmony_civ9fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct file *file = iocb->ki_filp;
40962306a36Sopenharmony_ci	struct p9_fid *fid = file->private_data;
41062306a36Sopenharmony_ci	ssize_t retval;
41162306a36Sopenharmony_ci	loff_t origin;
41262306a36Sopenharmony_ci	int err = 0;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "fid %d\n", fid->fid);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (!(fid->mode & (P9L_DIRECT | P9L_NOWRITECACHE))) {
41762306a36Sopenharmony_ci		p9_debug(P9_DEBUG_CACHE, "(cached)\n");
41862306a36Sopenharmony_ci		return generic_file_write_iter(iocb, from);
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	retval = generic_write_checks(iocb, from);
42262306a36Sopenharmony_ci	if (retval <= 0)
42362306a36Sopenharmony_ci		return retval;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	origin = iocb->ki_pos;
42662306a36Sopenharmony_ci	retval = p9_client_write(file->private_data, iocb->ki_pos, from, &err);
42762306a36Sopenharmony_ci	if (retval > 0) {
42862306a36Sopenharmony_ci		struct inode *inode = file_inode(file);
42962306a36Sopenharmony_ci		loff_t i_size;
43062306a36Sopenharmony_ci		unsigned long pg_start, pg_end;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		pg_start = origin >> PAGE_SHIFT;
43362306a36Sopenharmony_ci		pg_end = (origin + retval - 1) >> PAGE_SHIFT;
43462306a36Sopenharmony_ci		if (inode->i_mapping && inode->i_mapping->nrpages)
43562306a36Sopenharmony_ci			invalidate_inode_pages2_range(inode->i_mapping,
43662306a36Sopenharmony_ci						      pg_start, pg_end);
43762306a36Sopenharmony_ci		iocb->ki_pos += retval;
43862306a36Sopenharmony_ci		i_size = i_size_read(inode);
43962306a36Sopenharmony_ci		if (iocb->ki_pos > i_size) {
44062306a36Sopenharmony_ci			inode_add_bytes(inode, iocb->ki_pos - i_size);
44162306a36Sopenharmony_ci			/*
44262306a36Sopenharmony_ci			 * Need to serialize against i_size_write() in
44362306a36Sopenharmony_ci			 * v9fs_stat2inode()
44462306a36Sopenharmony_ci			 */
44562306a36Sopenharmony_ci			v9fs_i_size_write(inode, iocb->ki_pos);
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci		return retval;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci	return err;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int v9fs_file_fsync(struct file *filp, loff_t start, loff_t end,
45362306a36Sopenharmony_ci			   int datasync)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct p9_fid *fid;
45662306a36Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
45762306a36Sopenharmony_ci	struct p9_wstat wstat;
45862306a36Sopenharmony_ci	int retval;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	retval = file_write_and_wait_range(filp, start, end);
46162306a36Sopenharmony_ci	if (retval)
46262306a36Sopenharmony_ci		return retval;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	inode_lock(inode);
46562306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	fid = filp->private_data;
46862306a36Sopenharmony_ci	v9fs_blank_wstat(&wstat);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	retval = p9_client_wstat(fid, &wstat);
47162306a36Sopenharmony_ci	inode_unlock(inode);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return retval;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ciint v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
47762306a36Sopenharmony_ci			 int datasync)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct p9_fid *fid;
48062306a36Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
48162306a36Sopenharmony_ci	int retval;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	retval = file_write_and_wait_range(filp, start, end);
48462306a36Sopenharmony_ci	if (retval)
48562306a36Sopenharmony_ci		return retval;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	inode_lock(inode);
48862306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	fid = filp->private_data;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	retval = p9_client_fsync(fid, datasync);
49362306a36Sopenharmony_ci	inode_unlock(inode);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return retval;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic int
49962306a36Sopenharmony_civ9fs_file_mmap(struct file *filp, struct vm_area_struct *vma)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	int retval;
50262306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
50362306a36Sopenharmony_ci	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	p9_debug(P9_DEBUG_MMAP, "filp :%p\n", filp);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (!(v9ses->cache & CACHE_WRITEBACK)) {
50862306a36Sopenharmony_ci		p9_debug(P9_DEBUG_CACHE, "(read-only mmap mode)");
50962306a36Sopenharmony_ci		return generic_file_readonly_mmap(filp, vma);
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	retval = generic_file_mmap(filp, vma);
51362306a36Sopenharmony_ci	if (!retval)
51462306a36Sopenharmony_ci		vma->vm_ops = &v9fs_mmap_file_vm_ops;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	return retval;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic vm_fault_t
52062306a36Sopenharmony_civ9fs_vm_page_mkwrite(struct vm_fault *vmf)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct folio *folio = page_folio(vmf->page);
52362306a36Sopenharmony_ci	struct file *filp = vmf->vma->vm_file;
52462306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "folio %p fid %lx\n",
52862306a36Sopenharmony_ci		 folio, (unsigned long)filp->private_data);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* Wait for the page to be written to the cache before we allow it to
53162306a36Sopenharmony_ci	 * be modified.  We then assume the entire page will need writing back.
53262306a36Sopenharmony_ci	 */
53362306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE
53462306a36Sopenharmony_ci	if (folio_test_fscache(folio) &&
53562306a36Sopenharmony_ci	    folio_wait_fscache_killable(folio) < 0)
53662306a36Sopenharmony_ci		return VM_FAULT_NOPAGE;
53762306a36Sopenharmony_ci#endif
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/* Update file times before taking page lock */
54062306a36Sopenharmony_ci	file_update_time(filp);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (folio_lock_killable(folio) < 0)
54362306a36Sopenharmony_ci		return VM_FAULT_RETRY;
54462306a36Sopenharmony_ci	if (folio_mapping(folio) != inode->i_mapping)
54562306a36Sopenharmony_ci		goto out_unlock;
54662306a36Sopenharmony_ci	folio_wait_stable(folio);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	return VM_FAULT_LOCKED;
54962306a36Sopenharmony_ciout_unlock:
55062306a36Sopenharmony_ci	folio_unlock(folio);
55162306a36Sopenharmony_ci	return VM_FAULT_NOPAGE;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic void v9fs_mmap_vm_close(struct vm_area_struct *vma)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	struct inode *inode;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	struct writeback_control wbc = {
55962306a36Sopenharmony_ci		.nr_to_write = LONG_MAX,
56062306a36Sopenharmony_ci		.sync_mode = WB_SYNC_ALL,
56162306a36Sopenharmony_ci		.range_start = (loff_t)vma->vm_pgoff * PAGE_SIZE,
56262306a36Sopenharmony_ci		 /* absolute end, byte at end included */
56362306a36Sopenharmony_ci		.range_end = (loff_t)vma->vm_pgoff * PAGE_SIZE +
56462306a36Sopenharmony_ci			(vma->vm_end - vma->vm_start - 1),
56562306a36Sopenharmony_ci	};
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (!(vma->vm_flags & VM_SHARED))
56862306a36Sopenharmony_ci		return;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "9p VMA close, %p, flushing", vma);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	inode = file_inode(vma->vm_file);
57362306a36Sopenharmony_ci	filemap_fdatawrite_wbc(inode->i_mapping, &wbc);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic const struct vm_operations_struct v9fs_mmap_file_vm_ops = {
57762306a36Sopenharmony_ci	.close = v9fs_mmap_vm_close,
57862306a36Sopenharmony_ci	.fault = filemap_fault,
57962306a36Sopenharmony_ci	.map_pages = filemap_map_pages,
58062306a36Sopenharmony_ci	.page_mkwrite = v9fs_vm_page_mkwrite,
58162306a36Sopenharmony_ci};
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ciconst struct file_operations v9fs_file_operations = {
58462306a36Sopenharmony_ci	.llseek = generic_file_llseek,
58562306a36Sopenharmony_ci	.read_iter = v9fs_file_read_iter,
58662306a36Sopenharmony_ci	.write_iter = v9fs_file_write_iter,
58762306a36Sopenharmony_ci	.open = v9fs_file_open,
58862306a36Sopenharmony_ci	.release = v9fs_dir_release,
58962306a36Sopenharmony_ci	.lock = v9fs_file_lock,
59062306a36Sopenharmony_ci	.mmap = generic_file_readonly_mmap,
59162306a36Sopenharmony_ci	.splice_read = v9fs_file_splice_read,
59262306a36Sopenharmony_ci	.splice_write = iter_file_splice_write,
59362306a36Sopenharmony_ci	.fsync = v9fs_file_fsync,
59462306a36Sopenharmony_ci};
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ciconst struct file_operations v9fs_file_operations_dotl = {
59762306a36Sopenharmony_ci	.llseek = generic_file_llseek,
59862306a36Sopenharmony_ci	.read_iter = v9fs_file_read_iter,
59962306a36Sopenharmony_ci	.write_iter = v9fs_file_write_iter,
60062306a36Sopenharmony_ci	.open = v9fs_file_open,
60162306a36Sopenharmony_ci	.release = v9fs_dir_release,
60262306a36Sopenharmony_ci	.lock = v9fs_file_lock_dotl,
60362306a36Sopenharmony_ci	.flock = v9fs_file_flock_dotl,
60462306a36Sopenharmony_ci	.mmap = v9fs_file_mmap,
60562306a36Sopenharmony_ci	.splice_read = v9fs_file_splice_read,
60662306a36Sopenharmony_ci	.splice_write = iter_file_splice_write,
60762306a36Sopenharmony_ci	.fsync = v9fs_file_fsync_dotl,
60862306a36Sopenharmony_ci};
609