18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/nfs/write.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Write file data over NFS. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 148c2ecf20Sopenharmony_ci#include <linux/file.h> 158c2ecf20Sopenharmony_ci#include <linux/writeback.h> 168c2ecf20Sopenharmony_ci#include <linux/swap.h> 178c2ecf20Sopenharmony_ci#include <linux/migrate.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h> 208c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h> 218c2ecf20Sopenharmony_ci#include <linux/nfs_mount.h> 228c2ecf20Sopenharmony_ci#include <linux/nfs_page.h> 238c2ecf20Sopenharmony_ci#include <linux/backing-dev.h> 248c2ecf20Sopenharmony_ci#include <linux/export.h> 258c2ecf20Sopenharmony_ci#include <linux/freezer.h> 268c2ecf20Sopenharmony_ci#include <linux/wait.h> 278c2ecf20Sopenharmony_ci#include <linux/iversion.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 308c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "delegation.h" 338c2ecf20Sopenharmony_ci#include "internal.h" 348c2ecf20Sopenharmony_ci#include "iostat.h" 358c2ecf20Sopenharmony_ci#include "nfs4_fs.h" 368c2ecf20Sopenharmony_ci#include "fscache.h" 378c2ecf20Sopenharmony_ci#include "pnfs.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include "nfstrace.h" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_PAGECACHE 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define MIN_POOL_WRITE (32) 448c2ecf20Sopenharmony_ci#define MIN_POOL_COMMIT (4) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct nfs_io_completion { 478c2ecf20Sopenharmony_ci void (*complete)(void *data); 488c2ecf20Sopenharmony_ci void *data; 498c2ecf20Sopenharmony_ci struct kref refcount; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * Local function declarations 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_cistatic void nfs_redirty_request(struct nfs_page *req); 568c2ecf20Sopenharmony_cistatic const struct rpc_call_ops nfs_commit_ops; 578c2ecf20Sopenharmony_cistatic const struct nfs_pgio_completion_ops nfs_async_write_completion_ops; 588c2ecf20Sopenharmony_cistatic const struct nfs_commit_completion_ops nfs_commit_completion_ops; 598c2ecf20Sopenharmony_cistatic const struct nfs_rw_ops nfs_rw_write_ops; 608c2ecf20Sopenharmony_cistatic void nfs_inode_remove_request(struct nfs_page *req); 618c2ecf20Sopenharmony_cistatic void nfs_clear_request_commit(struct nfs_commit_info *cinfo, 628c2ecf20Sopenharmony_ci struct nfs_page *req); 638c2ecf20Sopenharmony_cistatic void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo, 648c2ecf20Sopenharmony_ci struct inode *inode); 658c2ecf20Sopenharmony_cistatic struct nfs_page * 668c2ecf20Sopenharmony_cinfs_page_search_commits_for_head_request_locked(struct nfs_inode *nfsi, 678c2ecf20Sopenharmony_ci struct page *page); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic struct kmem_cache *nfs_wdata_cachep; 708c2ecf20Sopenharmony_cistatic mempool_t *nfs_wdata_mempool; 718c2ecf20Sopenharmony_cistatic struct kmem_cache *nfs_cdata_cachep; 728c2ecf20Sopenharmony_cistatic mempool_t *nfs_commit_mempool; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct nfs_commit_data *nfs_commitdata_alloc(void) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct nfs_commit_data *p; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci p = kmem_cache_zalloc(nfs_cdata_cachep, nfs_io_gfp_mask()); 798c2ecf20Sopenharmony_ci if (!p) { 808c2ecf20Sopenharmony_ci p = mempool_alloc(nfs_commit_mempool, GFP_NOWAIT); 818c2ecf20Sopenharmony_ci if (!p) 828c2ecf20Sopenharmony_ci return NULL; 838c2ecf20Sopenharmony_ci memset(p, 0, sizeof(*p)); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&p->pages); 868c2ecf20Sopenharmony_ci return p; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_commitdata_alloc); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_civoid nfs_commit_free(struct nfs_commit_data *p) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci mempool_free(p, nfs_commit_mempool); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_commit_free); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic struct nfs_pgio_header *nfs_writehdr_alloc(void) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct nfs_pgio_header *p; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci p = kmem_cache_zalloc(nfs_wdata_cachep, nfs_io_gfp_mask()); 1018c2ecf20Sopenharmony_ci if (!p) { 1028c2ecf20Sopenharmony_ci p = mempool_alloc(nfs_wdata_mempool, GFP_NOWAIT); 1038c2ecf20Sopenharmony_ci if (!p) 1048c2ecf20Sopenharmony_ci return NULL; 1058c2ecf20Sopenharmony_ci memset(p, 0, sizeof(*p)); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci p->rw_mode = FMODE_WRITE; 1088c2ecf20Sopenharmony_ci return p; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void nfs_writehdr_free(struct nfs_pgio_header *hdr) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci mempool_free(hdr, nfs_wdata_mempool); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic struct nfs_io_completion *nfs_io_completion_alloc(gfp_t gfp_flags) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci return kmalloc(sizeof(struct nfs_io_completion), gfp_flags); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void nfs_io_completion_init(struct nfs_io_completion *ioc, 1228c2ecf20Sopenharmony_ci void (*complete)(void *), void *data) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci ioc->complete = complete; 1258c2ecf20Sopenharmony_ci ioc->data = data; 1268c2ecf20Sopenharmony_ci kref_init(&ioc->refcount); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void nfs_io_completion_release(struct kref *kref) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct nfs_io_completion *ioc = container_of(kref, 1328c2ecf20Sopenharmony_ci struct nfs_io_completion, refcount); 1338c2ecf20Sopenharmony_ci ioc->complete(ioc->data); 1348c2ecf20Sopenharmony_ci kfree(ioc); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void nfs_io_completion_get(struct nfs_io_completion *ioc) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci if (ioc != NULL) 1408c2ecf20Sopenharmony_ci kref_get(&ioc->refcount); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void nfs_io_completion_put(struct nfs_io_completion *ioc) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci if (ioc != NULL) 1468c2ecf20Sopenharmony_ci kref_put(&ioc->refcount, nfs_io_completion_release); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void 1508c2ecf20Sopenharmony_cinfs_page_set_inode_ref(struct nfs_page *req, struct inode *inode) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci if (!test_and_set_bit(PG_INODE_REF, &req->wb_flags)) { 1538c2ecf20Sopenharmony_ci kref_get(&req->wb_kref); 1548c2ecf20Sopenharmony_ci atomic_long_inc(&NFS_I(inode)->nrequests); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int 1598c2ecf20Sopenharmony_cinfs_cancel_remove_inode(struct nfs_page *req, struct inode *inode) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (!test_bit(PG_REMOVE, &req->wb_flags)) 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci ret = nfs_page_group_lock(req); 1668c2ecf20Sopenharmony_ci if (ret) 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci if (test_and_clear_bit(PG_REMOVE, &req->wb_flags)) 1698c2ecf20Sopenharmony_ci nfs_page_set_inode_ref(req, inode); 1708c2ecf20Sopenharmony_ci nfs_page_group_unlock(req); 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic struct nfs_page * 1758c2ecf20Sopenharmony_cinfs_page_private_request(struct page *page) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci if (!PagePrivate(page)) 1788c2ecf20Sopenharmony_ci return NULL; 1798c2ecf20Sopenharmony_ci return (struct nfs_page *)page_private(page); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* 1838c2ecf20Sopenharmony_ci * nfs_page_find_head_request_locked - find head request associated with @page 1848c2ecf20Sopenharmony_ci * 1858c2ecf20Sopenharmony_ci * must be called while holding the inode lock. 1868c2ecf20Sopenharmony_ci * 1878c2ecf20Sopenharmony_ci * returns matching head request with reference held, or NULL if not found. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_cistatic struct nfs_page * 1908c2ecf20Sopenharmony_cinfs_page_find_private_request(struct page *page) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct address_space *mapping = page_file_mapping(page); 1938c2ecf20Sopenharmony_ci struct nfs_page *req; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (!PagePrivate(page)) 1968c2ecf20Sopenharmony_ci return NULL; 1978c2ecf20Sopenharmony_ci spin_lock(&mapping->private_lock); 1988c2ecf20Sopenharmony_ci req = nfs_page_private_request(page); 1998c2ecf20Sopenharmony_ci if (req) { 2008c2ecf20Sopenharmony_ci WARN_ON_ONCE(req->wb_head != req); 2018c2ecf20Sopenharmony_ci kref_get(&req->wb_kref); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci spin_unlock(&mapping->private_lock); 2048c2ecf20Sopenharmony_ci return req; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic struct nfs_page * 2088c2ecf20Sopenharmony_cinfs_page_find_swap_request(struct page *page) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct inode *inode = page_file_mapping(page)->host; 2118c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 2128c2ecf20Sopenharmony_ci struct nfs_page *req = NULL; 2138c2ecf20Sopenharmony_ci if (!PageSwapCache(page)) 2148c2ecf20Sopenharmony_ci return NULL; 2158c2ecf20Sopenharmony_ci mutex_lock(&nfsi->commit_mutex); 2168c2ecf20Sopenharmony_ci if (PageSwapCache(page)) { 2178c2ecf20Sopenharmony_ci req = nfs_page_search_commits_for_head_request_locked(nfsi, 2188c2ecf20Sopenharmony_ci page); 2198c2ecf20Sopenharmony_ci if (req) { 2208c2ecf20Sopenharmony_ci WARN_ON_ONCE(req->wb_head != req); 2218c2ecf20Sopenharmony_ci kref_get(&req->wb_kref); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci mutex_unlock(&nfsi->commit_mutex); 2258c2ecf20Sopenharmony_ci return req; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* 2298c2ecf20Sopenharmony_ci * nfs_page_find_head_request - find head request associated with @page 2308c2ecf20Sopenharmony_ci * 2318c2ecf20Sopenharmony_ci * returns matching head request with reference held, or NULL if not found. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_cistatic struct nfs_page *nfs_page_find_head_request(struct page *page) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct nfs_page *req; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci req = nfs_page_find_private_request(page); 2388c2ecf20Sopenharmony_ci if (!req) 2398c2ecf20Sopenharmony_ci req = nfs_page_find_swap_request(page); 2408c2ecf20Sopenharmony_ci return req; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic struct nfs_page *nfs_find_and_lock_page_request(struct page *page) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct inode *inode = page_file_mapping(page)->host; 2468c2ecf20Sopenharmony_ci struct nfs_page *req, *head; 2478c2ecf20Sopenharmony_ci int ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci for (;;) { 2508c2ecf20Sopenharmony_ci req = nfs_page_find_head_request(page); 2518c2ecf20Sopenharmony_ci if (!req) 2528c2ecf20Sopenharmony_ci return req; 2538c2ecf20Sopenharmony_ci head = nfs_page_group_lock_head(req); 2548c2ecf20Sopenharmony_ci if (head != req) 2558c2ecf20Sopenharmony_ci nfs_release_request(req); 2568c2ecf20Sopenharmony_ci if (IS_ERR(head)) 2578c2ecf20Sopenharmony_ci return head; 2588c2ecf20Sopenharmony_ci ret = nfs_cancel_remove_inode(head, inode); 2598c2ecf20Sopenharmony_ci if (ret < 0) { 2608c2ecf20Sopenharmony_ci nfs_unlock_and_release_request(head); 2618c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci /* Ensure that nobody removed the request before we locked it */ 2648c2ecf20Sopenharmony_ci if (head == nfs_page_private_request(page)) 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci if (PageSwapCache(page)) 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci nfs_unlock_and_release_request(head); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci return head; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* Adjust the file length if we're writing beyond the end */ 2748c2ecf20Sopenharmony_cistatic void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct inode *inode = page_file_mapping(page)->host; 2778c2ecf20Sopenharmony_ci loff_t end, i_size; 2788c2ecf20Sopenharmony_ci pgoff_t end_index; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 2818c2ecf20Sopenharmony_ci i_size = i_size_read(inode); 2828c2ecf20Sopenharmony_ci end_index = (i_size - 1) >> PAGE_SHIFT; 2838c2ecf20Sopenharmony_ci if (i_size > 0 && page_index(page) < end_index) 2848c2ecf20Sopenharmony_ci goto out; 2858c2ecf20Sopenharmony_ci end = page_file_offset(page) + ((loff_t)offset+count); 2868c2ecf20Sopenharmony_ci if (i_size >= end) 2878c2ecf20Sopenharmony_ci goto out; 2888c2ecf20Sopenharmony_ci i_size_write(inode, end); 2898c2ecf20Sopenharmony_ci NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_SIZE; 2908c2ecf20Sopenharmony_ci nfs_inc_stats(inode, NFSIOS_EXTENDWRITE); 2918c2ecf20Sopenharmony_ciout: 2928c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* A writeback failed: mark the page as bad, and invalidate the page cache */ 2968c2ecf20Sopenharmony_cistatic void nfs_set_pageerror(struct address_space *mapping) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct inode *inode = mapping->host; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci nfs_zap_mapping(mapping->host, mapping); 3018c2ecf20Sopenharmony_ci /* Force file size revalidation */ 3028c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 3038c2ecf20Sopenharmony_ci NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED | 3048c2ecf20Sopenharmony_ci NFS_INO_REVAL_PAGECACHE | 3058c2ecf20Sopenharmony_ci NFS_INO_INVALID_SIZE; 3068c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic void nfs_mapping_set_error(struct page *page, int error) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct address_space *mapping = page_file_mapping(page); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci SetPageError(page); 3148c2ecf20Sopenharmony_ci filemap_set_wb_err(mapping, error); 3158c2ecf20Sopenharmony_ci if (mapping->host) 3168c2ecf20Sopenharmony_ci errseq_set(&mapping->host->i_sb->s_wb_err, 3178c2ecf20Sopenharmony_ci error == -ENOSPC ? -ENOSPC : -EIO); 3188c2ecf20Sopenharmony_ci nfs_set_pageerror(mapping); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* 3228c2ecf20Sopenharmony_ci * nfs_page_group_search_locked 3238c2ecf20Sopenharmony_ci * @head - head request of page group 3248c2ecf20Sopenharmony_ci * @page_offset - offset into page 3258c2ecf20Sopenharmony_ci * 3268c2ecf20Sopenharmony_ci * Search page group with head @head to find a request that contains the 3278c2ecf20Sopenharmony_ci * page offset @page_offset. 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * Returns a pointer to the first matching nfs request, or NULL if no 3308c2ecf20Sopenharmony_ci * match is found. 3318c2ecf20Sopenharmony_ci * 3328c2ecf20Sopenharmony_ci * Must be called with the page group lock held 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_cistatic struct nfs_page * 3358c2ecf20Sopenharmony_cinfs_page_group_search_locked(struct nfs_page *head, unsigned int page_offset) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct nfs_page *req; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci req = head; 3408c2ecf20Sopenharmony_ci do { 3418c2ecf20Sopenharmony_ci if (page_offset >= req->wb_pgbase && 3428c2ecf20Sopenharmony_ci page_offset < (req->wb_pgbase + req->wb_bytes)) 3438c2ecf20Sopenharmony_ci return req; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci req = req->wb_this_page; 3468c2ecf20Sopenharmony_ci } while (req != head); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return NULL; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci/* 3528c2ecf20Sopenharmony_ci * nfs_page_group_covers_page 3538c2ecf20Sopenharmony_ci * @head - head request of page group 3548c2ecf20Sopenharmony_ci * 3558c2ecf20Sopenharmony_ci * Return true if the page group with head @head covers the whole page, 3568c2ecf20Sopenharmony_ci * returns false otherwise 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_cistatic bool nfs_page_group_covers_page(struct nfs_page *req) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct nfs_page *tmp; 3618c2ecf20Sopenharmony_ci unsigned int pos = 0; 3628c2ecf20Sopenharmony_ci unsigned int len = nfs_page_length(req->wb_page); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci nfs_page_group_lock(req); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci for (;;) { 3678c2ecf20Sopenharmony_ci tmp = nfs_page_group_search_locked(req->wb_head, pos); 3688c2ecf20Sopenharmony_ci if (!tmp) 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci pos = tmp->wb_pgbase + tmp->wb_bytes; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci nfs_page_group_unlock(req); 3748c2ecf20Sopenharmony_ci return pos >= len; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* We can set the PG_uptodate flag if we see that a write request 3788c2ecf20Sopenharmony_ci * covers the full page. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic void nfs_mark_uptodate(struct nfs_page *req) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci if (PageUptodate(req->wb_page)) 3838c2ecf20Sopenharmony_ci return; 3848c2ecf20Sopenharmony_ci if (!nfs_page_group_covers_page(req)) 3858c2ecf20Sopenharmony_ci return; 3868c2ecf20Sopenharmony_ci SetPageUptodate(req->wb_page); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int wb_priority(struct writeback_control *wbc) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci int ret = 0; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (wbc->sync_mode == WB_SYNC_ALL) 3948c2ecf20Sopenharmony_ci ret = FLUSH_COND_STABLE; 3958c2ecf20Sopenharmony_ci return ret; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* 3998c2ecf20Sopenharmony_ci * NFS congestion control 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ciint nfs_congestion_kb; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci#define NFS_CONGESTION_ON_THRESH (nfs_congestion_kb >> (PAGE_SHIFT-10)) 4058c2ecf20Sopenharmony_ci#define NFS_CONGESTION_OFF_THRESH \ 4068c2ecf20Sopenharmony_ci (NFS_CONGESTION_ON_THRESH - (NFS_CONGESTION_ON_THRESH >> 2)) 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic void nfs_set_page_writeback(struct page *page) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct inode *inode = page_file_mapping(page)->host; 4118c2ecf20Sopenharmony_ci struct nfs_server *nfss = NFS_SERVER(inode); 4128c2ecf20Sopenharmony_ci int ret = test_set_page_writeback(page); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci WARN_ON_ONCE(ret != 0); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (atomic_long_inc_return(&nfss->writeback) > 4178c2ecf20Sopenharmony_ci NFS_CONGESTION_ON_THRESH) 4188c2ecf20Sopenharmony_ci set_bdi_congested(inode_to_bdi(inode), BLK_RW_ASYNC); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic void nfs_end_page_writeback(struct nfs_page *req) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct inode *inode = page_file_mapping(req->wb_page)->host; 4248c2ecf20Sopenharmony_ci struct nfs_server *nfss = NFS_SERVER(inode); 4258c2ecf20Sopenharmony_ci bool is_done; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci is_done = nfs_page_group_sync_on_bit(req, PG_WB_END); 4288c2ecf20Sopenharmony_ci nfs_unlock_request(req); 4298c2ecf20Sopenharmony_ci if (!is_done) 4308c2ecf20Sopenharmony_ci return; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci end_page_writeback(req->wb_page); 4338c2ecf20Sopenharmony_ci if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH) 4348c2ecf20Sopenharmony_ci clear_bdi_congested(inode_to_bdi(inode), BLK_RW_ASYNC); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/* 4388c2ecf20Sopenharmony_ci * nfs_destroy_unlinked_subrequests - destroy recently unlinked subrequests 4398c2ecf20Sopenharmony_ci * 4408c2ecf20Sopenharmony_ci * @destroy_list - request list (using wb_this_page) terminated by @old_head 4418c2ecf20Sopenharmony_ci * @old_head - the old head of the list 4428c2ecf20Sopenharmony_ci * 4438c2ecf20Sopenharmony_ci * All subrequests must be locked and removed from all lists, so at this point 4448c2ecf20Sopenharmony_ci * they are only "active" in this function, and possibly in nfs_wait_on_request 4458c2ecf20Sopenharmony_ci * with a reference held by some other context. 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_cistatic void 4488c2ecf20Sopenharmony_cinfs_destroy_unlinked_subrequests(struct nfs_page *destroy_list, 4498c2ecf20Sopenharmony_ci struct nfs_page *old_head, 4508c2ecf20Sopenharmony_ci struct inode *inode) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci while (destroy_list) { 4538c2ecf20Sopenharmony_ci struct nfs_page *subreq = destroy_list; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci destroy_list = (subreq->wb_this_page == old_head) ? 4568c2ecf20Sopenharmony_ci NULL : subreq->wb_this_page; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* Note: lock subreq in order to change subreq->wb_head */ 4598c2ecf20Sopenharmony_ci nfs_page_set_headlock(subreq); 4608c2ecf20Sopenharmony_ci WARN_ON_ONCE(old_head != subreq->wb_head); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* make sure old group is not used */ 4638c2ecf20Sopenharmony_ci subreq->wb_this_page = subreq; 4648c2ecf20Sopenharmony_ci subreq->wb_head = subreq; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci clear_bit(PG_REMOVE, &subreq->wb_flags); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Note: races with nfs_page_group_destroy() */ 4698c2ecf20Sopenharmony_ci if (!kref_read(&subreq->wb_kref)) { 4708c2ecf20Sopenharmony_ci /* Check if we raced with nfs_page_group_destroy() */ 4718c2ecf20Sopenharmony_ci if (test_and_clear_bit(PG_TEARDOWN, &subreq->wb_flags)) { 4728c2ecf20Sopenharmony_ci nfs_page_clear_headlock(subreq); 4738c2ecf20Sopenharmony_ci nfs_free_request(subreq); 4748c2ecf20Sopenharmony_ci } else 4758c2ecf20Sopenharmony_ci nfs_page_clear_headlock(subreq); 4768c2ecf20Sopenharmony_ci continue; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci nfs_page_clear_headlock(subreq); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci nfs_release_request(old_head); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (test_and_clear_bit(PG_INODE_REF, &subreq->wb_flags)) { 4838c2ecf20Sopenharmony_ci nfs_release_request(subreq); 4848c2ecf20Sopenharmony_ci atomic_long_dec(&NFS_I(inode)->nrequests); 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* subreq is now totally disconnected from page group or any 4888c2ecf20Sopenharmony_ci * write / commit lists. last chance to wake any waiters */ 4898c2ecf20Sopenharmony_ci nfs_unlock_and_release_request(subreq); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* 4948c2ecf20Sopenharmony_ci * nfs_join_page_group - destroy subrequests of the head req 4958c2ecf20Sopenharmony_ci * @head: the page used to lookup the "page group" of nfs_page structures 4968c2ecf20Sopenharmony_ci * @inode: Inode to which the request belongs. 4978c2ecf20Sopenharmony_ci * 4988c2ecf20Sopenharmony_ci * This function joins all sub requests to the head request by first 4998c2ecf20Sopenharmony_ci * locking all requests in the group, cancelling any pending operations 5008c2ecf20Sopenharmony_ci * and finally updating the head request to cover the whole range covered by 5018c2ecf20Sopenharmony_ci * the (former) group. All subrequests are removed from any write or commit 5028c2ecf20Sopenharmony_ci * lists, unlinked from the group and destroyed. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_civoid nfs_join_page_group(struct nfs_page *head, struct nfs_commit_info *cinfo, 5058c2ecf20Sopenharmony_ci struct inode *inode) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct nfs_page *subreq; 5088c2ecf20Sopenharmony_ci struct nfs_page *destroy_list = NULL; 5098c2ecf20Sopenharmony_ci unsigned int pgbase, off, bytes; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci pgbase = head->wb_pgbase; 5128c2ecf20Sopenharmony_ci bytes = head->wb_bytes; 5138c2ecf20Sopenharmony_ci off = head->wb_offset; 5148c2ecf20Sopenharmony_ci for (subreq = head->wb_this_page; subreq != head; 5158c2ecf20Sopenharmony_ci subreq = subreq->wb_this_page) { 5168c2ecf20Sopenharmony_ci /* Subrequests should always form a contiguous range */ 5178c2ecf20Sopenharmony_ci if (pgbase > subreq->wb_pgbase) { 5188c2ecf20Sopenharmony_ci off -= pgbase - subreq->wb_pgbase; 5198c2ecf20Sopenharmony_ci bytes += pgbase - subreq->wb_pgbase; 5208c2ecf20Sopenharmony_ci pgbase = subreq->wb_pgbase; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci bytes = max(subreq->wb_pgbase + subreq->wb_bytes 5238c2ecf20Sopenharmony_ci - pgbase, bytes); 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Set the head request's range to cover the former page group */ 5278c2ecf20Sopenharmony_ci head->wb_pgbase = pgbase; 5288c2ecf20Sopenharmony_ci head->wb_bytes = bytes; 5298c2ecf20Sopenharmony_ci head->wb_offset = off; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Now that all requests are locked, make sure they aren't on any list. 5328c2ecf20Sopenharmony_ci * Commit list removal accounting is done after locks are dropped */ 5338c2ecf20Sopenharmony_ci subreq = head; 5348c2ecf20Sopenharmony_ci do { 5358c2ecf20Sopenharmony_ci nfs_clear_request_commit(cinfo, subreq); 5368c2ecf20Sopenharmony_ci subreq = subreq->wb_this_page; 5378c2ecf20Sopenharmony_ci } while (subreq != head); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* unlink subrequests from head, destroy them later */ 5408c2ecf20Sopenharmony_ci if (head->wb_this_page != head) { 5418c2ecf20Sopenharmony_ci /* destroy list will be terminated by head */ 5428c2ecf20Sopenharmony_ci destroy_list = head->wb_this_page; 5438c2ecf20Sopenharmony_ci head->wb_this_page = head; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci nfs_destroy_unlinked_subrequests(destroy_list, head, inode); 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* 5508c2ecf20Sopenharmony_ci * nfs_lock_and_join_requests - join all subreqs to the head req 5518c2ecf20Sopenharmony_ci * @page: the page used to lookup the "page group" of nfs_page structures 5528c2ecf20Sopenharmony_ci * 5538c2ecf20Sopenharmony_ci * This function joins all sub requests to the head request by first 5548c2ecf20Sopenharmony_ci * locking all requests in the group, cancelling any pending operations 5558c2ecf20Sopenharmony_ci * and finally updating the head request to cover the whole range covered by 5568c2ecf20Sopenharmony_ci * the (former) group. All subrequests are removed from any write or commit 5578c2ecf20Sopenharmony_ci * lists, unlinked from the group and destroyed. 5588c2ecf20Sopenharmony_ci * 5598c2ecf20Sopenharmony_ci * Returns a locked, referenced pointer to the head request - which after 5608c2ecf20Sopenharmony_ci * this call is guaranteed to be the only request associated with the page. 5618c2ecf20Sopenharmony_ci * Returns NULL if no requests are found for @page, or a ERR_PTR if an 5628c2ecf20Sopenharmony_ci * error was encountered. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_cistatic struct nfs_page * 5658c2ecf20Sopenharmony_cinfs_lock_and_join_requests(struct page *page) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct inode *inode = page_file_mapping(page)->host; 5688c2ecf20Sopenharmony_ci struct nfs_page *head; 5698c2ecf20Sopenharmony_ci struct nfs_commit_info cinfo; 5708c2ecf20Sopenharmony_ci int ret; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci nfs_init_cinfo_from_inode(&cinfo, inode); 5738c2ecf20Sopenharmony_ci /* 5748c2ecf20Sopenharmony_ci * A reference is taken only on the head request which acts as a 5758c2ecf20Sopenharmony_ci * reference to the whole page group - the group will not be destroyed 5768c2ecf20Sopenharmony_ci * until the head reference is released. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_ci head = nfs_find_and_lock_page_request(page); 5798c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(head)) 5808c2ecf20Sopenharmony_ci return head; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* lock each request in the page group */ 5838c2ecf20Sopenharmony_ci ret = nfs_page_group_lock_subrequests(head); 5848c2ecf20Sopenharmony_ci if (ret < 0) { 5858c2ecf20Sopenharmony_ci nfs_unlock_and_release_request(head); 5868c2ecf20Sopenharmony_ci return ERR_PTR(ret); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci nfs_join_page_group(head, &cinfo, inode); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return head; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic void nfs_write_error(struct nfs_page *req, int error) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci trace_nfs_write_error(req, error); 5978c2ecf20Sopenharmony_ci nfs_mapping_set_error(req->wb_page, error); 5988c2ecf20Sopenharmony_ci nfs_inode_remove_request(req); 5998c2ecf20Sopenharmony_ci nfs_end_page_writeback(req); 6008c2ecf20Sopenharmony_ci nfs_release_request(req); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/* 6048c2ecf20Sopenharmony_ci * Find an associated nfs write request, and prepare to flush it out 6058c2ecf20Sopenharmony_ci * May return an error if the user signalled nfs_wait_on_request(). 6068c2ecf20Sopenharmony_ci */ 6078c2ecf20Sopenharmony_cistatic int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, 6088c2ecf20Sopenharmony_ci struct page *page) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct nfs_page *req; 6118c2ecf20Sopenharmony_ci int ret = 0; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci req = nfs_lock_and_join_requests(page); 6148c2ecf20Sopenharmony_ci if (!req) 6158c2ecf20Sopenharmony_ci goto out; 6168c2ecf20Sopenharmony_ci ret = PTR_ERR(req); 6178c2ecf20Sopenharmony_ci if (IS_ERR(req)) 6188c2ecf20Sopenharmony_ci goto out; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci nfs_set_page_writeback(page); 6218c2ecf20Sopenharmony_ci WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags)); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci /* If there is a fatal error that covers this write, just exit */ 6248c2ecf20Sopenharmony_ci ret = pgio->pg_error; 6258c2ecf20Sopenharmony_ci if (nfs_error_is_fatal_on_server(ret)) 6268c2ecf20Sopenharmony_ci goto out_launder; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci ret = 0; 6298c2ecf20Sopenharmony_ci if (!nfs_pageio_add_request(pgio, req)) { 6308c2ecf20Sopenharmony_ci ret = pgio->pg_error; 6318c2ecf20Sopenharmony_ci /* 6328c2ecf20Sopenharmony_ci * Remove the problematic req upon fatal errors on the server 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_ci if (nfs_error_is_fatal(ret)) { 6358c2ecf20Sopenharmony_ci if (nfs_error_is_fatal_on_server(ret)) 6368c2ecf20Sopenharmony_ci goto out_launder; 6378c2ecf20Sopenharmony_ci } else 6388c2ecf20Sopenharmony_ci ret = -EAGAIN; 6398c2ecf20Sopenharmony_ci nfs_redirty_request(req); 6408c2ecf20Sopenharmony_ci pgio->pg_error = 0; 6418c2ecf20Sopenharmony_ci } else 6428c2ecf20Sopenharmony_ci nfs_add_stats(page_file_mapping(page)->host, 6438c2ecf20Sopenharmony_ci NFSIOS_WRITEPAGES, 1); 6448c2ecf20Sopenharmony_ciout: 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ciout_launder: 6478c2ecf20Sopenharmony_ci nfs_write_error(req, ret); 6488c2ecf20Sopenharmony_ci return 0; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic int nfs_do_writepage(struct page *page, struct writeback_control *wbc, 6528c2ecf20Sopenharmony_ci struct nfs_pageio_descriptor *pgio) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci int ret; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci nfs_pageio_cond_complete(pgio, page_index(page)); 6578c2ecf20Sopenharmony_ci ret = nfs_page_async_flush(pgio, page); 6588c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 6598c2ecf20Sopenharmony_ci redirty_page_for_writepage(wbc, page); 6608c2ecf20Sopenharmony_ci ret = AOP_WRITEPAGE_ACTIVATE; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci return ret; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci/* 6668c2ecf20Sopenharmony_ci * Write an mmapped page to the server. 6678c2ecf20Sopenharmony_ci */ 6688c2ecf20Sopenharmony_cistatic int nfs_writepage_locked(struct page *page, 6698c2ecf20Sopenharmony_ci struct writeback_control *wbc) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct nfs_pageio_descriptor pgio; 6728c2ecf20Sopenharmony_ci struct inode *inode = page_file_mapping(page)->host; 6738c2ecf20Sopenharmony_ci int err; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE); 6768c2ecf20Sopenharmony_ci nfs_pageio_init_write(&pgio, inode, 0, 6778c2ecf20Sopenharmony_ci false, &nfs_async_write_completion_ops); 6788c2ecf20Sopenharmony_ci err = nfs_do_writepage(page, wbc, &pgio); 6798c2ecf20Sopenharmony_ci pgio.pg_error = 0; 6808c2ecf20Sopenharmony_ci nfs_pageio_complete(&pgio); 6818c2ecf20Sopenharmony_ci return err; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ciint nfs_writepage(struct page *page, struct writeback_control *wbc) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci int ret; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci ret = nfs_writepage_locked(page, wbc); 6898c2ecf20Sopenharmony_ci if (ret != AOP_WRITEPAGE_ACTIVATE) 6908c2ecf20Sopenharmony_ci unlock_page(page); 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic int nfs_writepages_callback(struct page *page, struct writeback_control *wbc, void *data) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci int ret; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci ret = nfs_do_writepage(page, wbc, data); 6998c2ecf20Sopenharmony_ci if (ret != AOP_WRITEPAGE_ACTIVATE) 7008c2ecf20Sopenharmony_ci unlock_page(page); 7018c2ecf20Sopenharmony_ci return ret; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic void nfs_io_completion_commit(void *inode) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci nfs_commit_inode(inode, 0); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ciint nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci struct inode *inode = mapping->host; 7128c2ecf20Sopenharmony_ci struct nfs_pageio_descriptor pgio; 7138c2ecf20Sopenharmony_ci struct nfs_io_completion *ioc; 7148c2ecf20Sopenharmony_ci int err; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci ioc = nfs_io_completion_alloc(GFP_KERNEL); 7198c2ecf20Sopenharmony_ci if (ioc) 7208c2ecf20Sopenharmony_ci nfs_io_completion_init(ioc, nfs_io_completion_commit, inode); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), false, 7238c2ecf20Sopenharmony_ci &nfs_async_write_completion_ops); 7248c2ecf20Sopenharmony_ci pgio.pg_io_completion = ioc; 7258c2ecf20Sopenharmony_ci err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio); 7268c2ecf20Sopenharmony_ci pgio.pg_error = 0; 7278c2ecf20Sopenharmony_ci nfs_pageio_complete(&pgio); 7288c2ecf20Sopenharmony_ci nfs_io_completion_put(ioc); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (err < 0) 7318c2ecf20Sopenharmony_ci goto out_err; 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ciout_err: 7348c2ecf20Sopenharmony_ci return err; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci/* 7388c2ecf20Sopenharmony_ci * Insert a write request into an inode 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_cistatic void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci struct address_space *mapping = page_file_mapping(req->wb_page); 7438c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci WARN_ON_ONCE(req->wb_this_page != req); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* Lock the request! */ 7488c2ecf20Sopenharmony_ci nfs_lock_request(req); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* 7518c2ecf20Sopenharmony_ci * Swap-space should not get truncated. Hence no need to plug the race 7528c2ecf20Sopenharmony_ci * with invalidate/truncate. 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_ci spin_lock(&mapping->private_lock); 7558c2ecf20Sopenharmony_ci if (!nfs_have_writebacks(inode) && 7568c2ecf20Sopenharmony_ci NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE)) 7578c2ecf20Sopenharmony_ci inode_inc_iversion_raw(inode); 7588c2ecf20Sopenharmony_ci if (likely(!PageSwapCache(req->wb_page))) { 7598c2ecf20Sopenharmony_ci set_bit(PG_MAPPED, &req->wb_flags); 7608c2ecf20Sopenharmony_ci SetPagePrivate(req->wb_page); 7618c2ecf20Sopenharmony_ci set_page_private(req->wb_page, (unsigned long)req); 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci spin_unlock(&mapping->private_lock); 7648c2ecf20Sopenharmony_ci atomic_long_inc(&nfsi->nrequests); 7658c2ecf20Sopenharmony_ci /* this a head request for a page group - mark it as having an 7668c2ecf20Sopenharmony_ci * extra reference so sub groups can follow suit. 7678c2ecf20Sopenharmony_ci * This flag also informs pgio layer when to bump nrequests when 7688c2ecf20Sopenharmony_ci * adding subrequests. */ 7698c2ecf20Sopenharmony_ci WARN_ON(test_and_set_bit(PG_INODE_REF, &req->wb_flags)); 7708c2ecf20Sopenharmony_ci kref_get(&req->wb_kref); 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci/* 7748c2ecf20Sopenharmony_ci * Remove a write request from an inode 7758c2ecf20Sopenharmony_ci */ 7768c2ecf20Sopenharmony_cistatic void nfs_inode_remove_request(struct nfs_page *req) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct address_space *mapping = page_file_mapping(req->wb_page); 7798c2ecf20Sopenharmony_ci struct inode *inode = mapping->host; 7808c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 7818c2ecf20Sopenharmony_ci struct nfs_page *head; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (nfs_page_group_sync_on_bit(req, PG_REMOVE)) { 7848c2ecf20Sopenharmony_ci head = req->wb_head; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci spin_lock(&mapping->private_lock); 7878c2ecf20Sopenharmony_ci if (likely(head->wb_page && !PageSwapCache(head->wb_page))) { 7888c2ecf20Sopenharmony_ci set_page_private(head->wb_page, 0); 7898c2ecf20Sopenharmony_ci ClearPagePrivate(head->wb_page); 7908c2ecf20Sopenharmony_ci clear_bit(PG_MAPPED, &head->wb_flags); 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci spin_unlock(&mapping->private_lock); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) { 7968c2ecf20Sopenharmony_ci nfs_release_request(req); 7978c2ecf20Sopenharmony_ci atomic_long_dec(&nfsi->nrequests); 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic void 8028c2ecf20Sopenharmony_cinfs_mark_request_dirty(struct nfs_page *req) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci if (req->wb_page) 8058c2ecf20Sopenharmony_ci __set_page_dirty_nobuffers(req->wb_page); 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci/* 8098c2ecf20Sopenharmony_ci * nfs_page_search_commits_for_head_request_locked 8108c2ecf20Sopenharmony_ci * 8118c2ecf20Sopenharmony_ci * Search through commit lists on @inode for the head request for @page. 8128c2ecf20Sopenharmony_ci * Must be called while holding the inode (which is cinfo) lock. 8138c2ecf20Sopenharmony_ci * 8148c2ecf20Sopenharmony_ci * Returns the head request if found, or NULL if not found. 8158c2ecf20Sopenharmony_ci */ 8168c2ecf20Sopenharmony_cistatic struct nfs_page * 8178c2ecf20Sopenharmony_cinfs_page_search_commits_for_head_request_locked(struct nfs_inode *nfsi, 8188c2ecf20Sopenharmony_ci struct page *page) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct nfs_page *freq, *t; 8218c2ecf20Sopenharmony_ci struct nfs_commit_info cinfo; 8228c2ecf20Sopenharmony_ci struct inode *inode = &nfsi->vfs_inode; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci nfs_init_cinfo_from_inode(&cinfo, inode); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* search through pnfs commit lists */ 8278c2ecf20Sopenharmony_ci freq = pnfs_search_commit_reqs(inode, &cinfo, page); 8288c2ecf20Sopenharmony_ci if (freq) 8298c2ecf20Sopenharmony_ci return freq->wb_head; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* Linearly search the commit list for the correct request */ 8328c2ecf20Sopenharmony_ci list_for_each_entry_safe(freq, t, &cinfo.mds->list, wb_list) { 8338c2ecf20Sopenharmony_ci if (freq->wb_page == page) 8348c2ecf20Sopenharmony_ci return freq->wb_head; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci return NULL; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci/** 8418c2ecf20Sopenharmony_ci * nfs_request_add_commit_list_locked - add request to a commit list 8428c2ecf20Sopenharmony_ci * @req: pointer to a struct nfs_page 8438c2ecf20Sopenharmony_ci * @dst: commit list head 8448c2ecf20Sopenharmony_ci * @cinfo: holds list lock and accounting info 8458c2ecf20Sopenharmony_ci * 8468c2ecf20Sopenharmony_ci * This sets the PG_CLEAN bit, updates the cinfo count of 8478c2ecf20Sopenharmony_ci * number of outstanding requests requiring a commit as well as 8488c2ecf20Sopenharmony_ci * the MM page stats. 8498c2ecf20Sopenharmony_ci * 8508c2ecf20Sopenharmony_ci * The caller must hold NFS_I(cinfo->inode)->commit_mutex, and the 8518c2ecf20Sopenharmony_ci * nfs_page lock. 8528c2ecf20Sopenharmony_ci */ 8538c2ecf20Sopenharmony_civoid 8548c2ecf20Sopenharmony_cinfs_request_add_commit_list_locked(struct nfs_page *req, struct list_head *dst, 8558c2ecf20Sopenharmony_ci struct nfs_commit_info *cinfo) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci set_bit(PG_CLEAN, &req->wb_flags); 8588c2ecf20Sopenharmony_ci nfs_list_add_request(req, dst); 8598c2ecf20Sopenharmony_ci atomic_long_inc(&cinfo->mds->ncommit); 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_request_add_commit_list_locked); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci/** 8648c2ecf20Sopenharmony_ci * nfs_request_add_commit_list - add request to a commit list 8658c2ecf20Sopenharmony_ci * @req: pointer to a struct nfs_page 8668c2ecf20Sopenharmony_ci * @cinfo: holds list lock and accounting info 8678c2ecf20Sopenharmony_ci * 8688c2ecf20Sopenharmony_ci * This sets the PG_CLEAN bit, updates the cinfo count of 8698c2ecf20Sopenharmony_ci * number of outstanding requests requiring a commit as well as 8708c2ecf20Sopenharmony_ci * the MM page stats. 8718c2ecf20Sopenharmony_ci * 8728c2ecf20Sopenharmony_ci * The caller must _not_ hold the cinfo->lock, but must be 8738c2ecf20Sopenharmony_ci * holding the nfs_page lock. 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_civoid 8768c2ecf20Sopenharmony_cinfs_request_add_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); 8798c2ecf20Sopenharmony_ci nfs_request_add_commit_list_locked(req, &cinfo->mds->list, cinfo); 8808c2ecf20Sopenharmony_ci mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); 8818c2ecf20Sopenharmony_ci if (req->wb_page) 8828c2ecf20Sopenharmony_ci nfs_mark_page_unstable(req->wb_page, cinfo); 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_request_add_commit_list); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci/** 8878c2ecf20Sopenharmony_ci * nfs_request_remove_commit_list - Remove request from a commit list 8888c2ecf20Sopenharmony_ci * @req: pointer to a nfs_page 8898c2ecf20Sopenharmony_ci * @cinfo: holds list lock and accounting info 8908c2ecf20Sopenharmony_ci * 8918c2ecf20Sopenharmony_ci * This clears the PG_CLEAN bit, and updates the cinfo's count of 8928c2ecf20Sopenharmony_ci * number of outstanding requests requiring a commit 8938c2ecf20Sopenharmony_ci * It does not update the MM page stats. 8948c2ecf20Sopenharmony_ci * 8958c2ecf20Sopenharmony_ci * The caller _must_ hold the cinfo->lock and the nfs_page lock. 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_civoid 8988c2ecf20Sopenharmony_cinfs_request_remove_commit_list(struct nfs_page *req, 8998c2ecf20Sopenharmony_ci struct nfs_commit_info *cinfo) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) 9028c2ecf20Sopenharmony_ci return; 9038c2ecf20Sopenharmony_ci nfs_list_remove_request(req); 9048c2ecf20Sopenharmony_ci atomic_long_dec(&cinfo->mds->ncommit); 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_request_remove_commit_list); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo, 9098c2ecf20Sopenharmony_ci struct inode *inode) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci cinfo->inode = inode; 9128c2ecf20Sopenharmony_ci cinfo->mds = &NFS_I(inode)->commit_info; 9138c2ecf20Sopenharmony_ci cinfo->ds = pnfs_get_ds_info(inode); 9148c2ecf20Sopenharmony_ci cinfo->dreq = NULL; 9158c2ecf20Sopenharmony_ci cinfo->completion_ops = &nfs_commit_completion_ops; 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_civoid nfs_init_cinfo(struct nfs_commit_info *cinfo, 9198c2ecf20Sopenharmony_ci struct inode *inode, 9208c2ecf20Sopenharmony_ci struct nfs_direct_req *dreq) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci if (dreq) 9238c2ecf20Sopenharmony_ci nfs_init_cinfo_from_dreq(cinfo, dreq); 9248c2ecf20Sopenharmony_ci else 9258c2ecf20Sopenharmony_ci nfs_init_cinfo_from_inode(cinfo, inode); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_init_cinfo); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci/* 9308c2ecf20Sopenharmony_ci * Add a request to the inode's commit list. 9318c2ecf20Sopenharmony_ci */ 9328c2ecf20Sopenharmony_civoid 9338c2ecf20Sopenharmony_cinfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg, 9348c2ecf20Sopenharmony_ci struct nfs_commit_info *cinfo, u32 ds_commit_idx) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci if (pnfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx)) 9378c2ecf20Sopenharmony_ci return; 9388c2ecf20Sopenharmony_ci nfs_request_add_commit_list(req, cinfo); 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic void 9428c2ecf20Sopenharmony_cinfs_clear_page_commit(struct page *page) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci dec_node_page_state(page, NR_WRITEBACK); 9458c2ecf20Sopenharmony_ci dec_wb_stat(&inode_to_bdi(page_file_mapping(page)->host)->wb, 9468c2ecf20Sopenharmony_ci WB_WRITEBACK); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci/* Called holding the request lock on @req */ 9508c2ecf20Sopenharmony_cistatic void nfs_clear_request_commit(struct nfs_commit_info *cinfo, 9518c2ecf20Sopenharmony_ci struct nfs_page *req) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci if (test_bit(PG_CLEAN, &req->wb_flags)) { 9548c2ecf20Sopenharmony_ci struct nfs_open_context *ctx = nfs_req_openctx(req); 9558c2ecf20Sopenharmony_ci struct inode *inode = d_inode(ctx->dentry); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci mutex_lock(&NFS_I(inode)->commit_mutex); 9588c2ecf20Sopenharmony_ci if (!pnfs_clear_request_commit(req, cinfo)) { 9598c2ecf20Sopenharmony_ci nfs_request_remove_commit_list(req, cinfo); 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci mutex_unlock(&NFS_I(inode)->commit_mutex); 9628c2ecf20Sopenharmony_ci nfs_clear_page_commit(req->wb_page); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ciint nfs_write_need_commit(struct nfs_pgio_header *hdr) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci if (hdr->verf.committed == NFS_DATA_SYNC) 9698c2ecf20Sopenharmony_ci return hdr->lseg == NULL; 9708c2ecf20Sopenharmony_ci return hdr->verf.committed != NFS_FILE_SYNC; 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic void nfs_async_write_init(struct nfs_pgio_header *hdr) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci nfs_io_completion_get(hdr->io_completion); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic void nfs_write_completion(struct nfs_pgio_header *hdr) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct nfs_commit_info cinfo; 9818c2ecf20Sopenharmony_ci unsigned long bytes = 0; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) 9848c2ecf20Sopenharmony_ci goto out; 9858c2ecf20Sopenharmony_ci nfs_init_cinfo_from_inode(&cinfo, hdr->inode); 9868c2ecf20Sopenharmony_ci while (!list_empty(&hdr->pages)) { 9878c2ecf20Sopenharmony_ci struct nfs_page *req = nfs_list_entry(hdr->pages.next); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci bytes += req->wb_bytes; 9908c2ecf20Sopenharmony_ci nfs_list_remove_request(req); 9918c2ecf20Sopenharmony_ci if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) && 9928c2ecf20Sopenharmony_ci (hdr->good_bytes < bytes)) { 9938c2ecf20Sopenharmony_ci trace_nfs_comp_error(req, hdr->error); 9948c2ecf20Sopenharmony_ci nfs_mapping_set_error(req->wb_page, hdr->error); 9958c2ecf20Sopenharmony_ci goto remove_req; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci if (nfs_write_need_commit(hdr)) { 9988c2ecf20Sopenharmony_ci /* Reset wb_nio, since the write was successful. */ 9998c2ecf20Sopenharmony_ci req->wb_nio = 0; 10008c2ecf20Sopenharmony_ci memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf)); 10018c2ecf20Sopenharmony_ci nfs_mark_request_commit(req, hdr->lseg, &cinfo, 10028c2ecf20Sopenharmony_ci hdr->pgio_mirror_idx); 10038c2ecf20Sopenharmony_ci goto next; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ciremove_req: 10068c2ecf20Sopenharmony_ci nfs_inode_remove_request(req); 10078c2ecf20Sopenharmony_cinext: 10088c2ecf20Sopenharmony_ci nfs_end_page_writeback(req); 10098c2ecf20Sopenharmony_ci nfs_release_request(req); 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ciout: 10128c2ecf20Sopenharmony_ci nfs_io_completion_put(hdr->io_completion); 10138c2ecf20Sopenharmony_ci hdr->release(hdr); 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ciunsigned long 10178c2ecf20Sopenharmony_cinfs_reqs_to_commit(struct nfs_commit_info *cinfo) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci return atomic_long_read(&cinfo->mds->ncommit); 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci/* NFS_I(cinfo->inode)->commit_mutex held by caller */ 10238c2ecf20Sopenharmony_ciint 10248c2ecf20Sopenharmony_cinfs_scan_commit_list(struct list_head *src, struct list_head *dst, 10258c2ecf20Sopenharmony_ci struct nfs_commit_info *cinfo, int max) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct nfs_page *req, *tmp; 10288c2ecf20Sopenharmony_ci int ret = 0; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci list_for_each_entry_safe(req, tmp, src, wb_list) { 10318c2ecf20Sopenharmony_ci kref_get(&req->wb_kref); 10328c2ecf20Sopenharmony_ci if (!nfs_lock_request(req)) { 10338c2ecf20Sopenharmony_ci nfs_release_request(req); 10348c2ecf20Sopenharmony_ci continue; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci nfs_request_remove_commit_list(req, cinfo); 10378c2ecf20Sopenharmony_ci clear_bit(PG_COMMIT_TO_DS, &req->wb_flags); 10388c2ecf20Sopenharmony_ci nfs_list_add_request(req, dst); 10398c2ecf20Sopenharmony_ci ret++; 10408c2ecf20Sopenharmony_ci if ((ret == max) && !cinfo->dreq) 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci cond_resched(); 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci return ret; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_scan_commit_list); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/* 10498c2ecf20Sopenharmony_ci * nfs_scan_commit - Scan an inode for commit requests 10508c2ecf20Sopenharmony_ci * @inode: NFS inode to scan 10518c2ecf20Sopenharmony_ci * @dst: mds destination list 10528c2ecf20Sopenharmony_ci * @cinfo: mds and ds lists of reqs ready to commit 10538c2ecf20Sopenharmony_ci * 10548c2ecf20Sopenharmony_ci * Moves requests from the inode's 'commit' request list. 10558c2ecf20Sopenharmony_ci * The requests are *not* checked to ensure that they form a contiguous set. 10568c2ecf20Sopenharmony_ci */ 10578c2ecf20Sopenharmony_ciint 10588c2ecf20Sopenharmony_cinfs_scan_commit(struct inode *inode, struct list_head *dst, 10598c2ecf20Sopenharmony_ci struct nfs_commit_info *cinfo) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci int ret = 0; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (!atomic_long_read(&cinfo->mds->ncommit)) 10648c2ecf20Sopenharmony_ci return 0; 10658c2ecf20Sopenharmony_ci mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); 10668c2ecf20Sopenharmony_ci if (atomic_long_read(&cinfo->mds->ncommit) > 0) { 10678c2ecf20Sopenharmony_ci const int max = INT_MAX; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ret = nfs_scan_commit_list(&cinfo->mds->list, dst, 10708c2ecf20Sopenharmony_ci cinfo, max); 10718c2ecf20Sopenharmony_ci ret += pnfs_scan_commit_lists(inode, cinfo, max - ret); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); 10748c2ecf20Sopenharmony_ci return ret; 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci/* 10788c2ecf20Sopenharmony_ci * Search for an existing write request, and attempt to update 10798c2ecf20Sopenharmony_ci * it to reflect a new dirty region on a given page. 10808c2ecf20Sopenharmony_ci * 10818c2ecf20Sopenharmony_ci * If the attempt fails, then the existing request is flushed out 10828c2ecf20Sopenharmony_ci * to disk. 10838c2ecf20Sopenharmony_ci */ 10848c2ecf20Sopenharmony_cistatic struct nfs_page *nfs_try_to_update_request(struct inode *inode, 10858c2ecf20Sopenharmony_ci struct page *page, 10868c2ecf20Sopenharmony_ci unsigned int offset, 10878c2ecf20Sopenharmony_ci unsigned int bytes) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci struct nfs_page *req; 10908c2ecf20Sopenharmony_ci unsigned int rqend; 10918c2ecf20Sopenharmony_ci unsigned int end; 10928c2ecf20Sopenharmony_ci int error; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci end = offset + bytes; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci req = nfs_lock_and_join_requests(page); 10978c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(req)) 10988c2ecf20Sopenharmony_ci return req; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci rqend = req->wb_offset + req->wb_bytes; 11018c2ecf20Sopenharmony_ci /* 11028c2ecf20Sopenharmony_ci * Tell the caller to flush out the request if 11038c2ecf20Sopenharmony_ci * the offsets are non-contiguous. 11048c2ecf20Sopenharmony_ci * Note: nfs_flush_incompatible() will already 11058c2ecf20Sopenharmony_ci * have flushed out requests having wrong owners. 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_ci if (offset > rqend || end < req->wb_offset) 11088c2ecf20Sopenharmony_ci goto out_flushme; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci /* Okay, the request matches. Update the region */ 11118c2ecf20Sopenharmony_ci if (offset < req->wb_offset) { 11128c2ecf20Sopenharmony_ci req->wb_offset = offset; 11138c2ecf20Sopenharmony_ci req->wb_pgbase = offset; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci if (end > rqend) 11168c2ecf20Sopenharmony_ci req->wb_bytes = end - req->wb_offset; 11178c2ecf20Sopenharmony_ci else 11188c2ecf20Sopenharmony_ci req->wb_bytes = rqend - req->wb_offset; 11198c2ecf20Sopenharmony_ci req->wb_nio = 0; 11208c2ecf20Sopenharmony_ci return req; 11218c2ecf20Sopenharmony_ciout_flushme: 11228c2ecf20Sopenharmony_ci /* 11238c2ecf20Sopenharmony_ci * Note: we mark the request dirty here because 11248c2ecf20Sopenharmony_ci * nfs_lock_and_join_requests() cannot preserve 11258c2ecf20Sopenharmony_ci * commit flags, so we have to replay the write. 11268c2ecf20Sopenharmony_ci */ 11278c2ecf20Sopenharmony_ci nfs_mark_request_dirty(req); 11288c2ecf20Sopenharmony_ci nfs_unlock_and_release_request(req); 11298c2ecf20Sopenharmony_ci error = nfs_wb_page(inode, page); 11308c2ecf20Sopenharmony_ci return (error < 0) ? ERR_PTR(error) : NULL; 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci/* 11348c2ecf20Sopenharmony_ci * Try to update an existing write request, or create one if there is none. 11358c2ecf20Sopenharmony_ci * 11368c2ecf20Sopenharmony_ci * Note: Should always be called with the Page Lock held to prevent races 11378c2ecf20Sopenharmony_ci * if we have to add a new request. Also assumes that the caller has 11388c2ecf20Sopenharmony_ci * already called nfs_flush_incompatible() if necessary. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_cistatic struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx, 11418c2ecf20Sopenharmony_ci struct page *page, unsigned int offset, unsigned int bytes) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci struct inode *inode = page_file_mapping(page)->host; 11448c2ecf20Sopenharmony_ci struct nfs_page *req; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci req = nfs_try_to_update_request(inode, page, offset, bytes); 11478c2ecf20Sopenharmony_ci if (req != NULL) 11488c2ecf20Sopenharmony_ci goto out; 11498c2ecf20Sopenharmony_ci req = nfs_create_request(ctx, page, offset, bytes); 11508c2ecf20Sopenharmony_ci if (IS_ERR(req)) 11518c2ecf20Sopenharmony_ci goto out; 11528c2ecf20Sopenharmony_ci nfs_inode_add_request(inode, req); 11538c2ecf20Sopenharmony_ciout: 11548c2ecf20Sopenharmony_ci return req; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page, 11588c2ecf20Sopenharmony_ci unsigned int offset, unsigned int count) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci struct nfs_page *req; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci req = nfs_setup_write_request(ctx, page, offset, count); 11638c2ecf20Sopenharmony_ci if (IS_ERR(req)) 11648c2ecf20Sopenharmony_ci return PTR_ERR(req); 11658c2ecf20Sopenharmony_ci /* Update file length */ 11668c2ecf20Sopenharmony_ci nfs_grow_file(page, offset, count); 11678c2ecf20Sopenharmony_ci nfs_mark_uptodate(req); 11688c2ecf20Sopenharmony_ci nfs_mark_request_dirty(req); 11698c2ecf20Sopenharmony_ci nfs_unlock_and_release_request(req); 11708c2ecf20Sopenharmony_ci return 0; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ciint nfs_flush_incompatible(struct file *file, struct page *page) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci struct nfs_open_context *ctx = nfs_file_open_context(file); 11768c2ecf20Sopenharmony_ci struct nfs_lock_context *l_ctx; 11778c2ecf20Sopenharmony_ci struct file_lock_context *flctx = file_inode(file)->i_flctx; 11788c2ecf20Sopenharmony_ci struct nfs_page *req; 11798c2ecf20Sopenharmony_ci int do_flush, status; 11808c2ecf20Sopenharmony_ci /* 11818c2ecf20Sopenharmony_ci * Look for a request corresponding to this page. If there 11828c2ecf20Sopenharmony_ci * is one, and it belongs to another file, we flush it out 11838c2ecf20Sopenharmony_ci * before we try to copy anything into the page. Do this 11848c2ecf20Sopenharmony_ci * due to the lack of an ACCESS-type call in NFSv2. 11858c2ecf20Sopenharmony_ci * Also do the same if we find a request from an existing 11868c2ecf20Sopenharmony_ci * dropped page. 11878c2ecf20Sopenharmony_ci */ 11888c2ecf20Sopenharmony_ci do { 11898c2ecf20Sopenharmony_ci req = nfs_page_find_head_request(page); 11908c2ecf20Sopenharmony_ci if (req == NULL) 11918c2ecf20Sopenharmony_ci return 0; 11928c2ecf20Sopenharmony_ci l_ctx = req->wb_lock_context; 11938c2ecf20Sopenharmony_ci do_flush = req->wb_page != page || 11948c2ecf20Sopenharmony_ci !nfs_match_open_context(nfs_req_openctx(req), ctx); 11958c2ecf20Sopenharmony_ci if (l_ctx && flctx && 11968c2ecf20Sopenharmony_ci !(list_empty_careful(&flctx->flc_posix) && 11978c2ecf20Sopenharmony_ci list_empty_careful(&flctx->flc_flock))) { 11988c2ecf20Sopenharmony_ci do_flush |= l_ctx->lockowner != current->files; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci nfs_release_request(req); 12018c2ecf20Sopenharmony_ci if (!do_flush) 12028c2ecf20Sopenharmony_ci return 0; 12038c2ecf20Sopenharmony_ci status = nfs_wb_page(page_file_mapping(page)->host, page); 12048c2ecf20Sopenharmony_ci } while (status == 0); 12058c2ecf20Sopenharmony_ci return status; 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci/* 12098c2ecf20Sopenharmony_ci * Avoid buffered writes when a open context credential's key would 12108c2ecf20Sopenharmony_ci * expire soon. 12118c2ecf20Sopenharmony_ci * 12128c2ecf20Sopenharmony_ci * Returns -EACCES if the key will expire within RPC_KEY_EXPIRE_FAIL. 12138c2ecf20Sopenharmony_ci * 12148c2ecf20Sopenharmony_ci * Return 0 and set a credential flag which triggers the inode to flush 12158c2ecf20Sopenharmony_ci * and performs NFS_FILE_SYNC writes if the key will expired within 12168c2ecf20Sopenharmony_ci * RPC_KEY_EXPIRE_TIMEO. 12178c2ecf20Sopenharmony_ci */ 12188c2ecf20Sopenharmony_ciint 12198c2ecf20Sopenharmony_cinfs_key_timeout_notify(struct file *filp, struct inode *inode) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci struct nfs_open_context *ctx = nfs_file_open_context(filp); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (nfs_ctx_key_to_expire(ctx, inode) && 12248c2ecf20Sopenharmony_ci !ctx->ll_cred) 12258c2ecf20Sopenharmony_ci /* Already expired! */ 12268c2ecf20Sopenharmony_ci return -EACCES; 12278c2ecf20Sopenharmony_ci return 0; 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci/* 12318c2ecf20Sopenharmony_ci * Test if the open context credential key is marked to expire soon. 12328c2ecf20Sopenharmony_ci */ 12338c2ecf20Sopenharmony_cibool nfs_ctx_key_to_expire(struct nfs_open_context *ctx, struct inode *inode) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth; 12368c2ecf20Sopenharmony_ci struct rpc_cred *cred = ctx->ll_cred; 12378c2ecf20Sopenharmony_ci struct auth_cred acred = { 12388c2ecf20Sopenharmony_ci .cred = ctx->cred, 12398c2ecf20Sopenharmony_ci }; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (cred && !cred->cr_ops->crmatch(&acred, cred, 0)) { 12428c2ecf20Sopenharmony_ci put_rpccred(cred); 12438c2ecf20Sopenharmony_ci ctx->ll_cred = NULL; 12448c2ecf20Sopenharmony_ci cred = NULL; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci if (!cred) 12478c2ecf20Sopenharmony_ci cred = auth->au_ops->lookup_cred(auth, &acred, 0); 12488c2ecf20Sopenharmony_ci if (!cred || IS_ERR(cred)) 12498c2ecf20Sopenharmony_ci return true; 12508c2ecf20Sopenharmony_ci ctx->ll_cred = cred; 12518c2ecf20Sopenharmony_ci return !!(cred->cr_ops->crkey_timeout && 12528c2ecf20Sopenharmony_ci cred->cr_ops->crkey_timeout(cred)); 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci/* 12568c2ecf20Sopenharmony_ci * If the page cache is marked as unsafe or invalid, then we can't rely on 12578c2ecf20Sopenharmony_ci * the PageUptodate() flag. In this case, we will need to turn off 12588c2ecf20Sopenharmony_ci * write optimisations that depend on the page contents being correct. 12598c2ecf20Sopenharmony_ci */ 12608c2ecf20Sopenharmony_cistatic bool nfs_write_pageuptodate(struct page *page, struct inode *inode) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (nfs_have_delegated_attributes(inode)) 12658c2ecf20Sopenharmony_ci goto out; 12668c2ecf20Sopenharmony_ci if (nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) 12678c2ecf20Sopenharmony_ci return false; 12688c2ecf20Sopenharmony_ci smp_rmb(); 12698c2ecf20Sopenharmony_ci if (test_bit(NFS_INO_INVALIDATING, &nfsi->flags)) 12708c2ecf20Sopenharmony_ci return false; 12718c2ecf20Sopenharmony_ciout: 12728c2ecf20Sopenharmony_ci if (nfsi->cache_validity & NFS_INO_INVALID_DATA) 12738c2ecf20Sopenharmony_ci return false; 12748c2ecf20Sopenharmony_ci return PageUptodate(page) != 0; 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic bool 12788c2ecf20Sopenharmony_ciis_whole_file_wrlock(struct file_lock *fl) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci return fl->fl_start == 0 && fl->fl_end == OFFSET_MAX && 12818c2ecf20Sopenharmony_ci fl->fl_type == F_WRLCK; 12828c2ecf20Sopenharmony_ci} 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci/* If we know the page is up to date, and we're not using byte range locks (or 12858c2ecf20Sopenharmony_ci * if we have the whole file locked for writing), it may be more efficient to 12868c2ecf20Sopenharmony_ci * extend the write to cover the entire page in order to avoid fragmentation 12878c2ecf20Sopenharmony_ci * inefficiencies. 12888c2ecf20Sopenharmony_ci * 12898c2ecf20Sopenharmony_ci * If the file is opened for synchronous writes then we can just skip the rest 12908c2ecf20Sopenharmony_ci * of the checks. 12918c2ecf20Sopenharmony_ci */ 12928c2ecf20Sopenharmony_cistatic int nfs_can_extend_write(struct file *file, struct page *page, struct inode *inode) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci int ret; 12958c2ecf20Sopenharmony_ci struct file_lock_context *flctx = inode->i_flctx; 12968c2ecf20Sopenharmony_ci struct file_lock *fl; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (file->f_flags & O_DSYNC) 12998c2ecf20Sopenharmony_ci return 0; 13008c2ecf20Sopenharmony_ci if (!nfs_write_pageuptodate(page, inode)) 13018c2ecf20Sopenharmony_ci return 0; 13028c2ecf20Sopenharmony_ci if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE)) 13038c2ecf20Sopenharmony_ci return 1; 13048c2ecf20Sopenharmony_ci if (!flctx || (list_empty_careful(&flctx->flc_flock) && 13058c2ecf20Sopenharmony_ci list_empty_careful(&flctx->flc_posix))) 13068c2ecf20Sopenharmony_ci return 1; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* Check to see if there are whole file write locks */ 13098c2ecf20Sopenharmony_ci ret = 0; 13108c2ecf20Sopenharmony_ci spin_lock(&flctx->flc_lock); 13118c2ecf20Sopenharmony_ci if (!list_empty(&flctx->flc_posix)) { 13128c2ecf20Sopenharmony_ci fl = list_first_entry(&flctx->flc_posix, struct file_lock, 13138c2ecf20Sopenharmony_ci fl_list); 13148c2ecf20Sopenharmony_ci if (is_whole_file_wrlock(fl)) 13158c2ecf20Sopenharmony_ci ret = 1; 13168c2ecf20Sopenharmony_ci } else if (!list_empty(&flctx->flc_flock)) { 13178c2ecf20Sopenharmony_ci fl = list_first_entry(&flctx->flc_flock, struct file_lock, 13188c2ecf20Sopenharmony_ci fl_list); 13198c2ecf20Sopenharmony_ci if (fl->fl_type == F_WRLCK) 13208c2ecf20Sopenharmony_ci ret = 1; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci spin_unlock(&flctx->flc_lock); 13238c2ecf20Sopenharmony_ci return ret; 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci/* 13278c2ecf20Sopenharmony_ci * Update and possibly write a cached page of an NFS file. 13288c2ecf20Sopenharmony_ci * 13298c2ecf20Sopenharmony_ci * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad 13308c2ecf20Sopenharmony_ci * things with a page scheduled for an RPC call (e.g. invalidate it). 13318c2ecf20Sopenharmony_ci */ 13328c2ecf20Sopenharmony_ciint nfs_updatepage(struct file *file, struct page *page, 13338c2ecf20Sopenharmony_ci unsigned int offset, unsigned int count) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci struct nfs_open_context *ctx = nfs_file_open_context(file); 13368c2ecf20Sopenharmony_ci struct address_space *mapping = page_file_mapping(page); 13378c2ecf20Sopenharmony_ci struct inode *inode = mapping->host; 13388c2ecf20Sopenharmony_ci int status = 0; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci dprintk("NFS: nfs_updatepage(%pD2 %d@%lld)\n", 13438c2ecf20Sopenharmony_ci file, count, (long long)(page_file_offset(page) + offset)); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (!count) 13468c2ecf20Sopenharmony_ci goto out; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (nfs_can_extend_write(file, page, inode)) { 13498c2ecf20Sopenharmony_ci count = max(count + offset, nfs_page_length(page)); 13508c2ecf20Sopenharmony_ci offset = 0; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci status = nfs_writepage_setup(ctx, page, offset, count); 13548c2ecf20Sopenharmony_ci if (status < 0) 13558c2ecf20Sopenharmony_ci nfs_set_pageerror(mapping); 13568c2ecf20Sopenharmony_ci else 13578c2ecf20Sopenharmony_ci __set_page_dirty_nobuffers(page); 13588c2ecf20Sopenharmony_ciout: 13598c2ecf20Sopenharmony_ci dprintk("NFS: nfs_updatepage returns %d (isize %lld)\n", 13608c2ecf20Sopenharmony_ci status, (long long)i_size_read(inode)); 13618c2ecf20Sopenharmony_ci return status; 13628c2ecf20Sopenharmony_ci} 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_cistatic int flush_task_priority(int how) 13658c2ecf20Sopenharmony_ci{ 13668c2ecf20Sopenharmony_ci switch (how & (FLUSH_HIGHPRI|FLUSH_LOWPRI)) { 13678c2ecf20Sopenharmony_ci case FLUSH_HIGHPRI: 13688c2ecf20Sopenharmony_ci return RPC_PRIORITY_HIGH; 13698c2ecf20Sopenharmony_ci case FLUSH_LOWPRI: 13708c2ecf20Sopenharmony_ci return RPC_PRIORITY_LOW; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci return RPC_PRIORITY_NORMAL; 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_cistatic void nfs_initiate_write(struct nfs_pgio_header *hdr, 13768c2ecf20Sopenharmony_ci struct rpc_message *msg, 13778c2ecf20Sopenharmony_ci const struct nfs_rpc_ops *rpc_ops, 13788c2ecf20Sopenharmony_ci struct rpc_task_setup *task_setup_data, int how) 13798c2ecf20Sopenharmony_ci{ 13808c2ecf20Sopenharmony_ci int priority = flush_task_priority(how); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci task_setup_data->priority = priority; 13838c2ecf20Sopenharmony_ci rpc_ops->write_setup(hdr, msg, &task_setup_data->rpc_client); 13848c2ecf20Sopenharmony_ci trace_nfs_initiate_write(hdr); 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci/* If a nfs_flush_* function fails, it should remove reqs from @head and 13888c2ecf20Sopenharmony_ci * call this on each, which will prepare them to be retried on next 13898c2ecf20Sopenharmony_ci * writeback using standard nfs. 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_cistatic void nfs_redirty_request(struct nfs_page *req) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci /* Bump the transmission count */ 13948c2ecf20Sopenharmony_ci req->wb_nio++; 13958c2ecf20Sopenharmony_ci nfs_mark_request_dirty(req); 13968c2ecf20Sopenharmony_ci set_bit(NFS_CONTEXT_RESEND_WRITES, &nfs_req_openctx(req)->flags); 13978c2ecf20Sopenharmony_ci nfs_end_page_writeback(req); 13988c2ecf20Sopenharmony_ci nfs_release_request(req); 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic void nfs_async_write_error(struct list_head *head, int error) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci struct nfs_page *req; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci while (!list_empty(head)) { 14068c2ecf20Sopenharmony_ci req = nfs_list_entry(head->next); 14078c2ecf20Sopenharmony_ci nfs_list_remove_request(req); 14088c2ecf20Sopenharmony_ci if (nfs_error_is_fatal_on_server(error)) 14098c2ecf20Sopenharmony_ci nfs_write_error(req, error); 14108c2ecf20Sopenharmony_ci else 14118c2ecf20Sopenharmony_ci nfs_redirty_request(req); 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci} 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_cistatic void nfs_async_write_reschedule_io(struct nfs_pgio_header *hdr) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci nfs_async_write_error(&hdr->pages, 0); 14188c2ecf20Sopenharmony_ci filemap_fdatawrite_range(hdr->inode->i_mapping, hdr->args.offset, 14198c2ecf20Sopenharmony_ci hdr->args.offset + hdr->args.count - 1); 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cistatic const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = { 14238c2ecf20Sopenharmony_ci .init_hdr = nfs_async_write_init, 14248c2ecf20Sopenharmony_ci .error_cleanup = nfs_async_write_error, 14258c2ecf20Sopenharmony_ci .completion = nfs_write_completion, 14268c2ecf20Sopenharmony_ci .reschedule_io = nfs_async_write_reschedule_io, 14278c2ecf20Sopenharmony_ci}; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_civoid nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, 14308c2ecf20Sopenharmony_ci struct inode *inode, int ioflags, bool force_mds, 14318c2ecf20Sopenharmony_ci const struct nfs_pgio_completion_ops *compl_ops) 14328c2ecf20Sopenharmony_ci{ 14338c2ecf20Sopenharmony_ci struct nfs_server *server = NFS_SERVER(inode); 14348c2ecf20Sopenharmony_ci const struct nfs_pageio_ops *pg_ops = &nfs_pgio_rw_ops; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci#ifdef CONFIG_NFS_V4_1 14378c2ecf20Sopenharmony_ci if (server->pnfs_curr_ld && !force_mds) 14388c2ecf20Sopenharmony_ci pg_ops = server->pnfs_curr_ld->pg_write_ops; 14398c2ecf20Sopenharmony_ci#endif 14408c2ecf20Sopenharmony_ci nfs_pageio_init(pgio, inode, pg_ops, compl_ops, &nfs_rw_write_ops, 14418c2ecf20Sopenharmony_ci server->wsize, ioflags); 14428c2ecf20Sopenharmony_ci} 14438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_pageio_init_write); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_civoid nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio) 14468c2ecf20Sopenharmony_ci{ 14478c2ecf20Sopenharmony_ci struct nfs_pgio_mirror *mirror; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci if (pgio->pg_ops && pgio->pg_ops->pg_cleanup) 14508c2ecf20Sopenharmony_ci pgio->pg_ops->pg_cleanup(pgio); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci pgio->pg_ops = &nfs_pgio_rw_ops; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci nfs_pageio_stop_mirroring(pgio); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci mirror = &pgio->pg_mirrors[0]; 14578c2ecf20Sopenharmony_ci mirror->pg_bsize = NFS_SERVER(pgio->pg_inode)->wsize; 14588c2ecf20Sopenharmony_ci} 14598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_civoid nfs_commit_prepare(struct rpc_task *task, void *calldata) 14638c2ecf20Sopenharmony_ci{ 14648c2ecf20Sopenharmony_ci struct nfs_commit_data *data = calldata; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci NFS_PROTO(data->inode)->commit_rpc_prepare(task, data); 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci/* 14708c2ecf20Sopenharmony_ci * Special version of should_remove_suid() that ignores capabilities. 14718c2ecf20Sopenharmony_ci */ 14728c2ecf20Sopenharmony_cistatic int nfs_should_remove_suid(const struct inode *inode) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci umode_t mode = inode->i_mode; 14758c2ecf20Sopenharmony_ci int kill = 0; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci /* suid always must be killed */ 14788c2ecf20Sopenharmony_ci if (unlikely(mode & S_ISUID)) 14798c2ecf20Sopenharmony_ci kill = ATTR_KILL_SUID; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci /* 14828c2ecf20Sopenharmony_ci * sgid without any exec bits is just a mandatory locking mark; leave 14838c2ecf20Sopenharmony_ci * it alone. If some exec bits are set, it's a real sgid; kill it. 14848c2ecf20Sopenharmony_ci */ 14858c2ecf20Sopenharmony_ci if (unlikely((mode & S_ISGID) && (mode & S_IXGRP))) 14868c2ecf20Sopenharmony_ci kill |= ATTR_KILL_SGID; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci if (unlikely(kill && S_ISREG(mode))) 14898c2ecf20Sopenharmony_ci return kill; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci return 0; 14928c2ecf20Sopenharmony_ci} 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_cistatic void nfs_writeback_check_extend(struct nfs_pgio_header *hdr, 14958c2ecf20Sopenharmony_ci struct nfs_fattr *fattr) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct nfs_pgio_args *argp = &hdr->args; 14988c2ecf20Sopenharmony_ci struct nfs_pgio_res *resp = &hdr->res; 14998c2ecf20Sopenharmony_ci u64 size = argp->offset + resp->count; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci if (!(fattr->valid & NFS_ATTR_FATTR_SIZE)) 15028c2ecf20Sopenharmony_ci fattr->size = size; 15038c2ecf20Sopenharmony_ci if (nfs_size_to_loff_t(fattr->size) < i_size_read(hdr->inode)) { 15048c2ecf20Sopenharmony_ci fattr->valid &= ~NFS_ATTR_FATTR_SIZE; 15058c2ecf20Sopenharmony_ci return; 15068c2ecf20Sopenharmony_ci } 15078c2ecf20Sopenharmony_ci if (size != fattr->size) 15088c2ecf20Sopenharmony_ci return; 15098c2ecf20Sopenharmony_ci /* Set attribute barrier */ 15108c2ecf20Sopenharmony_ci nfs_fattr_set_barrier(fattr); 15118c2ecf20Sopenharmony_ci /* ...and update size */ 15128c2ecf20Sopenharmony_ci fattr->valid |= NFS_ATTR_FATTR_SIZE; 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_civoid nfs_writeback_update_inode(struct nfs_pgio_header *hdr) 15168c2ecf20Sopenharmony_ci{ 15178c2ecf20Sopenharmony_ci struct nfs_fattr *fattr = &hdr->fattr; 15188c2ecf20Sopenharmony_ci struct inode *inode = hdr->inode; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 15218c2ecf20Sopenharmony_ci nfs_writeback_check_extend(hdr, fattr); 15228c2ecf20Sopenharmony_ci nfs_post_op_update_inode_force_wcc_locked(inode, fattr); 15238c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 15248c2ecf20Sopenharmony_ci} 15258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_writeback_update_inode); 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci/* 15288c2ecf20Sopenharmony_ci * This function is called when the WRITE call is complete. 15298c2ecf20Sopenharmony_ci */ 15308c2ecf20Sopenharmony_cistatic int nfs_writeback_done(struct rpc_task *task, 15318c2ecf20Sopenharmony_ci struct nfs_pgio_header *hdr, 15328c2ecf20Sopenharmony_ci struct inode *inode) 15338c2ecf20Sopenharmony_ci{ 15348c2ecf20Sopenharmony_ci int status; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* 15378c2ecf20Sopenharmony_ci * ->write_done will attempt to use post-op attributes to detect 15388c2ecf20Sopenharmony_ci * conflicting writes by other clients. A strict interpretation 15398c2ecf20Sopenharmony_ci * of close-to-open would allow us to continue caching even if 15408c2ecf20Sopenharmony_ci * another writer had changed the file, but some applications 15418c2ecf20Sopenharmony_ci * depend on tighter cache coherency when writing. 15428c2ecf20Sopenharmony_ci */ 15438c2ecf20Sopenharmony_ci status = NFS_PROTO(inode)->write_done(task, hdr); 15448c2ecf20Sopenharmony_ci if (status != 0) 15458c2ecf20Sopenharmony_ci return status; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count); 15488c2ecf20Sopenharmony_ci trace_nfs_writeback_done(task, hdr); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci if (hdr->res.verf->committed < hdr->args.stable && 15518c2ecf20Sopenharmony_ci task->tk_status >= 0) { 15528c2ecf20Sopenharmony_ci /* We tried a write call, but the server did not 15538c2ecf20Sopenharmony_ci * commit data to stable storage even though we 15548c2ecf20Sopenharmony_ci * requested it. 15558c2ecf20Sopenharmony_ci * Note: There is a known bug in Tru64 < 5.0 in which 15568c2ecf20Sopenharmony_ci * the server reports NFS_DATA_SYNC, but performs 15578c2ecf20Sopenharmony_ci * NFS_FILE_SYNC. We therefore implement this checking 15588c2ecf20Sopenharmony_ci * as a dprintk() in order to avoid filling syslog. 15598c2ecf20Sopenharmony_ci */ 15608c2ecf20Sopenharmony_ci static unsigned long complain; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci /* Note this will print the MDS for a DS write */ 15638c2ecf20Sopenharmony_ci if (time_before(complain, jiffies)) { 15648c2ecf20Sopenharmony_ci dprintk("NFS: faulty NFS server %s:" 15658c2ecf20Sopenharmony_ci " (committed = %d) != (stable = %d)\n", 15668c2ecf20Sopenharmony_ci NFS_SERVER(inode)->nfs_client->cl_hostname, 15678c2ecf20Sopenharmony_ci hdr->res.verf->committed, hdr->args.stable); 15688c2ecf20Sopenharmony_ci complain = jiffies + 300 * HZ; 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci /* Deal with the suid/sgid bit corner case */ 15738c2ecf20Sopenharmony_ci if (nfs_should_remove_suid(inode)) { 15748c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 15758c2ecf20Sopenharmony_ci NFS_I(inode)->cache_validity |= NFS_INO_INVALID_OTHER; 15768c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci return 0; 15798c2ecf20Sopenharmony_ci} 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci/* 15828c2ecf20Sopenharmony_ci * This function is called when the WRITE call is complete. 15838c2ecf20Sopenharmony_ci */ 15848c2ecf20Sopenharmony_cistatic void nfs_writeback_result(struct rpc_task *task, 15858c2ecf20Sopenharmony_ci struct nfs_pgio_header *hdr) 15868c2ecf20Sopenharmony_ci{ 15878c2ecf20Sopenharmony_ci struct nfs_pgio_args *argp = &hdr->args; 15888c2ecf20Sopenharmony_ci struct nfs_pgio_res *resp = &hdr->res; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci if (resp->count < argp->count) { 15918c2ecf20Sopenharmony_ci static unsigned long complain; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci /* This a short write! */ 15948c2ecf20Sopenharmony_ci nfs_inc_stats(hdr->inode, NFSIOS_SHORTWRITE); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci /* Has the server at least made some progress? */ 15978c2ecf20Sopenharmony_ci if (resp->count == 0) { 15988c2ecf20Sopenharmony_ci if (time_before(complain, jiffies)) { 15998c2ecf20Sopenharmony_ci printk(KERN_WARNING 16008c2ecf20Sopenharmony_ci "NFS: Server wrote zero bytes, expected %u.\n", 16018c2ecf20Sopenharmony_ci argp->count); 16028c2ecf20Sopenharmony_ci complain = jiffies + 300 * HZ; 16038c2ecf20Sopenharmony_ci } 16048c2ecf20Sopenharmony_ci nfs_set_pgio_error(hdr, -EIO, argp->offset); 16058c2ecf20Sopenharmony_ci task->tk_status = -EIO; 16068c2ecf20Sopenharmony_ci return; 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* For non rpc-based layout drivers, retry-through-MDS */ 16108c2ecf20Sopenharmony_ci if (!task->tk_ops) { 16118c2ecf20Sopenharmony_ci hdr->pnfs_error = -EAGAIN; 16128c2ecf20Sopenharmony_ci return; 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci /* Was this an NFSv2 write or an NFSv3 stable write? */ 16168c2ecf20Sopenharmony_ci if (resp->verf->committed != NFS_UNSTABLE) { 16178c2ecf20Sopenharmony_ci /* Resend from where the server left off */ 16188c2ecf20Sopenharmony_ci hdr->mds_offset += resp->count; 16198c2ecf20Sopenharmony_ci argp->offset += resp->count; 16208c2ecf20Sopenharmony_ci argp->pgbase += resp->count; 16218c2ecf20Sopenharmony_ci argp->count -= resp->count; 16228c2ecf20Sopenharmony_ci } else { 16238c2ecf20Sopenharmony_ci /* Resend as a stable write in order to avoid 16248c2ecf20Sopenharmony_ci * headaches in the case of a server crash. 16258c2ecf20Sopenharmony_ci */ 16268c2ecf20Sopenharmony_ci argp->stable = NFS_FILE_SYNC; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci resp->count = 0; 16298c2ecf20Sopenharmony_ci resp->verf->committed = 0; 16308c2ecf20Sopenharmony_ci rpc_restart_call_prepare(task); 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci} 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_cistatic int wait_on_commit(struct nfs_mds_commit_info *cinfo) 16358c2ecf20Sopenharmony_ci{ 16368c2ecf20Sopenharmony_ci return wait_var_event_killable(&cinfo->rpcs_out, 16378c2ecf20Sopenharmony_ci !atomic_read(&cinfo->rpcs_out)); 16388c2ecf20Sopenharmony_ci} 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_civoid nfs_commit_begin(struct nfs_mds_commit_info *cinfo) 16418c2ecf20Sopenharmony_ci{ 16428c2ecf20Sopenharmony_ci atomic_inc(&cinfo->rpcs_out); 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_cibool nfs_commit_end(struct nfs_mds_commit_info *cinfo) 16468c2ecf20Sopenharmony_ci{ 16478c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&cinfo->rpcs_out)) { 16488c2ecf20Sopenharmony_ci wake_up_var(&cinfo->rpcs_out); 16498c2ecf20Sopenharmony_ci return true; 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci return false; 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_civoid nfs_commitdata_release(struct nfs_commit_data *data) 16558c2ecf20Sopenharmony_ci{ 16568c2ecf20Sopenharmony_ci put_nfs_open_context(data->context); 16578c2ecf20Sopenharmony_ci nfs_commit_free(data); 16588c2ecf20Sopenharmony_ci} 16598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_commitdata_release); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ciint nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data, 16628c2ecf20Sopenharmony_ci const struct nfs_rpc_ops *nfs_ops, 16638c2ecf20Sopenharmony_ci const struct rpc_call_ops *call_ops, 16648c2ecf20Sopenharmony_ci int how, int flags) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct rpc_task *task; 16678c2ecf20Sopenharmony_ci int priority = flush_task_priority(how); 16688c2ecf20Sopenharmony_ci struct rpc_message msg = { 16698c2ecf20Sopenharmony_ci .rpc_argp = &data->args, 16708c2ecf20Sopenharmony_ci .rpc_resp = &data->res, 16718c2ecf20Sopenharmony_ci .rpc_cred = data->cred, 16728c2ecf20Sopenharmony_ci }; 16738c2ecf20Sopenharmony_ci struct rpc_task_setup task_setup_data = { 16748c2ecf20Sopenharmony_ci .task = &data->task, 16758c2ecf20Sopenharmony_ci .rpc_client = clnt, 16768c2ecf20Sopenharmony_ci .rpc_message = &msg, 16778c2ecf20Sopenharmony_ci .callback_ops = call_ops, 16788c2ecf20Sopenharmony_ci .callback_data = data, 16798c2ecf20Sopenharmony_ci .workqueue = nfsiod_workqueue, 16808c2ecf20Sopenharmony_ci .flags = RPC_TASK_ASYNC | flags, 16818c2ecf20Sopenharmony_ci .priority = priority, 16828c2ecf20Sopenharmony_ci }; 16838c2ecf20Sopenharmony_ci /* Set up the initial task struct. */ 16848c2ecf20Sopenharmony_ci nfs_ops->commit_setup(data, &msg, &task_setup_data.rpc_client); 16858c2ecf20Sopenharmony_ci trace_nfs_initiate_commit(data); 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci dprintk("NFS: initiated commit call\n"); 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci task = rpc_run_task(&task_setup_data); 16908c2ecf20Sopenharmony_ci if (IS_ERR(task)) 16918c2ecf20Sopenharmony_ci return PTR_ERR(task); 16928c2ecf20Sopenharmony_ci if (how & FLUSH_SYNC) 16938c2ecf20Sopenharmony_ci rpc_wait_for_completion_task(task); 16948c2ecf20Sopenharmony_ci rpc_put_task(task); 16958c2ecf20Sopenharmony_ci return 0; 16968c2ecf20Sopenharmony_ci} 16978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_initiate_commit); 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_cistatic loff_t nfs_get_lwb(struct list_head *head) 17008c2ecf20Sopenharmony_ci{ 17018c2ecf20Sopenharmony_ci loff_t lwb = 0; 17028c2ecf20Sopenharmony_ci struct nfs_page *req; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci list_for_each_entry(req, head, wb_list) 17058c2ecf20Sopenharmony_ci if (lwb < (req_offset(req) + req->wb_bytes)) 17068c2ecf20Sopenharmony_ci lwb = req_offset(req) + req->wb_bytes; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci return lwb; 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci/* 17128c2ecf20Sopenharmony_ci * Set up the argument/result storage required for the RPC call. 17138c2ecf20Sopenharmony_ci */ 17148c2ecf20Sopenharmony_civoid nfs_init_commit(struct nfs_commit_data *data, 17158c2ecf20Sopenharmony_ci struct list_head *head, 17168c2ecf20Sopenharmony_ci struct pnfs_layout_segment *lseg, 17178c2ecf20Sopenharmony_ci struct nfs_commit_info *cinfo) 17188c2ecf20Sopenharmony_ci{ 17198c2ecf20Sopenharmony_ci struct nfs_page *first; 17208c2ecf20Sopenharmony_ci struct nfs_open_context *ctx; 17218c2ecf20Sopenharmony_ci struct inode *inode; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci /* Set up the RPC argument and reply structs 17248c2ecf20Sopenharmony_ci * NB: take care not to mess about with data->commit et al. */ 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (head) 17278c2ecf20Sopenharmony_ci list_splice_init(head, &data->pages); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci first = nfs_list_entry(data->pages.next); 17308c2ecf20Sopenharmony_ci ctx = nfs_req_openctx(first); 17318c2ecf20Sopenharmony_ci inode = d_inode(ctx->dentry); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci data->inode = inode; 17348c2ecf20Sopenharmony_ci data->cred = ctx->cred; 17358c2ecf20Sopenharmony_ci data->lseg = lseg; /* reference transferred */ 17368c2ecf20Sopenharmony_ci /* only set lwb for pnfs commit */ 17378c2ecf20Sopenharmony_ci if (lseg) 17388c2ecf20Sopenharmony_ci data->lwb = nfs_get_lwb(&data->pages); 17398c2ecf20Sopenharmony_ci data->mds_ops = &nfs_commit_ops; 17408c2ecf20Sopenharmony_ci data->completion_ops = cinfo->completion_ops; 17418c2ecf20Sopenharmony_ci data->dreq = cinfo->dreq; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci data->args.fh = NFS_FH(data->inode); 17448c2ecf20Sopenharmony_ci /* Note: we always request a commit of the entire inode */ 17458c2ecf20Sopenharmony_ci data->args.offset = 0; 17468c2ecf20Sopenharmony_ci data->args.count = 0; 17478c2ecf20Sopenharmony_ci data->context = get_nfs_open_context(ctx); 17488c2ecf20Sopenharmony_ci data->res.fattr = &data->fattr; 17498c2ecf20Sopenharmony_ci data->res.verf = &data->verf; 17508c2ecf20Sopenharmony_ci nfs_fattr_init(&data->fattr); 17518c2ecf20Sopenharmony_ci nfs_commit_begin(cinfo->mds); 17528c2ecf20Sopenharmony_ci} 17538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_init_commit); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_civoid nfs_retry_commit(struct list_head *page_list, 17568c2ecf20Sopenharmony_ci struct pnfs_layout_segment *lseg, 17578c2ecf20Sopenharmony_ci struct nfs_commit_info *cinfo, 17588c2ecf20Sopenharmony_ci u32 ds_commit_idx) 17598c2ecf20Sopenharmony_ci{ 17608c2ecf20Sopenharmony_ci struct nfs_page *req; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci while (!list_empty(page_list)) { 17638c2ecf20Sopenharmony_ci req = nfs_list_entry(page_list->next); 17648c2ecf20Sopenharmony_ci nfs_list_remove_request(req); 17658c2ecf20Sopenharmony_ci nfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx); 17668c2ecf20Sopenharmony_ci if (!cinfo->dreq) 17678c2ecf20Sopenharmony_ci nfs_clear_page_commit(req->wb_page); 17688c2ecf20Sopenharmony_ci nfs_unlock_and_release_request(req); 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci} 17718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_retry_commit); 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_cistatic void 17748c2ecf20Sopenharmony_cinfs_commit_resched_write(struct nfs_commit_info *cinfo, 17758c2ecf20Sopenharmony_ci struct nfs_page *req) 17768c2ecf20Sopenharmony_ci{ 17778c2ecf20Sopenharmony_ci __set_page_dirty_nobuffers(req->wb_page); 17788c2ecf20Sopenharmony_ci} 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci/* 17818c2ecf20Sopenharmony_ci * Commit dirty pages 17828c2ecf20Sopenharmony_ci */ 17838c2ecf20Sopenharmony_cistatic int 17848c2ecf20Sopenharmony_cinfs_commit_list(struct inode *inode, struct list_head *head, int how, 17858c2ecf20Sopenharmony_ci struct nfs_commit_info *cinfo) 17868c2ecf20Sopenharmony_ci{ 17878c2ecf20Sopenharmony_ci struct nfs_commit_data *data; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* another commit raced with us */ 17908c2ecf20Sopenharmony_ci if (list_empty(head)) 17918c2ecf20Sopenharmony_ci return 0; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci data = nfs_commitdata_alloc(); 17948c2ecf20Sopenharmony_ci if (!data) { 17958c2ecf20Sopenharmony_ci nfs_retry_commit(head, NULL, cinfo, -1); 17968c2ecf20Sopenharmony_ci return -ENOMEM; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci /* Set up the argument struct */ 18008c2ecf20Sopenharmony_ci nfs_init_commit(data, head, NULL, cinfo); 18018c2ecf20Sopenharmony_ci return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode), 18028c2ecf20Sopenharmony_ci data->mds_ops, how, RPC_TASK_CRED_NOREF); 18038c2ecf20Sopenharmony_ci} 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci/* 18068c2ecf20Sopenharmony_ci * COMMIT call returned 18078c2ecf20Sopenharmony_ci */ 18088c2ecf20Sopenharmony_cistatic void nfs_commit_done(struct rpc_task *task, void *calldata) 18098c2ecf20Sopenharmony_ci{ 18108c2ecf20Sopenharmony_ci struct nfs_commit_data *data = calldata; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci dprintk("NFS: %5u nfs_commit_done (status %d)\n", 18138c2ecf20Sopenharmony_ci task->tk_pid, task->tk_status); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci /* Call the NFS version-specific code */ 18168c2ecf20Sopenharmony_ci NFS_PROTO(data->inode)->commit_done(task, data); 18178c2ecf20Sopenharmony_ci trace_nfs_commit_done(task, data); 18188c2ecf20Sopenharmony_ci} 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_cistatic void nfs_commit_release_pages(struct nfs_commit_data *data) 18218c2ecf20Sopenharmony_ci{ 18228c2ecf20Sopenharmony_ci const struct nfs_writeverf *verf = data->res.verf; 18238c2ecf20Sopenharmony_ci struct nfs_page *req; 18248c2ecf20Sopenharmony_ci int status = data->task.tk_status; 18258c2ecf20Sopenharmony_ci struct nfs_commit_info cinfo; 18268c2ecf20Sopenharmony_ci struct nfs_server *nfss; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci while (!list_empty(&data->pages)) { 18298c2ecf20Sopenharmony_ci req = nfs_list_entry(data->pages.next); 18308c2ecf20Sopenharmony_ci nfs_list_remove_request(req); 18318c2ecf20Sopenharmony_ci if (req->wb_page) 18328c2ecf20Sopenharmony_ci nfs_clear_page_commit(req->wb_page); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci dprintk("NFS: commit (%s/%llu %d@%lld)", 18358c2ecf20Sopenharmony_ci nfs_req_openctx(req)->dentry->d_sb->s_id, 18368c2ecf20Sopenharmony_ci (unsigned long long)NFS_FILEID(d_inode(nfs_req_openctx(req)->dentry)), 18378c2ecf20Sopenharmony_ci req->wb_bytes, 18388c2ecf20Sopenharmony_ci (long long)req_offset(req)); 18398c2ecf20Sopenharmony_ci if (status < 0) { 18408c2ecf20Sopenharmony_ci if (req->wb_page) { 18418c2ecf20Sopenharmony_ci trace_nfs_commit_error(req, status); 18428c2ecf20Sopenharmony_ci nfs_mapping_set_error(req->wb_page, status); 18438c2ecf20Sopenharmony_ci nfs_inode_remove_request(req); 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci dprintk_cont(", error = %d\n", status); 18468c2ecf20Sopenharmony_ci goto next; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci /* Okay, COMMIT succeeded, apparently. Check the verifier 18508c2ecf20Sopenharmony_ci * returned by the server against all stored verfs. */ 18518c2ecf20Sopenharmony_ci if (nfs_write_match_verf(verf, req)) { 18528c2ecf20Sopenharmony_ci /* We have a match */ 18538c2ecf20Sopenharmony_ci if (req->wb_page) 18548c2ecf20Sopenharmony_ci nfs_inode_remove_request(req); 18558c2ecf20Sopenharmony_ci dprintk_cont(" OK\n"); 18568c2ecf20Sopenharmony_ci goto next; 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci /* We have a mismatch. Write the page again */ 18598c2ecf20Sopenharmony_ci dprintk_cont(" mismatch\n"); 18608c2ecf20Sopenharmony_ci nfs_mark_request_dirty(req); 18618c2ecf20Sopenharmony_ci set_bit(NFS_CONTEXT_RESEND_WRITES, &nfs_req_openctx(req)->flags); 18628c2ecf20Sopenharmony_ci next: 18638c2ecf20Sopenharmony_ci nfs_unlock_and_release_request(req); 18648c2ecf20Sopenharmony_ci /* Latency breaker */ 18658c2ecf20Sopenharmony_ci cond_resched(); 18668c2ecf20Sopenharmony_ci } 18678c2ecf20Sopenharmony_ci nfss = NFS_SERVER(data->inode); 18688c2ecf20Sopenharmony_ci if (atomic_long_read(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH) 18698c2ecf20Sopenharmony_ci clear_bdi_congested(inode_to_bdi(data->inode), BLK_RW_ASYNC); 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci nfs_init_cinfo(&cinfo, data->inode, data->dreq); 18728c2ecf20Sopenharmony_ci nfs_commit_end(cinfo.mds); 18738c2ecf20Sopenharmony_ci} 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_cistatic void nfs_commit_release(void *calldata) 18768c2ecf20Sopenharmony_ci{ 18778c2ecf20Sopenharmony_ci struct nfs_commit_data *data = calldata; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci data->completion_ops->completion(data); 18808c2ecf20Sopenharmony_ci nfs_commitdata_release(calldata); 18818c2ecf20Sopenharmony_ci} 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_cistatic const struct rpc_call_ops nfs_commit_ops = { 18848c2ecf20Sopenharmony_ci .rpc_call_prepare = nfs_commit_prepare, 18858c2ecf20Sopenharmony_ci .rpc_call_done = nfs_commit_done, 18868c2ecf20Sopenharmony_ci .rpc_release = nfs_commit_release, 18878c2ecf20Sopenharmony_ci}; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_cistatic const struct nfs_commit_completion_ops nfs_commit_completion_ops = { 18908c2ecf20Sopenharmony_ci .completion = nfs_commit_release_pages, 18918c2ecf20Sopenharmony_ci .resched_write = nfs_commit_resched_write, 18928c2ecf20Sopenharmony_ci}; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ciint nfs_generic_commit_list(struct inode *inode, struct list_head *head, 18958c2ecf20Sopenharmony_ci int how, struct nfs_commit_info *cinfo) 18968c2ecf20Sopenharmony_ci{ 18978c2ecf20Sopenharmony_ci int status; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci status = pnfs_commit_list(inode, head, how, cinfo); 19008c2ecf20Sopenharmony_ci if (status == PNFS_NOT_ATTEMPTED) 19018c2ecf20Sopenharmony_ci status = nfs_commit_list(inode, head, how, cinfo); 19028c2ecf20Sopenharmony_ci return status; 19038c2ecf20Sopenharmony_ci} 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_cistatic int __nfs_commit_inode(struct inode *inode, int how, 19068c2ecf20Sopenharmony_ci struct writeback_control *wbc) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci LIST_HEAD(head); 19098c2ecf20Sopenharmony_ci struct nfs_commit_info cinfo; 19108c2ecf20Sopenharmony_ci int may_wait = how & FLUSH_SYNC; 19118c2ecf20Sopenharmony_ci int ret, nscan; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci how &= ~FLUSH_SYNC; 19148c2ecf20Sopenharmony_ci nfs_init_cinfo_from_inode(&cinfo, inode); 19158c2ecf20Sopenharmony_ci nfs_commit_begin(cinfo.mds); 19168c2ecf20Sopenharmony_ci for (;;) { 19178c2ecf20Sopenharmony_ci ret = nscan = nfs_scan_commit(inode, &head, &cinfo); 19188c2ecf20Sopenharmony_ci if (ret <= 0) 19198c2ecf20Sopenharmony_ci break; 19208c2ecf20Sopenharmony_ci ret = nfs_generic_commit_list(inode, &head, how, &cinfo); 19218c2ecf20Sopenharmony_ci if (ret < 0) 19228c2ecf20Sopenharmony_ci break; 19238c2ecf20Sopenharmony_ci ret = 0; 19248c2ecf20Sopenharmony_ci if (wbc && wbc->sync_mode == WB_SYNC_NONE) { 19258c2ecf20Sopenharmony_ci if (nscan < wbc->nr_to_write) 19268c2ecf20Sopenharmony_ci wbc->nr_to_write -= nscan; 19278c2ecf20Sopenharmony_ci else 19288c2ecf20Sopenharmony_ci wbc->nr_to_write = 0; 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci if (nscan < INT_MAX) 19318c2ecf20Sopenharmony_ci break; 19328c2ecf20Sopenharmony_ci cond_resched(); 19338c2ecf20Sopenharmony_ci } 19348c2ecf20Sopenharmony_ci nfs_commit_end(cinfo.mds); 19358c2ecf20Sopenharmony_ci if (ret || !may_wait) 19368c2ecf20Sopenharmony_ci return ret; 19378c2ecf20Sopenharmony_ci return wait_on_commit(cinfo.mds); 19388c2ecf20Sopenharmony_ci} 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ciint nfs_commit_inode(struct inode *inode, int how) 19418c2ecf20Sopenharmony_ci{ 19428c2ecf20Sopenharmony_ci return __nfs_commit_inode(inode, how, NULL); 19438c2ecf20Sopenharmony_ci} 19448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_commit_inode); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ciint nfs_write_inode(struct inode *inode, struct writeback_control *wbc) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 19498c2ecf20Sopenharmony_ci int flags = FLUSH_SYNC; 19508c2ecf20Sopenharmony_ci int ret = 0; 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci if (wbc->sync_mode == WB_SYNC_NONE) { 19538c2ecf20Sopenharmony_ci /* no commits means nothing needs to be done */ 19548c2ecf20Sopenharmony_ci if (!atomic_long_read(&nfsi->commit_info.ncommit)) 19558c2ecf20Sopenharmony_ci goto check_requests_outstanding; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci /* Don't commit yet if this is a non-blocking flush and there 19588c2ecf20Sopenharmony_ci * are a lot of outstanding writes for this mapping. 19598c2ecf20Sopenharmony_ci */ 19608c2ecf20Sopenharmony_ci if (mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK)) 19618c2ecf20Sopenharmony_ci goto out_mark_dirty; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci /* don't wait for the COMMIT response */ 19648c2ecf20Sopenharmony_ci flags = 0; 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci ret = __nfs_commit_inode(inode, flags, wbc); 19688c2ecf20Sopenharmony_ci if (!ret) { 19698c2ecf20Sopenharmony_ci if (flags & FLUSH_SYNC) 19708c2ecf20Sopenharmony_ci return 0; 19718c2ecf20Sopenharmony_ci } else if (atomic_long_read(&nfsi->commit_info.ncommit)) 19728c2ecf20Sopenharmony_ci goto out_mark_dirty; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_cicheck_requests_outstanding: 19758c2ecf20Sopenharmony_ci if (!atomic_read(&nfsi->commit_info.rpcs_out)) 19768c2ecf20Sopenharmony_ci return ret; 19778c2ecf20Sopenharmony_ciout_mark_dirty: 19788c2ecf20Sopenharmony_ci __mark_inode_dirty(inode, I_DIRTY_DATASYNC); 19798c2ecf20Sopenharmony_ci return ret; 19808c2ecf20Sopenharmony_ci} 19818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_write_inode); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci/* 19848c2ecf20Sopenharmony_ci * Wrapper for filemap_write_and_wait_range() 19858c2ecf20Sopenharmony_ci * 19868c2ecf20Sopenharmony_ci * Needed for pNFS in order to ensure data becomes visible to the 19878c2ecf20Sopenharmony_ci * client. 19888c2ecf20Sopenharmony_ci */ 19898c2ecf20Sopenharmony_ciint nfs_filemap_write_and_wait_range(struct address_space *mapping, 19908c2ecf20Sopenharmony_ci loff_t lstart, loff_t lend) 19918c2ecf20Sopenharmony_ci{ 19928c2ecf20Sopenharmony_ci int ret; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci ret = filemap_write_and_wait_range(mapping, lstart, lend); 19958c2ecf20Sopenharmony_ci if (ret == 0) 19968c2ecf20Sopenharmony_ci ret = pnfs_sync_inode(mapping->host, true); 19978c2ecf20Sopenharmony_ci return ret; 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_filemap_write_and_wait_range); 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci/* 20028c2ecf20Sopenharmony_ci * flush the inode to disk. 20038c2ecf20Sopenharmony_ci */ 20048c2ecf20Sopenharmony_ciint nfs_wb_all(struct inode *inode) 20058c2ecf20Sopenharmony_ci{ 20068c2ecf20Sopenharmony_ci int ret; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci trace_nfs_writeback_inode_enter(inode); 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci ret = filemap_write_and_wait(inode->i_mapping); 20118c2ecf20Sopenharmony_ci if (ret) 20128c2ecf20Sopenharmony_ci goto out; 20138c2ecf20Sopenharmony_ci ret = nfs_commit_inode(inode, FLUSH_SYNC); 20148c2ecf20Sopenharmony_ci if (ret < 0) 20158c2ecf20Sopenharmony_ci goto out; 20168c2ecf20Sopenharmony_ci pnfs_sync_inode(inode, true); 20178c2ecf20Sopenharmony_ci ret = 0; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ciout: 20208c2ecf20Sopenharmony_ci trace_nfs_writeback_inode_exit(inode, ret); 20218c2ecf20Sopenharmony_ci return ret; 20228c2ecf20Sopenharmony_ci} 20238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_wb_all); 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ciint nfs_wb_page_cancel(struct inode *inode, struct page *page) 20268c2ecf20Sopenharmony_ci{ 20278c2ecf20Sopenharmony_ci struct nfs_page *req; 20288c2ecf20Sopenharmony_ci int ret = 0; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci wait_on_page_writeback(page); 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci /* blocking call to cancel all requests and join to a single (head) 20338c2ecf20Sopenharmony_ci * request */ 20348c2ecf20Sopenharmony_ci req = nfs_lock_and_join_requests(page); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci if (IS_ERR(req)) { 20378c2ecf20Sopenharmony_ci ret = PTR_ERR(req); 20388c2ecf20Sopenharmony_ci } else if (req) { 20398c2ecf20Sopenharmony_ci /* all requests from this page have been cancelled by 20408c2ecf20Sopenharmony_ci * nfs_lock_and_join_requests, so just remove the head 20418c2ecf20Sopenharmony_ci * request from the inode / page_private pointer and 20428c2ecf20Sopenharmony_ci * release it */ 20438c2ecf20Sopenharmony_ci nfs_inode_remove_request(req); 20448c2ecf20Sopenharmony_ci nfs_unlock_and_release_request(req); 20458c2ecf20Sopenharmony_ci } 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci return ret; 20488c2ecf20Sopenharmony_ci} 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci/* 20518c2ecf20Sopenharmony_ci * Write back all requests on one page - we do this before reading it. 20528c2ecf20Sopenharmony_ci */ 20538c2ecf20Sopenharmony_ciint nfs_wb_page(struct inode *inode, struct page *page) 20548c2ecf20Sopenharmony_ci{ 20558c2ecf20Sopenharmony_ci loff_t range_start = page_file_offset(page); 20568c2ecf20Sopenharmony_ci loff_t range_end = range_start + (loff_t)(PAGE_SIZE - 1); 20578c2ecf20Sopenharmony_ci struct writeback_control wbc = { 20588c2ecf20Sopenharmony_ci .sync_mode = WB_SYNC_ALL, 20598c2ecf20Sopenharmony_ci .nr_to_write = 0, 20608c2ecf20Sopenharmony_ci .range_start = range_start, 20618c2ecf20Sopenharmony_ci .range_end = range_end, 20628c2ecf20Sopenharmony_ci }; 20638c2ecf20Sopenharmony_ci int ret; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci trace_nfs_writeback_page_enter(inode); 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci for (;;) { 20688c2ecf20Sopenharmony_ci wait_on_page_writeback(page); 20698c2ecf20Sopenharmony_ci if (clear_page_dirty_for_io(page)) { 20708c2ecf20Sopenharmony_ci ret = nfs_writepage_locked(page, &wbc); 20718c2ecf20Sopenharmony_ci if (ret < 0) 20728c2ecf20Sopenharmony_ci goto out_error; 20738c2ecf20Sopenharmony_ci continue; 20748c2ecf20Sopenharmony_ci } 20758c2ecf20Sopenharmony_ci ret = 0; 20768c2ecf20Sopenharmony_ci if (!PagePrivate(page)) 20778c2ecf20Sopenharmony_ci break; 20788c2ecf20Sopenharmony_ci ret = nfs_commit_inode(inode, FLUSH_SYNC); 20798c2ecf20Sopenharmony_ci if (ret < 0) 20808c2ecf20Sopenharmony_ci goto out_error; 20818c2ecf20Sopenharmony_ci } 20828c2ecf20Sopenharmony_ciout_error: 20838c2ecf20Sopenharmony_ci trace_nfs_writeback_page_exit(inode, ret); 20848c2ecf20Sopenharmony_ci return ret; 20858c2ecf20Sopenharmony_ci} 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci#ifdef CONFIG_MIGRATION 20888c2ecf20Sopenharmony_ciint nfs_migrate_page(struct address_space *mapping, struct page *newpage, 20898c2ecf20Sopenharmony_ci struct page *page, enum migrate_mode mode) 20908c2ecf20Sopenharmony_ci{ 20918c2ecf20Sopenharmony_ci /* 20928c2ecf20Sopenharmony_ci * If PagePrivate is set, then the page is currently associated with 20938c2ecf20Sopenharmony_ci * an in-progress read or write request. Don't try to migrate it. 20948c2ecf20Sopenharmony_ci * 20958c2ecf20Sopenharmony_ci * FIXME: we could do this in principle, but we'll need a way to ensure 20968c2ecf20Sopenharmony_ci * that we can safely release the inode reference while holding 20978c2ecf20Sopenharmony_ci * the page lock. 20988c2ecf20Sopenharmony_ci */ 20998c2ecf20Sopenharmony_ci if (PagePrivate(page)) 21008c2ecf20Sopenharmony_ci return -EBUSY; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci if (!nfs_fscache_release_page(page, GFP_KERNEL)) 21038c2ecf20Sopenharmony_ci return -EBUSY; 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci return migrate_page(mapping, newpage, page, mode); 21068c2ecf20Sopenharmony_ci} 21078c2ecf20Sopenharmony_ci#endif 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ciint __init nfs_init_writepagecache(void) 21108c2ecf20Sopenharmony_ci{ 21118c2ecf20Sopenharmony_ci nfs_wdata_cachep = kmem_cache_create("nfs_write_data", 21128c2ecf20Sopenharmony_ci sizeof(struct nfs_pgio_header), 21138c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, 21148c2ecf20Sopenharmony_ci NULL); 21158c2ecf20Sopenharmony_ci if (nfs_wdata_cachep == NULL) 21168c2ecf20Sopenharmony_ci return -ENOMEM; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE, 21198c2ecf20Sopenharmony_ci nfs_wdata_cachep); 21208c2ecf20Sopenharmony_ci if (nfs_wdata_mempool == NULL) 21218c2ecf20Sopenharmony_ci goto out_destroy_write_cache; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci nfs_cdata_cachep = kmem_cache_create("nfs_commit_data", 21248c2ecf20Sopenharmony_ci sizeof(struct nfs_commit_data), 21258c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, 21268c2ecf20Sopenharmony_ci NULL); 21278c2ecf20Sopenharmony_ci if (nfs_cdata_cachep == NULL) 21288c2ecf20Sopenharmony_ci goto out_destroy_write_mempool; 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT, 21318c2ecf20Sopenharmony_ci nfs_cdata_cachep); 21328c2ecf20Sopenharmony_ci if (nfs_commit_mempool == NULL) 21338c2ecf20Sopenharmony_ci goto out_destroy_commit_cache; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci /* 21368c2ecf20Sopenharmony_ci * NFS congestion size, scale with available memory. 21378c2ecf20Sopenharmony_ci * 21388c2ecf20Sopenharmony_ci * 64MB: 8192k 21398c2ecf20Sopenharmony_ci * 128MB: 11585k 21408c2ecf20Sopenharmony_ci * 256MB: 16384k 21418c2ecf20Sopenharmony_ci * 512MB: 23170k 21428c2ecf20Sopenharmony_ci * 1GB: 32768k 21438c2ecf20Sopenharmony_ci * 2GB: 46340k 21448c2ecf20Sopenharmony_ci * 4GB: 65536k 21458c2ecf20Sopenharmony_ci * 8GB: 92681k 21468c2ecf20Sopenharmony_ci * 16GB: 131072k 21478c2ecf20Sopenharmony_ci * 21488c2ecf20Sopenharmony_ci * This allows larger machines to have larger/more transfers. 21498c2ecf20Sopenharmony_ci * Limit the default to 256M 21508c2ecf20Sopenharmony_ci */ 21518c2ecf20Sopenharmony_ci nfs_congestion_kb = (16*int_sqrt(totalram_pages())) << (PAGE_SHIFT-10); 21528c2ecf20Sopenharmony_ci if (nfs_congestion_kb > 256*1024) 21538c2ecf20Sopenharmony_ci nfs_congestion_kb = 256*1024; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci return 0; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ciout_destroy_commit_cache: 21588c2ecf20Sopenharmony_ci kmem_cache_destroy(nfs_cdata_cachep); 21598c2ecf20Sopenharmony_ciout_destroy_write_mempool: 21608c2ecf20Sopenharmony_ci mempool_destroy(nfs_wdata_mempool); 21618c2ecf20Sopenharmony_ciout_destroy_write_cache: 21628c2ecf20Sopenharmony_ci kmem_cache_destroy(nfs_wdata_cachep); 21638c2ecf20Sopenharmony_ci return -ENOMEM; 21648c2ecf20Sopenharmony_ci} 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_civoid nfs_destroy_writepagecache(void) 21678c2ecf20Sopenharmony_ci{ 21688c2ecf20Sopenharmony_ci mempool_destroy(nfs_commit_mempool); 21698c2ecf20Sopenharmony_ci kmem_cache_destroy(nfs_cdata_cachep); 21708c2ecf20Sopenharmony_ci mempool_destroy(nfs_wdata_mempool); 21718c2ecf20Sopenharmony_ci kmem_cache_destroy(nfs_wdata_cachep); 21728c2ecf20Sopenharmony_ci} 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_cistatic const struct nfs_rw_ops nfs_rw_write_ops = { 21758c2ecf20Sopenharmony_ci .rw_alloc_header = nfs_writehdr_alloc, 21768c2ecf20Sopenharmony_ci .rw_free_header = nfs_writehdr_free, 21778c2ecf20Sopenharmony_ci .rw_done = nfs_writeback_done, 21788c2ecf20Sopenharmony_ci .rw_result = nfs_writeback_result, 21798c2ecf20Sopenharmony_ci .rw_initiate = nfs_initiate_write, 21808c2ecf20Sopenharmony_ci}; 2181