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