11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/fs/nfs/read.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Block I/O for NFS 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * Partial copy of Linus' read cache modifications to fs/nfs/file.c 71da177e4SLinus Torvalds * modified for async RPC by okir@monad.swb.de 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds #include <linux/time.h> 111da177e4SLinus Torvalds #include <linux/kernel.h> 121da177e4SLinus Torvalds #include <linux/errno.h> 131da177e4SLinus Torvalds #include <linux/fcntl.h> 141da177e4SLinus Torvalds #include <linux/stat.h> 151da177e4SLinus Torvalds #include <linux/mm.h> 161da177e4SLinus Torvalds #include <linux/slab.h> 171da177e4SLinus Torvalds #include <linux/pagemap.h> 181da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h> 191da177e4SLinus Torvalds #include <linux/nfs_fs.h> 201da177e4SLinus Torvalds #include <linux/nfs_page.h> 2164419a9bSAndy Adamson #include <linux/module.h> 221da177e4SLinus Torvalds 23bae724efSFred Isaman #include "pnfs.h" 241da177e4SLinus Torvalds 25f11c88afSAndy Adamson #include "nfs4_fs.h" 2649a70f27STrond Myklebust #include "internal.h" 2791d5b470SChuck Lever #include "iostat.h" 289a9fc1c0SDavid Howells #include "fscache.h" 2991d5b470SChuck Lever 301da177e4SLinus Torvalds #define NFSDBG_FACILITY NFSDBG_PAGECACHE 311da177e4SLinus Torvalds 321751c363STrond Myklebust static const struct nfs_pageio_ops nfs_pageio_read_ops; 334db6e0b7SFred Isaman static const struct rpc_call_ops nfs_read_common_ops; 34061ae2edSFred Isaman static const struct nfs_pgio_completion_ops nfs_async_read_completion_ops; 351da177e4SLinus Torvalds 36e18b890bSChristoph Lameter static struct kmem_cache *nfs_rdata_cachep; 371da177e4SLinus Torvalds 384db6e0b7SFred Isaman struct nfs_read_header *nfs_readhdr_alloc() 393feb2d49STrond Myklebust { 404db6e0b7SFred Isaman struct nfs_read_header *rhdr; 413feb2d49STrond Myklebust 424db6e0b7SFred Isaman rhdr = kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL); 434db6e0b7SFred Isaman if (rhdr) { 444db6e0b7SFred Isaman struct nfs_pgio_header *hdr = &rhdr->header; 45cd841605SFred Isaman 46cd841605SFred Isaman INIT_LIST_HEAD(&hdr->pages); 474db6e0b7SFred Isaman INIT_LIST_HEAD(&hdr->rpc_list); 484db6e0b7SFred Isaman spin_lock_init(&hdr->lock); 494db6e0b7SFred Isaman atomic_set(&hdr->refcnt, 0); 504db6e0b7SFred Isaman } 514db6e0b7SFred Isaman return rhdr; 524db6e0b7SFred Isaman } 534db6e0b7SFred Isaman 54584aa810SFred Isaman static struct nfs_read_data *nfs_readdata_alloc(struct nfs_pgio_header *hdr, 554db6e0b7SFred Isaman unsigned int pagecount) 564db6e0b7SFred Isaman { 574db6e0b7SFred Isaman struct nfs_read_data *data, *prealloc; 584db6e0b7SFred Isaman 594db6e0b7SFred Isaman prealloc = &container_of(hdr, struct nfs_read_header, header)->rpc_data; 604db6e0b7SFred Isaman if (prealloc->header == NULL) 614db6e0b7SFred Isaman data = prealloc; 624db6e0b7SFred Isaman else 634db6e0b7SFred Isaman data = kzalloc(sizeof(*data), GFP_KERNEL); 644db6e0b7SFred Isaman if (!data) 654db6e0b7SFred Isaman goto out; 664db6e0b7SFred Isaman 674db6e0b7SFred Isaman if (nfs_pgarray_set(&data->pages, pagecount)) { 68cd841605SFred Isaman data->header = hdr; 694db6e0b7SFred Isaman atomic_inc(&hdr->refcnt); 704db6e0b7SFred Isaman } else { 714db6e0b7SFred Isaman if (data != prealloc) 724db6e0b7SFred Isaman kfree(data); 734db6e0b7SFred Isaman data = NULL; 743feb2d49STrond Myklebust } 754db6e0b7SFred Isaman out: 764db6e0b7SFred Isaman return data; 773feb2d49STrond Myklebust } 783feb2d49STrond Myklebust 79cd841605SFred Isaman void nfs_readhdr_free(struct nfs_pgio_header *hdr) 803feb2d49STrond Myklebust { 81cd841605SFred Isaman struct nfs_read_header *rhdr = container_of(hdr, struct nfs_read_header, header); 82cd841605SFred Isaman 83cd841605SFred Isaman kmem_cache_free(nfs_rdata_cachep, rhdr); 843feb2d49STrond Myklebust } 853feb2d49STrond Myklebust 86493292ddSTrond Myklebust void nfs_readdata_release(struct nfs_read_data *rdata) 871da177e4SLinus Torvalds { 884db6e0b7SFred Isaman struct nfs_pgio_header *hdr = rdata->header; 894db6e0b7SFred Isaman struct nfs_read_header *read_header = container_of(hdr, struct nfs_read_header, header); 904db6e0b7SFred Isaman 91383ba719STrond Myklebust put_nfs_open_context(rdata->args.context); 9230dd374fSFred Isaman if (rdata->pages.pagevec != rdata->pages.page_array) 9330dd374fSFred Isaman kfree(rdata->pages.pagevec); 944db6e0b7SFred Isaman if (rdata != &read_header->rpc_data) 954db6e0b7SFred Isaman kfree(rdata); 964db6e0b7SFred Isaman else 974db6e0b7SFred Isaman rdata->header = NULL; 984db6e0b7SFred Isaman if (atomic_dec_and_test(&hdr->refcnt)) 99061ae2edSFred Isaman hdr->completion_ops->completion(hdr); 1001da177e4SLinus Torvalds } 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds static 1031da177e4SLinus Torvalds int nfs_return_empty_page(struct page *page) 1041da177e4SLinus Torvalds { 105eebd2aa3SChristoph Lameter zero_user(page, 0, PAGE_CACHE_SIZE); 1061da177e4SLinus Torvalds SetPageUptodate(page); 1071da177e4SLinus Torvalds unlock_page(page); 1081da177e4SLinus Torvalds return 0; 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 11162e4a769STrond Myklebust void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, 112061ae2edSFred Isaman struct inode *inode, 113061ae2edSFred Isaman const struct nfs_pgio_completion_ops *compl_ops) 1141751c363STrond Myklebust { 115061ae2edSFred Isaman nfs_pageio_init(pgio, inode, &nfs_pageio_read_ops, compl_ops, 1161751c363STrond Myklebust NFS_SERVER(inode)->rsize, 0); 1171751c363STrond Myklebust } 1181751c363STrond Myklebust 119493292ddSTrond Myklebust void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio) 120493292ddSTrond Myklebust { 121493292ddSTrond Myklebust pgio->pg_ops = &nfs_pageio_read_ops; 122493292ddSTrond Myklebust pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->rsize; 123493292ddSTrond Myklebust } 1241f945357STrond Myklebust EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); 125493292ddSTrond Myklebust 126584aa810SFred Isaman void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, 127061ae2edSFred Isaman struct inode *inode, 128061ae2edSFred Isaman const struct nfs_pgio_completion_ops *compl_ops) 1291751c363STrond Myklebust { 130061ae2edSFred Isaman if (!pnfs_pageio_init_read(pgio, inode, compl_ops)) 131061ae2edSFred Isaman nfs_pageio_init_read_mds(pgio, inode, compl_ops); 1321751c363STrond Myklebust } 1331751c363STrond Myklebust 134f42b293dSDavid Howells int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, 1351da177e4SLinus Torvalds struct page *page) 1361da177e4SLinus Torvalds { 1371da177e4SLinus Torvalds struct nfs_page *new; 1381da177e4SLinus Torvalds unsigned int len; 139c76069bdSFred Isaman struct nfs_pageio_descriptor pgio; 1401da177e4SLinus Torvalds 14149a70f27STrond Myklebust len = nfs_page_length(page); 1421da177e4SLinus Torvalds if (len == 0) 1431da177e4SLinus Torvalds return nfs_return_empty_page(page); 1441da177e4SLinus Torvalds new = nfs_create_request(ctx, inode, page, 0, len); 1451da177e4SLinus Torvalds if (IS_ERR(new)) { 1461da177e4SLinus Torvalds unlock_page(page); 1471da177e4SLinus Torvalds return PTR_ERR(new); 1481da177e4SLinus Torvalds } 1491da177e4SLinus Torvalds if (len < PAGE_CACHE_SIZE) 150eebd2aa3SChristoph Lameter zero_user_segment(page, len, PAGE_CACHE_SIZE); 1511da177e4SLinus Torvalds 152061ae2edSFred Isaman nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops); 153d8007d4dSTrond Myklebust nfs_pageio_add_request(&pgio, new); 1541751c363STrond Myklebust nfs_pageio_complete(&pgio); 1551da177e4SLinus Torvalds return 0; 1561da177e4SLinus Torvalds } 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds static void nfs_readpage_release(struct nfs_page *req) 1591da177e4SLinus Torvalds { 1603d4ff43dSAl Viro struct inode *d_inode = req->wb_context->dentry->d_inode; 1617f8e05f6SDavid Howells 1627f8e05f6SDavid Howells if (PageUptodate(req->wb_page)) 1637f8e05f6SDavid Howells nfs_readpage_to_fscache(d_inode, req->wb_page, 0); 1647f8e05f6SDavid Howells 1651da177e4SLinus Torvalds unlock_page(req->wb_page); 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds dprintk("NFS: read done (%s/%Ld %d@%Ld)\n", 1683d4ff43dSAl Viro req->wb_context->dentry->d_inode->i_sb->s_id, 1693d4ff43dSAl Viro (long long)NFS_FILEID(req->wb_context->dentry->d_inode), 1701da177e4SLinus Torvalds req->wb_bytes, 1711da177e4SLinus Torvalds (long long)req_offset(req)); 17210d2c46fSNick Wilson nfs_release_request(req); 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds 1754db6e0b7SFred Isaman /* Note io was page aligned */ 176061ae2edSFred Isaman static void nfs_read_completion(struct nfs_pgio_header *hdr) 1774db6e0b7SFred Isaman { 1784db6e0b7SFred Isaman unsigned long bytes = 0; 1794db6e0b7SFred Isaman 1804db6e0b7SFred Isaman if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) 1814db6e0b7SFred Isaman goto out; 1824db6e0b7SFred Isaman while (!list_empty(&hdr->pages)) { 1834db6e0b7SFred Isaman struct nfs_page *req = nfs_list_entry(hdr->pages.next); 1844db6e0b7SFred Isaman struct page *page = req->wb_page; 1854db6e0b7SFred Isaman 1864db6e0b7SFred Isaman if (test_bit(NFS_IOHDR_EOF, &hdr->flags)) { 1874db6e0b7SFred Isaman if (bytes > hdr->good_bytes) 1884db6e0b7SFred Isaman zero_user(page, 0, PAGE_SIZE); 1894db6e0b7SFred Isaman else if (hdr->good_bytes - bytes < PAGE_SIZE) 1904db6e0b7SFred Isaman zero_user_segment(page, 1914db6e0b7SFred Isaman hdr->good_bytes & ~PAGE_MASK, 1924db6e0b7SFred Isaman PAGE_SIZE); 1934db6e0b7SFred Isaman } 194*4bd8b010STrond Myklebust bytes += req->wb_bytes; 195*4bd8b010STrond Myklebust if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) { 196*4bd8b010STrond Myklebust if (bytes <= hdr->good_bytes) 197*4bd8b010STrond Myklebust SetPageUptodate(page); 198*4bd8b010STrond Myklebust } else 1994db6e0b7SFred Isaman SetPageUptodate(page); 2004db6e0b7SFred Isaman nfs_list_remove_request(req); 2014db6e0b7SFred Isaman nfs_readpage_release(req); 2024db6e0b7SFred Isaman } 2034db6e0b7SFred Isaman out: 2044db6e0b7SFred Isaman hdr->release(hdr); 2054db6e0b7SFred Isaman } 2064db6e0b7SFred Isaman 207c5996c4eSFred Isaman int nfs_initiate_read(struct rpc_clnt *clnt, 208c5996c4eSFred Isaman struct nfs_read_data *data, 20964419a9bSAndy Adamson const struct rpc_call_ops *call_ops) 21064419a9bSAndy Adamson { 211cd841605SFred Isaman struct inode *inode = data->header->inode; 21264419a9bSAndy Adamson int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0; 21364419a9bSAndy Adamson struct rpc_task *task; 21464419a9bSAndy Adamson struct rpc_message msg = { 21564419a9bSAndy Adamson .rpc_argp = &data->args, 21664419a9bSAndy Adamson .rpc_resp = &data->res, 217cd841605SFred Isaman .rpc_cred = data->header->cred, 21864419a9bSAndy Adamson }; 21964419a9bSAndy Adamson struct rpc_task_setup task_setup_data = { 22064419a9bSAndy Adamson .task = &data->task, 22164419a9bSAndy Adamson .rpc_client = clnt, 22264419a9bSAndy Adamson .rpc_message = &msg, 22364419a9bSAndy Adamson .callback_ops = call_ops, 22464419a9bSAndy Adamson .callback_data = data, 22564419a9bSAndy Adamson .workqueue = nfsiod_workqueue, 22664419a9bSAndy Adamson .flags = RPC_TASK_ASYNC | swap_flags, 22764419a9bSAndy Adamson }; 22864419a9bSAndy Adamson 22964419a9bSAndy Adamson /* Set up the initial task struct. */ 23064419a9bSAndy Adamson NFS_PROTO(inode)->read_setup(data, &msg); 23164419a9bSAndy Adamson 23264419a9bSAndy Adamson dprintk("NFS: %5u initiated read call (req %s/%lld, %u bytes @ " 23364419a9bSAndy Adamson "offset %llu)\n", 23464419a9bSAndy Adamson data->task.tk_pid, 23564419a9bSAndy Adamson inode->i_sb->s_id, 23664419a9bSAndy Adamson (long long)NFS_FILEID(inode), 23764419a9bSAndy Adamson data->args.count, 23864419a9bSAndy Adamson (unsigned long long)data->args.offset); 23964419a9bSAndy Adamson 24064419a9bSAndy Adamson task = rpc_run_task(&task_setup_data); 24164419a9bSAndy Adamson if (IS_ERR(task)) 24264419a9bSAndy Adamson return PTR_ERR(task); 24364419a9bSAndy Adamson rpc_put_task(task); 24464419a9bSAndy Adamson return 0; 24564419a9bSAndy Adamson } 246dc70d7b3SAndy Adamson EXPORT_SYMBOL_GPL(nfs_initiate_read); 24764419a9bSAndy Adamson 2481da177e4SLinus Torvalds /* 2491da177e4SLinus Torvalds * Set up the NFS read request struct 2501da177e4SLinus Torvalds */ 2514db6e0b7SFred Isaman static void nfs_read_rpcsetup(struct nfs_read_data *data, 2526e4efd56STrond Myklebust unsigned int count, unsigned int offset) 2531da177e4SLinus Torvalds { 2544db6e0b7SFred Isaman struct nfs_page *req = data->header->req; 2551da177e4SLinus Torvalds 2564db6e0b7SFred Isaman data->args.fh = NFS_FH(data->header->inode); 2571da177e4SLinus Torvalds data->args.offset = req_offset(req) + offset; 2581da177e4SLinus Torvalds data->args.pgbase = req->wb_pgbase + offset; 25930dd374fSFred Isaman data->args.pages = data->pages.pagevec; 2601da177e4SLinus Torvalds data->args.count = count; 261383ba719STrond Myklebust data->args.context = get_nfs_open_context(req->wb_context); 262f11ac8dbSTrond Myklebust data->args.lock_context = req->wb_lock_context; 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds data->res.fattr = &data->fattr; 2651da177e4SLinus Torvalds data->res.count = count; 2661da177e4SLinus Torvalds data->res.eof = 0; 2670e574af1STrond Myklebust nfs_fattr_init(&data->fattr); 2686e4efd56STrond Myklebust } 2691da177e4SLinus Torvalds 2706e4efd56STrond Myklebust static int nfs_do_read(struct nfs_read_data *data, 271493292ddSTrond Myklebust const struct rpc_call_ops *call_ops) 2726e4efd56STrond Myklebust { 273cd841605SFred Isaman struct inode *inode = data->header->inode; 2746e4efd56STrond Myklebust 275c5996c4eSFred Isaman return nfs_initiate_read(NFS_CLIENT(inode), data, call_ops); 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds 278275acaafSTrond Myklebust static int 279275acaafSTrond Myklebust nfs_do_multiple_reads(struct list_head *head, 280493292ddSTrond Myklebust const struct rpc_call_ops *call_ops) 281275acaafSTrond Myklebust { 282275acaafSTrond Myklebust struct nfs_read_data *data; 283275acaafSTrond Myklebust int ret = 0; 284275acaafSTrond Myklebust 285275acaafSTrond Myklebust while (!list_empty(head)) { 286275acaafSTrond Myklebust int ret2; 287275acaafSTrond Myklebust 2884db6e0b7SFred Isaman data = list_first_entry(head, struct nfs_read_data, list); 289275acaafSTrond Myklebust list_del_init(&data->list); 290275acaafSTrond Myklebust 291493292ddSTrond Myklebust ret2 = nfs_do_read(data, call_ops); 292275acaafSTrond Myklebust if (ret == 0) 293275acaafSTrond Myklebust ret = ret2; 294275acaafSTrond Myklebust } 295275acaafSTrond Myklebust return ret; 296275acaafSTrond Myklebust } 297275acaafSTrond Myklebust 298061ae2edSFred Isaman static void 2991da177e4SLinus Torvalds nfs_async_read_error(struct list_head *head) 3001da177e4SLinus Torvalds { 3011da177e4SLinus Torvalds struct nfs_page *req; 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds while (!list_empty(head)) { 3041da177e4SLinus Torvalds req = nfs_list_entry(head->next); 3051da177e4SLinus Torvalds nfs_list_remove_request(req); 3061da177e4SLinus Torvalds nfs_readpage_release(req); 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds 310061ae2edSFred Isaman static const struct nfs_pgio_completion_ops nfs_async_read_completion_ops = { 311061ae2edSFred Isaman .error_cleanup = nfs_async_read_error, 312061ae2edSFred Isaman .completion = nfs_read_completion, 313061ae2edSFred Isaman }; 314061ae2edSFred Isaman 31525b11dcdSTrond Myklebust static void nfs_pagein_error(struct nfs_pageio_descriptor *desc, 31625b11dcdSTrond Myklebust struct nfs_pgio_header *hdr) 31725b11dcdSTrond Myklebust { 31825b11dcdSTrond Myklebust set_bit(NFS_IOHDR_REDO, &hdr->flags); 31925b11dcdSTrond Myklebust while (!list_empty(&hdr->rpc_list)) { 32025b11dcdSTrond Myklebust struct nfs_read_data *data = list_first_entry(&hdr->rpc_list, 32125b11dcdSTrond Myklebust struct nfs_read_data, list); 32225b11dcdSTrond Myklebust list_del(&data->list); 32325b11dcdSTrond Myklebust nfs_readdata_release(data); 32425b11dcdSTrond Myklebust } 32525b11dcdSTrond Myklebust desc->pg_completion_ops->error_cleanup(&desc->pg_list); 32625b11dcdSTrond Myklebust } 32725b11dcdSTrond Myklebust 3281da177e4SLinus Torvalds /* 3291da177e4SLinus Torvalds * Generate multiple requests to fill a single page. 3301da177e4SLinus Torvalds * 3311da177e4SLinus Torvalds * We optimize to reduce the number of read operations on the wire. If we 3321da177e4SLinus Torvalds * detect that we're reading a page, or an area of a page, that is past the 3331da177e4SLinus Torvalds * end of file, we do not generate NFS read operations but just clear the 3341da177e4SLinus Torvalds * parts of the page that would have come back zero from the server anyway. 3351da177e4SLinus Torvalds * 3361da177e4SLinus Torvalds * We rely on the cached value of i_size to make this determination; another 3371da177e4SLinus Torvalds * client can fill pages on the server past our cached end-of-file, but we 3381da177e4SLinus Torvalds * won't see the new data until our attribute cache is updated. This is more 3391da177e4SLinus Torvalds * or less conventional NFS client behavior. 3401da177e4SLinus Torvalds */ 3414db6e0b7SFred Isaman static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc, 3424db6e0b7SFred Isaman struct nfs_pgio_header *hdr) 3431da177e4SLinus Torvalds { 3444db6e0b7SFred Isaman struct nfs_page *req = hdr->req; 3451da177e4SLinus Torvalds struct page *page = req->wb_page; 3461da177e4SLinus Torvalds struct nfs_read_data *data; 347d097971dSTrond Myklebust size_t rsize = desc->pg_bsize, nbytes; 348e9f7bee1STrond Myklebust unsigned int offset; 3491da177e4SLinus Torvalds 350275acaafSTrond Myklebust offset = 0; 351c76069bdSFred Isaman nbytes = desc->pg_count; 352e9f7bee1STrond Myklebust do { 353e9f7bee1STrond Myklebust size_t len = min(nbytes,rsize); 354e9f7bee1STrond Myklebust 3554db6e0b7SFred Isaman data = nfs_readdata_alloc(hdr, 1); 35625b11dcdSTrond Myklebust if (!data) { 35725b11dcdSTrond Myklebust nfs_pagein_error(desc, hdr); 35825b11dcdSTrond Myklebust return -ENOMEM; 35925b11dcdSTrond Myklebust } 36030dd374fSFred Isaman data->pages.pagevec[0] = page; 3614db6e0b7SFred Isaman nfs_read_rpcsetup(data, len, offset); 3624db6e0b7SFred Isaman list_add(&data->list, &hdr->rpc_list); 363e9f7bee1STrond Myklebust nbytes -= len; 364275acaafSTrond Myklebust offset += len; 365e9f7bee1STrond Myklebust } while (nbytes != 0); 36625b11dcdSTrond Myklebust 36725b11dcdSTrond Myklebust nfs_list_remove_request(req); 36825b11dcdSTrond Myklebust nfs_list_add_request(req, &hdr->pages); 3694db6e0b7SFred Isaman desc->pg_rpc_callops = &nfs_read_common_ops; 3709146ab50STrond Myklebust return 0; 3711da177e4SLinus Torvalds } 3721da177e4SLinus Torvalds 3734db6e0b7SFred Isaman static int nfs_pagein_one(struct nfs_pageio_descriptor *desc, 3744db6e0b7SFred Isaman struct nfs_pgio_header *hdr) 3751da177e4SLinus Torvalds { 3761da177e4SLinus Torvalds struct nfs_page *req; 3771da177e4SLinus Torvalds struct page **pages; 3781da177e4SLinus Torvalds struct nfs_read_data *data; 379c76069bdSFred Isaman struct list_head *head = &desc->pg_list; 3801da177e4SLinus Torvalds 3814db6e0b7SFred Isaman data = nfs_readdata_alloc(hdr, nfs_page_array_len(desc->pg_base, 382c76069bdSFred Isaman desc->pg_count)); 3834db6e0b7SFred Isaman if (!data) { 38425b11dcdSTrond Myklebust nfs_pagein_error(desc, hdr); 3859146ab50STrond Myklebust return -ENOMEM; 386bae724efSFred Isaman } 3871da177e4SLinus Torvalds 38830dd374fSFred Isaman pages = data->pages.pagevec; 3891da177e4SLinus Torvalds while (!list_empty(head)) { 3901da177e4SLinus Torvalds req = nfs_list_entry(head->next); 3911da177e4SLinus Torvalds nfs_list_remove_request(req); 3924db6e0b7SFred Isaman nfs_list_add_request(req, &hdr->pages); 3931da177e4SLinus Torvalds *pages++ = req->wb_page; 3941da177e4SLinus Torvalds } 3951da177e4SLinus Torvalds 3964db6e0b7SFred Isaman nfs_read_rpcsetup(data, desc->pg_count, 0); 3974db6e0b7SFred Isaman list_add(&data->list, &hdr->rpc_list); 3984db6e0b7SFred Isaman desc->pg_rpc_callops = &nfs_read_common_ops; 3999146ab50STrond Myklebust return 0; 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds 4024db6e0b7SFred Isaman int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, 4034db6e0b7SFred Isaman struct nfs_pgio_header *hdr) 404493292ddSTrond Myklebust { 405493292ddSTrond Myklebust if (desc->pg_bsize < PAGE_CACHE_SIZE) 4064db6e0b7SFred Isaman return nfs_pagein_multi(desc, hdr); 4074db6e0b7SFred Isaman return nfs_pagein_one(desc, hdr); 408493292ddSTrond Myklebust } 409493292ddSTrond Myklebust 410493292ddSTrond Myklebust static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) 4111751c363STrond Myklebust { 4124db6e0b7SFred Isaman struct nfs_read_header *rhdr; 4134db6e0b7SFred Isaman struct nfs_pgio_header *hdr; 414275acaafSTrond Myklebust int ret; 415275acaafSTrond Myklebust 4164db6e0b7SFred Isaman rhdr = nfs_readhdr_alloc(); 4174db6e0b7SFred Isaman if (!rhdr) { 418061ae2edSFred Isaman desc->pg_completion_ops->error_cleanup(&desc->pg_list); 4194db6e0b7SFred Isaman return -ENOMEM; 4204db6e0b7SFred Isaman } 4214db6e0b7SFred Isaman hdr = &rhdr->header; 4224db6e0b7SFred Isaman nfs_pgheader_init(desc, hdr, nfs_readhdr_free); 4234db6e0b7SFred Isaman atomic_inc(&hdr->refcnt); 4244db6e0b7SFred Isaman ret = nfs_generic_pagein(desc, hdr); 425275acaafSTrond Myklebust if (ret == 0) 4264db6e0b7SFred Isaman ret = nfs_do_multiple_reads(&hdr->rpc_list, 4274db6e0b7SFred Isaman desc->pg_rpc_callops); 4284db6e0b7SFred Isaman if (atomic_dec_and_test(&hdr->refcnt)) 429061ae2edSFred Isaman hdr->completion_ops->completion(hdr); 430275acaafSTrond Myklebust return ret; 4311751c363STrond Myklebust } 4321751c363STrond Myklebust 4331751c363STrond Myklebust static const struct nfs_pageio_ops nfs_pageio_read_ops = { 4341751c363STrond Myklebust .pg_test = nfs_generic_pg_test, 4351751c363STrond Myklebust .pg_doio = nfs_generic_pg_readpages, 4361751c363STrond Myklebust }; 4371751c363STrond Myklebust 4381da177e4SLinus Torvalds /* 4390b671301STrond Myklebust * This is the callback from RPC telling us whether a reply was 4400b671301STrond Myklebust * received or some error occurred (timeout or socket shutdown). 4410b671301STrond Myklebust */ 4420b671301STrond Myklebust int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) 4430b671301STrond Myklebust { 444cd841605SFred Isaman struct inode *inode = data->header->inode; 4450b671301STrond Myklebust int status; 4460b671301STrond Myklebust 4473110ff80SHarvey Harrison dprintk("NFS: %s: %5u, (status %d)\n", __func__, task->tk_pid, 4480b671301STrond Myklebust task->tk_status); 4490b671301STrond Myklebust 450cd841605SFred Isaman status = NFS_PROTO(inode)->read_done(task, data); 4510b671301STrond Myklebust if (status != 0) 4520b671301STrond Myklebust return status; 4530b671301STrond Myklebust 454cd841605SFred Isaman nfs_add_stats(inode, NFSIOS_SERVERREADBYTES, data->res.count); 4550b671301STrond Myklebust 4560b671301STrond Myklebust if (task->tk_status == -ESTALE) { 457cd841605SFred Isaman set_bit(NFS_INO_STALE, &NFS_I(inode)->flags); 458cd841605SFred Isaman nfs_mark_for_revalidate(inode); 4590b671301STrond Myklebust } 4600b671301STrond Myklebust return 0; 4610b671301STrond Myklebust } 4620b671301STrond Myklebust 463fdd1e74cSTrond Myklebust static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data) 4640b671301STrond Myklebust { 4650b671301STrond Myklebust struct nfs_readargs *argp = &data->args; 4660b671301STrond Myklebust struct nfs_readres *resp = &data->res; 4670b671301STrond Myklebust 4680b671301STrond Myklebust /* This is a short read! */ 469cd841605SFred Isaman nfs_inc_stats(data->header->inode, NFSIOS_SHORTREAD); 4700b671301STrond Myklebust /* Has the server at least made some progress? */ 4714db6e0b7SFred Isaman if (resp->count == 0) { 4724db6e0b7SFred Isaman nfs_set_pgio_error(data->header, -EIO, argp->offset); 473d61e612aSTrond Myklebust return; 4744db6e0b7SFred Isaman } 4750b671301STrond Myklebust /* Yes, so retry the read at the end of the data */ 476cbdabc7fSAndy Adamson data->mds_offset += resp->count; 4770b671301STrond Myklebust argp->offset += resp->count; 4780b671301STrond Myklebust argp->pgbase += resp->count; 4790b671301STrond Myklebust argp->count -= resp->count; 480d00c5d43STrond Myklebust rpc_restart_call_prepare(task); 4810b671301STrond Myklebust } 4820b671301STrond Myklebust 4834db6e0b7SFred Isaman static void nfs_readpage_result_common(struct rpc_task *task, void *calldata) 4841da177e4SLinus Torvalds { 485ec06c096STrond Myklebust struct nfs_read_data *data = calldata; 4864db6e0b7SFred Isaman struct nfs_pgio_header *hdr = data->header; 4871da177e4SLinus Torvalds 4884db6e0b7SFred Isaman /* Note the only returns of nfs_readpage_result are 0 and -EAGAIN */ 489ec06c096STrond Myklebust if (nfs_readpage_result(task, data) != 0) 490ec06c096STrond Myklebust return; 491fdd1e74cSTrond Myklebust if (task->tk_status < 0) 4924db6e0b7SFred Isaman nfs_set_pgio_error(hdr, task->tk_status, data->args.offset); 4934db6e0b7SFred Isaman else if (data->res.eof) { 4944db6e0b7SFred Isaman loff_t bound; 495fdd1e74cSTrond Myklebust 4964db6e0b7SFred Isaman bound = data->args.offset + data->res.count; 4974db6e0b7SFred Isaman spin_lock(&hdr->lock); 4984db6e0b7SFred Isaman if (bound < hdr->io_start + hdr->good_bytes) { 4994db6e0b7SFred Isaman set_bit(NFS_IOHDR_EOF, &hdr->flags); 5004db6e0b7SFred Isaman clear_bit(NFS_IOHDR_ERROR, &hdr->flags); 5014db6e0b7SFred Isaman hdr->good_bytes = bound - hdr->io_start; 5024db6e0b7SFred Isaman } 5034db6e0b7SFred Isaman spin_unlock(&hdr->lock); 5044db6e0b7SFred Isaman } else if (data->res.count != data->args.count) 505fdd1e74cSTrond Myklebust nfs_readpage_retry(task, data); 5060b671301STrond Myklebust } 507fdd1e74cSTrond Myklebust 5084db6e0b7SFred Isaman static void nfs_readpage_release_common(void *calldata) 509fdd1e74cSTrond Myklebust { 5104db6e0b7SFred Isaman nfs_readdata_release(calldata); 5111da177e4SLinus Torvalds } 5121da177e4SLinus Torvalds 513f11c88afSAndy Adamson void nfs_read_prepare(struct rpc_task *task, void *calldata) 514f11c88afSAndy Adamson { 515f11c88afSAndy Adamson struct nfs_read_data *data = calldata; 516cd841605SFred Isaman NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data); 517f11c88afSAndy Adamson } 518f11c88afSAndy Adamson 5194db6e0b7SFred Isaman static const struct rpc_call_ops nfs_read_common_ops = { 520f11c88afSAndy Adamson .rpc_call_prepare = nfs_read_prepare, 5214db6e0b7SFred Isaman .rpc_call_done = nfs_readpage_result_common, 5224db6e0b7SFred Isaman .rpc_release = nfs_readpage_release_common, 523ec06c096STrond Myklebust }; 524ec06c096STrond Myklebust 5251da177e4SLinus Torvalds /* 5261da177e4SLinus Torvalds * Read a page over NFS. 5271da177e4SLinus Torvalds * We read the page synchronously in the following case: 5281da177e4SLinus Torvalds * - The error flag is set for this page. This happens only when a 5291da177e4SLinus Torvalds * previous async read operation failed. 5301da177e4SLinus Torvalds */ 5311da177e4SLinus Torvalds int nfs_readpage(struct file *file, struct page *page) 5321da177e4SLinus Torvalds { 5331da177e4SLinus Torvalds struct nfs_open_context *ctx; 5341da177e4SLinus Torvalds struct inode *inode = page->mapping->host; 5351da177e4SLinus Torvalds int error; 5361da177e4SLinus Torvalds 5371da177e4SLinus Torvalds dprintk("NFS: nfs_readpage (%p %ld@%lu)\n", 5381da177e4SLinus Torvalds page, PAGE_CACHE_SIZE, page->index); 53991d5b470SChuck Lever nfs_inc_stats(inode, NFSIOS_VFSREADPAGE); 54091d5b470SChuck Lever nfs_add_stats(inode, NFSIOS_READPAGES, 1); 54191d5b470SChuck Lever 5421da177e4SLinus Torvalds /* 5431da177e4SLinus Torvalds * Try to flush any pending writes to the file.. 5441da177e4SLinus Torvalds * 5451da177e4SLinus Torvalds * NOTE! Because we own the page lock, there cannot 5461da177e4SLinus Torvalds * be any new pending writes generated at this point 5471da177e4SLinus Torvalds * for this page (other pages can be written to). 5481da177e4SLinus Torvalds */ 5491da177e4SLinus Torvalds error = nfs_wb_page(inode, page); 5501da177e4SLinus Torvalds if (error) 551de05a0ccSTrond Myklebust goto out_unlock; 552de05a0ccSTrond Myklebust if (PageUptodate(page)) 553de05a0ccSTrond Myklebust goto out_unlock; 5541da177e4SLinus Torvalds 5555f004cf2STrond Myklebust error = -ESTALE; 5565f004cf2STrond Myklebust if (NFS_STALE(inode)) 557de05a0ccSTrond Myklebust goto out_unlock; 5585f004cf2STrond Myklebust 5591da177e4SLinus Torvalds if (file == NULL) { 560cf1308ffSTrond Myklebust error = -EBADF; 561d530838bSTrond Myklebust ctx = nfs_find_open_context(inode, NULL, FMODE_READ); 5621da177e4SLinus Torvalds if (ctx == NULL) 563de05a0ccSTrond Myklebust goto out_unlock; 5641da177e4SLinus Torvalds } else 565cd3758e3STrond Myklebust ctx = get_nfs_open_context(nfs_file_open_context(file)); 5661da177e4SLinus Torvalds 5679a9fc1c0SDavid Howells if (!IS_SYNC(inode)) { 5689a9fc1c0SDavid Howells error = nfs_readpage_from_fscache(ctx, inode, page); 5699a9fc1c0SDavid Howells if (error == 0) 5709a9fc1c0SDavid Howells goto out; 5719a9fc1c0SDavid Howells } 5729a9fc1c0SDavid Howells 5738e0969f0STrond Myklebust error = nfs_readpage_async(ctx, inode, page); 5748e0969f0STrond Myklebust 5759a9fc1c0SDavid Howells out: 5761da177e4SLinus Torvalds put_nfs_open_context(ctx); 5771da177e4SLinus Torvalds return error; 578de05a0ccSTrond Myklebust out_unlock: 5791da177e4SLinus Torvalds unlock_page(page); 5801da177e4SLinus Torvalds return error; 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds struct nfs_readdesc { 5848b09bee3STrond Myklebust struct nfs_pageio_descriptor *pgio; 5851da177e4SLinus Torvalds struct nfs_open_context *ctx; 5861da177e4SLinus Torvalds }; 5871da177e4SLinus Torvalds 5881da177e4SLinus Torvalds static int 5891da177e4SLinus Torvalds readpage_async_filler(void *data, struct page *page) 5901da177e4SLinus Torvalds { 5911da177e4SLinus Torvalds struct nfs_readdesc *desc = (struct nfs_readdesc *)data; 5921da177e4SLinus Torvalds struct inode *inode = page->mapping->host; 5931da177e4SLinus Torvalds struct nfs_page *new; 5941da177e4SLinus Torvalds unsigned int len; 595de05a0ccSTrond Myklebust int error; 5961da177e4SLinus Torvalds 59749a70f27STrond Myklebust len = nfs_page_length(page); 5981da177e4SLinus Torvalds if (len == 0) 5991da177e4SLinus Torvalds return nfs_return_empty_page(page); 600de05a0ccSTrond Myklebust 6011da177e4SLinus Torvalds new = nfs_create_request(desc->ctx, inode, page, 0, len); 602de05a0ccSTrond Myklebust if (IS_ERR(new)) 603de05a0ccSTrond Myklebust goto out_error; 604de05a0ccSTrond Myklebust 6051da177e4SLinus Torvalds if (len < PAGE_CACHE_SIZE) 606eebd2aa3SChristoph Lameter zero_user_segment(page, len, PAGE_CACHE_SIZE); 607f8512ad0SFred Isaman if (!nfs_pageio_add_request(desc->pgio, new)) { 608f8512ad0SFred Isaman error = desc->pgio->pg_error; 609f8512ad0SFred Isaman goto out_unlock; 610f8512ad0SFred Isaman } 6111da177e4SLinus Torvalds return 0; 612de05a0ccSTrond Myklebust out_error: 613de05a0ccSTrond Myklebust error = PTR_ERR(new); 614de05a0ccSTrond Myklebust out_unlock: 615de05a0ccSTrond Myklebust unlock_page(page); 616de05a0ccSTrond Myklebust return error; 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds int nfs_readpages(struct file *filp, struct address_space *mapping, 6201da177e4SLinus Torvalds struct list_head *pages, unsigned nr_pages) 6211da177e4SLinus Torvalds { 6228b09bee3STrond Myklebust struct nfs_pageio_descriptor pgio; 6231da177e4SLinus Torvalds struct nfs_readdesc desc = { 6248b09bee3STrond Myklebust .pgio = &pgio, 6251da177e4SLinus Torvalds }; 6261da177e4SLinus Torvalds struct inode *inode = mapping->host; 6278b09bee3STrond Myklebust unsigned long npages; 6285f004cf2STrond Myklebust int ret = -ESTALE; 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds dprintk("NFS: nfs_readpages (%s/%Ld %d)\n", 6311da177e4SLinus Torvalds inode->i_sb->s_id, 6321da177e4SLinus Torvalds (long long)NFS_FILEID(inode), 6331da177e4SLinus Torvalds nr_pages); 63491d5b470SChuck Lever nfs_inc_stats(inode, NFSIOS_VFSREADPAGES); 6351da177e4SLinus Torvalds 6365f004cf2STrond Myklebust if (NFS_STALE(inode)) 6375f004cf2STrond Myklebust goto out; 6385f004cf2STrond Myklebust 6391da177e4SLinus Torvalds if (filp == NULL) { 640d530838bSTrond Myklebust desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ); 6411da177e4SLinus Torvalds if (desc.ctx == NULL) 6421da177e4SLinus Torvalds return -EBADF; 6431da177e4SLinus Torvalds } else 644cd3758e3STrond Myklebust desc.ctx = get_nfs_open_context(nfs_file_open_context(filp)); 6459a9fc1c0SDavid Howells 6469a9fc1c0SDavid Howells /* attempt to read as many of the pages as possible from the cache 6479a9fc1c0SDavid Howells * - this returns -ENOBUFS immediately if the cookie is negative 6489a9fc1c0SDavid Howells */ 6499a9fc1c0SDavid Howells ret = nfs_readpages_from_fscache(desc.ctx, inode, mapping, 6509a9fc1c0SDavid Howells pages, &nr_pages); 6519a9fc1c0SDavid Howells if (ret == 0) 6529a9fc1c0SDavid Howells goto read_complete; /* all pages were read */ 6539a9fc1c0SDavid Howells 654061ae2edSFred Isaman nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops); 6558b09bee3STrond Myklebust 6561da177e4SLinus Torvalds ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); 6578b09bee3STrond Myklebust 6588b09bee3STrond Myklebust nfs_pageio_complete(&pgio); 6598b09bee3STrond Myklebust npages = (pgio.pg_bytes_written + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 6608b09bee3STrond Myklebust nfs_add_stats(inode, NFSIOS_READPAGES, npages); 6619a9fc1c0SDavid Howells read_complete: 6621da177e4SLinus Torvalds put_nfs_open_context(desc.ctx); 6635f004cf2STrond Myklebust out: 6641da177e4SLinus Torvalds return ret; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 667f7b422b1SDavid Howells int __init nfs_init_readpagecache(void) 6681da177e4SLinus Torvalds { 6691da177e4SLinus Torvalds nfs_rdata_cachep = kmem_cache_create("nfs_read_data", 670cd841605SFred Isaman sizeof(struct nfs_read_header), 6711da177e4SLinus Torvalds 0, SLAB_HWCACHE_ALIGN, 67220c2df83SPaul Mundt NULL); 6731da177e4SLinus Torvalds if (nfs_rdata_cachep == NULL) 6741da177e4SLinus Torvalds return -ENOMEM; 6751da177e4SLinus Torvalds 6761da177e4SLinus Torvalds return 0; 6771da177e4SLinus Torvalds } 6781da177e4SLinus Torvalds 679266bee88SDavid Brownell void nfs_destroy_readpagecache(void) 6801da177e4SLinus Torvalds { 6811a1d92c1SAlexey Dobriyan kmem_cache_destroy(nfs_rdata_cachep); 6821da177e4SLinus Torvalds } 683