18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/nfs/file.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1992  Rick Sladkey
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Changes Copyright (C) 1994 by Florian La Roche
88c2ecf20Sopenharmony_ci *   - Do not copy data too often around in the kernel.
98c2ecf20Sopenharmony_ci *   - In nfs_file_read the return value of kmalloc wasn't checked.
108c2ecf20Sopenharmony_ci *   - Put in a better version of read look-ahead buffering. Original idea
118c2ecf20Sopenharmony_ci *     and implementation by Wai S Kok elekokws@ee.nus.sg.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *  Expire cache on write to a file by Wai S Kok (Oct 1994).
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *  Total rewrite of read side for new NFS buffer cache.. Linus.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *  nfs regular file handling functions
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/time.h>
228c2ecf20Sopenharmony_ci#include <linux/kernel.h>
238c2ecf20Sopenharmony_ci#include <linux/errno.h>
248c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
258c2ecf20Sopenharmony_ci#include <linux/stat.h>
268c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h>
278c2ecf20Sopenharmony_ci#include <linux/nfs_mount.h>
288c2ecf20Sopenharmony_ci#include <linux/mm.h>
298c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
308c2ecf20Sopenharmony_ci#include <linux/gfp.h>
318c2ecf20Sopenharmony_ci#include <linux/swap.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "delegation.h"
368c2ecf20Sopenharmony_ci#include "internal.h"
378c2ecf20Sopenharmony_ci#include "iostat.h"
388c2ecf20Sopenharmony_ci#include "fscache.h"
398c2ecf20Sopenharmony_ci#include "pnfs.h"
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include "nfstrace.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_FILE
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic const struct vm_operations_struct nfs_file_vm_ops;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Hack for future NFS swap support */
488c2ecf20Sopenharmony_ci#ifndef IS_SWAPFILE
498c2ecf20Sopenharmony_ci# define IS_SWAPFILE(inode)	(0)
508c2ecf20Sopenharmony_ci#endif
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ciint nfs_check_flags(int flags)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT))
558c2ecf20Sopenharmony_ci		return -EINVAL;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_check_flags);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * Open file
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic int
658c2ecf20Sopenharmony_cinfs_file_open(struct inode *inode, struct file *filp)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	int res;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	dprintk("NFS: open file(%pD2)\n", filp);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSOPEN);
728c2ecf20Sopenharmony_ci	res = nfs_check_flags(filp->f_flags);
738c2ecf20Sopenharmony_ci	if (res)
748c2ecf20Sopenharmony_ci		return res;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	res = nfs_open(inode, filp);
778c2ecf20Sopenharmony_ci	return res;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ciint
818c2ecf20Sopenharmony_cinfs_file_release(struct inode *inode, struct file *filp)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	dprintk("NFS: release(%pD2)\n", filp);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSRELEASE);
868c2ecf20Sopenharmony_ci	nfs_file_clear_open_context(filp);
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_release);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/**
928c2ecf20Sopenharmony_ci * nfs_revalidate_size - Revalidate the file size
938c2ecf20Sopenharmony_ci * @inode: pointer to inode struct
948c2ecf20Sopenharmony_ci * @filp: pointer to struct file
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci * Revalidates the file length. This is basically a wrapper around
978c2ecf20Sopenharmony_ci * nfs_revalidate_inode() that takes into account the fact that we may
988c2ecf20Sopenharmony_ci * have cached writes (in which case we don't care about the server's
998c2ecf20Sopenharmony_ci * idea of what the file length is), or O_DIRECT (in which case we
1008c2ecf20Sopenharmony_ci * shouldn't trust the cache).
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic int nfs_revalidate_file_size(struct inode *inode, struct file *filp)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (filp->f_flags & O_DIRECT)
1078c2ecf20Sopenharmony_ci		goto force_reval;
1088c2ecf20Sopenharmony_ci	if (nfs_check_cache_invalid(inode, NFS_INO_REVAL_PAGECACHE))
1098c2ecf20Sopenharmony_ci		goto force_reval;
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ciforce_reval:
1128c2ecf20Sopenharmony_ci	return __nfs_revalidate_inode(server, inode);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciloff_t nfs_file_llseek(struct file *filp, loff_t offset, int whence)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	dprintk("NFS: llseek file(%pD2, %lld, %d)\n",
1188c2ecf20Sopenharmony_ci			filp, offset, whence);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/*
1218c2ecf20Sopenharmony_ci	 * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate
1228c2ecf20Sopenharmony_ci	 * the cached file length
1238c2ecf20Sopenharmony_ci	 */
1248c2ecf20Sopenharmony_ci	if (whence != SEEK_SET && whence != SEEK_CUR) {
1258c2ecf20Sopenharmony_ci		struct inode *inode = filp->f_mapping->host;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		int retval = nfs_revalidate_file_size(inode, filp);
1288c2ecf20Sopenharmony_ci		if (retval < 0)
1298c2ecf20Sopenharmony_ci			return (loff_t)retval;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return generic_file_llseek(filp, offset, whence);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_llseek);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/*
1378c2ecf20Sopenharmony_ci * Flush all dirty pages, and check for write errors.
1388c2ecf20Sopenharmony_ci */
1398c2ecf20Sopenharmony_cistatic int
1408c2ecf20Sopenharmony_cinfs_file_flush(struct file *file, fl_owner_t id)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct inode	*inode = file_inode(file);
1438c2ecf20Sopenharmony_ci	errseq_t since;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	dprintk("NFS: flush(%pD2)\n", file);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
1488c2ecf20Sopenharmony_ci	if ((file->f_mode & FMODE_WRITE) == 0)
1498c2ecf20Sopenharmony_ci		return 0;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* Flush writes to the server and return any errors */
1528c2ecf20Sopenharmony_ci	since = filemap_sample_wb_err(file->f_mapping);
1538c2ecf20Sopenharmony_ci	nfs_wb_all(inode);
1548c2ecf20Sopenharmony_ci	return filemap_check_wb_err(file->f_mapping, since);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cissize_t
1588c2ecf20Sopenharmony_cinfs_file_read(struct kiocb *iocb, struct iov_iter *to)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(iocb->ki_filp);
1618c2ecf20Sopenharmony_ci	ssize_t result;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (iocb->ki_flags & IOCB_DIRECT)
1648c2ecf20Sopenharmony_ci		return nfs_file_direct_read(iocb, to, false);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	dprintk("NFS: read(%pD2, %zu@%lu)\n",
1678c2ecf20Sopenharmony_ci		iocb->ki_filp,
1688c2ecf20Sopenharmony_ci		iov_iter_count(to), (unsigned long) iocb->ki_pos);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	nfs_start_io_read(inode);
1718c2ecf20Sopenharmony_ci	result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping);
1728c2ecf20Sopenharmony_ci	if (!result) {
1738c2ecf20Sopenharmony_ci		result = generic_file_read_iter(iocb, to);
1748c2ecf20Sopenharmony_ci		if (result > 0)
1758c2ecf20Sopenharmony_ci			nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, result);
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci	nfs_end_io_read(inode);
1788c2ecf20Sopenharmony_ci	return result;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_read);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciint
1838c2ecf20Sopenharmony_cinfs_file_mmap(struct file * file, struct vm_area_struct * vma)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
1868c2ecf20Sopenharmony_ci	int	status;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	dprintk("NFS: mmap(%pD2)\n", file);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* Note: generic_file_mmap() returns ENOSYS on nommu systems
1918c2ecf20Sopenharmony_ci	 *       so we call that before revalidating the mapping
1928c2ecf20Sopenharmony_ci	 */
1938c2ecf20Sopenharmony_ci	status = generic_file_mmap(file, vma);
1948c2ecf20Sopenharmony_ci	if (!status) {
1958c2ecf20Sopenharmony_ci		vma->vm_ops = &nfs_file_vm_ops;
1968c2ecf20Sopenharmony_ci		status = nfs_revalidate_mapping(inode, file->f_mapping);
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci	return status;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_mmap);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/*
2038c2ecf20Sopenharmony_ci * Flush any dirty pages for this process, and check for write errors.
2048c2ecf20Sopenharmony_ci * The return status from this call provides a reliable indication of
2058c2ecf20Sopenharmony_ci * whether any write errors occurred for this process.
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_cistatic int
2088c2ecf20Sopenharmony_cinfs_file_fsync_commit(struct file *file, int datasync)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
2118c2ecf20Sopenharmony_ci	int ret, ret2;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	dprintk("NFS: fsync file(%pD2) datasync %d\n", file, datasync);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
2168c2ecf20Sopenharmony_ci	ret = nfs_commit_inode(inode, FLUSH_SYNC);
2178c2ecf20Sopenharmony_ci	ret2 = file_check_and_advance_wb_err(file);
2188c2ecf20Sopenharmony_ci	if (ret2 < 0)
2198c2ecf20Sopenharmony_ci		return ret2;
2208c2ecf20Sopenharmony_ci	return ret;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciint
2248c2ecf20Sopenharmony_cinfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct nfs_open_context *ctx = nfs_file_open_context(file);
2278c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
2288c2ecf20Sopenharmony_ci	int ret;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	trace_nfs_fsync_enter(inode);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	for (;;) {
2338c2ecf20Sopenharmony_ci		ret = file_write_and_wait_range(file, start, end);
2348c2ecf20Sopenharmony_ci		if (ret != 0)
2358c2ecf20Sopenharmony_ci			break;
2368c2ecf20Sopenharmony_ci		ret = nfs_file_fsync_commit(file, datasync);
2378c2ecf20Sopenharmony_ci		if (ret != 0)
2388c2ecf20Sopenharmony_ci			break;
2398c2ecf20Sopenharmony_ci		ret = pnfs_sync_inode(inode, !!datasync);
2408c2ecf20Sopenharmony_ci		if (ret != 0)
2418c2ecf20Sopenharmony_ci			break;
2428c2ecf20Sopenharmony_ci		if (!test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags))
2438c2ecf20Sopenharmony_ci			break;
2448c2ecf20Sopenharmony_ci		/*
2458c2ecf20Sopenharmony_ci		 * If nfs_file_fsync_commit detected a server reboot, then
2468c2ecf20Sopenharmony_ci		 * resend all dirty pages that might have been covered by
2478c2ecf20Sopenharmony_ci		 * the NFS_CONTEXT_RESEND_WRITES flag
2488c2ecf20Sopenharmony_ci		 */
2498c2ecf20Sopenharmony_ci		start = 0;
2508c2ecf20Sopenharmony_ci		end = LLONG_MAX;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	trace_nfs_fsync_exit(inode, ret);
2548c2ecf20Sopenharmony_ci	return ret;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_fsync);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci/*
2598c2ecf20Sopenharmony_ci * Decide whether a read/modify/write cycle may be more efficient
2608c2ecf20Sopenharmony_ci * then a modify/write/read cycle when writing to a page in the
2618c2ecf20Sopenharmony_ci * page cache.
2628c2ecf20Sopenharmony_ci *
2638c2ecf20Sopenharmony_ci * Some pNFS layout drivers can only read/write at a certain block
2648c2ecf20Sopenharmony_ci * granularity like all block devices and therefore we must perform
2658c2ecf20Sopenharmony_ci * read/modify/write whenever a page hasn't read yet and the data
2668c2ecf20Sopenharmony_ci * to be written there is not aligned to a block boundary and/or
2678c2ecf20Sopenharmony_ci * smaller than the block size.
2688c2ecf20Sopenharmony_ci *
2698c2ecf20Sopenharmony_ci * The modify/write/read cycle may occur if a page is read before
2708c2ecf20Sopenharmony_ci * being completely filled by the writer.  In this situation, the
2718c2ecf20Sopenharmony_ci * page must be completely written to stable storage on the server
2728c2ecf20Sopenharmony_ci * before it can be refilled by reading in the page from the server.
2738c2ecf20Sopenharmony_ci * This can lead to expensive, small, FILE_SYNC mode writes being
2748c2ecf20Sopenharmony_ci * done.
2758c2ecf20Sopenharmony_ci *
2768c2ecf20Sopenharmony_ci * It may be more efficient to read the page first if the file is
2778c2ecf20Sopenharmony_ci * open for reading in addition to writing, the page is not marked
2788c2ecf20Sopenharmony_ci * as Uptodate, it is not dirty or waiting to be committed,
2798c2ecf20Sopenharmony_ci * indicating that it was previously allocated and then modified,
2808c2ecf20Sopenharmony_ci * that there were valid bytes of data in that range of the file,
2818c2ecf20Sopenharmony_ci * and that the new data won't completely replace the old data in
2828c2ecf20Sopenharmony_ci * that range of the file.
2838c2ecf20Sopenharmony_ci */
2848c2ecf20Sopenharmony_cistatic bool nfs_full_page_write(struct page *page, loff_t pos, unsigned int len)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	unsigned int pglen = nfs_page_length(page);
2878c2ecf20Sopenharmony_ci	unsigned int offset = pos & (PAGE_SIZE - 1);
2888c2ecf20Sopenharmony_ci	unsigned int end = offset + len;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return !pglen || (end >= pglen && !offset);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic bool nfs_want_read_modify_write(struct file *file, struct page *page,
2948c2ecf20Sopenharmony_ci			loff_t pos, unsigned int len)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	/*
2978c2ecf20Sopenharmony_ci	 * Up-to-date pages, those with ongoing or full-page write
2988c2ecf20Sopenharmony_ci	 * don't need read/modify/write
2998c2ecf20Sopenharmony_ci	 */
3008c2ecf20Sopenharmony_ci	if (PageUptodate(page) || PagePrivate(page) ||
3018c2ecf20Sopenharmony_ci	    nfs_full_page_write(page, pos, len))
3028c2ecf20Sopenharmony_ci		return false;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (pnfs_ld_read_whole_page(file->f_mapping->host))
3058c2ecf20Sopenharmony_ci		return true;
3068c2ecf20Sopenharmony_ci	/* Open for reading too? */
3078c2ecf20Sopenharmony_ci	if (file->f_mode & FMODE_READ)
3088c2ecf20Sopenharmony_ci		return true;
3098c2ecf20Sopenharmony_ci	return false;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/*
3138c2ecf20Sopenharmony_ci * This does the "real" work of the write. We must allocate and lock the
3148c2ecf20Sopenharmony_ci * page to be sent back to the generic routine, which then copies the
3158c2ecf20Sopenharmony_ci * data from user space.
3168c2ecf20Sopenharmony_ci *
3178c2ecf20Sopenharmony_ci * If the writer ends up delaying the write, the writer needs to
3188c2ecf20Sopenharmony_ci * increment the page use counts until he is done with the page.
3198c2ecf20Sopenharmony_ci */
3208c2ecf20Sopenharmony_cistatic int nfs_write_begin(struct file *file, struct address_space *mapping,
3218c2ecf20Sopenharmony_ci			loff_t pos, unsigned len, unsigned flags,
3228c2ecf20Sopenharmony_ci			struct page **pagep, void **fsdata)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	int ret;
3258c2ecf20Sopenharmony_ci	pgoff_t index = pos >> PAGE_SHIFT;
3268c2ecf20Sopenharmony_ci	struct page *page;
3278c2ecf20Sopenharmony_ci	int once_thru = 0;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: write_begin(%pD2(%lu), %u@%lld)\n",
3308c2ecf20Sopenharmony_ci		file, mapping->host->i_ino, len, (long long) pos);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistart:
3338c2ecf20Sopenharmony_ci	page = grab_cache_page_write_begin(mapping, index, flags);
3348c2ecf20Sopenharmony_ci	if (!page)
3358c2ecf20Sopenharmony_ci		return -ENOMEM;
3368c2ecf20Sopenharmony_ci	*pagep = page;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	ret = nfs_flush_incompatible(file, page);
3398c2ecf20Sopenharmony_ci	if (ret) {
3408c2ecf20Sopenharmony_ci		unlock_page(page);
3418c2ecf20Sopenharmony_ci		put_page(page);
3428c2ecf20Sopenharmony_ci	} else if (!once_thru &&
3438c2ecf20Sopenharmony_ci		   nfs_want_read_modify_write(file, page, pos, len)) {
3448c2ecf20Sopenharmony_ci		once_thru = 1;
3458c2ecf20Sopenharmony_ci		ret = nfs_readpage(file, page);
3468c2ecf20Sopenharmony_ci		put_page(page);
3478c2ecf20Sopenharmony_ci		if (!ret)
3488c2ecf20Sopenharmony_ci			goto start;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci	return ret;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic int nfs_write_end(struct file *file, struct address_space *mapping,
3548c2ecf20Sopenharmony_ci			loff_t pos, unsigned len, unsigned copied,
3558c2ecf20Sopenharmony_ci			struct page *page, void *fsdata)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	unsigned offset = pos & (PAGE_SIZE - 1);
3588c2ecf20Sopenharmony_ci	struct nfs_open_context *ctx = nfs_file_open_context(file);
3598c2ecf20Sopenharmony_ci	int status;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: write_end(%pD2(%lu), %u@%lld)\n",
3628c2ecf20Sopenharmony_ci		file, mapping->host->i_ino, len, (long long) pos);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/*
3658c2ecf20Sopenharmony_ci	 * Zero any uninitialised parts of the page, and then mark the page
3668c2ecf20Sopenharmony_ci	 * as up to date if it turns out that we're extending the file.
3678c2ecf20Sopenharmony_ci	 */
3688c2ecf20Sopenharmony_ci	if (!PageUptodate(page)) {
3698c2ecf20Sopenharmony_ci		unsigned pglen = nfs_page_length(page);
3708c2ecf20Sopenharmony_ci		unsigned end = offset + copied;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		if (pglen == 0) {
3738c2ecf20Sopenharmony_ci			zero_user_segments(page, 0, offset,
3748c2ecf20Sopenharmony_ci					end, PAGE_SIZE);
3758c2ecf20Sopenharmony_ci			SetPageUptodate(page);
3768c2ecf20Sopenharmony_ci		} else if (end >= pglen) {
3778c2ecf20Sopenharmony_ci			zero_user_segment(page, end, PAGE_SIZE);
3788c2ecf20Sopenharmony_ci			if (offset == 0)
3798c2ecf20Sopenharmony_ci				SetPageUptodate(page);
3808c2ecf20Sopenharmony_ci		} else
3818c2ecf20Sopenharmony_ci			zero_user_segment(page, pglen, PAGE_SIZE);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	status = nfs_updatepage(file, page, offset, copied);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	unlock_page(page);
3878c2ecf20Sopenharmony_ci	put_page(page);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (status < 0)
3908c2ecf20Sopenharmony_ci		return status;
3918c2ecf20Sopenharmony_ci	NFS_I(mapping->host)->write_io += copied;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (nfs_ctx_key_to_expire(ctx, mapping->host))
3948c2ecf20Sopenharmony_ci		nfs_wb_all(mapping->host);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return copied;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/*
4008c2ecf20Sopenharmony_ci * Partially or wholly invalidate a page
4018c2ecf20Sopenharmony_ci * - Release the private state associated with a page if undergoing complete
4028c2ecf20Sopenharmony_ci *   page invalidation
4038c2ecf20Sopenharmony_ci * - Called if either PG_private or PG_fscache is set on the page
4048c2ecf20Sopenharmony_ci * - Caller holds page lock
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_cistatic void nfs_invalidate_page(struct page *page, unsigned int offset,
4078c2ecf20Sopenharmony_ci				unsigned int length)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: invalidate_page(%p, %u, %u)\n",
4108c2ecf20Sopenharmony_ci		 page, offset, length);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (offset != 0 || length < PAGE_SIZE)
4138c2ecf20Sopenharmony_ci		return;
4148c2ecf20Sopenharmony_ci	/* Cancel any unstarted writes on this page */
4158c2ecf20Sopenharmony_ci	nfs_wb_page_cancel(page_file_mapping(page)->host, page);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	nfs_fscache_invalidate_page(page, page->mapping->host);
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci/*
4218c2ecf20Sopenharmony_ci * Attempt to release the private state associated with a page
4228c2ecf20Sopenharmony_ci * - Called if either PG_private or PG_fscache is set on the page
4238c2ecf20Sopenharmony_ci * - Caller holds page lock
4248c2ecf20Sopenharmony_ci * - Return true (may release page) or false (may not)
4258c2ecf20Sopenharmony_ci */
4268c2ecf20Sopenharmony_cistatic int nfs_release_page(struct page *page, gfp_t gfp)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* If PagePrivate() is set, then the page is not freeable */
4318c2ecf20Sopenharmony_ci	if (PagePrivate(page))
4328c2ecf20Sopenharmony_ci		return 0;
4338c2ecf20Sopenharmony_ci	return nfs_fscache_release_page(page, gfp);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic void nfs_check_dirty_writeback(struct page *page,
4378c2ecf20Sopenharmony_ci				bool *dirty, bool *writeback)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct nfs_inode *nfsi;
4408c2ecf20Sopenharmony_ci	struct address_space *mapping = page_file_mapping(page);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (!mapping || PageSwapCache(page))
4438c2ecf20Sopenharmony_ci		return;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	/*
4468c2ecf20Sopenharmony_ci	 * Check if an unstable page is currently being committed and
4478c2ecf20Sopenharmony_ci	 * if so, have the VM treat it as if the page is under writeback
4488c2ecf20Sopenharmony_ci	 * so it will not block due to pages that will shortly be freeable.
4498c2ecf20Sopenharmony_ci	 */
4508c2ecf20Sopenharmony_ci	nfsi = NFS_I(mapping->host);
4518c2ecf20Sopenharmony_ci	if (atomic_read(&nfsi->commit_info.rpcs_out)) {
4528c2ecf20Sopenharmony_ci		*writeback = true;
4538c2ecf20Sopenharmony_ci		return;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/*
4578c2ecf20Sopenharmony_ci	 * If PagePrivate() is set, then the page is not freeable and as the
4588c2ecf20Sopenharmony_ci	 * inode is not being committed, it's not going to be cleaned in the
4598c2ecf20Sopenharmony_ci	 * near future so treat it as dirty
4608c2ecf20Sopenharmony_ci	 */
4618c2ecf20Sopenharmony_ci	if (PagePrivate(page))
4628c2ecf20Sopenharmony_ci		*dirty = true;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci/*
4668c2ecf20Sopenharmony_ci * Attempt to clear the private state associated with a page when an error
4678c2ecf20Sopenharmony_ci * occurs that requires the cached contents of an inode to be written back or
4688c2ecf20Sopenharmony_ci * destroyed
4698c2ecf20Sopenharmony_ci * - Called if either PG_private or fscache is set on the page
4708c2ecf20Sopenharmony_ci * - Caller holds page lock
4718c2ecf20Sopenharmony_ci * - Return 0 if successful, -error otherwise
4728c2ecf20Sopenharmony_ci */
4738c2ecf20Sopenharmony_cistatic int nfs_launder_page(struct page *page)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct inode *inode = page_file_mapping(page)->host;
4768c2ecf20Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: launder_page(%ld, %llu)\n",
4798c2ecf20Sopenharmony_ci		inode->i_ino, (long long)page_offset(page));
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	nfs_fscache_wait_on_page_write(nfsi, page);
4828c2ecf20Sopenharmony_ci	return nfs_wb_page(inode, page);
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
4868c2ecf20Sopenharmony_ci						sector_t *span)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	unsigned long blocks;
4898c2ecf20Sopenharmony_ci	long long isize;
4908c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
4918c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt = NFS_CLIENT(inode);
4928c2ecf20Sopenharmony_ci	struct nfs_client *cl = NFS_SERVER(inode)->nfs_client;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	spin_lock(&inode->i_lock);
4958c2ecf20Sopenharmony_ci	blocks = inode->i_blocks;
4968c2ecf20Sopenharmony_ci	isize = inode->i_size;
4978c2ecf20Sopenharmony_ci	spin_unlock(&inode->i_lock);
4988c2ecf20Sopenharmony_ci	if (blocks*512 < isize) {
4998c2ecf20Sopenharmony_ci		pr_warn("swap activate: swapfile has holes\n");
5008c2ecf20Sopenharmony_ci		return -EINVAL;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	*span = sis->pages;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (cl->rpc_ops->enable_swap)
5078c2ecf20Sopenharmony_ci		cl->rpc_ops->enable_swap(inode);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	return rpc_clnt_swap_activate(clnt);
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic void nfs_swap_deactivate(struct file *file)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
5158c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt = NFS_CLIENT(inode);
5168c2ecf20Sopenharmony_ci	struct nfs_client *cl = NFS_SERVER(inode)->nfs_client;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	rpc_clnt_swap_deactivate(clnt);
5198c2ecf20Sopenharmony_ci	if (cl->rpc_ops->disable_swap)
5208c2ecf20Sopenharmony_ci		cl->rpc_ops->disable_swap(file_inode(file));
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ciconst struct address_space_operations nfs_file_aops = {
5248c2ecf20Sopenharmony_ci	.readpage = nfs_readpage,
5258c2ecf20Sopenharmony_ci	.readpages = nfs_readpages,
5268c2ecf20Sopenharmony_ci	.set_page_dirty = __set_page_dirty_nobuffers,
5278c2ecf20Sopenharmony_ci	.writepage = nfs_writepage,
5288c2ecf20Sopenharmony_ci	.writepages = nfs_writepages,
5298c2ecf20Sopenharmony_ci	.write_begin = nfs_write_begin,
5308c2ecf20Sopenharmony_ci	.write_end = nfs_write_end,
5318c2ecf20Sopenharmony_ci	.invalidatepage = nfs_invalidate_page,
5328c2ecf20Sopenharmony_ci	.releasepage = nfs_release_page,
5338c2ecf20Sopenharmony_ci	.direct_IO = nfs_direct_IO,
5348c2ecf20Sopenharmony_ci#ifdef CONFIG_MIGRATION
5358c2ecf20Sopenharmony_ci	.migratepage = nfs_migrate_page,
5368c2ecf20Sopenharmony_ci#endif
5378c2ecf20Sopenharmony_ci	.launder_page = nfs_launder_page,
5388c2ecf20Sopenharmony_ci	.is_dirty_writeback = nfs_check_dirty_writeback,
5398c2ecf20Sopenharmony_ci	.error_remove_page = generic_error_remove_page,
5408c2ecf20Sopenharmony_ci	.swap_activate = nfs_swap_activate,
5418c2ecf20Sopenharmony_ci	.swap_deactivate = nfs_swap_deactivate,
5428c2ecf20Sopenharmony_ci};
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci/*
5458c2ecf20Sopenharmony_ci * Notification that a PTE pointing to an NFS page is about to be made
5468c2ecf20Sopenharmony_ci * writable, implying that someone is about to modify the page through a
5478c2ecf20Sopenharmony_ci * shared-writable mapping
5488c2ecf20Sopenharmony_ci */
5498c2ecf20Sopenharmony_cistatic vm_fault_t nfs_vm_page_mkwrite(struct vm_fault *vmf)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	struct page *page = vmf->page;
5528c2ecf20Sopenharmony_ci	struct file *filp = vmf->vma->vm_file;
5538c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filp);
5548c2ecf20Sopenharmony_ci	unsigned pagelen;
5558c2ecf20Sopenharmony_ci	vm_fault_t ret = VM_FAULT_NOPAGE;
5568c2ecf20Sopenharmony_ci	struct address_space *mapping;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	dfprintk(PAGECACHE, "NFS: vm_page_mkwrite(%pD2(%lu), offset %lld)\n",
5598c2ecf20Sopenharmony_ci		filp, filp->f_mapping->host->i_ino,
5608c2ecf20Sopenharmony_ci		(long long)page_offset(page));
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	sb_start_pagefault(inode->i_sb);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/* make sure the cache has finished storing the page */
5658c2ecf20Sopenharmony_ci	nfs_fscache_wait_on_page_write(NFS_I(inode), page);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	wait_on_bit_action(&NFS_I(inode)->flags, NFS_INO_INVALIDATING,
5688c2ecf20Sopenharmony_ci			nfs_wait_bit_killable, TASK_KILLABLE);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	lock_page(page);
5718c2ecf20Sopenharmony_ci	mapping = page_file_mapping(page);
5728c2ecf20Sopenharmony_ci	if (mapping != inode->i_mapping)
5738c2ecf20Sopenharmony_ci		goto out_unlock;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	wait_on_page_writeback(page);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	pagelen = nfs_page_length(page);
5788c2ecf20Sopenharmony_ci	if (pagelen == 0)
5798c2ecf20Sopenharmony_ci		goto out_unlock;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	ret = VM_FAULT_LOCKED;
5828c2ecf20Sopenharmony_ci	if (nfs_flush_incompatible(filp, page) == 0 &&
5838c2ecf20Sopenharmony_ci	    nfs_updatepage(filp, page, 0, pagelen) == 0)
5848c2ecf20Sopenharmony_ci		goto out;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	ret = VM_FAULT_SIGBUS;
5878c2ecf20Sopenharmony_ciout_unlock:
5888c2ecf20Sopenharmony_ci	unlock_page(page);
5898c2ecf20Sopenharmony_ciout:
5908c2ecf20Sopenharmony_ci	sb_end_pagefault(inode->i_sb);
5918c2ecf20Sopenharmony_ci	return ret;
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic const struct vm_operations_struct nfs_file_vm_ops = {
5958c2ecf20Sopenharmony_ci	.fault = filemap_fault,
5968c2ecf20Sopenharmony_ci	.map_pages = filemap_map_pages,
5978c2ecf20Sopenharmony_ci	.page_mkwrite = nfs_vm_page_mkwrite,
5988c2ecf20Sopenharmony_ci};
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic int nfs_need_check_write(struct file *filp, struct inode *inode,
6018c2ecf20Sopenharmony_ci				int error)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct nfs_open_context *ctx;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	ctx = nfs_file_open_context(filp);
6068c2ecf20Sopenharmony_ci	if (nfs_error_is_fatal_on_server(error) ||
6078c2ecf20Sopenharmony_ci	    nfs_ctx_key_to_expire(ctx, inode))
6088c2ecf20Sopenharmony_ci		return 1;
6098c2ecf20Sopenharmony_ci	return 0;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cissize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	struct file *file = iocb->ki_filp;
6158c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
6168c2ecf20Sopenharmony_ci	unsigned long written = 0;
6178c2ecf20Sopenharmony_ci	ssize_t result;
6188c2ecf20Sopenharmony_ci	errseq_t since;
6198c2ecf20Sopenharmony_ci	int error;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	result = nfs_key_timeout_notify(file, inode);
6228c2ecf20Sopenharmony_ci	if (result)
6238c2ecf20Sopenharmony_ci		return result;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (iocb->ki_flags & IOCB_DIRECT)
6268c2ecf20Sopenharmony_ci		return nfs_file_direct_write(iocb, from, false);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	dprintk("NFS: write(%pD2, %zu@%Ld)\n",
6298c2ecf20Sopenharmony_ci		file, iov_iter_count(from), (long long) iocb->ki_pos);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	if (IS_SWAPFILE(inode))
6328c2ecf20Sopenharmony_ci		goto out_swapfile;
6338c2ecf20Sopenharmony_ci	/*
6348c2ecf20Sopenharmony_ci	 * O_APPEND implies that we must revalidate the file length.
6358c2ecf20Sopenharmony_ci	 */
6368c2ecf20Sopenharmony_ci	if (iocb->ki_flags & IOCB_APPEND) {
6378c2ecf20Sopenharmony_ci		result = nfs_revalidate_file_size(inode, file);
6388c2ecf20Sopenharmony_ci		if (result)
6398c2ecf20Sopenharmony_ci			goto out;
6408c2ecf20Sopenharmony_ci	}
6418c2ecf20Sopenharmony_ci	if (iocb->ki_pos > i_size_read(inode))
6428c2ecf20Sopenharmony_ci		nfs_revalidate_mapping(inode, file->f_mapping);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	since = filemap_sample_wb_err(file->f_mapping);
6458c2ecf20Sopenharmony_ci	nfs_start_io_write(inode);
6468c2ecf20Sopenharmony_ci	result = generic_write_checks(iocb, from);
6478c2ecf20Sopenharmony_ci	if (result > 0) {
6488c2ecf20Sopenharmony_ci		current->backing_dev_info = inode_to_bdi(inode);
6498c2ecf20Sopenharmony_ci		result = generic_perform_write(file, from, iocb->ki_pos);
6508c2ecf20Sopenharmony_ci		current->backing_dev_info = NULL;
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci	nfs_end_io_write(inode);
6538c2ecf20Sopenharmony_ci	if (result <= 0)
6548c2ecf20Sopenharmony_ci		goto out;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	written = result;
6578c2ecf20Sopenharmony_ci	iocb->ki_pos += written;
6588c2ecf20Sopenharmony_ci	result = generic_write_sync(iocb, written);
6598c2ecf20Sopenharmony_ci	if (result < 0)
6608c2ecf20Sopenharmony_ci		goto out;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* Return error values */
6638c2ecf20Sopenharmony_ci	error = filemap_check_wb_err(file->f_mapping, since);
6648c2ecf20Sopenharmony_ci	if (nfs_need_check_write(file, inode, error)) {
6658c2ecf20Sopenharmony_ci		int err = nfs_wb_all(inode);
6668c2ecf20Sopenharmony_ci		if (err < 0)
6678c2ecf20Sopenharmony_ci			result = err;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci	nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
6708c2ecf20Sopenharmony_ciout:
6718c2ecf20Sopenharmony_ci	return result;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ciout_swapfile:
6748c2ecf20Sopenharmony_ci	printk(KERN_INFO "NFS: attempt to write to active swap file!\n");
6758c2ecf20Sopenharmony_ci	return -ETXTBSY;
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_write);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic int
6808c2ecf20Sopenharmony_cido_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
6838c2ecf20Sopenharmony_ci	int status = 0;
6848c2ecf20Sopenharmony_ci	unsigned int saved_type = fl->fl_type;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* Try local locking first */
6878c2ecf20Sopenharmony_ci	posix_test_lock(filp, fl);
6888c2ecf20Sopenharmony_ci	if (fl->fl_type != F_UNLCK) {
6898c2ecf20Sopenharmony_ci		/* found a conflict */
6908c2ecf20Sopenharmony_ci		goto out;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci	fl->fl_type = saved_type;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
6958c2ecf20Sopenharmony_ci		goto out_noconflict;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (is_local)
6988c2ecf20Sopenharmony_ci		goto out_noconflict;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	status = NFS_PROTO(inode)->lock(filp, cmd, fl);
7018c2ecf20Sopenharmony_ciout:
7028c2ecf20Sopenharmony_ci	return status;
7038c2ecf20Sopenharmony_ciout_noconflict:
7048c2ecf20Sopenharmony_ci	fl->fl_type = F_UNLCK;
7058c2ecf20Sopenharmony_ci	goto out;
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cistatic int
7098c2ecf20Sopenharmony_cido_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
7128c2ecf20Sopenharmony_ci	struct nfs_lock_context *l_ctx;
7138c2ecf20Sopenharmony_ci	int status;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	/*
7168c2ecf20Sopenharmony_ci	 * Flush all pending writes before doing anything
7178c2ecf20Sopenharmony_ci	 * with locks..
7188c2ecf20Sopenharmony_ci	 */
7198c2ecf20Sopenharmony_ci	nfs_wb_all(inode);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	l_ctx = nfs_get_lock_context(nfs_file_open_context(filp));
7228c2ecf20Sopenharmony_ci	if (!IS_ERR(l_ctx)) {
7238c2ecf20Sopenharmony_ci		status = nfs_iocounter_wait(l_ctx);
7248c2ecf20Sopenharmony_ci		nfs_put_lock_context(l_ctx);
7258c2ecf20Sopenharmony_ci		/*  NOTE: special case
7268c2ecf20Sopenharmony_ci		 * 	If we're signalled while cleaning up locks on process exit, we
7278c2ecf20Sopenharmony_ci		 * 	still need to complete the unlock.
7288c2ecf20Sopenharmony_ci		 */
7298c2ecf20Sopenharmony_ci		if (status < 0 && !(fl->fl_flags & FL_CLOSE))
7308c2ecf20Sopenharmony_ci			return status;
7318c2ecf20Sopenharmony_ci	}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/*
7348c2ecf20Sopenharmony_ci	 * Use local locking if mounted with "-onolock" or with appropriate
7358c2ecf20Sopenharmony_ci	 * "-olocal_lock="
7368c2ecf20Sopenharmony_ci	 */
7378c2ecf20Sopenharmony_ci	if (!is_local)
7388c2ecf20Sopenharmony_ci		status = NFS_PROTO(inode)->lock(filp, cmd, fl);
7398c2ecf20Sopenharmony_ci	else
7408c2ecf20Sopenharmony_ci		status = locks_lock_file_wait(filp, fl);
7418c2ecf20Sopenharmony_ci	return status;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistatic int
7458c2ecf20Sopenharmony_cido_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
7488c2ecf20Sopenharmony_ci	int status;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	/*
7518c2ecf20Sopenharmony_ci	 * Flush all pending writes before doing anything
7528c2ecf20Sopenharmony_ci	 * with locks..
7538c2ecf20Sopenharmony_ci	 */
7548c2ecf20Sopenharmony_ci	status = nfs_sync_mapping(filp->f_mapping);
7558c2ecf20Sopenharmony_ci	if (status != 0)
7568c2ecf20Sopenharmony_ci		goto out;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	/*
7598c2ecf20Sopenharmony_ci	 * Use local locking if mounted with "-onolock" or with appropriate
7608c2ecf20Sopenharmony_ci	 * "-olocal_lock="
7618c2ecf20Sopenharmony_ci	 */
7628c2ecf20Sopenharmony_ci	if (!is_local)
7638c2ecf20Sopenharmony_ci		status = NFS_PROTO(inode)->lock(filp, cmd, fl);
7648c2ecf20Sopenharmony_ci	else
7658c2ecf20Sopenharmony_ci		status = locks_lock_file_wait(filp, fl);
7668c2ecf20Sopenharmony_ci	if (status < 0)
7678c2ecf20Sopenharmony_ci		goto out;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	/*
7708c2ecf20Sopenharmony_ci	 * Invalidate cache to prevent missing any changes.  If
7718c2ecf20Sopenharmony_ci	 * the file is mapped, clear the page cache as well so
7728c2ecf20Sopenharmony_ci	 * those mappings will be loaded.
7738c2ecf20Sopenharmony_ci	 *
7748c2ecf20Sopenharmony_ci	 * This makes locking act as a cache coherency point.
7758c2ecf20Sopenharmony_ci	 */
7768c2ecf20Sopenharmony_ci	nfs_sync_mapping(filp->f_mapping);
7778c2ecf20Sopenharmony_ci	if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) {
7788c2ecf20Sopenharmony_ci		nfs_zap_caches(inode);
7798c2ecf20Sopenharmony_ci		if (mapping_mapped(filp->f_mapping))
7808c2ecf20Sopenharmony_ci			nfs_revalidate_mapping(inode, filp->f_mapping);
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ciout:
7838c2ecf20Sopenharmony_ci	return status;
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci/*
7878c2ecf20Sopenharmony_ci * Lock a (portion of) a file
7888c2ecf20Sopenharmony_ci */
7898c2ecf20Sopenharmony_ciint nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
7928c2ecf20Sopenharmony_ci	int ret = -ENOLCK;
7938c2ecf20Sopenharmony_ci	int is_local = 0;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	dprintk("NFS: lock(%pD2, t=%x, fl=%x, r=%lld:%lld)\n",
7968c2ecf20Sopenharmony_ci			filp, fl->fl_type, fl->fl_flags,
7978c2ecf20Sopenharmony_ci			(long long)fl->fl_start, (long long)fl->fl_end);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSLOCK);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	/* No mandatory locks over NFS */
8028c2ecf20Sopenharmony_ci	if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
8038c2ecf20Sopenharmony_ci		goto out_err;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FCNTL)
8068c2ecf20Sopenharmony_ci		is_local = 1;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if (NFS_PROTO(inode)->lock_check_bounds != NULL) {
8098c2ecf20Sopenharmony_ci		ret = NFS_PROTO(inode)->lock_check_bounds(fl);
8108c2ecf20Sopenharmony_ci		if (ret < 0)
8118c2ecf20Sopenharmony_ci			goto out_err;
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	if (IS_GETLK(cmd))
8158c2ecf20Sopenharmony_ci		ret = do_getlk(filp, cmd, fl, is_local);
8168c2ecf20Sopenharmony_ci	else if (fl->fl_type == F_UNLCK)
8178c2ecf20Sopenharmony_ci		ret = do_unlk(filp, cmd, fl, is_local);
8188c2ecf20Sopenharmony_ci	else
8198c2ecf20Sopenharmony_ci		ret = do_setlk(filp, cmd, fl, is_local);
8208c2ecf20Sopenharmony_ciout_err:
8218c2ecf20Sopenharmony_ci	return ret;
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_lock);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci/*
8268c2ecf20Sopenharmony_ci * Lock a (portion of) a file
8278c2ecf20Sopenharmony_ci */
8288c2ecf20Sopenharmony_ciint nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	struct inode *inode = filp->f_mapping->host;
8318c2ecf20Sopenharmony_ci	int is_local = 0;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	dprintk("NFS: flock(%pD2, t=%x, fl=%x)\n",
8348c2ecf20Sopenharmony_ci			filp, fl->fl_type, fl->fl_flags);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (!(fl->fl_flags & FL_FLOCK))
8378c2ecf20Sopenharmony_ci		return -ENOLCK;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/*
8408c2ecf20Sopenharmony_ci	 * The NFSv4 protocol doesn't support LOCK_MAND, which is not part of
8418c2ecf20Sopenharmony_ci	 * any standard. In principle we might be able to support LOCK_MAND
8428c2ecf20Sopenharmony_ci	 * on NFSv2/3 since NLMv3/4 support DOS share modes, but for now the
8438c2ecf20Sopenharmony_ci	 * NFS code is not set up for it.
8448c2ecf20Sopenharmony_ci	 */
8458c2ecf20Sopenharmony_ci	if (fl->fl_type & LOCK_MAND)
8468c2ecf20Sopenharmony_ci		return -EINVAL;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK)
8498c2ecf20Sopenharmony_ci		is_local = 1;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	/* We're simulating flock() locks using posix locks on the server */
8528c2ecf20Sopenharmony_ci	if (fl->fl_type == F_UNLCK)
8538c2ecf20Sopenharmony_ci		return do_unlk(filp, cmd, fl, is_local);
8548c2ecf20Sopenharmony_ci	return do_setlk(filp, cmd, fl, is_local);
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_flock);
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ciconst struct file_operations nfs_file_operations = {
8598c2ecf20Sopenharmony_ci	.llseek		= nfs_file_llseek,
8608c2ecf20Sopenharmony_ci	.read_iter	= nfs_file_read,
8618c2ecf20Sopenharmony_ci	.write_iter	= nfs_file_write,
8628c2ecf20Sopenharmony_ci	.mmap		= nfs_file_mmap,
8638c2ecf20Sopenharmony_ci	.open		= nfs_file_open,
8648c2ecf20Sopenharmony_ci	.flush		= nfs_file_flush,
8658c2ecf20Sopenharmony_ci	.release	= nfs_file_release,
8668c2ecf20Sopenharmony_ci	.fsync		= nfs_file_fsync,
8678c2ecf20Sopenharmony_ci	.lock		= nfs_lock,
8688c2ecf20Sopenharmony_ci	.flock		= nfs_flock,
8698c2ecf20Sopenharmony_ci	.splice_read	= generic_file_splice_read,
8708c2ecf20Sopenharmony_ci	.splice_write	= iter_file_splice_write,
8718c2ecf20Sopenharmony_ci	.check_flags	= nfs_check_flags,
8728c2ecf20Sopenharmony_ci	.setlease	= simple_nosetlease,
8738c2ecf20Sopenharmony_ci};
8748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_file_operations);
875