📄 read.c
字号:
/* * linux/fs/nfs/read.c * * Block I/O for NFS * * Partial copy of Linus' read cache modifications to fs/nfs/file.c * modified for async RPC by okir@monad.swb.de */#include <linux/time.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/fcntl.h>#include <linux/stat.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/pagemap.h>#include <linux/sunrpc/clnt.h>#include <linux/nfs_fs.h>#include <linux/nfs_page.h>#include <linux/smp_lock.h>#include <asm/system.h>#include "internal.h"#include "iostat.h"#define NFSDBG_FACILITY NFSDBG_PAGECACHEstatic int nfs_pagein_multi(struct inode *, struct list_head *, unsigned int, size_t, int);static int nfs_pagein_one(struct inode *, struct list_head *, unsigned int, size_t, int);static const struct rpc_call_ops nfs_read_partial_ops;static const struct rpc_call_ops nfs_read_full_ops;static struct kmem_cache *nfs_rdata_cachep;static mempool_t *nfs_rdata_mempool;#define MIN_POOL_READ (32)struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount){ struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_NOFS); if (p) { memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->pages); p->npages = pagecount; if (pagecount <= ARRAY_SIZE(p->page_array)) p->pagevec = p->page_array; else { p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); if (!p->pagevec) { mempool_free(p, nfs_rdata_mempool); p = NULL; } } } return p;}static void nfs_readdata_rcu_free(struct rcu_head *head){ struct nfs_read_data *p = container_of(head, struct nfs_read_data, task.u.tk_rcu); if (p && (p->pagevec != &p->page_array[0])) kfree(p->pagevec); mempool_free(p, nfs_rdata_mempool);}static void nfs_readdata_free(struct nfs_read_data *rdata){ call_rcu_bh(&rdata->task.u.tk_rcu, nfs_readdata_rcu_free);}void nfs_readdata_release(void *data){ nfs_readdata_free(data);}staticint nfs_return_empty_page(struct page *page){ zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0); SetPageUptodate(page); unlock_page(page); return 0;}static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data){ unsigned int remainder = data->args.count - data->res.count; unsigned int base = data->args.pgbase + data->res.count; unsigned int pglen; struct page **pages; if (data->res.eof == 0 || remainder == 0) return; /* * Note: "remainder" can never be negative, since we check for * this in the XDR code. */ pages = &data->args.pages[base >> PAGE_CACHE_SHIFT]; base &= ~PAGE_CACHE_MASK; pglen = PAGE_CACHE_SIZE - base; for (;;) { if (remainder <= pglen) { zero_user_page(*pages, base, remainder, KM_USER0); break; } zero_user_page(*pages, base, pglen, KM_USER0); pages++; remainder -= pglen; pglen = PAGE_CACHE_SIZE; base = 0; }}static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, struct page *page){ LIST_HEAD(one_request); struct nfs_page *new; unsigned int len; len = nfs_page_length(page); if (len == 0) return nfs_return_empty_page(page); new = nfs_create_request(ctx, inode, page, 0, len); if (IS_ERR(new)) { unlock_page(page); return PTR_ERR(new); } if (len < PAGE_CACHE_SIZE) zero_user_page(page, len, PAGE_CACHE_SIZE - len, KM_USER0); nfs_list_add_request(new, &one_request); if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE) nfs_pagein_multi(inode, &one_request, 1, len, 0); else nfs_pagein_one(inode, &one_request, 1, len, 0); return 0;}static void nfs_readpage_release(struct nfs_page *req){ unlock_page(req->wb_page); dprintk("NFS: read done (%s/%Ld %d@%Ld)\n", req->wb_context->path.dentry->d_inode->i_sb->s_id, (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); nfs_clear_request(req); nfs_release_request(req);}/* * Set up the NFS read request struct */static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, const struct rpc_call_ops *call_ops, unsigned int count, unsigned int offset){ struct inode *inode; int flags; data->req = req; data->inode = inode = req->wb_context->path.dentry->d_inode; data->cred = req->wb_context->cred; data->args.fh = NFS_FH(inode); data->args.offset = req_offset(req) + offset; data->args.pgbase = req->wb_pgbase + offset; data->args.pages = data->pagevec; data->args.count = count; data->args.context = req->wb_context; data->res.fattr = &data->fattr; data->res.count = count; data->res.eof = 0; nfs_fattr_init(&data->fattr); /* Set up the initial task struct. */ flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); rpc_init_task(&data->task, NFS_CLIENT(inode), flags, call_ops, data); NFS_PROTO(inode)->read_setup(data); data->task.tk_cookie = (unsigned long)inode; dprintk("NFS: %5u initiated read call (req %s/%Ld, %u bytes @ offset %Lu)\n", data->task.tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), count, (unsigned long long)data->args.offset);}static voidnfs_async_read_error(struct list_head *head){ struct nfs_page *req; while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); SetPageError(req->wb_page); nfs_readpage_release(req); }}/* * Start an async read operation */static void nfs_execute_read(struct nfs_read_data *data){ struct rpc_clnt *clnt = NFS_CLIENT(data->inode); sigset_t oldset; rpc_clnt_sigmask(clnt, &oldset); rpc_execute(&data->task); rpc_clnt_sigunmask(clnt, &oldset);}/* * Generate multiple requests to fill a single page. * * We optimize to reduce the number of read operations on the wire. If we * detect that we're reading a page, or an area of a page, that is past the * end of file, we do not generate NFS read operations but just clear the * parts of the page that would have come back zero from the server anyway. * * We rely on the cached value of i_size to make this determination; another * client can fill pages on the server past our cached end-of-file, but we * won't see the new data until our attribute cache is updated. This is more * or less conventional NFS client behavior. */static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags){ struct nfs_page *req = nfs_list_entry(head->next); struct page *page = req->wb_page; struct nfs_read_data *data; size_t rsize = NFS_SERVER(inode)->rsize, nbytes; unsigned int offset; int requests = 0; LIST_HEAD(list); nfs_list_remove_request(req); nbytes = count; do { size_t len = min(nbytes,rsize); data = nfs_readdata_alloc(1); if (!data) goto out_bad; INIT_LIST_HEAD(&data->pages); list_add(&data->pages, &list); requests++; nbytes -= len; } while(nbytes != 0); atomic_set(&req->wb_complete, requests); ClearPageError(page); offset = 0; nbytes = count; do { data = list_entry(list.next, struct nfs_read_data, pages); list_del_init(&data->pages); data->pagevec[0] = page; if (nbytes < rsize) rsize = nbytes; nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, rsize, offset); offset += rsize; nbytes -= rsize; nfs_execute_read(data); } while (nbytes != 0); return 0;out_bad: while (!list_empty(&list)) { data = list_entry(list.next, struct nfs_read_data, pages); list_del(&data->pages); nfs_readdata_free(data); } SetPageError(page); nfs_readpage_release(req); return -ENOMEM;}static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags){ struct nfs_page *req; struct page **pages; struct nfs_read_data *data; data = nfs_readdata_alloc(npages); if (!data) goto out_bad; INIT_LIST_HEAD(&data->pages); pages = data->pagevec; while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -