162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/nfs/file.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1992  Rick Sladkey
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Changes Copyright (C) 1994 by Florian La Roche
862306a36Sopenharmony_ci *   - Do not copy data too often around in the kernel.
962306a36Sopenharmony_ci *   - In nfs_file_read the return value of kmalloc wasn't checked.
1062306a36Sopenharmony_ci *   - Put in a better version of read look-ahead buffering. Original idea
1162306a36Sopenharmony_ci *     and implementation by Wai S Kok elekokws@ee.nus.sg.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *  Expire cache on write to a file by Wai S Kok (Oct 1994).
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *  Total rewrite of read side for new NFS buffer cache.. Linus.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *  nfs regular file handling functions
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/time.h>
2262306a36Sopenharmony_ci#include <linux/kernel.h>
2362306a36Sopenharmony_ci#include <linux/errno.h>
2462306a36Sopenharmony_ci#include <linux/fcntl.h>
2562306a36Sopenharmony_ci#include <linux/stat.h>
2662306a36Sopenharmony_ci#include <linux/nfs_fs.h>
2762306a36Sopenharmony_ci#include <linux/nfs_mount.h>
2862306a36Sopenharmony_ci#include <linux/mm.h>
2962306a36Sopenharmony_ci#include <linux/pagemap.h>
3062306a36Sopenharmony_ci#include <linux/gfp.h>
3162306a36Sopenharmony_ci#include <linux/swap.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <linux/uaccess.h>
3462306a36Sopenharmony_ci#include <linux/filelock.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include "delegation.h"
3762306a36Sopenharmony_ci#include "internal.h"
3862306a36Sopenharmony_ci#include "iostat.h"
3962306a36Sopenharmony_ci#include "fscache.h"
4062306a36Sopenharmony_ci#include "pnfs.h"
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include "nfstrace.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_FILE
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic const struct vm_operations_struct nfs_file_vm_ops;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciint nfs_check_flags(int flags)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT))
5162306a36Sopenharmony_ci		return -EINVAL;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return 0;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_check_flags);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * Open file
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistatic int
6162306a36Sopenharmony_cinfs_file_open(struct inode *inode, struct file *filp)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	int res;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	dprintk("NFS: open file(%pD2)\n", filp);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSOPEN);
6862306a36Sopenharmony_ci	res = nfs_check_flags(filp->f_flags);
6962306a36Sopenharmony_ci	if (res)
7062306a36Sopenharmony_ci		return res;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	res = nfs_open(inode, filp);
7362306a36Sopenharmony_ci	if (res == 0)
7462306a36Sopenharmony_ci		filp->f_mode |= FMODE_CAN_ODIRECT;
7562306a36Sopenharmony_ci	return res;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ciint
7962306a36Sopenharmony_cinfs_file_release(struct inode *inode, struct file *filp)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	dprintk("NFS: release(%pD2)\n", filp);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSRELEASE);
8462306a36Sopenharmony_ci	nfs_file_clear_open_context(filp);
8562306a36Sopenharmony_ci	nfs_fscache_release_file(inode, filp);
8662306a36Sopenharmony_ci	return 0;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_release);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/**
9162306a36Sopenharmony_ci * nfs_revalidate_file_size - Revalidate the file size
9262306a36Sopenharmony_ci * @inode: pointer to inode struct
9362306a36Sopenharmony_ci * @filp: pointer to struct file
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci * Revalidates the file length. This is basically a wrapper around
9662306a36Sopenharmony_ci * nfs_revalidate_inode() that takes into account the fact that we may
9762306a36Sopenharmony_ci * have cached writes (in which case we don't care about the server's
9862306a36Sopenharmony_ci * idea of what the file length is), or O_DIRECT (in which case we
9962306a36Sopenharmony_ci * shouldn't trust the cache).
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_cistatic int nfs_revalidate_file_size(struct inode *inode, struct file *filp)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (filp->f_flags & O_DIRECT)
10662306a36Sopenharmony_ci		goto force_reval;
10762306a36Sopenharmony_ci	if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_SIZE))
10862306a36Sopenharmony_ci		goto force_reval;
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ciforce_reval:
11162306a36Sopenharmony_ci	return __nfs_revalidate_inode(server, inode);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciloff_t nfs_file_llseek(struct file *filp, loff_t offset, int whence)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	dprintk("NFS: llseek file(%pD2, %lld, %d)\n",
11762306a36Sopenharmony_ci			filp, offset, whence);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/*
12062306a36Sopenharmony_ci	 * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate
12162306a36Sopenharmony_ci	 * the cached file length
12262306a36Sopenharmony_ci	 */
12362306a36Sopenharmony_ci	if (whence != SEEK_SET && whence != SEEK_CUR) {
12462306a36Sopenharmony_ci		struct inode *inode = filp->f_mapping->host;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		int retval = nfs_revalidate_file_size(inode, filp);
12762306a36Sopenharmony_ci		if (retval < 0)
12862306a36Sopenharmony_ci			return (loff_t)retval;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return generic_file_llseek(filp, offset, whence);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_llseek);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/*
13662306a36Sopenharmony_ci * Flush all dirty pages, and check for write errors.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic int
13962306a36Sopenharmony_cinfs_file_flush(struct file *file, fl_owner_t id)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct inode	*inode = file_inode(file);
14262306a36Sopenharmony_ci	errseq_t since;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	dprintk("NFS: flush(%pD2)\n", file);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
14762306a36Sopenharmony_ci	if ((file->f_mode & FMODE_WRITE) == 0)
14862306a36Sopenharmony_ci		return 0;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Flush writes to the server and return any errors */
15162306a36Sopenharmony_ci	since = filemap_sample_wb_err(file->f_mapping);
15262306a36Sopenharmony_ci	nfs_wb_all(inode);
15362306a36Sopenharmony_ci	return filemap_check_wb_err(file->f_mapping, since);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cissize_t
15762306a36Sopenharmony_cinfs_file_read(struct kiocb *iocb, struct iov_iter *to)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct inode *inode = file_inode(iocb->ki_filp);
16062306a36Sopenharmony_ci	ssize_t result;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (iocb->ki_flags & IOCB_DIRECT)
16362306a36Sopenharmony_ci		return nfs_file_direct_read(iocb, to, false);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	dprintk("NFS: read(%pD2, %zu@%lu)\n",
16662306a36Sopenharmony_ci		iocb->ki_filp,
16762306a36Sopenharmony_ci		iov_iter_count(to), (unsigned long) iocb->ki_pos);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	nfs_start_io_read(inode);
17062306a36Sopenharmony_ci	result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping);
17162306a36Sopenharmony_ci	if (!result) {
17262306a36Sopenharmony_ci		result = generic_file_read_iter(iocb, to);
17362306a36Sopenharmony_ci		if (result > 0)
17462306a36Sopenharmony_ci			nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, result);
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	nfs_end_io_read(inode);
17762306a36Sopenharmony_ci	return result;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_read);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cissize_t
18262306a36Sopenharmony_cinfs_file_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe,
18362306a36Sopenharmony_ci		     size_t len, unsigned int flags)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct inode *inode = file_inode(in);
18662306a36Sopenharmony_ci	ssize_t result;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	dprintk("NFS: splice_read(%pD2, %zu@%llu)\n", in, len, *ppos);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	nfs_start_io_read(inode);
19162306a36Sopenharmony_ci	result = nfs_revalidate_mapping(inode, in->f_mapping);
19262306a36Sopenharmony_ci	if (!result) {
19362306a36Sopenharmony_ci		result = filemap_splice_read(in, ppos, pipe, len, flags);
19462306a36Sopenharmony_ci		if (result > 0)
19562306a36Sopenharmony_ci			nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, result);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci	nfs_end_io_read(inode);
19862306a36Sopenharmony_ci	return result;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_splice_read);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ciint
20362306a36Sopenharmony_cinfs_file_mmap(struct file *file, struct vm_area_struct *vma)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
20662306a36Sopenharmony_ci	int	status;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	dprintk("NFS: mmap(%pD2)\n", file);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Note: generic_file_mmap() returns ENOSYS on nommu systems
21162306a36Sopenharmony_ci	 *       so we call that before revalidating the mapping
21262306a36Sopenharmony_ci	 */
21362306a36Sopenharmony_ci	status = generic_file_mmap(file, vma);
21462306a36Sopenharmony_ci	if (!status) {
21562306a36Sopenharmony_ci		vma->vm_ops = &nfs_file_vm_ops;
21662306a36Sopenharmony_ci		status = nfs_revalidate_mapping(inode, file->f_mapping);
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci	return status;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_mmap);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/*
22362306a36Sopenharmony_ci * Flush any dirty pages for this process, and check for write errors.
22462306a36Sopenharmony_ci * The return status from this call provides a reliable indication of
22562306a36Sopenharmony_ci * whether any write errors occurred for this process.
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_cistatic int
22862306a36Sopenharmony_cinfs_file_fsync_commit(struct file *file, int datasync)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
23162306a36Sopenharmony_ci	int ret, ret2;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	dprintk("NFS: fsync file(%pD2) datasync %d\n", file, datasync);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
23662306a36Sopenharmony_ci	ret = nfs_commit_inode(inode, FLUSH_SYNC);
23762306a36Sopenharmony_ci	ret2 = file_check_and_advance_wb_err(file);
23862306a36Sopenharmony_ci	if (ret2 < 0)
23962306a36Sopenharmony_ci		return ret2;
24062306a36Sopenharmony_ci	return ret;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ciint
24462306a36Sopenharmony_cinfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
24762306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
24862306a36Sopenharmony_ci	long save_nredirtied = atomic_long_read(&nfsi->redirtied_pages);
24962306a36Sopenharmony_ci	long nredirtied;
25062306a36Sopenharmony_ci	int ret;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	trace_nfs_fsync_enter(inode);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	for (;;) {
25562306a36Sopenharmony_ci		ret = file_write_and_wait_range(file, start, end);
25662306a36Sopenharmony_ci		if (ret != 0)
25762306a36Sopenharmony_ci			break;
25862306a36Sopenharmony_ci		ret = nfs_file_fsync_commit(file, datasync);
25962306a36Sopenharmony_ci		if (ret != 0)
26062306a36Sopenharmony_ci			break;
26162306a36Sopenharmony_ci		ret = pnfs_sync_inode(inode, !!datasync);
26262306a36Sopenharmony_ci		if (ret != 0)
26362306a36Sopenharmony_ci			break;
26462306a36Sopenharmony_ci		nredirtied = atomic_long_read(&nfsi->redirtied_pages);
26562306a36Sopenharmony_ci		if (nredirtied == save_nredirtied)
26662306a36Sopenharmony_ci			break;
26762306a36Sopenharmony_ci		save_nredirtied = nredirtied;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	trace_nfs_fsync_exit(inode, ret);
27162306a36Sopenharmony_ci	return ret;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_fsync);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/*
27662306a36Sopenharmony_ci * Decide whether a read/modify/write cycle may be more efficient
27762306a36Sopenharmony_ci * then a modify/write/read cycle when writing to a page in the
27862306a36Sopenharmony_ci * page cache.
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * Some pNFS layout drivers can only read/write at a certain block
28162306a36Sopenharmony_ci * granularity like all block devices and therefore we must perform
28262306a36Sopenharmony_ci * read/modify/write whenever a page hasn't read yet and the data
28362306a36Sopenharmony_ci * to be written there is not aligned to a block boundary and/or
28462306a36Sopenharmony_ci * smaller than the block size.
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * The modify/write/read cycle may occur if a page is read before
28762306a36Sopenharmony_ci * being completely filled by the writer.  In this situation, the
28862306a36Sopenharmony_ci * page must be completely written to stable storage on the server
28962306a36Sopenharmony_ci * before it can be refilled by reading in the page from the server.
29062306a36Sopenharmony_ci * This can lead to expensive, small, FILE_SYNC mode writes being
29162306a36Sopenharmony_ci * done.
29262306a36Sopenharmony_ci *
29362306a36Sopenharmony_ci * It may be more efficient to read the page first if the file is
29462306a36Sopenharmony_ci * open for reading in addition to writing, the page is not marked
29562306a36Sopenharmony_ci * as Uptodate, it is not dirty or waiting to be committed,
29662306a36Sopenharmony_ci * indicating that it was previously allocated and then modified,
29762306a36Sopenharmony_ci * that there were valid bytes of data in that range of the file,
29862306a36Sopenharmony_ci * and that the new data won't completely replace the old data in
29962306a36Sopenharmony_ci * that range of the file.
30062306a36Sopenharmony_ci */
30162306a36Sopenharmony_cistatic bool nfs_folio_is_full_write(struct folio *folio, loff_t pos,
30262306a36Sopenharmony_ci				    unsigned int len)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	unsigned int pglen = nfs_folio_length(folio);
30562306a36Sopenharmony_ci	unsigned int offset = offset_in_folio(folio, pos);
30662306a36Sopenharmony_ci	unsigned int end = offset + len;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return !pglen || (end >= pglen && !offset);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic bool nfs_want_read_modify_write(struct file *file, struct folio *folio,
31262306a36Sopenharmony_ci				       loff_t pos, unsigned int len)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	/*
31562306a36Sopenharmony_ci	 * Up-to-date pages, those with ongoing or full-page write
31662306a36Sopenharmony_ci	 * don't need read/modify/write
31762306a36Sopenharmony_ci	 */
31862306a36Sopenharmony_ci	if (folio_test_uptodate(folio) || folio_test_private(folio) ||
31962306a36Sopenharmony_ci	    nfs_folio_is_full_write(folio, pos, len))
32062306a36Sopenharmony_ci		return false;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (pnfs_ld_read_whole_page(file_inode(file)))
32362306a36Sopenharmony_ci		return true;
32462306a36Sopenharmony_ci	/* Open for reading too? */
32562306a36Sopenharmony_ci	if (file->f_mode & FMODE_READ)
32662306a36Sopenharmony_ci		return true;
32762306a36Sopenharmony_ci	return false;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/*
33162306a36Sopenharmony_ci * This does the "real" work of the write. We must allocate and lock the
33262306a36Sopenharmony_ci * page to be sent back to the generic routine, which then copies the
33362306a36Sopenharmony_ci * data from user space.
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci * If the writer ends up delaying the write, the writer needs to
33662306a36Sopenharmony_ci * increment the page use counts until he is done with the page.
33762306a36Sopenharmony_ci */
33862306a36Sopenharmony_cistatic int nfs_write_begin(struct file *file, struct address_space *mapping,
33962306a36Sopenharmony_ci			   loff_t pos, unsigned len, struct page **pagep,
34062306a36Sopenharmony_ci			   void **fsdata)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct folio *folio;
34362306a36Sopenharmony_ci	int once_thru = 0;
34462306a36Sopenharmony_ci	int ret;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: write_begin(%pD2(%lu), %u@%lld)\n",
34762306a36Sopenharmony_ci		file, mapping->host->i_ino, len, (long long) pos);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistart:
35062306a36Sopenharmony_ci	folio = __filemap_get_folio(mapping, pos >> PAGE_SHIFT, FGP_WRITEBEGIN,
35162306a36Sopenharmony_ci				    mapping_gfp_mask(mapping));
35262306a36Sopenharmony_ci	if (IS_ERR(folio))
35362306a36Sopenharmony_ci		return PTR_ERR(folio);
35462306a36Sopenharmony_ci	*pagep = &folio->page;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	ret = nfs_flush_incompatible(file, folio);
35762306a36Sopenharmony_ci	if (ret) {
35862306a36Sopenharmony_ci		folio_unlock(folio);
35962306a36Sopenharmony_ci		folio_put(folio);
36062306a36Sopenharmony_ci	} else if (!once_thru &&
36162306a36Sopenharmony_ci		   nfs_want_read_modify_write(file, folio, pos, len)) {
36262306a36Sopenharmony_ci		once_thru = 1;
36362306a36Sopenharmony_ci		ret = nfs_read_folio(file, folio);
36462306a36Sopenharmony_ci		folio_put(folio);
36562306a36Sopenharmony_ci		if (!ret)
36662306a36Sopenharmony_ci			goto start;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	return ret;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic int nfs_write_end(struct file *file, struct address_space *mapping,
37262306a36Sopenharmony_ci			 loff_t pos, unsigned len, unsigned copied,
37362306a36Sopenharmony_ci			 struct page *page, void *fsdata)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct nfs_open_context *ctx = nfs_file_open_context(file);
37662306a36Sopenharmony_ci	struct folio *folio = page_folio(page);
37762306a36Sopenharmony_ci	unsigned offset = offset_in_folio(folio, pos);
37862306a36Sopenharmony_ci	int status;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: write_end(%pD2(%lu), %u@%lld)\n",
38162306a36Sopenharmony_ci		file, mapping->host->i_ino, len, (long long) pos);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/*
38462306a36Sopenharmony_ci	 * Zero any uninitialised parts of the page, and then mark the page
38562306a36Sopenharmony_ci	 * as up to date if it turns out that we're extending the file.
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	if (!folio_test_uptodate(folio)) {
38862306a36Sopenharmony_ci		size_t fsize = folio_size(folio);
38962306a36Sopenharmony_ci		unsigned pglen = nfs_folio_length(folio);
39062306a36Sopenharmony_ci		unsigned end = offset + copied;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		if (pglen == 0) {
39362306a36Sopenharmony_ci			folio_zero_segments(folio, 0, offset, end, fsize);
39462306a36Sopenharmony_ci			folio_mark_uptodate(folio);
39562306a36Sopenharmony_ci		} else if (end >= pglen) {
39662306a36Sopenharmony_ci			folio_zero_segment(folio, end, fsize);
39762306a36Sopenharmony_ci			if (offset == 0)
39862306a36Sopenharmony_ci				folio_mark_uptodate(folio);
39962306a36Sopenharmony_ci		} else
40062306a36Sopenharmony_ci			folio_zero_segment(folio, pglen, fsize);
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	status = nfs_update_folio(file, folio, offset, copied);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	folio_unlock(folio);
40662306a36Sopenharmony_ci	folio_put(folio);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (status < 0)
40962306a36Sopenharmony_ci		return status;
41062306a36Sopenharmony_ci	NFS_I(mapping->host)->write_io += copied;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (nfs_ctx_key_to_expire(ctx, mapping->host))
41362306a36Sopenharmony_ci		nfs_wb_all(mapping->host);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return copied;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/*
41962306a36Sopenharmony_ci * Partially or wholly invalidate a page
42062306a36Sopenharmony_ci * - Release the private state associated with a page if undergoing complete
42162306a36Sopenharmony_ci *   page invalidation
42262306a36Sopenharmony_ci * - Called if either PG_private or PG_fscache is set on the page
42362306a36Sopenharmony_ci * - Caller holds page lock
42462306a36Sopenharmony_ci */
42562306a36Sopenharmony_cistatic void nfs_invalidate_folio(struct folio *folio, size_t offset,
42662306a36Sopenharmony_ci				size_t length)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct inode *inode = folio_file_mapping(folio)->host;
42962306a36Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: invalidate_folio(%lu, %zu, %zu)\n",
43062306a36Sopenharmony_ci		 folio->index, offset, length);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (offset != 0 || length < folio_size(folio))
43362306a36Sopenharmony_ci		return;
43462306a36Sopenharmony_ci	/* Cancel any unstarted writes on this page */
43562306a36Sopenharmony_ci	nfs_wb_folio_cancel(inode, folio);
43662306a36Sopenharmony_ci	folio_wait_fscache(folio);
43762306a36Sopenharmony_ci	trace_nfs_invalidate_folio(inode, folio);
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci/*
44162306a36Sopenharmony_ci * Attempt to release the private state associated with a folio
44262306a36Sopenharmony_ci * - Called if either private or fscache flags are set on the folio
44362306a36Sopenharmony_ci * - Caller holds folio lock
44462306a36Sopenharmony_ci * - Return true (may release folio) or false (may not)
44562306a36Sopenharmony_ci */
44662306a36Sopenharmony_cistatic bool nfs_release_folio(struct folio *folio, gfp_t gfp)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: release_folio(%p)\n", folio);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* If the private flag is set, then the folio is not freeable */
45162306a36Sopenharmony_ci	if (folio_test_private(folio)) {
45262306a36Sopenharmony_ci		if ((current_gfp_context(gfp) & GFP_KERNEL) != GFP_KERNEL ||
45362306a36Sopenharmony_ci		    current_is_kswapd())
45462306a36Sopenharmony_ci			return false;
45562306a36Sopenharmony_ci		if (nfs_wb_folio(folio_file_mapping(folio)->host, folio) < 0)
45662306a36Sopenharmony_ci			return false;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	return nfs_fscache_release_folio(folio, gfp);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void nfs_check_dirty_writeback(struct folio *folio,
46262306a36Sopenharmony_ci				bool *dirty, bool *writeback)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct nfs_inode *nfsi;
46562306a36Sopenharmony_ci	struct address_space *mapping = folio->mapping;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/*
46862306a36Sopenharmony_ci	 * Check if an unstable folio is currently being committed and
46962306a36Sopenharmony_ci	 * if so, have the VM treat it as if the folio is under writeback
47062306a36Sopenharmony_ci	 * so it will not block due to folios that will shortly be freeable.
47162306a36Sopenharmony_ci	 */
47262306a36Sopenharmony_ci	nfsi = NFS_I(mapping->host);
47362306a36Sopenharmony_ci	if (atomic_read(&nfsi->commit_info.rpcs_out)) {
47462306a36Sopenharmony_ci		*writeback = true;
47562306a36Sopenharmony_ci		return;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/*
47962306a36Sopenharmony_ci	 * If the private flag is set, then the folio is not freeable
48062306a36Sopenharmony_ci	 * and as the inode is not being committed, it's not going to
48162306a36Sopenharmony_ci	 * be cleaned in the near future so treat it as dirty
48262306a36Sopenharmony_ci	 */
48362306a36Sopenharmony_ci	if (folio_test_private(folio))
48462306a36Sopenharmony_ci		*dirty = true;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/*
48862306a36Sopenharmony_ci * Attempt to clear the private state associated with a page when an error
48962306a36Sopenharmony_ci * occurs that requires the cached contents of an inode to be written back or
49062306a36Sopenharmony_ci * destroyed
49162306a36Sopenharmony_ci * - Called if either PG_private or fscache is set on the page
49262306a36Sopenharmony_ci * - Caller holds page lock
49362306a36Sopenharmony_ci * - Return 0 if successful, -error otherwise
49462306a36Sopenharmony_ci */
49562306a36Sopenharmony_cistatic int nfs_launder_folio(struct folio *folio)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	struct inode *inode = folio->mapping->host;
49862306a36Sopenharmony_ci	int ret;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: launder_folio(%ld, %llu)\n",
50162306a36Sopenharmony_ci		inode->i_ino, folio_pos(folio));
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	folio_wait_fscache(folio);
50462306a36Sopenharmony_ci	ret = nfs_wb_folio(inode, folio);
50562306a36Sopenharmony_ci	trace_nfs_launder_folio_done(inode, folio, ret);
50662306a36Sopenharmony_ci	return ret;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
51062306a36Sopenharmony_ci						sector_t *span)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	unsigned long blocks;
51362306a36Sopenharmony_ci	long long isize;
51462306a36Sopenharmony_ci	int ret;
51562306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
51662306a36Sopenharmony_ci	struct rpc_clnt *clnt = NFS_CLIENT(inode);
51762306a36Sopenharmony_ci	struct nfs_client *cl = NFS_SERVER(inode)->nfs_client;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
52062306a36Sopenharmony_ci	blocks = inode->i_blocks;
52162306a36Sopenharmony_ci	isize = inode->i_size;
52262306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
52362306a36Sopenharmony_ci	if (blocks*512 < isize) {
52462306a36Sopenharmony_ci		pr_warn("swap activate: swapfile has holes\n");
52562306a36Sopenharmony_ci		return -EINVAL;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	ret = rpc_clnt_swap_activate(clnt);
52962306a36Sopenharmony_ci	if (ret)
53062306a36Sopenharmony_ci		return ret;
53162306a36Sopenharmony_ci	ret = add_swap_extent(sis, 0, sis->max, 0);
53262306a36Sopenharmony_ci	if (ret < 0) {
53362306a36Sopenharmony_ci		rpc_clnt_swap_deactivate(clnt);
53462306a36Sopenharmony_ci		return ret;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	*span = sis->pages;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (cl->rpc_ops->enable_swap)
54062306a36Sopenharmony_ci		cl->rpc_ops->enable_swap(inode);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	sis->flags |= SWP_FS_OPS;
54362306a36Sopenharmony_ci	return ret;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic void nfs_swap_deactivate(struct file *file)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
54962306a36Sopenharmony_ci	struct rpc_clnt *clnt = NFS_CLIENT(inode);
55062306a36Sopenharmony_ci	struct nfs_client *cl = NFS_SERVER(inode)->nfs_client;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	rpc_clnt_swap_deactivate(clnt);
55362306a36Sopenharmony_ci	if (cl->rpc_ops->disable_swap)
55462306a36Sopenharmony_ci		cl->rpc_ops->disable_swap(file_inode(file));
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ciconst struct address_space_operations nfs_file_aops = {
55862306a36Sopenharmony_ci	.read_folio = nfs_read_folio,
55962306a36Sopenharmony_ci	.readahead = nfs_readahead,
56062306a36Sopenharmony_ci	.dirty_folio = filemap_dirty_folio,
56162306a36Sopenharmony_ci	.writepage = nfs_writepage,
56262306a36Sopenharmony_ci	.writepages = nfs_writepages,
56362306a36Sopenharmony_ci	.write_begin = nfs_write_begin,
56462306a36Sopenharmony_ci	.write_end = nfs_write_end,
56562306a36Sopenharmony_ci	.invalidate_folio = nfs_invalidate_folio,
56662306a36Sopenharmony_ci	.release_folio = nfs_release_folio,
56762306a36Sopenharmony_ci	.migrate_folio = nfs_migrate_folio,
56862306a36Sopenharmony_ci	.launder_folio = nfs_launder_folio,
56962306a36Sopenharmony_ci	.is_dirty_writeback = nfs_check_dirty_writeback,
57062306a36Sopenharmony_ci	.error_remove_page = generic_error_remove_page,
57162306a36Sopenharmony_ci	.swap_activate = nfs_swap_activate,
57262306a36Sopenharmony_ci	.swap_deactivate = nfs_swap_deactivate,
57362306a36Sopenharmony_ci	.swap_rw = nfs_swap_rw,
57462306a36Sopenharmony_ci};
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci/*
57762306a36Sopenharmony_ci * Notification that a PTE pointing to an NFS page is about to be made
57862306a36Sopenharmony_ci * writable, implying that someone is about to modify the page through a
57962306a36Sopenharmony_ci * shared-writable mapping
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_cistatic vm_fault_t nfs_vm_page_mkwrite(struct vm_fault *vmf)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct file *filp = vmf->vma->vm_file;
58462306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
58562306a36Sopenharmony_ci	unsigned pagelen;
58662306a36Sopenharmony_ci	vm_fault_t ret = VM_FAULT_NOPAGE;
58762306a36Sopenharmony_ci	struct address_space *mapping;
58862306a36Sopenharmony_ci	struct folio *folio = page_folio(vmf->page);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: vm_page_mkwrite(%pD2(%lu), offset %lld)\n",
59162306a36Sopenharmony_ci		 filp, filp->f_mapping->host->i_ino,
59262306a36Sopenharmony_ci		 (long long)folio_file_pos(folio));
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	sb_start_pagefault(inode->i_sb);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	/* make sure the cache has finished storing the page */
59762306a36Sopenharmony_ci	if (folio_test_fscache(folio) &&
59862306a36Sopenharmony_ci	    folio_wait_fscache_killable(folio) < 0) {
59962306a36Sopenharmony_ci		ret = VM_FAULT_RETRY;
60062306a36Sopenharmony_ci		goto out;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	wait_on_bit_action(&NFS_I(inode)->flags, NFS_INO_INVALIDATING,
60462306a36Sopenharmony_ci			   nfs_wait_bit_killable,
60562306a36Sopenharmony_ci			   TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	folio_lock(folio);
60862306a36Sopenharmony_ci	mapping = folio_file_mapping(folio);
60962306a36Sopenharmony_ci	if (mapping != inode->i_mapping)
61062306a36Sopenharmony_ci		goto out_unlock;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	folio_wait_writeback(folio);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	pagelen = nfs_folio_length(folio);
61562306a36Sopenharmony_ci	if (pagelen == 0)
61662306a36Sopenharmony_ci		goto out_unlock;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	ret = VM_FAULT_LOCKED;
61962306a36Sopenharmony_ci	if (nfs_flush_incompatible(filp, folio) == 0 &&
62062306a36Sopenharmony_ci	    nfs_update_folio(filp, folio, 0, pagelen) == 0)
62162306a36Sopenharmony_ci		goto out;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	ret = VM_FAULT_SIGBUS;
62462306a36Sopenharmony_ciout_unlock:
62562306a36Sopenharmony_ci	folio_unlock(folio);
62662306a36Sopenharmony_ciout:
62762306a36Sopenharmony_ci	sb_end_pagefault(inode->i_sb);
62862306a36Sopenharmony_ci	return ret;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic const struct vm_operations_struct nfs_file_vm_ops = {
63262306a36Sopenharmony_ci	.fault = filemap_fault,
63362306a36Sopenharmony_ci	.map_pages = filemap_map_pages,
63462306a36Sopenharmony_ci	.page_mkwrite = nfs_vm_page_mkwrite,
63562306a36Sopenharmony_ci};
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cissize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct file *file = iocb->ki_filp;
64062306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
64162306a36Sopenharmony_ci	unsigned int mntflags = NFS_SERVER(inode)->flags;
64262306a36Sopenharmony_ci	ssize_t result, written;
64362306a36Sopenharmony_ci	errseq_t since;
64462306a36Sopenharmony_ci	int error;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	result = nfs_key_timeout_notify(file, inode);
64762306a36Sopenharmony_ci	if (result)
64862306a36Sopenharmony_ci		return result;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (iocb->ki_flags & IOCB_DIRECT)
65162306a36Sopenharmony_ci		return nfs_file_direct_write(iocb, from, false);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	dprintk("NFS: write(%pD2, %zu@%Ld)\n",
65462306a36Sopenharmony_ci		file, iov_iter_count(from), (long long) iocb->ki_pos);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (IS_SWAPFILE(inode))
65762306a36Sopenharmony_ci		goto out_swapfile;
65862306a36Sopenharmony_ci	/*
65962306a36Sopenharmony_ci	 * O_APPEND implies that we must revalidate the file length.
66062306a36Sopenharmony_ci	 */
66162306a36Sopenharmony_ci	if (iocb->ki_flags & IOCB_APPEND || iocb->ki_pos > i_size_read(inode)) {
66262306a36Sopenharmony_ci		result = nfs_revalidate_file_size(inode, file);
66362306a36Sopenharmony_ci		if (result)
66462306a36Sopenharmony_ci			return result;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	nfs_clear_invalid_mapping(file->f_mapping);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	since = filemap_sample_wb_err(file->f_mapping);
67062306a36Sopenharmony_ci	nfs_start_io_write(inode);
67162306a36Sopenharmony_ci	result = generic_write_checks(iocb, from);
67262306a36Sopenharmony_ci	if (result > 0)
67362306a36Sopenharmony_ci		result = generic_perform_write(iocb, from);
67462306a36Sopenharmony_ci	nfs_end_io_write(inode);
67562306a36Sopenharmony_ci	if (result <= 0)
67662306a36Sopenharmony_ci		goto out;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	written = result;
67962306a36Sopenharmony_ci	nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (mntflags & NFS_MOUNT_WRITE_EAGER) {
68262306a36Sopenharmony_ci		result = filemap_fdatawrite_range(file->f_mapping,
68362306a36Sopenharmony_ci						  iocb->ki_pos - written,
68462306a36Sopenharmony_ci						  iocb->ki_pos - 1);
68562306a36Sopenharmony_ci		if (result < 0)
68662306a36Sopenharmony_ci			goto out;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci	if (mntflags & NFS_MOUNT_WRITE_WAIT) {
68962306a36Sopenharmony_ci		filemap_fdatawait_range(file->f_mapping,
69062306a36Sopenharmony_ci					iocb->ki_pos - written,
69162306a36Sopenharmony_ci					iocb->ki_pos - 1);
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci	result = generic_write_sync(iocb, written);
69462306a36Sopenharmony_ci	if (result < 0)
69562306a36Sopenharmony_ci		return result;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ciout:
69862306a36Sopenharmony_ci	/* Return error values */
69962306a36Sopenharmony_ci	error = filemap_check_wb_err(file->f_mapping, since);
70062306a36Sopenharmony_ci	switch (error) {
70162306a36Sopenharmony_ci	default:
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci	case -EDQUOT:
70462306a36Sopenharmony_ci	case -EFBIG:
70562306a36Sopenharmony_ci	case -ENOSPC:
70662306a36Sopenharmony_ci		nfs_wb_all(inode);
70762306a36Sopenharmony_ci		error = file_check_and_advance_wb_err(file);
70862306a36Sopenharmony_ci		if (error < 0)
70962306a36Sopenharmony_ci			result = error;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci	return result;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ciout_swapfile:
71462306a36Sopenharmony_ci	printk(KERN_INFO "NFS: attempt to write to active swap file!\n");
71562306a36Sopenharmony_ci	return -ETXTBSY;
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_write);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int
72062306a36Sopenharmony_cido_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
72362306a36Sopenharmony_ci	int status = 0;
72462306a36Sopenharmony_ci	unsigned int saved_type = fl->fl_type;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* Try local locking first */
72762306a36Sopenharmony_ci	posix_test_lock(filp, fl);
72862306a36Sopenharmony_ci	if (fl->fl_type != F_UNLCK) {
72962306a36Sopenharmony_ci		/* found a conflict */
73062306a36Sopenharmony_ci		goto out;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci	fl->fl_type = saved_type;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
73562306a36Sopenharmony_ci		goto out_noconflict;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (is_local)
73862306a36Sopenharmony_ci		goto out_noconflict;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	status = NFS_PROTO(inode)->lock(filp, cmd, fl);
74162306a36Sopenharmony_ciout:
74262306a36Sopenharmony_ci	return status;
74362306a36Sopenharmony_ciout_noconflict:
74462306a36Sopenharmony_ci	fl->fl_type = F_UNLCK;
74562306a36Sopenharmony_ci	goto out;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic int
74962306a36Sopenharmony_cido_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
75262306a36Sopenharmony_ci	struct nfs_lock_context *l_ctx;
75362306a36Sopenharmony_ci	int status;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/*
75662306a36Sopenharmony_ci	 * Flush all pending writes before doing anything
75762306a36Sopenharmony_ci	 * with locks..
75862306a36Sopenharmony_ci	 */
75962306a36Sopenharmony_ci	nfs_wb_all(inode);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	l_ctx = nfs_get_lock_context(nfs_file_open_context(filp));
76262306a36Sopenharmony_ci	if (!IS_ERR(l_ctx)) {
76362306a36Sopenharmony_ci		status = nfs_iocounter_wait(l_ctx);
76462306a36Sopenharmony_ci		nfs_put_lock_context(l_ctx);
76562306a36Sopenharmony_ci		/*  NOTE: special case
76662306a36Sopenharmony_ci		 * 	If we're signalled while cleaning up locks on process exit, we
76762306a36Sopenharmony_ci		 * 	still need to complete the unlock.
76862306a36Sopenharmony_ci		 */
76962306a36Sopenharmony_ci		if (status < 0 && !(fl->fl_flags & FL_CLOSE))
77062306a36Sopenharmony_ci			return status;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	/*
77462306a36Sopenharmony_ci	 * Use local locking if mounted with "-onolock" or with appropriate
77562306a36Sopenharmony_ci	 * "-olocal_lock="
77662306a36Sopenharmony_ci	 */
77762306a36Sopenharmony_ci	if (!is_local)
77862306a36Sopenharmony_ci		status = NFS_PROTO(inode)->lock(filp, cmd, fl);
77962306a36Sopenharmony_ci	else
78062306a36Sopenharmony_ci		status = locks_lock_file_wait(filp, fl);
78162306a36Sopenharmony_ci	return status;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_cistatic int
78562306a36Sopenharmony_cido_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
78862306a36Sopenharmony_ci	int status;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	/*
79162306a36Sopenharmony_ci	 * Flush all pending writes before doing anything
79262306a36Sopenharmony_ci	 * with locks..
79362306a36Sopenharmony_ci	 */
79462306a36Sopenharmony_ci	status = nfs_sync_mapping(filp->f_mapping);
79562306a36Sopenharmony_ci	if (status != 0)
79662306a36Sopenharmony_ci		goto out;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	/*
79962306a36Sopenharmony_ci	 * Use local locking if mounted with "-onolock" or with appropriate
80062306a36Sopenharmony_ci	 * "-olocal_lock="
80162306a36Sopenharmony_ci	 */
80262306a36Sopenharmony_ci	if (!is_local)
80362306a36Sopenharmony_ci		status = NFS_PROTO(inode)->lock(filp, cmd, fl);
80462306a36Sopenharmony_ci	else
80562306a36Sopenharmony_ci		status = locks_lock_file_wait(filp, fl);
80662306a36Sopenharmony_ci	if (status < 0)
80762306a36Sopenharmony_ci		goto out;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/*
81062306a36Sopenharmony_ci	 * Invalidate cache to prevent missing any changes.  If
81162306a36Sopenharmony_ci	 * the file is mapped, clear the page cache as well so
81262306a36Sopenharmony_ci	 * those mappings will be loaded.
81362306a36Sopenharmony_ci	 *
81462306a36Sopenharmony_ci	 * This makes locking act as a cache coherency point.
81562306a36Sopenharmony_ci	 */
81662306a36Sopenharmony_ci	nfs_sync_mapping(filp->f_mapping);
81762306a36Sopenharmony_ci	if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) {
81862306a36Sopenharmony_ci		nfs_zap_caches(inode);
81962306a36Sopenharmony_ci		if (mapping_mapped(filp->f_mapping))
82062306a36Sopenharmony_ci			nfs_revalidate_mapping(inode, filp->f_mapping);
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ciout:
82362306a36Sopenharmony_ci	return status;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci/*
82762306a36Sopenharmony_ci * Lock a (portion of) a file
82862306a36Sopenharmony_ci */
82962306a36Sopenharmony_ciint nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
83262306a36Sopenharmony_ci	int ret = -ENOLCK;
83362306a36Sopenharmony_ci	int is_local = 0;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	dprintk("NFS: lock(%pD2, t=%x, fl=%x, r=%lld:%lld)\n",
83662306a36Sopenharmony_ci			filp, fl->fl_type, fl->fl_flags,
83762306a36Sopenharmony_ci			(long long)fl->fl_start, (long long)fl->fl_end);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSLOCK);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (fl->fl_flags & FL_RECLAIM)
84262306a36Sopenharmony_ci		return -ENOGRACE;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FCNTL)
84562306a36Sopenharmony_ci		is_local = 1;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (NFS_PROTO(inode)->lock_check_bounds != NULL) {
84862306a36Sopenharmony_ci		ret = NFS_PROTO(inode)->lock_check_bounds(fl);
84962306a36Sopenharmony_ci		if (ret < 0)
85062306a36Sopenharmony_ci			goto out_err;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (IS_GETLK(cmd))
85462306a36Sopenharmony_ci		ret = do_getlk(filp, cmd, fl, is_local);
85562306a36Sopenharmony_ci	else if (fl->fl_type == F_UNLCK)
85662306a36Sopenharmony_ci		ret = do_unlk(filp, cmd, fl, is_local);
85762306a36Sopenharmony_ci	else
85862306a36Sopenharmony_ci		ret = do_setlk(filp, cmd, fl, is_local);
85962306a36Sopenharmony_ciout_err:
86062306a36Sopenharmony_ci	return ret;
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_lock);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci/*
86562306a36Sopenharmony_ci * Lock a (portion of) a file
86662306a36Sopenharmony_ci */
86762306a36Sopenharmony_ciint nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
87062306a36Sopenharmony_ci	int is_local = 0;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	dprintk("NFS: flock(%pD2, t=%x, fl=%x)\n",
87362306a36Sopenharmony_ci			filp, fl->fl_type, fl->fl_flags);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (!(fl->fl_flags & FL_FLOCK))
87662306a36Sopenharmony_ci		return -ENOLCK;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK)
87962306a36Sopenharmony_ci		is_local = 1;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* We're simulating flock() locks using posix locks on the server */
88262306a36Sopenharmony_ci	if (fl->fl_type == F_UNLCK)
88362306a36Sopenharmony_ci		return do_unlk(filp, cmd, fl, is_local);
88462306a36Sopenharmony_ci	return do_setlk(filp, cmd, fl, is_local);
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_flock);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ciconst struct file_operations nfs_file_operations = {
88962306a36Sopenharmony_ci	.llseek		= nfs_file_llseek,
89062306a36Sopenharmony_ci	.read_iter	= nfs_file_read,
89162306a36Sopenharmony_ci	.write_iter	= nfs_file_write,
89262306a36Sopenharmony_ci	.mmap		= nfs_file_mmap,
89362306a36Sopenharmony_ci	.open		= nfs_file_open,
89462306a36Sopenharmony_ci	.flush		= nfs_file_flush,
89562306a36Sopenharmony_ci	.release	= nfs_file_release,
89662306a36Sopenharmony_ci	.fsync		= nfs_file_fsync,
89762306a36Sopenharmony_ci	.lock		= nfs_lock,
89862306a36Sopenharmony_ci	.flock		= nfs_flock,
89962306a36Sopenharmony_ci	.splice_read	= nfs_file_splice_read,
90062306a36Sopenharmony_ci	.splice_write	= iter_file_splice_write,
90162306a36Sopenharmony_ci	.check_flags	= nfs_check_flags,
90262306a36Sopenharmony_ci	.setlease	= simple_nosetlease,
90362306a36Sopenharmony_ci};
90462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_operations);
905