Commit 63a5ba42 authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

[PATCH] A basic NFSv4 client for 2.5.x

This is a nontrivial change to the NFS client.

In this patch, we finish modifying the async READ path so that it is
version-agnostic.  We define a new nfs_rpc_op ->setup_read(), and move
the v2- and v3-specific code in nfs_read_rpcsetup() there.  We also
have to change nfs_readpage() result so that the 'count' of bytes
read is a parameter.  The extra parameter means that it can no longer
be ->tk_exit().  Instead, it is called from a version-specific ->tk_exit()
routine which is set in ->read_setup().

The upshot of all this is that the version-specific part of the
async READ path has been encapsulated in a new nfs_rpc_op
->read_setup(), and NFSv4 can share the logic for asynchronous
READ's with NFSv2 and v3.
parent aa1e1176
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/nfs.h> #include <linux/nfs.h>
#include <linux/nfs3.h> #include <linux/nfs3.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#define NFSDBG_FACILITY NFSDBG_PROC #define NFSDBG_FACILITY NFSDBG_PROC
...@@ -646,6 +647,51 @@ nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -646,6 +647,51 @@ nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int); extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int);
static void
nfs3_read_done(struct rpc_task *task)
{
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
if (nfs_async_handle_jukebox(task))
return;
nfs_readpage_result(task, data->u.v3.res.count, data->u.v3.res.eof);
}
static void
nfs3_proc_read_setup(struct nfs_read_data *data, unsigned int count)
{
struct rpc_task *task = &data->task;
struct inode *inode = data->inode;
struct nfs_page *req;
int flags;
struct rpc_message msg;
req = nfs_list_entry(data->pages.next);
data->u.v3.args.fh = NFS_FH(inode);
data->u.v3.args.offset = req_offset(req) + req->wb_offset;
data->u.v3.args.pgbase = req->wb_offset;
data->u.v3.args.pages = data->pagevec;
data->u.v3.args.count = count;
data->u.v3.res.fattr = &data->fattr;
data->u.v3.res.count = count;
data->u.v3.res.eof = 0;
/* N.B. Do we need to test? Never called for swapfile inode */
flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
/* Finalize the task. */
rpc_init_task(task, NFS_CLIENT(inode), nfs3_read_done, flags);
task->tk_calldata = data;
/* Release requests */
task->tk_release = nfs_readdata_release;
msg.rpc_proc = NFS3PROC_READ;
msg.rpc_argp = &data->u.v3.args;
msg.rpc_resp = &data->u.v3.res;
msg.rpc_cred = data->cred;
rpc_call_setup(&data->task, &msg, 0);
}
struct nfs_rpc_ops nfs_v3_clientops = { struct nfs_rpc_ops nfs_v3_clientops = {
.version = 3, /* protocol version */ .version = 3, /* protocol version */
.getroot = nfs3_proc_get_root, .getroot = nfs3_proc_get_root,
...@@ -669,4 +715,5 @@ struct nfs_rpc_ops nfs_v3_clientops = { ...@@ -669,4 +715,5 @@ struct nfs_rpc_ops nfs_v3_clientops = {
.mknod = nfs3_proc_mknod, .mknod = nfs3_proc_mknod,
.statfs = nfs3_proc_statfs, .statfs = nfs3_proc_statfs,
.decode_dirent = nfs3_decode_dirent, .decode_dirent = nfs3_decode_dirent,
.read_setup = nfs3_proc_read_setup,
}; };
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <linux/nfs.h> #include <linux/nfs.h>
#include <linux/nfs2.h> #include <linux/nfs2.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#define NFSDBG_FACILITY NFSDBG_PROC #define NFSDBG_FACILITY NFSDBG_PROC
...@@ -469,6 +470,48 @@ nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -469,6 +470,48 @@ nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int); extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int);
static void
nfs_read_done(struct rpc_task *task)
{
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
nfs_readpage_result(task, data->u.v3.res.count, data->u.v3.res.eof);
}
static void
nfs_proc_read_setup(struct nfs_read_data *data, unsigned int count)
{
struct rpc_task *task = &data->task;
struct inode *inode = data->inode;
struct nfs_page *req;
int flags;
struct rpc_message msg;
req = nfs_list_entry(data->pages.next);
data->u.v3.args.fh = NFS_FH(inode);
data->u.v3.args.offset = req_offset(req) + req->wb_offset;
data->u.v3.args.pgbase = req->wb_offset;
data->u.v3.args.pages = data->pagevec;
data->u.v3.args.count = count;
data->u.v3.res.fattr = &data->fattr;
data->u.v3.res.count = count;
data->u.v3.res.eof = 0;
/* N.B. Do we need to test? Never called for swapfile inode */
flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
/* Finalize the task. */
rpc_init_task(task, NFS_CLIENT(inode), nfs_read_done, flags);
task->tk_calldata = data;
/* Release requests */
task->tk_release = nfs_readdata_release;
msg.rpc_proc = NFSPROC_READ;
msg.rpc_argp = &data->u.v3.args;
msg.rpc_resp = &data->u.v3.res;
msg.rpc_cred = data->cred;
rpc_call_setup(&data->task, &msg, 0);
}
struct nfs_rpc_ops nfs_v2_clientops = { struct nfs_rpc_ops nfs_v2_clientops = {
.version = 2, /* protocol version */ .version = 2, /* protocol version */
.getroot = nfs_proc_get_root, .getroot = nfs_proc_get_root,
...@@ -493,4 +536,5 @@ struct nfs_rpc_ops nfs_v2_clientops = { ...@@ -493,4 +536,5 @@ struct nfs_rpc_ops nfs_v2_clientops = {
.mknod = nfs_proc_mknod, .mknod = nfs_proc_mknod,
.statfs = nfs_proc_statfs, .statfs = nfs_proc_statfs,
.decode_dirent = nfs_decode_dirent, .decode_dirent = nfs_decode_dirent,
.read_setup = nfs_proc_read_setup,
}; };
...@@ -34,34 +34,6 @@ ...@@ -34,34 +34,6 @@
#define NFSDBG_FACILITY NFSDBG_PAGECACHE #define NFSDBG_FACILITY NFSDBG_PAGECACHE
struct nfs_read_data {
struct rpc_task task;
struct inode *inode;
struct rpc_cred *cred;
struct nfs_fattr fattr; /* fattr storage */
struct list_head pages; /* Coalesced read requests */
struct page *pagevec[NFS_READ_MAXIOV];
union {
struct {
struct nfs_readargs args;
struct nfs_readres res;
} v3; /* also v2 */
#ifdef CONFIG_NFS_V4
/* NFSv4 data will come here... */
#endif
} u;
};
/*
* Local function declarations
*/
static void nfs_readpage_result(struct rpc_task *task);
/* Hack for future NFS swap support */
#ifndef IS_SWAPFILE
# define IS_SWAPFILE(inode) (0)
#endif
static kmem_cache_t *nfs_rdata_cachep; static kmem_cache_t *nfs_rdata_cachep;
static __inline__ struct nfs_read_data *nfs_readdata_alloc(void) static __inline__ struct nfs_read_data *nfs_readdata_alloc(void)
...@@ -71,7 +43,6 @@ static __inline__ struct nfs_read_data *nfs_readdata_alloc(void) ...@@ -71,7 +43,6 @@ static __inline__ struct nfs_read_data *nfs_readdata_alloc(void)
if (p) { if (p) {
memset(p, 0, sizeof(*p)); memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages); INIT_LIST_HEAD(&p->pages);
p->u.v3.args.pages = p->pagevec;
} }
return p; return p;
} }
...@@ -81,7 +52,7 @@ static __inline__ void nfs_readdata_free(struct nfs_read_data *p) ...@@ -81,7 +52,7 @@ static __inline__ void nfs_readdata_free(struct nfs_read_data *p)
kmem_cache_free(nfs_rdata_cachep, p); kmem_cache_free(nfs_rdata_cachep, p);
} }
static void nfs_readdata_release(struct rpc_task *task) void nfs_readdata_release(struct rpc_task *task)
{ {
struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata;
nfs_readdata_free(data); nfs_readdata_free(data);
...@@ -197,31 +168,32 @@ nfs_readpage_async(struct file *file, struct inode *inode, struct page *page) ...@@ -197,31 +168,32 @@ nfs_readpage_async(struct file *file, struct inode *inode, struct page *page)
static void static void
nfs_read_rpcsetup(struct list_head *head, struct nfs_read_data *data) nfs_read_rpcsetup(struct list_head *head, struct nfs_read_data *data)
{ {
struct nfs_readargs *args = &data->u.v3.args; struct inode *inode;
struct nfs_readres *res = &data->u.v3.res;
struct nfs_page *req; struct nfs_page *req;
struct page **pages; struct page **pages;
unsigned int count; unsigned int count;
pages = &args->pages[0]; pages = data->pagevec;
count = 0; count = 0;
while (!list_empty(head)) { while (!list_empty(head)) {
struct nfs_page *req = nfs_list_entry(head->next); req = nfs_list_entry(head->next);
nfs_list_remove_request(req); nfs_list_remove_request(req);
nfs_list_add_request(req, &data->pages); nfs_list_add_request(req, &data->pages);
*pages++ = req->wb_page; *pages++ = req->wb_page;
count += req->wb_bytes; count += req->wb_bytes;
} }
req = nfs_list_entry(data->pages.next); req = nfs_list_entry(data->pages.next);
data->inode = req->wb_inode; data->inode = inode = req->wb_inode;
data->cred = req->wb_cred; data->cred = req->wb_cred;
args->fh = NFS_FH(req->wb_inode);
args->offset = req_offset(req) + req->wb_offset; NFS_PROTO(inode)->read_setup(data, count);
args->pgbase = req->wb_offset;
args->count = count; dprintk("NFS: %4d initiated read call (req %s/%Ld, %u bytes @ offset %Lu.\n",
res->fattr = &data->fattr; data->task.tk_pid,
res->count = count; inode->i_sb->s_id,
res->eof = 0; (long long)NFS_FILEID(inode),
count,
(unsigned long long)req_offset(req) + req->wb_offset);
} }
static void static void
...@@ -245,50 +217,20 @@ nfs_async_read_error(struct list_head *head) ...@@ -245,50 +217,20 @@ nfs_async_read_error(struct list_head *head)
static int static int
nfs_pagein_one(struct list_head *head, struct inode *inode) nfs_pagein_one(struct list_head *head, struct inode *inode)
{ {
struct rpc_task *task;
struct rpc_clnt *clnt = NFS_CLIENT(inode); struct rpc_clnt *clnt = NFS_CLIENT(inode);
struct nfs_read_data *data; struct nfs_read_data *data;
struct rpc_message msg;
int flags;
sigset_t oldset; sigset_t oldset;
data = nfs_readdata_alloc(); data = nfs_readdata_alloc();
if (!data) if (!data)
goto out_bad; goto out_bad;
task = &data->task;
/* N.B. Do we need to test? Never called for swapfile inode */
flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
nfs_read_rpcsetup(head, data); nfs_read_rpcsetup(head, data);
/* Finalize the task. */
rpc_init_task(task, clnt, nfs_readpage_result, flags);
task->tk_calldata = data;
/* Release requests */
task->tk_release = nfs_readdata_release;
#ifdef CONFIG_NFS_V3
msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_READ : NFSPROC_READ;
#else
msg.rpc_proc = NFSPROC_READ;
#endif
msg.rpc_argp = &data->u.v3.args;
msg.rpc_resp = &data->u.v3.res;
msg.rpc_cred = data->cred;
/* Start the async call */ /* Start the async call */
dprintk("NFS: %4d initiated read call (req %s/%Ld, %u bytes @ offset %Lu.\n",
task->tk_pid,
inode->i_sb->s_id,
(long long)NFS_FILEID(inode),
(unsigned int)data->u.v3.args.count,
(unsigned long long)data->u.v3.args.offset);
rpc_clnt_sigmask(clnt, &oldset); rpc_clnt_sigmask(clnt, &oldset);
rpc_call_setup(task, &msg, 0);
lock_kernel(); lock_kernel();
rpc_execute(task); rpc_execute(&data->task);
unlock_kernel(); unlock_kernel();
rpc_clnt_sigunmask(clnt, &oldset); rpc_clnt_sigunmask(clnt, &oldset);
return 0; return 0;
...@@ -408,20 +350,15 @@ int nfs_pagein_inode(struct inode *inode, unsigned long idx_start, ...@@ -408,20 +350,15 @@ int nfs_pagein_inode(struct inode *inode, unsigned long idx_start,
* This is the callback from RPC telling us whether a reply was * This is the callback from RPC telling us whether a reply was
* received or some error occurred (timeout or socket shutdown). * received or some error occurred (timeout or socket shutdown).
*/ */
static void void
nfs_readpage_result(struct rpc_task *task) nfs_readpage_result(struct rpc_task *task, unsigned int count, int eof)
{ {
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
struct inode *inode = data->inode; struct inode *inode = data->inode;
unsigned int count = data->u.v3.res.count;
int eof = data->u.v3.res.eof;
dprintk("NFS: %4d nfs_readpage_result, (status %d)\n", dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
task->tk_pid, task->tk_status); task->tk_pid, task->tk_status);
if (nfs_async_handle_jukebox(task))
return;
nfs_refresh_inode(inode, &data->fattr); nfs_refresh_inode(inode, &data->fattr);
while (!list_empty(&data->pages)) { while (!list_empty(&data->pages)) {
struct nfs_page *req = nfs_list_entry(data->pages.next); struct nfs_page *req = nfs_list_entry(data->pages.next);
......
...@@ -379,6 +379,11 @@ nfs_wb_file(struct inode *inode, struct file *file) ...@@ -379,6 +379,11 @@ nfs_wb_file(struct inode *inode, struct file *file)
return (error < 0) ? error : 0; return (error < 0) ? error : 0;
} }
/* Hack for future NFS swap support */
#ifndef IS_SWAPFILE
# define IS_SWAPFILE(inode) (0)
#endif
/* /*
* linux/fs/nfs/read.c * linux/fs/nfs/read.c
*/ */
...@@ -387,6 +392,8 @@ extern int nfs_pagein_inode(struct inode *, unsigned long, unsigned int); ...@@ -387,6 +392,8 @@ extern int nfs_pagein_inode(struct inode *, unsigned long, unsigned int);
extern int nfs_pagein_list(struct list_head *, int); extern int nfs_pagein_list(struct list_head *, int);
extern int nfs_scan_lru_read(struct nfs_server *, struct list_head *); extern int nfs_scan_lru_read(struct nfs_server *, struct list_head *);
extern int nfs_scan_lru_read_timeout(struct nfs_server *, struct list_head *); extern int nfs_scan_lru_read_timeout(struct nfs_server *, struct list_head *);
extern void nfs_readpage_result(struct rpc_task *, unsigned int count, int eof);
extern void nfs_readdata_release(struct rpc_task *);
/* /*
* linux/fs/mount_clnt.c * linux/fs/mount_clnt.c
......
...@@ -308,6 +308,24 @@ struct nfs3_readdirres { ...@@ -308,6 +308,24 @@ struct nfs3_readdirres {
int plus; int plus;
}; };
struct nfs_read_data {
struct rpc_task task;
struct inode *inode;
struct rpc_cred *cred;
struct nfs_fattr fattr; /* fattr storage */
struct list_head pages; /* Coalesced read requests */
struct page *pagevec[NFS_READ_MAXIOV];
union {
struct {
struct nfs_readargs args;
struct nfs_readres res;
} v3; /* also v2 */
#ifdef CONFIG_NFS_V4
/* NFSv4 data will come here... */
#endif
} u;
};
/* /*
* RPC procedure vector for NFSv2/NFSv3 demuxing * RPC procedure vector for NFSv2/NFSv3 demuxing
*/ */
...@@ -355,6 +373,7 @@ struct nfs_rpc_ops { ...@@ -355,6 +373,7 @@ struct nfs_rpc_ops {
int (*statfs) (struct nfs_server *, struct nfs_fh *, int (*statfs) (struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *); struct nfs_fsinfo *);
u32 * (*decode_dirent)(u32 *, struct nfs_entry *, int plus); u32 * (*decode_dirent)(u32 *, struct nfs_entry *, int plus);
void (*read_setup) (struct nfs_read_data *, unsigned int count);
}; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment