Commit 829186b7 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: use the (more efficient) NFSv2/v3-like XDR scheme for generating

READDIR RPC calls.
parent 1513be7e
......@@ -51,9 +51,6 @@
#define NFS4_POLL_RETRY_TIME (15*HZ)
#define GET_OP(cp,name) &cp->ops[cp->req_nops].u.name
#define OPNUM(cp) cp->ops[cp->req_nops].opnum
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *);
extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
......@@ -72,15 +69,6 @@ static inline int nfs4_map_errors(int err)
return err;
}
static void
nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops,
struct nfs_server *server, char *tag)
{
memset(cp, 0, sizeof(*cp));
cp->ops = ops;
cp->server = server;
}
/*
* This is our standard bitmap for GETATTR requests.
*/
......@@ -116,37 +104,21 @@ u32 nfs4_pathconf_bitmap[2] = {
0
};
static void
nfs4_setup_putfh(struct nfs4_compound *cp, struct nfs_fh *fhandle)
{
struct nfs4_putfh *putfh = GET_OP(cp, putfh);
putfh->pf_fhandle = fhandle;
OPNUM(cp) = OP_PUTFH;
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)
static void nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry,
struct nfs4_readdir_arg *readdir)
{
u32 *start, *p;
struct nfs4_readdir *readdir = GET_OP(cp, readdir);
BUG_ON(bufsize < 80);
readdir->rd_cookie = (cookie > 2) ? cookie : 0;
memcpy(&readdir->rd_req_verifier, verifier, sizeof(readdir->rd_req_verifier));
readdir->rd_count = bufsize;
readdir->rd_bmval[0] = FATTR4_WORD0_FILEID;
readdir->rd_bmval[1] = 0;
readdir->rd_pages = pages;
readdir->rd_pgbase = 0;
OPNUM(cp) = OP_READDIR;
cp->req_nops++;
if (cookie >= 2)
BUG_ON(readdir->count < 80);
if (cookie > 2) {
readdir->cookie = (cookie > 2) ? cookie : 0;
memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier));
return;
}
readdir->cookie = 0;
memset(&readdir->verifier, 0, sizeof(readdir->verifier));
if (cookie == 2)
return;
/*
......@@ -156,7 +128,7 @@ nfs4_setup_readdir(struct nfs4_compound *cp, u64 cookie, u32 *verifier,
* when talking to the server, we always send cookie 0
* instead of 1 or 2.
*/
start = p = (u32 *)kmap_atomic(*pages, KM_USER0);
start = p = (u32 *)kmap_atomic(*readdir->pages, KM_USER0);
if (cookie == 0) {
*p++ = xdr_one; /* next */
......@@ -168,7 +140,7 @@ nfs4_setup_readdir(struct nfs4_compound *cp, u64 cookie, u32 *verifier,
*p++ = xdr_one; /* bitmap length */
*p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */
*p++ = htonl(8); /* attribute buffer length */
p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_inode));
p = xdr_encode_hyper(p, dentry->d_inode->i_ino);
}
*p++ = xdr_one; /* next */
......@@ -180,10 +152,10 @@ nfs4_setup_readdir(struct nfs4_compound *cp, u64 cookie, u32 *verifier,
*p++ = xdr_one; /* bitmap length */
*p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */
*p++ = htonl(8); /* attribute buffer length */
p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_parent->d_inode));
p = xdr_encode_hyper(p, dentry->d_parent->d_inode->i_ino);
readdir->rd_pgbase = (char *)p - (char *)start;
readdir->rd_count -= readdir->rd_pgbase;
readdir->pgbase = (char *)p - (char *)start;
readdir->count -= readdir->pgbase;
kunmap_atomic(start, KM_USER0);
}
......@@ -197,47 +169,6 @@ renew_lease(struct nfs_server *server, unsigned long timestamp)
spin_unlock(&clp->cl_lock);
}
static inline void
process_lease(struct nfs4_compound *cp)
{
/*
* Generic lease processing: If this operation contains a
* lease-renewing operation, and it succeeded, update the RENEW time
* in the superblock. Instead of the current time, we use the time
* when the request was sent out. (All we know is that the lease was
* renewed sometime between then and now, and we have to assume the
* worst case.)
*
* Notes:
* (1) renewd doesn't acquire the spinlock when messing with
* server->last_renewal; this is OK since rpciod always runs
* under the BKL.
* (2) cp->timestamp was set at the end of XDR encode.
*/
if (!cp->renew_index)
return;
if (!cp->toplevel_status || cp->resp_nops > cp->renew_index)
renew_lease(cp->server, cp->timestamp);
}
static int
nfs4_call_compound(struct nfs4_compound *cp, struct rpc_cred *cred, int flags)
{
int status;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND],
.rpc_argp = cp,
.rpc_resp = cp,
.rpc_cred = cred,
};
status = rpc_call_sync(cp->server->client, &msg, flags);
if (!status)
process_lease(cp);
return status;
}
static inline void
process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr)
{
......@@ -1209,24 +1140,31 @@ static int nfs4_proc_mkdir(struct inode *dir, struct qstr *name,
return nfs4_map_errors(status);
}
static int
nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page *page, unsigned int count, int plus)
{
struct inode *dir = dentry->d_inode;
struct nfs4_compound compound;
struct nfs4_op ops[2];
struct nfs4_readdir_arg args = {
.fh = NFS_FH(dir),
.pages = &page,
.pgbase = 0,
.count = count,
};
struct nfs4_readdir_res res;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READDIR],
.rpc_argp = &args,
.rpc_resp = &res,
.rpc_cred = cred,
};
int status;
lock_kernel();
nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "readdir");
nfs4_setup_putfh(&compound, NFS_FH(dir));
nfs4_setup_readdir(&compound, cookie, NFS_COOKIEVERF(dir), &page, count, dentry);
status = nfs4_call_compound(&compound, cred, 0);
nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
res.pgbase = args.pgbase;
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (status == 0)
memcpy(NFS_COOKIEVERF(dir), ops[1].u.readdir.rd_resp_verifier.data, NFS4_VERIFIER_SIZE);
memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
unlock_kernel();
return nfs4_map_errors(status);
}
......
......@@ -138,6 +138,12 @@ static int nfs_stat_to_errno(int);
#define NFS4_dec_readlink_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz)
#define NFS4_enc_readdir_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + 9)
#define NFS4_dec_readdir_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz + 2)
#define NFS4_enc_write_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + 8)
......@@ -941,8 +947,7 @@ encode_read(struct xdr_stream *xdr, struct nfs_readargs *args)
return 0;
}
static int
encode_readdir(struct xdr_stream *xdr, struct nfs4_readdir *readdir, struct rpc_rqst *req)
static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req)
{
struct rpc_auth *auth = req->rq_task->tk_auth;
int replen;
......@@ -950,21 +955,21 @@ encode_readdir(struct xdr_stream *xdr, struct nfs4_readdir *readdir, struct rpc_
RESERVE_SPACE(32+sizeof(nfs4_verifier));
WRITE32(OP_READDIR);
WRITE64(readdir->rd_cookie);
WRITEMEM(readdir->rd_req_verifier.data, sizeof(readdir->rd_req_verifier.data));
WRITE32(readdir->rd_count >> 5); /* meaningless "dircount" field */
WRITE32(readdir->rd_count);
WRITE64(readdir->cookie);
WRITEMEM(readdir->verifier.data, sizeof(readdir->verifier.data));
WRITE32(readdir->count >> 5); /* meaningless "dircount" field */
WRITE32(readdir->count);
WRITE32(2);
WRITE32(readdir->rd_bmval[0]);
WRITE32(readdir->rd_bmval[1]);
WRITE32(FATTR4_WORD0_FILEID);
WRITE32(0);
/* set up reply iovec
* toplevel_status + taglen + rescount + OP_PUTFH + status
* + OP_READDIR + status + verifer(2) = 9
*/
replen = (RPC_REPHDRSIZE + auth->au_rslack + 9) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen, readdir->rd_pages,
readdir->rd_pgbase, readdir->rd_count);
xdr_inline_pages(&req->rq_rcv_buf, replen, readdir->pages,
readdir->pgbase, readdir->count);
return 0;
}
......@@ -1127,56 +1132,10 @@ encode_write(struct xdr_stream *xdr, struct nfs_writeargs *args)
return 0;
}
/* FIXME: this sucks */
static int
encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqst *req)
{
struct compound_hdr hdr = {
.taglen = cp->taglen,
.tag = cp->tag,
.nops = cp->req_nops,
};
int i, status = 0;
encode_compound_hdr(xdr, &hdr);
for (i = 0; i < cp->req_nops; i++) {
switch (cp->ops[i].opnum) {
case OP_PUTFH:
status = encode_putfh(xdr, cp->ops[i].u.putfh.pf_fhandle);
break;
case OP_READDIR:
status = encode_readdir(xdr, &cp->ops[i].u.readdir, req);
break;
default:
BUG();
}
if (status)
return status;
}
return 0;
}
/*
* END OF "GENERIC" ENCODE ROUTINES.
*/
/*
* Encode COMPOUND argument
*/
static int
nfs4_xdr_enc_compound(struct rpc_rqst *req, uint32_t *p, struct nfs4_compound *cp)
{
struct xdr_stream xdr;
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
status = encode_compound(&xdr, cp, req);
cp->timestamp = jiffies;
return status;
}
/*
* Encode an ACCESS request
*/
......@@ -1564,6 +1523,27 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, uint32_t *p, const struct
return status;
}
/*
* Encode a READDIR request
*/
static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, uint32_t *p, const struct nfs4_readdir_arg *args)
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 2,
};
int 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_readdir(&xdr, args, req);
out:
return status;
}
/*
* Encode a READ request
*/
......@@ -2956,8 +2936,7 @@ decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *re
return 0;
}
static int
decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir *readdir)
static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir)
{
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
struct page *page = *rcvbuf->pages;
......@@ -2971,7 +2950,7 @@ decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir
if (status)
return status;
READ_BUF(8);
COPYMEM(readdir->rd_resp_verifier.data, 8);
COPYMEM(readdir->verifier.data, 8);
hdrlen = (char *) p - (char *) iov->iov_base;
recvd = req->rq_received - hdrlen;
......@@ -2979,9 +2958,9 @@ decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir
pglen = recvd;
xdr_read_pages(xdr, pglen);
BUG_ON(pglen + readdir->rd_pgbase > PAGE_CACHE_SIZE);
BUG_ON(pglen + readdir->pgbase > PAGE_CACHE_SIZE);
kaddr = p = (uint32_t *) kmap_atomic(page, KM_USER0);
end = (uint32_t *) ((char *)p + pglen + readdir->rd_pgbase);
end = (uint32_t *) ((char *)p + pglen + readdir->pgbase);
entry = p;
for (nr = 0; *p++; nr++) {
if (p + 3 > end)
......@@ -3206,53 +3185,6 @@ decode_write(struct xdr_stream *xdr, struct nfs_writeres *res)
return 0;
}
/* FIXME: this sucks */
static int
decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqst *req)
{
struct compound_hdr hdr;
struct nfs4_op *op;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
cp->toplevel_status = hdr.status;
/*
* We need this if our zero-copy I/O is going to work. Rumor has
* it that the spec will soon mandate it...
*/
if (hdr.taglen != cp->taglen)
dprintk("nfs4: non-conforming server returns tag length mismatch!\n");
cp->resp_nops = hdr.nops;
if (hdr.nops > cp->req_nops) {
dprintk("nfs4: resp_nops > req_nops!\n");
goto xdr_error;
}
op = &cp->ops[0];
for (cp->nops = 0; cp->nops < cp->resp_nops; cp->nops++, op++) {
switch (op->opnum) {
case OP_PUTFH:
status = decode_putfh(xdr);
break;
case OP_READDIR:
status = decode_readdir(xdr, req, &op->u.readdir);
break;
default:
BUG();
return -EIO;
}
if (status)
break;
}
DECODE_TAIL;
}
/*
* Decode OPEN_DOWNGRADE response
*/
......@@ -3279,27 +3211,6 @@ nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_clos
* END OF "GENERIC" DECODE ROUTINES.
*/
/*
* Decode COMPOUND response
*/
static int
nfs4_xdr_dec_compound(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_compound *cp)
{
struct xdr_stream xdr;
int status;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
if ((status = decode_compound(&xdr, cp, rqstp)))
goto out;
status = 0;
if (cp->toplevel_status)
status = -nfs_stat_to_errno(cp->toplevel_status);
out:
return status;
}
/*
* Decode ACCESS response
*/
......@@ -3691,6 +3602,27 @@ static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, uint32_t *p, void *res)
return status;
}
/*
* Decode READDIR response
*/
static int nfs4_xdr_dec_readdir(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_readdir_res *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_readdir(&xdr, rqstp, res);
out:
return status;
}
/*
* Decode Read response
*/
......@@ -3995,7 +3927,6 @@ nfs_stat_to_errno(int stat)
}
struct rpc_procinfo nfs4_procedures[] = {
PROC(COMPOUND, enc_compound, dec_compound),
PROC(READ, enc_read, dec_read),
PROC(WRITE, enc_write, dec_write),
PROC(COMMIT, enc_commit, dec_commit),
......@@ -4023,6 +3954,7 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(PATHCONF, enc_pathconf, dec_pathconf),
PROC(STATFS, enc_statfs, dec_statfs),
PROC(READLINK, enc_readlink, dec_readlink),
PROC(READDIR, enc_readdir, dec_readdir),
};
struct rpc_version nfs_version4 = {
......
......@@ -286,7 +286,6 @@ enum lock_type4 {
enum {
NFSPROC4_CLNT_NULL = 0, /* Unused */
NFSPROC4_CLNT_COMPOUND, /* Soon to be unused */
NFSPROC4_CLNT_READ,
NFSPROC4_CLNT_WRITE,
NFSPROC4_CLNT_COMMIT,
......@@ -314,6 +313,7 @@ enum {
NFSPROC4_CLNT_PATHCONF,
NFSPROC4_CLNT_STATFS,
NFSPROC4_CLNT_READLINK,
NFSPROC4_CLNT_READDIR,
};
#endif
......
......@@ -496,11 +496,6 @@ struct nfs4_accessres {
u32 access;
};
struct nfs4_close {
char * cl_stateid; /* request */
u32 cl_seqid; /* request */
};
struct nfs4_create_arg {
u32 ftype;
union {
......@@ -524,12 +519,6 @@ struct nfs4_create_res {
struct nfs4_change_info dir_cinfo;
};
#define cr_textlen u.symlink.textlen
#define cr_text u.symlink.text
#define cr_specdata1 u.device.specdata1
#define cr_specdata2 u.device.specdata2
struct nfs4_getattr {
u32 * gt_bmval; /* request */
struct nfs_fattr * gt_attrs; /* response */
......@@ -568,44 +557,23 @@ struct nfs4_lookup_root_arg {
const u32 * bitmask;
};
struct nfs4_open {
struct nfs4_client * op_client_state; /* request */
u32 op_share_access; /* request */
u32 op_opentype; /* request */
u32 op_createmode; /* request */
union { /* request */
struct iattr * attrs; /* UNCHECKED, GUARDED */
nfs4_verifier verifier; /* EXCLUSIVE */
} u;
struct qstr * op_name; /* request */
char * op_stateid; /* response */
struct nfs4_change_info * op_cinfo; /* response */
u32 * op_rflags; /* response */
};
#define op_attrs u.attrs
#define op_verifier u.verifier
struct nfs4_open_confirm {
char * oc_stateid; /* request */
};
struct nfs4_pathconf_arg {
const struct nfs_fh * fh;
const u32 * bitmask;
};
struct nfs4_putfh {
struct nfs_fh * pf_fhandle; /* request */
struct nfs4_readdir_arg {
const struct nfs_fh * fh;
u64 cookie;
nfs4_verifier verifier;
u32 count;
struct page ** pages; /* zero-copy data */
unsigned int pgbase; /* zero-copy data */
};
struct nfs4_readdir {
u64 rd_cookie; /* request */
nfs4_verifier rd_req_verifier; /* request */
u32 rd_count; /* request */
u32 rd_bmval[2]; /* request */
nfs4_verifier rd_resp_verifier; /* response */
struct page ** rd_pages; /* zero-copy data */
unsigned int rd_pgbase; /* zero-copy data */
struct nfs4_readdir_res {
nfs4_verifier verifier;
unsigned int pgbase;
};
struct nfs4_readlink {
......@@ -631,11 +599,6 @@ struct nfs4_rename_res {
struct nfs4_change_info new_cinfo;
};
struct nfs4_setattr {
char * st_stateid; /* request */
struct iattr * st_iap; /* request */
};
struct nfs4_setclientid {
nfs4_verifier sc_verifier; /* request */
char * sc_name; /* request */
......@@ -651,46 +614,6 @@ struct nfs4_statfs_arg {
const u32 * bitmask;
};
struct nfs4_op {
u32 opnum;
union {
struct nfs4_close close;
struct nfs4_open open;
struct nfs4_open_confirm open_confirm;
struct nfs4_putfh putfh;
struct nfs4_readdir readdir;
struct nfs4_readlink readlink;
struct nfs4_client * renew;
struct nfs4_setattr setattr;
} u;
};
struct nfs4_compound {
unsigned int flags; /* defined below */
struct nfs_server * server;
/* RENEW information */
int renew_index;
unsigned long timestamp;
/* scratch variables for XDR encode/decode */
int nops;
u32 * p;
u32 * end;
/* the individual COMPOUND operations */
struct nfs4_op *ops;
/* request */
int req_nops;
u32 taglen;
char * tag;
/* response */
int resp_nops;
int toplevel_status;
};
#endif /* CONFIG_NFS_V4 */
struct nfs_page;
......
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