Commit fa09df5e authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

[PATCH] Clean up NFSv4 READ xdr path

This creates a clean XDR path for the NFSv4 read requests instead of
routing through encode_compound()/decode_compound().  This eliminates
the intermediate step of setting up a struct nfs4_compound before
proceeding to XDR encoding, and removes the large 'switch()' statements
from the codepath altogether.
parent 95ed636d
......@@ -685,11 +685,10 @@ 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 (nfs3_async_handle_jukebox(task))
return;
nfs_readpage_result(task, data->u.v3.res.count, data->u.v3.res.eof);
/* Call back common NFS readpage processing */
nfs_readpage_result(task);
}
static void
......@@ -701,20 +700,20 @@ nfs3_proc_read_setup(struct nfs_read_data *data, unsigned int count)
int flags;
struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_READ],
.rpc_argp = &data->u.v3.args,
.rpc_resp = &data->u.v3.res,
.rpc_argp = &data->args,
.rpc_resp = &data->res,
.rpc_cred = data->cred,
};
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;
data->args.fh = NFS_FH(inode);
data->args.offset = req_offset(req) + req->wb_offset;
data->args.pgbase = req->wb_offset;
data->args.pages = data->pagevec;
data->args.count = count;
data->res.fattr = &data->fattr;
data->res.count = count;
data->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);
......
......@@ -401,23 +401,6 @@ nfs4_setup_open_confirm(struct nfs4_compound *cp, char *stateid)
cp->renew_index = cp->req_nops;
}
static void
nfs4_setup_read(struct nfs4_compound *cp, u64 offset, u32 length,
struct page **pages, unsigned int pgbase, u32 *eofp, u32 *bytes_read)
{
struct nfs4_read *read = GET_OP(cp, read);
read->rd_offset = offset;
read->rd_length = length;
read->rd_pages = pages;
read->rd_pgbase = pgbase;
read->rd_eof = eofp;
read->rd_bytes_read = bytes_read;
OPNUM(cp) = OP_READ;
cp->req_nops++;
}
static void
nfs4_setup_readdir(struct nfs4_compound *cp, u64 cookie, u32 *verifier,
struct page **pages, unsigned int bufsize, struct dentry *dentry)
......@@ -613,11 +596,18 @@ nfs4_setup_write(struct nfs4_compound *cp, u64 offset, u32 length, int stable,
cp->req_nops++;
}
static void
renew_lease(struct nfs_server *server, unsigned long timestamp)
{
spin_lock(&renew_lock);
if (time_before(server->last_renewal,timestamp))
server->last_renewal = timestamp;
spin_unlock(&renew_lock);
}
static inline void
process_lease(struct nfs4_compound *cp)
{
struct nfs_server *server;
/*
* Generic lease processing: If this operation contains a
* lease-renewing operation, and it succeeded, update the RENEW time
......@@ -634,13 +624,8 @@ process_lease(struct nfs4_compound *cp)
*/
if (!cp->renew_index)
return;
if (!cp->toplevel_status || cp->resp_nops > cp->renew_index) {
server = cp->server;
spin_lock(&renew_lock);
if (server->last_renewal < cp->timestamp)
server->last_renewal = cp->timestamp;
spin_unlock(&renew_lock);
}
if (!cp->toplevel_status || cp->resp_nops > cp->renew_index)
renew_lease(cp->server, cp->timestamp);
}
static int
......@@ -1003,20 +988,35 @@ nfs4_proc_read(struct inode *inode, struct rpc_cred *cred,
unsigned int base, unsigned int count,
struct page *page, int *eofp)
{
u64 offset = page_offset(page) + base;
struct nfs4_compound compound;
struct nfs4_op ops[2];
u32 bytes_read;
int status;
struct nfs_server *server = NFS_SERVER(inode);
uint64_t offset = page_offset(page) + base;
struct nfs_readargs arg = {
.fh = NFS_FH(inode),
.offset = offset,
.count = count,
.pgbase = base,
.pages = &page,
};
struct nfs_readres res = {
.fattr = fattr,
.count = count,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ],
.rpc_argp = &arg,
.rpc_resp = &res,
.rpc_cred = cred,
};
unsigned long timestamp = jiffies;
int status;
dprintk("NFS call read %d @ %Ld\n", count, (long long)offset);
fattr->valid = 0;
nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "read [sync]");
nfs4_setup_putfh(&compound, NFS_FH(inode));
nfs4_setup_read(&compound, offset, count, &page, base, eofp, &bytes_read);
status = nfs4_call_compound(&compound, cred, 0);
if (status >= 0)
status = bytes_read;
status = rpc_call_sync(server->client, &msg, flags);
if (!status)
renew_lease(server, timestamp);
dprintk("NFS reply read: %d\n", status);
*eofp = res.eof;
return status;
}
......@@ -1363,31 +1363,35 @@ nfs4_read_done(struct rpc_task *task)
{
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
process_lease(&data->u.v4.compound);
nfs_readpage_result(task, data->u.v4.res_count, data->u.v4.res_eof);
if (task->tk_status > 0)
renew_lease(NFS_SERVER(data->inode), data->timestamp);
/* Call back common NFS readpage processing */
nfs_readpage_result(task);
}
static void
nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count)
{
struct rpc_task *task = &data->task;
struct nfs4_compound *cp = &data->u.v4.compound;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND],
.rpc_argp = cp,
.rpc_resp = cp,
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ],
.rpc_argp = &data->args,
.rpc_resp = &data->res,
.rpc_cred = data->cred,
};
struct inode *inode = data->inode;
struct nfs_page *req = nfs_list_entry(data->pages.next);
int flags;
nfs4_setup_compound(cp, data->u.v4.ops, NFS_SERVER(inode), "read [async]");
nfs4_setup_putfh(cp, NFS_FH(inode));
nfs4_setup_read(cp, req_offset(req) + req->wb_offset,
count, data->pagevec, req->wb_offset,
&data->u.v4.res_eof,
&data->u.v4.res_count);
data->args.fh = NFS_FH(inode);
data->args.offset = req_offset(req) + req->wb_offset;
data->args.pgbase = req->wb_offset;
data->args.pages = data->pagevec;
data->args.count = count;
data->res.fattr = &data->fattr;
data->res.count = count;
data->res.eof = 0;
data->timestamp = jiffies;
/* N.B. Do we need to test? Never called for swapfile inode */
flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
......
......@@ -64,8 +64,29 @@
extern int nfs_stat_to_errno(int);
/* NFSv4 COMPOUND tags are only wanted for debugging purposes */
#ifdef DEBUG
#define NFS4_MAXTAGLEN 20
#else
#define NFS4_MAXTAGLEN 0
#endif
#define compound_encode_hdr_maxsz 3 + (NFS4_MAXTAGLEN >> 2)
#define compound_decode_hdr_maxsz 2 + (NFS4_MAXTAGLEN >> 2)
#define op_encode_hdr_maxsz 1
#define op_decode_hdr_maxsz 2
#define encode_putfh_maxsz op_encode_hdr_maxsz + 1 + \
(NFS4_FHSIZE >> 2)
#define decode_putfh_maxsz op_decode_hdr_maxsz
#define NFS4_enc_compound_sz 1024 /* XXX: large enough? */
#define NFS4_dec_compound_sz 1024 /* XXX: large enough? */
#define NFS4_enc_read_sz compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + 7
#define NFS4_dec_read_sz compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz + 2
static struct {
unsigned int mode;
......@@ -131,6 +152,7 @@ encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
uint32_t *p;
dprintk("encode_compound: tag=%.*s\n", (int)hdr->taglen, hdr->tag);
BUG_ON(hdr->taglen > NFS4_MAXTAGLEN);
RESERVE_SPACE(12+XDR_QUADLEN(hdr->taglen));
WRITE32(hdr->taglen);
WRITEMEM(hdr->tag, hdr->taglen);
......@@ -472,15 +494,15 @@ encode_open_confirm(struct xdr_stream *xdr, struct nfs4_open_confirm *open_confi
}
static int
encode_putfh(struct xdr_stream *xdr, struct nfs4_putfh *putfh)
encode_putfh(struct xdr_stream *xdr, struct nfs_fh *fh)
{
int len = putfh->pf_fhandle->size;
int len = fh->size;
uint32_t *p;
RESERVE_SPACE(8 + len);
WRITE32(OP_PUTFH);
WRITE32(len);
WRITEMEM(putfh->pf_fhandle->data, len);
WRITEMEM(fh->data, len);
return 0;
}
......@@ -497,10 +519,8 @@ encode_putrootfh(struct xdr_stream *xdr)
}
static int
encode_read(struct xdr_stream *xdr, struct nfs4_read *read, struct rpc_rqst *req)
encode_read(struct xdr_stream *xdr, struct nfs_readargs *args)
{
struct rpc_auth *auth = req->rq_task->tk_auth;
int replen;
uint32_t *p;
RESERVE_SPACE(32);
......@@ -509,16 +529,8 @@ encode_read(struct xdr_stream *xdr, struct nfs4_read *read, struct rpc_rqst *req
WRITE32(0);
WRITE32(0);
WRITE32(0);
WRITE64(read->rd_offset);
WRITE32(read->rd_length);
/* set up reply iovec
* toplevel status + taglen + rescount + OP_PUTFH + status
* + OP_READ + status + eof + datalen = 9
*/
replen = (RPC_REPHDRSIZE + auth->au_rslack + 9) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen,
read->rd_pages, read->rd_pgbase, read->rd_length);
WRITE64(args->offset);
WRITE32(args->count);
return 0;
}
......@@ -759,14 +771,11 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
status = encode_open_confirm(xdr, &cp->ops[i].u.open_confirm);
break;
case OP_PUTFH:
status = encode_putfh(xdr, &cp->ops[i].u.putfh);
status = encode_putfh(xdr, cp->ops[i].u.putfh.pf_fhandle);
break;
case OP_PUTROOTFH:
status = encode_putrootfh(xdr);
break;
case OP_READ:
status = encode_read(xdr, &cp->ops[i].u.read, req);
break;
case OP_READDIR:
status = encode_readdir(xdr, &cp->ops[i].u.readdir, req);
break;
......@@ -829,6 +838,37 @@ nfs4_xdr_enc_compound(struct rpc_rqst *req, uint32_t *p, struct nfs4_compound *c
return status;
}
/*
* Encode a READ request
*/
static int
nfs4_xdr_enc_read(struct rpc_rqst *req, uint32_t *p, struct nfs_readargs *args)
{
struct rpc_auth *auth = req->rq_task->tk_auth;
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 2,
};
int replen, status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
if (status)
goto out;
status = encode_read(&xdr, args);
/* set up reply iovec
* toplevel status + taglen=0 + rescount + OP_PUTFH + status
* + OP_READ + status + eof + datalen = 9
*/
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_read_sz) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen,
args->pages, args->pgbase, args->count);
out:
return status;
}
/*
* START OF "GENERIC" DECODE ROUTINES.
......@@ -1368,7 +1408,7 @@ decode_putrootfh(struct xdr_stream *xdr)
}
static int
decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_read *read)
decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res)
{
struct iovec *iov = req->rq_rvec;
uint32_t *p;
......@@ -1382,14 +1422,6 @@ decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_read *read
READ32(eof);
READ32(count);
hdrlen = (u8 *) p - (u8 *) iov->iov_base;
if (iov->iov_len < hdrlen) {
printk(KERN_WARNING "NFS: READ reply header overflowed:"
"length %u > %Zu\n", hdrlen, iov->iov_len);
return -errno_NFSERR_IO;
} else if (iov->iov_len != hdrlen) {
dprintk("NFS: READ header is short. iovec will be shifted.\n");
xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
}
recvd = req->rq_received - hdrlen;
if (count > recvd) {
printk(KERN_WARNING "NFS: server cheating in read reply: "
......@@ -1397,9 +1429,9 @@ decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_read *read
count = recvd;
eof = 0;
}
if (read->rd_eof)
*read->rd_eof = eof;
*read->rd_bytes_read = count;
xdr_read_pages(xdr, count);
res->eof = eof;
res->count = count;
return 0;
}
......@@ -1421,17 +1453,10 @@ decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir
COPYMEM(readdir->rd_resp_verifier, 8);
hdrlen = (char *) p - (char *) iov->iov_base;
if (iov->iov_len < hdrlen) {
printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
"length %d > %Zu\n", hdrlen, iov->iov_len);
return -EIO;
} else if (iov->iov_len != hdrlen) {
dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
}
recvd = req->rq_received - hdrlen;
if (pglen > recvd)
pglen = recvd;
xdr_read_pages(xdr, pglen);
BUG_ON(pglen + readdir->rd_pgbase > PAGE_CACHE_SIZE);
p = (uint32_t *) kmap(page);
......@@ -1731,9 +1756,6 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
case OP_PUTROOTFH:
status = decode_putrootfh(xdr);
break;
case OP_READ:
status = decode_read(xdr, req, &op->u.read);
break;
case OP_READDIR:
status = decode_readdir(xdr, req, &op->u.readdir);
break;
......@@ -1802,6 +1824,30 @@ nfs4_xdr_dec_compound(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_compound
return status;
}
/*
* Decode Read response
*/
static int
nfs4_xdr_dec_read(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_readres *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
status = decode_compound_hdr(&xdr, &hdr);
if (status)
goto out;
status = decode_putfh(&xdr);
if (status)
goto out;
status = decode_read(&xdr, rqstp, res);
if (!status)
status = -nfs_stat_to_errno(hdr.status);
out:
return status;
}
uint32_t *
nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
{
......@@ -1854,6 +1900,7 @@ nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
struct rpc_procinfo nfs4_procedures[] = {
PROC(COMPOUND, enc_compound, dec_compound),
PROC(READ, enc_read, dec_read),
};
struct rpc_version nfs_version4 = {
......
......@@ -520,13 +520,6 @@ nfs_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
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)
{
......@@ -536,26 +529,26 @@ nfs_proc_read_setup(struct nfs_read_data *data, unsigned int count)
int flags;
struct rpc_message msg = {
.rpc_proc = &nfs_procedures[NFSPROC_READ],
.rpc_argp = &data->u.v3.args,
.rpc_resp = &data->u.v3.res,
.rpc_argp = &data->args,
.rpc_resp = &data->res,
.rpc_cred = data->cred,
};
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;
data->args.fh = NFS_FH(inode);
data->args.offset = req_offset(req) + req->wb_offset;
data->args.pgbase = req->wb_offset;
data->args.pages = data->pagevec;
data->args.count = count;
data->res.fattr = &data->fattr;
data->res.count = count;
data->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);
rpc_init_task(task, NFS_CLIENT(inode), nfs_readpage_result, flags);
task->tk_calldata = data;
/* Release requests */
task->tk_release = nfs_readdata_release;
......
......@@ -255,11 +255,12 @@ nfs_pagein_list(struct list_head *head, int rpages)
* received or some error occurred (timeout or socket shutdown).
*/
void
nfs_readpage_result(struct rpc_task *task, unsigned int count, int eof)
nfs_readpage_result(struct rpc_task *task)
{
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
struct inode *inode = data->inode;
struct nfs_fattr *fattr = &data->fattr;
unsigned int count = data->res.count;
dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
task->tk_pid, task->tk_status);
......@@ -279,7 +280,7 @@ nfs_readpage_result(struct rpc_task *task, unsigned int count, int eof)
req->wb_bytes - count);
kunmap(page);
if (eof ||
if (data->res.eof ||
((fattr->valid & NFS_ATTR_FATTR) &&
((req_offset(req) + req->wb_offset + count) >= fattr->size)))
SetPageUptodate(page);
......
......@@ -203,6 +203,7 @@ enum open_delegation_type4 {
enum {
NFSPROC4_CLNT_NULL = 0, /* Unused */
NFSPROC4_CLNT_COMPOUND, /* Soon to be unused */
NFSPROC4_CLNT_READ,
};
#endif
......
......@@ -369,7 +369,7 @@ extern int nfs_readpage(struct file *, struct page *);
extern int nfs_readpages(struct file *, struct address_space *,
struct list_head *, unsigned);
extern int nfs_pagein_list(struct list_head *, int);
extern void nfs_readpage_result(struct rpc_task *, unsigned int count, int eof);
extern void nfs_readpage_result(struct rpc_task *);
extern void nfs_readdata_release(struct rpc_task *);
/*
......
......@@ -422,15 +422,6 @@ struct nfs4_putfh {
struct nfs_fh * pf_fhandle; /* request */
};
struct nfs4_read {
u64 rd_offset; /* request */
u32 rd_length; /* request */
u32 *rd_eof; /* response */
u32 *rd_bytes_read; /* response */
struct page ** rd_pages; /* zero-copy data */
unsigned int rd_pgbase; /* zero-copy data */
};
struct nfs4_readdir {
u64 rd_cookie; /* request */
nfs4_verifier rd_req_verifier; /* request */
......@@ -500,7 +491,6 @@ struct nfs4_op {
struct nfs4_open open;
struct nfs4_open_confirm open_confirm;
struct nfs4_putfh putfh;
struct nfs4_read read;
struct nfs4_readdir readdir;
struct nfs4_readlink readlink;
struct nfs4_remove remove;
......@@ -548,20 +538,11 @@ struct nfs_read_data {
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 */
struct nfs_readargs args;
struct nfs_readres res;
#ifdef CONFIG_NFS_V4
struct {
struct nfs4_compound compound;
struct nfs4_op ops[3];
u32 res_count;
u32 res_eof;
} v4;
unsigned long timestamp; /* For lease renewal */
#endif
} u;
};
struct nfs_write_data {
......
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