fs/nfs/direct.c | 9 fs/nfs/inode.c | 23 fs/nfs/nfs2xdr.c | 6 fs/nfs/nfs3proc.c | 55 fs/nfs/nfs3xdr.c | 6 fs/nfs/nfs4proc.c | 1121 ++++--------- fs/nfs/nfs4state.c | 2 fs/nfs/nfs4xdr.c | 2801 ++++++++++++++++++++-------------- fs/nfs/nfsroot.c | 216 +- fs/nfs/pagelist.c | 3 fs/nfs/proc.c | 42 fs/nfs/read.c | 341 +++- fs/nfs/write.c | 532 ++++-- include/linux/nfs4.h | 129 - include/linux/nfs_fs.h | 16 include/linux/nfs_fs_sb.h | 11 include/linux/nfs_page.h | 42 include/linux/nfs_xdr.h | 288 +-- include/linux/sunrpc/sched.h | 66 include/linux/sunrpc/xdr.h | 91 - include/linux/sunrpc/xprt.h | 2 net/sunrpc/auth_gss/auth_gss.c | 9 net/sunrpc/auth_gss/gss_krb5_crypto.c | 35 net/sunrpc/clnt.c | 12 net/sunrpc/sched.c | 244 ++ net/sunrpc/sunrpc_syms.c | 3 net/sunrpc/xdr.c | 204 ++ net/sunrpc/xprt.c | 26 28 files changed, 3705 insertions(+), 2630 deletions(-) diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/direct.c linux-2.6.5-31-server_caps/fs/nfs/direct.c --- linux-2.6.5-rc2/fs/nfs/direct.c 2004-03-25 00:30:05.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/direct.c 2004-03-25 00:35:45.000000000 -0500 @@ -128,6 +128,7 @@ nfs_direct_read_seg(struct inode *inode, .inode = inode, .args = { .fh = NFS_FH(inode), + .lockowner = current->files, }, .res = { .fattr = &rdata.fattr, @@ -258,6 +259,7 @@ nfs_direct_write_seg(struct inode *inode .inode = inode, .args = { .fh = NFS_FH(inode), + .lockowner = current->files, }, .res = { .fattr = &wdata.fattr, @@ -335,8 +337,7 @@ retry: VERF_SIZE) != 0) goto sync_retry; } - nfs_end_data_update(inode); - NFS_FLAGS(inode) |= NFS_INO_INVALID_DATA; + nfs_end_data_update_defer(inode); return tot_bytes; @@ -395,10 +396,6 @@ nfs_direct_write(struct inode *inode, st if (result < size) break; } - /* Zap the page cache if we managed to write */ - if (tot_bytes > 0) - invalidate_remote_inode(inode); - return tot_bytes; } diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/inode.c linux-2.6.5-31-server_caps/fs/nfs/inode.c --- linux-2.6.5-rc2/fs/nfs/inode.c 2004-03-25 00:30:03.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/inode.c 2004-03-25 00:35:45.000000000 -0500 @@ -1012,6 +1012,8 @@ void nfs_begin_data_update(struct inode * nfs_end_data_update * @inode - pointer to inode * Declare end of the operations that will update file data + * This will mark the inode as immediately needing revalidation + * of its attribute cache. */ void nfs_end_data_update(struct inode *inode) { @@ -1027,6 +1029,27 @@ void nfs_end_data_update(struct inode *i } /** + * nfs_end_data_update_defer + * @inode - pointer to inode + * Declare end of the operations that will update file data + * This will defer marking the inode as needing revalidation + * unless there are no other pending updates. + */ +void nfs_end_data_update_defer(struct inode *inode) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + if (atomic_dec_and_test(&nfsi->data_updates)) { + /* Mark the attribute cache for revalidation */ + nfsi->flags |= NFS_INO_INVALID_ATTR; + /* Directories and symlinks: invalidate page cache too */ + if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + nfsi->flags |= NFS_INO_INVALID_DATA; + nfsi->cache_change_attribute ++; + } +} + +/** * nfs_refresh_inode - verify consistency of the inode attribute cache * @inode - pointer to inode * @fattr - updated attributes diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/nfs2xdr.c linux-2.6.5-31-server_caps/fs/nfs/nfs2xdr.c --- linux-2.6.5-rc2/fs/nfs/nfs2xdr.c 2004-03-25 00:29:42.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/nfs2xdr.c 2004-03-25 00:37:38.000000000 -0500 @@ -231,7 +231,7 @@ nfs_xdr_readargs(struct rpc_rqst *req, u static int nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) { - struct iovec *iov = req->rq_rvec; + struct iovec *iov = req->rq_rcv_buf.head; int status, count, recvd, hdrlen; if ((status = ntohl(*p++))) @@ -250,7 +250,7 @@ nfs_xdr_readres(struct rpc_rqst *req, u3 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen); } - recvd = req->rq_received - hdrlen; + recvd = req->rq_rcv_buf.len - hdrlen; if (count > recvd) { printk(KERN_WARNING "NFS: server cheating in read reply: " "count %d > recvd %d\n", count, recvd); @@ -396,7 +396,7 @@ nfs_xdr_readdirres(struct rpc_rqst *req, } pglen = rcvbuf->page_len; - recvd = req->rq_received - hdrlen; + recvd = rcvbuf->len - hdrlen; if (pglen > recvd) pglen = recvd; page = rcvbuf->pages; diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/nfs3proc.c linux-2.6.5-31-server_caps/fs/nfs/nfs3proc.c --- linux-2.6.5-rc2/fs/nfs/nfs3proc.c 2004-03-25 00:29:50.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/nfs3proc.c 2004-03-25 00:35:13.000000000 -0500 @@ -729,11 +729,10 @@ nfs3_read_done(struct rpc_task *task) } static void -nfs3_proc_read_setup(struct nfs_read_data *data, unsigned int count) +nfs3_proc_read_setup(struct nfs_read_data *data) { struct rpc_task *task = &data->task; struct inode *inode = data->inode; - struct nfs_page *req; int flags; struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_READ], @@ -741,27 +740,13 @@ nfs3_proc_read_setup(struct nfs_read_dat .rpc_resp = &data->res, .rpc_cred = data->cred, }; - - req = nfs_list_entry(data->pages.next); - data->args.fh = NFS_FH(inode); - data->args.offset = req_offset(req); - data->args.pgbase = req->wb_pgbase; - 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), nfs3_read_done, flags); - task->tk_calldata = data; - /* Release requests */ - task->tk_release = nfs_readdata_release; - - rpc_call_setup(&data->task, &msg, 0); + rpc_call_setup(task, &msg, 0); } static void @@ -778,11 +763,10 @@ nfs3_write_done(struct rpc_task *task) } static void -nfs3_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) +nfs3_proc_write_setup(struct nfs_write_data *data, int how) { struct rpc_task *task = &data->task; struct inode *inode = data->inode; - struct nfs_page *req; int stable; int flags; struct rpc_message msg = { @@ -799,28 +783,14 @@ nfs3_proc_write_setup(struct nfs_write_d stable = NFS_DATA_SYNC; } else stable = NFS_UNSTABLE; - - req = nfs_list_entry(data->pages.next); - data->args.fh = NFS_FH(inode); - data->args.offset = req_offset(req); - data->args.pgbase = req->wb_pgbase; - data->args.count = count; data->args.stable = stable; - data->args.pages = data->pagevec; - data->res.fattr = &data->fattr; - data->res.count = count; - data->res.verf = &data->verf; /* Set the initial flags for the task. */ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ rpc_init_task(task, NFS_CLIENT(inode), nfs3_write_done, flags); - task->tk_calldata = data; - /* Release requests */ - task->tk_release = nfs_writedata_release; - - rpc_call_setup(&data->task, &msg, 0); + rpc_call_setup(task, &msg, 0); } static void @@ -837,7 +807,7 @@ nfs3_commit_done(struct rpc_task *task) } static void -nfs3_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) +nfs3_proc_commit_setup(struct nfs_write_data *data, int how) { struct rpc_task *task = &data->task; struct inode *inode = data->inode; @@ -849,23 +819,12 @@ nfs3_proc_commit_setup(struct nfs_write_ .rpc_cred = data->cred, }; - data->args.fh = NFS_FH(data->inode); - data->args.offset = start; - data->args.count = len; - data->res.count = len; - data->res.fattr = &data->fattr; - data->res.verf = &data->verf; - /* Set the initial flags for the task. */ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ rpc_init_task(task, NFS_CLIENT(inode), nfs3_commit_done, flags); - task->tk_calldata = data; - /* Release requests */ - task->tk_release = nfs_commit_release; - - rpc_call_setup(&data->task, &msg, 0); + rpc_call_setup(task, &msg, 0); } /* diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/nfs3xdr.c linux-2.6.5-31-server_caps/fs/nfs/nfs3xdr.c --- linux-2.6.5-rc2/fs/nfs/nfs3xdr.c 2004-03-25 00:30:15.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/nfs3xdr.c 2004-03-25 00:37:38.000000000 -0500 @@ -515,7 +515,7 @@ nfs3_xdr_readdirres(struct rpc_rqst *req } pglen = rcvbuf->page_len; - recvd = req->rq_received - hdrlen; + recvd = rcvbuf->len - hdrlen; if (pglen > recvd) pglen = recvd; page = rcvbuf->pages; @@ -758,7 +758,7 @@ nfs3_xdr_readlinkres(struct rpc_rqst *re static int nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) { - struct iovec *iov = req->rq_rvec; + struct iovec *iov = req->rq_rcv_buf.head; int status, count, ocount, recvd, hdrlen; status = ntohl(*p++); @@ -789,7 +789,7 @@ nfs3_xdr_readres(struct rpc_rqst *req, u xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen); } - recvd = req->rq_received - hdrlen; + recvd = req->rq_rcv_buf.len - hdrlen; if (count > recvd) { printk(KERN_WARNING "NFS: server cheating in read reply: " "count %d > recvd %d\n", count, recvd); diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/nfs4proc.c linux-2.6.5-31-server_caps/fs/nfs/nfs4proc.c --- linux-2.6.5-rc2/fs/nfs/nfs4proc.c 2004-03-25 00:30:02.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/nfs4proc.c 2004-03-25 14:27:20.000000000 -0500 @@ -51,10 +51,7 @@ #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_proc_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); +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); extern struct rpc_procinfo nfs4_procedures[]; @@ -72,102 +69,10 @@ static inline int nfs4_map_errors(int er 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; -} - -static void -nfs4_setup_access(struct nfs4_compound *cp, u32 req_access, u32 *resp_supported, u32 *resp_access) -{ - struct nfs4_access *access = GET_OP(cp, access); - - access->ac_req_access = req_access; - access->ac_resp_supported = resp_supported; - access->ac_resp_access = resp_access; - - OPNUM(cp) = OP_ACCESS; - cp->req_nops++; -} - -static void -nfs4_setup_create_dir(struct nfs4_compound *cp, struct qstr *name, - struct iattr *sattr, struct nfs4_change_info *info) -{ - struct nfs4_create *create = GET_OP(cp, create); - - create->cr_ftype = NF4DIR; - create->cr_namelen = name->len; - create->cr_name = name->name; - create->cr_attrs = sattr; - create->cr_cinfo = info; - - OPNUM(cp) = OP_CREATE; - cp->req_nops++; -} - -static void -nfs4_setup_create_symlink(struct nfs4_compound *cp, struct qstr *name, - struct qstr *linktext, struct iattr *sattr, - struct nfs4_change_info *info) -{ - struct nfs4_create *create = GET_OP(cp, create); - - create->cr_ftype = NF4LNK; - create->cr_textlen = linktext->len; - create->cr_text = linktext->name; - create->cr_namelen = name->len; - create->cr_name = name->name; - create->cr_attrs = sattr; - create->cr_cinfo = info; - - OPNUM(cp) = OP_CREATE; - cp->req_nops++; -} - -static void -nfs4_setup_create_special(struct nfs4_compound *cp, struct qstr *name, - dev_t dev, struct iattr *sattr, - struct nfs4_change_info *info) -{ - int mode = sattr->ia_mode; - struct nfs4_create *create = GET_OP(cp, create); - - BUG_ON(!(sattr->ia_valid & ATTR_MODE)); - BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode)); - - if (S_ISFIFO(mode)) - create->cr_ftype = NF4FIFO; - else if (S_ISBLK(mode)) { - create->cr_ftype = NF4BLK; - create->cr_specdata1 = MAJOR(dev); - create->cr_specdata2 = MINOR(dev); - } - else if (S_ISCHR(mode)) { - create->cr_ftype = NF4CHR; - create->cr_specdata1 = MAJOR(dev); - create->cr_specdata2 = MINOR(dev); - } - else - create->cr_ftype = NF4SOCK; - - create->cr_namelen = name->len; - create->cr_name = name->name; - create->cr_attrs = sattr; - create->cr_cinfo = info; - - OPNUM(cp) = OP_CREATE; - cp->req_nops++; -} - /* * This is our standard bitmap for GETATTR requests. */ -u32 nfs4_fattr_bitmap[2] = { +const u32 nfs4_fattr_bitmap[2] = { FATTR4_WORD0_TYPE | FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE @@ -184,7 +89,7 @@ u32 nfs4_fattr_bitmap[2] = { | FATTR4_WORD1_TIME_MODIFY }; -u32 nfs4_statfs_bitmap[2] = { +const u32 nfs4_statfs_bitmap[2] = { FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL, @@ -199,121 +104,28 @@ u32 nfs4_pathconf_bitmap[2] = { 0 }; -static inline void -__nfs4_setup_getattr(struct nfs4_compound *cp, u32 *bitmap, - struct nfs_fattr *fattr, - struct nfs_fsstat *fsstat, - struct nfs_pathconf *pathconf) -{ - struct nfs4_getattr *getattr = GET_OP(cp, getattr); - - getattr->gt_bmval = bitmap; - getattr->gt_attrs = fattr; - getattr->gt_fsstat = fsstat; - getattr->gt_pathconf = pathconf; - - OPNUM(cp) = OP_GETATTR; - cp->req_nops++; -} - -static void -nfs4_setup_getattr(struct nfs4_compound *cp, - struct nfs_fattr *fattr) -{ - __nfs4_setup_getattr(cp, nfs4_fattr_bitmap, fattr, - NULL, NULL); -} - -static void -nfs4_setup_statfs(struct nfs4_compound *cp, - struct nfs_fsstat *fsstat) -{ - __nfs4_setup_getattr(cp, nfs4_statfs_bitmap, - NULL, fsstat, NULL); -} - -static void -nfs4_setup_pathconf(struct nfs4_compound *cp, - struct nfs_pathconf *pathconf) -{ - __nfs4_setup_getattr(cp, nfs4_pathconf_bitmap, - NULL, NULL, pathconf); -} - -static void -nfs4_setup_getfh(struct nfs4_compound *cp, struct nfs_fh *fhandle) -{ - struct nfs4_getfh *getfh = GET_OP(cp, getfh); - - getfh->gf_fhandle = fhandle; - - OPNUM(cp) = OP_GETFH; - cp->req_nops++; -} - -static void -nfs4_setup_link(struct nfs4_compound *cp, struct qstr *name, - struct nfs4_change_info *info) -{ - struct nfs4_link *link = GET_OP(cp, link); - - link->ln_namelen = name->len; - link->ln_name = name->name; - link->ln_cinfo = info; - - OPNUM(cp) = OP_LINK; - cp->req_nops++; -} - -static void -nfs4_setup_lookup(struct nfs4_compound *cp, struct qstr *q) -{ - struct nfs4_lookup *lookup = GET_OP(cp, lookup); - - lookup->lo_name = q; - - OPNUM(cp) = OP_LOOKUP; - cp->req_nops++; -} - -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_putrootfh(struct nfs4_compound *cp) -{ - OPNUM(cp) = OP_PUTROOTFH; - cp->req_nops++; -} +const u32 nfs4_fsinfo_bitmap[2] = { FATTR4_WORD0_MAXFILESIZE + | FATTR4_WORD0_MAXREAD + | FATTR4_WORD0_MAXWRITE + | FATTR4_WORD0_LEASE_TIME, + 0 +}; -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++; + BUG_ON(readdir->count < 80); + if (cookie > 2) { + readdir->cookie = (cookie > 2) ? cookie : 0; + memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier)); + return; + } - if (cookie >= 2) + readdir->cookie = 0; + memset(&readdir->verifier, 0, sizeof(readdir->verifier)); + if (cookie == 2) return; /* @@ -323,7 +135,7 @@ nfs4_setup_readdir(struct nfs4_compound * 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 */ @@ -335,7 +147,7 @@ nfs4_setup_readdir(struct nfs4_compound *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 */ @@ -347,70 +159,14 @@ nfs4_setup_readdir(struct nfs4_compound *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); } static void -nfs4_setup_readlink(struct nfs4_compound *cp, int count, struct page **pages) -{ - struct nfs4_readlink *readlink = GET_OP(cp, readlink); - - readlink->rl_count = count; - readlink->rl_pages = pages; - - OPNUM(cp) = OP_READLINK; - cp->req_nops++; -} - -static void -nfs4_setup_remove(struct nfs4_compound *cp, struct qstr *name, struct nfs4_change_info *cinfo) -{ - struct nfs4_remove *remove = GET_OP(cp, remove); - - remove->rm_namelen = name->len; - remove->rm_name = name->name; - remove->rm_cinfo = cinfo; - - OPNUM(cp) = OP_REMOVE; - cp->req_nops++; -} - -static void -nfs4_setup_rename(struct nfs4_compound *cp, struct qstr *old, struct qstr *new, - struct nfs4_change_info *old_cinfo, struct nfs4_change_info *new_cinfo) -{ - struct nfs4_rename *rename = GET_OP(cp, rename); - - rename->rn_oldnamelen = old->len; - rename->rn_oldname = old->name; - rename->rn_newnamelen = new->len; - rename->rn_newname = new->name; - rename->rn_src_cinfo = old_cinfo; - rename->rn_dst_cinfo = new_cinfo; - - OPNUM(cp) = OP_RENAME; - cp->req_nops++; -} - -static void -nfs4_setup_restorefh(struct nfs4_compound *cp) -{ - OPNUM(cp) = OP_RESTOREFH; - cp->req_nops++; -} - -static void -nfs4_setup_savefh(struct nfs4_compound *cp) -{ - OPNUM(cp) = OP_SAVEFH; - cp->req_nops++; -} - -static void renew_lease(struct nfs_server *server, unsigned long timestamp) { struct nfs4_client *clp = server->nfs4_state; @@ -420,58 +176,12 @@ renew_lease(struct nfs_server *server, u spin_unlock(&clp->cl_lock); } -static inline void -process_lease(struct nfs4_compound *cp) +static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinfo) { - /* - * 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; -} + struct nfs_inode *nfsi = NFS_I(inode); -static inline void -process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr) -{ - BUG_ON((fattr->valid & NFS_ATTR_FATTR) == 0); - BUG_ON((fattr->valid & NFS_ATTR_FATTR_V4) == 0); - - if (fattr->change_attr == info->after) { - fattr->pre_change_attr = info->before; - fattr->valid |= NFS_ATTR_PRE_CHANGE; - fattr->timestamp = jiffies; - } + if (cinfo->before == nfsi->change_attr && cinfo->atomic) + nfsi->change_attr = cinfo->after; } /* @@ -487,12 +197,6 @@ nfs4_open_reclaim(struct nfs4_state_owne struct nfs_fattr fattr = { .valid = 0, }; - struct nfs4_change_info d_cinfo; - struct nfs4_getattr f_getattr = { - .gt_bmval = nfs4_fattr_bitmap, - .gt_attrs = &fattr, - }; - struct nfs_open_reclaimargs o_arg = { .fh = NFS_FH(inode), .seqid = sp->so_seqid, @@ -500,11 +204,10 @@ nfs4_open_reclaim(struct nfs4_state_owne .share_access = state->state, .clientid = server->nfs4_state->cl_clientid, .claim = NFS4_OPEN_CLAIM_PREVIOUS, - .f_getattr = &f_getattr, + .bitmask = server->attr_bitmask, }; struct nfs_openres o_res = { - .cinfo = &d_cinfo, - .f_getattr = &f_getattr, + .f_attr = &fattr, .server = server, /* Grrr */ }; struct rpc_message msg = { @@ -534,36 +237,21 @@ nfs4_do_open(struct inode *dir, struct q struct nfs4_state *state = NULL; struct nfs_server *server = NFS_SERVER(dir); struct inode *inode = NULL; - struct nfs4_change_info d_cinfo; int status; - struct nfs_fattr d_attr = { - .valid = 0, - }; struct nfs_fattr f_attr = { .valid = 0, }; - struct nfs4_getattr f_getattr = { - .gt_bmval = nfs4_fattr_bitmap, - .gt_attrs = &f_attr, - }; - struct nfs4_getattr d_getattr = { - .gt_bmval = nfs4_fattr_bitmap, - .gt_attrs = &d_attr, - }; struct nfs_openargs o_arg = { .fh = NFS_FH(dir), .share_access = flags & (FMODE_READ|FMODE_WRITE), .opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE, .createmode = (flags & O_EXCL) ? NFS4_CREATE_EXCLUSIVE : NFS4_CREATE_UNCHECKED, .name = name, - .f_getattr = &f_getattr, - .d_getattr = &d_getattr, .server = server, + .bitmask = server->attr_bitmask, }; struct nfs_openres o_res = { - .cinfo = &d_cinfo, - .f_getattr = &f_getattr, - .d_getattr = &d_getattr, + .f_attr = &f_attr, .server = server, }; struct rpc_message msg = { @@ -596,8 +284,7 @@ retry: nfs4_increment_seqid(status, sp); if (status) goto out_up; - process_cinfo(&d_cinfo, &d_attr); - nfs_refresh_inode(dir, &d_attr); + update_changeattr(dir, &o_res.cinfo); status = -ENOMEM; inode = nfs_fhget(dir->i_sb, &o_res.fh, &f_attr); @@ -679,18 +366,14 @@ nfs4_do_setattr(struct nfs_server *serve struct nfs_fh *fhandle, struct iattr *sattr, struct nfs4_state *state) { - struct nfs4_getattr getattr = { - .gt_bmval = nfs4_fattr_bitmap, - .gt_attrs = fattr, - }; struct nfs_setattrargs arg = { .fh = fhandle, .iap = sattr, - .attr = &getattr, .server = server, + .bitmask = server->attr_bitmask, }; struct nfs_setattrres res = { - .attr = &getattr, + .fattr = fattr, .server = server, }; struct rpc_message msg = { @@ -834,30 +517,85 @@ nfs4_open_revalidate(struct inode *dir, return 0; } -static int -nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) + +static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) +{ + struct nfs4_server_caps_res res = {}; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SERVER_CAPS], + .rpc_argp = fhandle, + .rpc_resp = &res, + }; + int status; + + status = rpc_call_sync(server->client, &msg, 0); + if (status == 0) { + memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask)); + if (res.attr_bitmask[0] & FATTR4_WORD0_ACL) + server->caps |= NFS_CAP_ACLS; + if (res.has_links != 0) + server->caps |= NFS_CAP_HARDLINKS; + if (res.has_symlinks != 0) + server->caps |= NFS_CAP_SYMLINKS; + server->acl_bitmask = res.acl_bitmask; + } + return status; +} + +static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fsinfo *info) +{ + struct nfs_fattr * fattr = info->fattr; + struct nfs4_lookup_root_arg args = { + .bitmask = nfs4_fattr_bitmap, + }; + struct nfs4_lookup_res res = { + .server = server, + .fattr = fattr, + .fh = fhandle, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP_ROOT], + .rpc_argp = &args, + .rpc_resp = &res, + }; + fattr->valid = 0; + return rpc_call_sync(server->client, &msg, 0); +} + +static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fsinfo *info) { - struct nfs4_compound compound; - struct nfs4_op ops[4]; struct nfs_fattr * fattr = info->fattr; unsigned char * p; struct qstr q; - int status; + struct nfs4_lookup_arg args = { + .dir_fh = fhandle, + .name = &q, + .bitmask = nfs4_fattr_bitmap, + }; + struct nfs4_lookup_res res = { + .server = server, + .fattr = fattr, + .fh = fhandle, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP], + .rpc_argp = &args, + .rpc_resp = &res, + }; + int status; /* * Now we do a separate LOOKUP for each component of the mount path. * The LOOKUPs are done separately so that we can conveniently * catch an ERR_WRONGSEC if it occurs along the way... */ - p = server->mnt_path; - fattr->valid = 0; - nfs4_setup_compound(&compound, ops, server, "getrootfh"); - nfs4_setup_putrootfh(&compound); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fhandle); - if ((status = nfs4_call_compound(&compound, NULL, 0))) + status = nfs4_lookup_root(server, fhandle, info); + if (status) goto out; + + p = server->mnt_path; for (;;) { while (*p == '/') p++; @@ -869,12 +607,7 @@ nfs4_proc_get_root(struct nfs_server *se q.len = p - q.name; fattr->valid = 0; - nfs4_setup_compound(&compound, ops, server, "mount"); - nfs4_setup_putfh(&compound, fhandle); - nfs4_setup_lookup(&compound, &q); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fhandle); - status = nfs4_call_compound(&compound, NULL, 0); + status = rpc_call_sync(server->client, &msg, 0); if (!status) continue; if (status == -ENOENT) { @@ -883,24 +616,34 @@ nfs4_proc_get_root(struct nfs_server *se } break; } + if (status == 0) + status = nfs4_server_capabilities(server, fhandle); + if (status == 0) + status = nfs4_do_fsinfo(server, fhandle, info); out: - if (status) - return nfs4_map_errors(status); - return nfs4_proc_fsinfo(server, fhandle, info); + return nfs4_map_errors(status); } -static int -nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) +static int nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) { - struct nfs4_compound compound; - struct nfs4_op ops[2]; - + struct nfs_server *server = NFS_SERVER(inode); + struct nfs4_getattr_arg args = { + .fh = NFS_FH(inode), + .bitmask = server->attr_bitmask, + }; + struct nfs4_getattr_res res = { + .fattr = fattr, + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETATTR], + .rpc_argp = &args, + .rpc_resp = &res, + }; + fattr->valid = 0; - nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "getattr"); - nfs4_setup_putfh(&compound, NFS_FH(inode)); - nfs4_setup_getattr(&compound, fattr); - return nfs4_map_errors(nfs4_call_compound(&compound, NULL, 0)); + return nfs4_map_errors(rpc_call_sync(NFS_CLIENT(inode), &msg, 0)); } /* @@ -962,75 +705,72 @@ out: return status; } -static int -nfs4_proc_lookup(struct inode *dir, struct qstr *name, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) +static int nfs4_proc_lookup(struct inode *dir, struct qstr *name, + struct nfs_fh *fhandle, struct nfs_fattr *fattr) { - struct nfs4_compound compound; - struct nfs4_op ops[5]; - struct nfs_fattr dir_attr; - int status; - - dir_attr.valid = 0; + int status; + struct nfs_server *server = NFS_SERVER(dir); + struct nfs4_lookup_arg args = { + .bitmask = server->attr_bitmask, + .dir_fh = NFS_FH(dir), + .name = name, + }; + struct nfs4_lookup_res res = { + .server = server, + .fattr = fattr, + .fh = fhandle, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP], + .rpc_argp = &args, + .rpc_resp = &res, + }; + fattr->valid = 0; dprintk("NFS call lookup %s\n", name->name); - nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "lookup"); - nfs4_setup_putfh(&compound, NFS_FH(dir)); - nfs4_setup_getattr(&compound, &dir_attr); - nfs4_setup_lookup(&compound, name); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fhandle); - status = nfs4_call_compound(&compound, NULL, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); dprintk("NFS reply lookup: %d\n", status); - - if (status >= 0) - status = nfs_refresh_inode(dir, &dir_attr); return nfs4_map_errors(status); } -static int -nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) +static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) { - struct nfs4_compound compound; - struct nfs4_op ops[3]; - struct nfs_fattr fattr; - u32 req_access = 0, resp_supported, resp_access; int status; - - fattr.valid = 0; + struct nfs4_accessargs args = { + .fh = NFS_FH(inode), + }; + struct nfs4_accessres res = { 0 }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS], + .rpc_argp = &args, + .rpc_resp = &res, + .rpc_cred = cred, + }; /* * Determine which access bits we want to ask for... */ if (mode & MAY_READ) - req_access |= NFS4_ACCESS_READ; + args.access |= NFS4_ACCESS_READ; if (S_ISDIR(inode->i_mode)) { if (mode & MAY_WRITE) - req_access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE; + args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE; if (mode & MAY_EXEC) - req_access |= NFS4_ACCESS_LOOKUP; + args.access |= NFS4_ACCESS_LOOKUP; } else { if (mode & MAY_WRITE) - req_access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND; + args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND; if (mode & MAY_EXEC) - req_access |= NFS4_ACCESS_EXECUTE; + args.access |= NFS4_ACCESS_EXECUTE; } - - nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "access"); - nfs4_setup_putfh(&compound, NFS_FH(inode)); - nfs4_setup_getattr(&compound, &fattr); - nfs4_setup_access(&compound, req_access, &resp_supported, &resp_access); - status = nfs4_call_compound(&compound, cred, 0); - nfs_refresh_inode(inode, &fattr); - + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); if (!status) { - if (req_access != resp_supported) { + if (args.access != res.supported) { printk(KERN_NOTICE "NFS: server didn't support all access bits!\n"); status = -ENOTSUPP; - } - else if (req_access != resp_access) + } else if ((args.access & res.access) != args.access) status = -EACCES; } return nfs4_map_errors(status); @@ -1060,16 +800,20 @@ nfs4_proc_access(struct inode *inode, st * Both of these changes to the XDR layer would in fact be quite * minor, but I decided to leave them for a subsequent patch. */ -static int -nfs4_proc_readlink(struct inode *inode, struct page *page) +static int nfs4_proc_readlink(struct inode *inode, struct page *page) { - struct nfs4_compound compound; - struct nfs4_op ops[2]; + struct nfs4_readlink args = { + .fh = NFS_FH(inode), + .count = PAGE_CACHE_SIZE, + .pages = &page, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READLINK], + .rpc_argp = &args, + .rpc_resp = NULL, + }; - nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "readlink"); - nfs4_setup_putfh(&compound, NFS_FH(inode)); - nfs4_setup_readlink(&compound, PAGE_CACHE_SIZE, &page); - return nfs4_map_errors(nfs4_call_compound(&compound, NULL, 0)); + return nfs4_map_errors(rpc_call_sync(NFS_CLIENT(inode), &msg, 0)); } static int @@ -1096,10 +840,10 @@ nfs4_proc_read(struct nfs_read_data *rda if (filp) { struct nfs4_state *state; state = (struct nfs4_state *)filp->private_data; - nfs4_copy_stateid(&rdata->args.stateid, state, rdata->lockowner); + rdata->args.state = state; msg.rpc_cred = state->owner->so_cred; } else { - memcpy(&rdata->args.stateid, &zero_stateid, sizeof(rdata->args.stateid)); + rdata->args.state = NULL; msg.rpc_cred = NFS_I(inode)->mm_cred; } @@ -1134,10 +878,10 @@ nfs4_proc_write(struct nfs_write_data *w if (filp) { struct nfs4_state *state; state = (struct nfs4_state *)filp->private_data; - nfs4_copy_stateid(&wdata->args.stateid, state, wdata->lockowner); + wdata->args.state = state; msg.rpc_cred = state->owner->so_cred; } else { - memcpy(&wdata->args.stateid, &zero_stateid, sizeof(wdata->args.stateid)); + wdata->args.state = NULL; msg.rpc_cred = NFS_I(inode)->mm_cred; } @@ -1166,15 +910,10 @@ nfs4_proc_commit(struct nfs_write_data * /* * Try first to use O_WRONLY, then O_RDWR stateid. */ - if (filp) { - struct nfs4_state *state; - state = (struct nfs4_state *)filp->private_data; - nfs4_copy_stateid(&cdata->args.stateid, state, cdata->lockowner); - msg.rpc_cred = state->owner->so_cred; - } else { - memcpy(&cdata->args.stateid, &zero_stateid, sizeof(cdata->args.stateid)); + if (filp) + msg.rpc_cred = ((struct nfs4_state *)filp->private_data)->owner->so_cred; + else msg.rpc_cred = NFS_I(inode)->mm_cred; - } fattr->valid = 0; status = rpc_call_sync(server->client, &msg, 0); @@ -1226,301 +965,318 @@ nfs4_proc_create(struct inode *dir, stru return inode; } -static int -nfs4_proc_remove(struct inode *dir, struct qstr *name) +static int nfs4_proc_remove(struct inode *dir, struct qstr *name) { - struct nfs4_compound compound; - struct nfs4_op ops[3]; - struct nfs4_change_info dir_cinfo; - struct nfs_fattr dir_attr; + struct nfs4_remove_arg args = { + .fh = NFS_FH(dir), + .name = name, + }; + struct nfs4_change_info res; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE], + .rpc_argp = &args, + .rpc_resp = &res, + }; int status; - dir_attr.valid = 0; - nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "remove"); - nfs4_setup_putfh(&compound, NFS_FH(dir)); - nfs4_setup_remove(&compound, name, &dir_cinfo); - nfs4_setup_getattr(&compound, &dir_attr); - status = nfs4_call_compound(&compound, NULL, 0); - - if (!status) { - process_cinfo(&dir_cinfo, &dir_attr); - nfs_refresh_inode(dir, &dir_attr); - } + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + if (status == 0) + update_changeattr(dir, &res); return nfs4_map_errors(status); } struct unlink_desc { - struct nfs4_compound compound; - struct nfs4_op ops[3]; - struct nfs4_change_info cinfo; - struct nfs_fattr attrs; + struct nfs4_remove_arg args; + struct nfs4_change_info res; }; -static int -nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +static int nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, + struct qstr *name) { - struct unlink_desc * up; - struct nfs4_compound * cp; + struct unlink_desc *up; up = (struct unlink_desc *) kmalloc(sizeof(*up), GFP_KERNEL); if (!up) return -ENOMEM; - cp = &up->compound; - nfs4_setup_compound(cp, up->ops, NFS_SERVER(dir->d_inode), "unlink_setup"); - nfs4_setup_putfh(cp, NFS_FH(dir->d_inode)); - nfs4_setup_remove(cp, name, &up->cinfo); - nfs4_setup_getattr(cp, &up->attrs); + up->args.fh = NFS_FH(dir->d_inode); + up->args.name = name; - msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND]; - msg->rpc_argp = cp; - msg->rpc_resp = cp; + msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; + msg->rpc_argp = &up->args; + msg->rpc_resp = &up->res; return 0; } -static int -nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task) +static int nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task) { struct rpc_message *msg = &task->tk_msg; struct unlink_desc *up; - if (msg->rpc_argp) { - up = (struct unlink_desc *) msg->rpc_argp; - process_lease(&up->compound); - process_cinfo(&up->cinfo, &up->attrs); - nfs_refresh_inode(dir->d_inode, &up->attrs); + if (msg->rpc_resp != NULL) { + up = container_of(msg->rpc_resp, struct unlink_desc, res); + update_changeattr(dir->d_inode, &up->res); kfree(up); + msg->rpc_resp = NULL; msg->rpc_argp = NULL; } return 0; } -static int -nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, - struct inode *new_dir, struct qstr *new_name) +static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, + struct inode *new_dir, struct qstr *new_name) { - struct nfs4_compound compound; - struct nfs4_op ops[7]; - struct nfs4_change_info old_cinfo, new_cinfo; - struct nfs_fattr old_dir_attr, new_dir_attr; + struct nfs4_rename_arg arg = { + .old_dir = NFS_FH(old_dir), + .new_dir = NFS_FH(new_dir), + .old_name = old_name, + .new_name = new_name, + }; + struct nfs4_rename_res res = { }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; - - old_dir_attr.valid = 0; - new_dir_attr.valid = 0; - nfs4_setup_compound(&compound, ops, NFS_SERVER(old_dir), "rename"); - nfs4_setup_putfh(&compound, NFS_FH(old_dir)); - nfs4_setup_savefh(&compound); - nfs4_setup_putfh(&compound, NFS_FH(new_dir)); - nfs4_setup_rename(&compound, old_name, new_name, &old_cinfo, &new_cinfo); - nfs4_setup_getattr(&compound, &new_dir_attr); - nfs4_setup_restorefh(&compound); - nfs4_setup_getattr(&compound, &old_dir_attr); - status = nfs4_call_compound(&compound, NULL, 0); + status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0); if (!status) { - process_cinfo(&old_cinfo, &old_dir_attr); - process_cinfo(&new_cinfo, &new_dir_attr); - nfs_refresh_inode(old_dir, &old_dir_attr); - nfs_refresh_inode(new_dir, &new_dir_attr); + update_changeattr(old_dir, &res.old_cinfo); + update_changeattr(new_dir, &res.new_cinfo); } return nfs4_map_errors(status); } -static int -nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) +static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) { - struct nfs4_compound compound; - struct nfs4_op ops[7]; - struct nfs4_change_info dir_cinfo; - struct nfs_fattr dir_attr, fattr; + struct nfs4_link_arg arg = { + .fh = NFS_FH(inode), + .dir_fh = NFS_FH(dir), + .name = name, + }; + struct nfs4_change_info cinfo = { }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK], + .rpc_argp = &arg, + .rpc_resp = &cinfo, + }; int status; - - dir_attr.valid = 0; - fattr.valid = 0; - - nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "link"); - nfs4_setup_putfh(&compound, NFS_FH(inode)); - nfs4_setup_savefh(&compound); - nfs4_setup_putfh(&compound, NFS_FH(dir)); - nfs4_setup_link(&compound, name, &dir_cinfo); - nfs4_setup_getattr(&compound, &dir_attr); - nfs4_setup_restorefh(&compound); - nfs4_setup_getattr(&compound, &fattr); - status = nfs4_call_compound(&compound, NULL, 0); - if (!status) { - process_cinfo(&dir_cinfo, &dir_attr); - nfs_refresh_inode(dir, &dir_attr); - nfs_refresh_inode(inode, &fattr); - } + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); + if (!status) + update_changeattr(dir, &cinfo); + return nfs4_map_errors(status); } -static int -nfs4_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, - struct iattr *sattr, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - struct nfs4_compound compound; - struct nfs4_op ops[7]; - struct nfs_fattr dir_attr; - struct nfs4_change_info dir_cinfo; +static int nfs4_proc_symlink(struct inode *dir, struct qstr *name, + struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) +{ + struct nfs_server *server = NFS_SERVER(dir); + struct nfs4_create_arg arg = { + .dir_fh = NFS_FH(dir), + .server = server, + .name = name, + .attrs = sattr, + .ftype = NF4LNK, + .bitmask = server->attr_bitmask, + }; + struct nfs4_create_res res = { + .server = server, + .fh = fhandle, + .fattr = fattr, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; - dir_attr.valid = 0; + arg.u.symlink = path; fattr->valid = 0; - nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "symlink"); - nfs4_setup_putfh(&compound, NFS_FH(dir)); - nfs4_setup_savefh(&compound); - nfs4_setup_create_symlink(&compound, name, path, sattr, &dir_cinfo); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fhandle); - nfs4_setup_restorefh(&compound); - nfs4_setup_getattr(&compound, &dir_attr); - status = nfs4_call_compound(&compound, NULL, 0); - - if (!status) { - process_cinfo(&dir_cinfo, &dir_attr); - nfs_refresh_inode(dir, &dir_attr); - } + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + if (!status) + update_changeattr(dir, &res.dir_cinfo); return nfs4_map_errors(status); } -static int -nfs4_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) +static int nfs4_proc_mkdir(struct inode *dir, struct qstr *name, + struct iattr *sattr, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) { - struct nfs4_compound compound; - struct nfs4_op ops[7]; - struct nfs_fattr dir_attr; - struct nfs4_change_info dir_cinfo; + struct nfs_server *server = NFS_SERVER(dir); + struct nfs4_create_arg arg = { + .dir_fh = NFS_FH(dir), + .server = server, + .name = name, + .attrs = sattr, + .ftype = NF4DIR, + .bitmask = server->attr_bitmask, + }; + struct nfs4_create_res res = { + .server = server, + .fh = fhandle, + .fattr = fattr, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; - dir_attr.valid = 0; fattr->valid = 0; - nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "mkdir"); - nfs4_setup_putfh(&compound, NFS_FH(dir)); - nfs4_setup_savefh(&compound); - nfs4_setup_create_dir(&compound, name, sattr, &dir_cinfo); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fhandle); - nfs4_setup_restorefh(&compound); - nfs4_setup_getattr(&compound, &dir_attr); - status = nfs4_call_compound(&compound, NULL, 0); - - if (!status) { - process_cinfo(&dir_cinfo, &dir_attr); - nfs_refresh_inode(dir, &dir_attr); - } + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + if (!status) + update_changeattr(dir, &res.dir_cinfo); 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); } -static int -nfs4_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr, - dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr) +static int nfs4_proc_mknod(struct inode *dir, struct qstr *name, + struct iattr *sattr, dev_t rdev, struct nfs_fh *fh, + struct nfs_fattr *fattr) { - struct nfs4_compound compound; - struct nfs4_op ops[7]; - struct nfs_fattr dir_attr; - struct nfs4_change_info dir_cinfo; + struct nfs_server *server = NFS_SERVER(dir); + struct nfs4_create_arg arg = { + .dir_fh = NFS_FH(dir), + .server = server, + .name = name, + .attrs = sattr, + .bitmask = server->attr_bitmask, + }; + struct nfs4_create_res res = { + .server = server, + .fh = fh, + .fattr = fattr, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; + int mode = sattr->ia_mode; - dir_attr.valid = 0; fattr->valid = 0; - - nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "mknod"); - nfs4_setup_putfh(&compound, NFS_FH(dir)); - nfs4_setup_savefh(&compound); - nfs4_setup_create_special(&compound, name, rdev,sattr, &dir_cinfo); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fh); - nfs4_setup_restorefh(&compound); - nfs4_setup_getattr(&compound, &dir_attr); - status = nfs4_call_compound(&compound, NULL, 0); - if (!status) { - process_cinfo(&dir_cinfo, &dir_attr); - nfs_refresh_inode(dir, &dir_attr); + BUG_ON(!(sattr->ia_valid & ATTR_MODE)); + BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode)); + if (S_ISFIFO(mode)) + arg.ftype = NF4FIFO; + else if (S_ISBLK(mode)) { + arg.ftype = NF4BLK; + arg.u.device.specdata1 = MAJOR(rdev); + arg.u.device.specdata2 = MINOR(rdev); } + else if (S_ISCHR(mode)) { + arg.ftype = NF4CHR; + arg.u.device.specdata1 = MAJOR(rdev); + arg.u.device.specdata2 = MINOR(rdev); + } + else + arg.ftype = NF4SOCK; + + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + if (!status) + update_changeattr(dir, &res.dir_cinfo); return nfs4_map_errors(status); } -static int -nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, +static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat) { - struct nfs4_compound compound; - struct nfs4_op ops[2]; + struct nfs4_statfs_arg args = { + .fh = fhandle, + .bitmask = server->attr_bitmask, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_STATFS], + .rpc_argp = &args, + .rpc_resp = fsstat, + }; - nfs4_setup_compound(&compound, ops, server, "statfs"); - nfs4_setup_putfh(&compound, fhandle); - nfs4_setup_statfs(&compound, fsstat); - return nfs4_map_errors(nfs4_call_compound(&compound, NULL, 0)); + fsstat->fattr->valid = 0; + return nfs4_map_errors(rpc_call_sync(server->client, &msg, 0)); } -static int -nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *fsinfo) +static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fsinfo *fsinfo) { + struct nfs4_fsinfo_arg args = { + .fh = fhandle, + .bitmask = server->attr_bitmask, + }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO], - .rpc_argp = fhandle, + .rpc_argp = &args, .rpc_resp = fsinfo, }; return nfs4_map_errors(rpc_call_sync(server->client, &msg, 0)); } -static int -nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_pathconf *pathconf) +static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo) { - struct nfs4_compound compound; - struct nfs4_op ops[2]; - - nfs4_setup_compound(&compound, ops, server, "statfs"); - nfs4_setup_putfh(&compound, fhandle); - nfs4_setup_pathconf(&compound, pathconf); - return nfs4_map_errors(nfs4_call_compound(&compound, NULL, 0)); + fsinfo->fattr->valid = 0; + return nfs4_map_errors(nfs4_do_fsinfo(server, fhandle, fsinfo)); } -static void -nfs4_restart_read(struct rpc_task *task) +static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_pathconf *pathconf) { - struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; - struct nfs_page *req; + struct nfs4_pathconf_arg args = { + .fh = fhandle, + .bitmask = server->attr_bitmask, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF], + .rpc_argp = &args, + .rpc_resp = pathconf, + }; - rpc_restart_call(task); - req = nfs_list_entry(data->pages.next); - if (req->wb_state) - nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); - else - memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); + /* None of the pathconf attributes are mandatory to implement */ + if ((args.bitmask[0] & nfs4_pathconf_bitmap[0]) == 0) { + memset(pathconf, 0, sizeof(*pathconf)); + return 0; + } + + pathconf->fattr->valid = 0; + return nfs4_map_errors(rpc_call_sync(server->client, &msg, 0)); } static void @@ -1530,7 +1286,7 @@ nfs4_read_done(struct rpc_task *task) struct inode *inode = data->inode; if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { - task->tk_action = nfs4_restart_read; + rpc_restart_call(task); return; } if (task->tk_status > 0) @@ -1540,7 +1296,7 @@ nfs4_read_done(struct rpc_task *task) } static void -nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) +nfs4_proc_read_setup(struct nfs_read_data *data) { struct rpc_task *task = &data->task; struct rpc_message msg = { @@ -1550,59 +1306,26 @@ nfs4_proc_read_setup(struct nfs_read_dat .rpc_cred = data->cred, }; struct inode *inode = data->inode; - struct nfs_page *req = nfs_list_entry(data->pages.next); int flags; - data->args.fh = NFS_FH(inode); - data->args.offset = req_offset(req); - data->args.pgbase = req->wb_pgbase; - 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; - data->lockowner = req->wb_lockowner; - if (req->wb_state) - nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); - else - memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); - /* 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), nfs4_read_done, flags); - task->tk_calldata = data; - /* Release requests */ - task->tk_release = nfs_readdata_release; - rpc_call_setup(task, &msg, 0); } static void -nfs4_restart_write(struct rpc_task *task) -{ - struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata; - struct nfs_page *req; - - rpc_restart_call(task); - req = nfs_list_entry(data->pages.next); - if (req->wb_state) - nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); - else - memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); -} - -static void nfs4_write_done(struct rpc_task *task) { struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct inode *inode = data->inode; if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { - task->tk_action = nfs4_restart_write; + rpc_restart_call(task); return; } if (task->tk_status >= 0) @@ -1612,7 +1335,7 @@ nfs4_write_done(struct rpc_task *task) } static void -nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) +nfs4_proc_write_setup(struct nfs_write_data *data, int how) { struct rpc_task *task = &data->task; struct rpc_message msg = { @@ -1622,7 +1345,6 @@ nfs4_proc_write_setup(struct nfs_write_d .rpc_cred = data->cred, }; struct inode *inode = data->inode; - struct nfs_page *req = nfs_list_entry(data->pages.next); int stable; int flags; @@ -1633,33 +1355,15 @@ nfs4_proc_write_setup(struct nfs_write_d stable = NFS_DATA_SYNC; } else stable = NFS_UNSTABLE; - - data->args.fh = NFS_FH(inode); - data->args.offset = req_offset(req); - data->args.pgbase = req->wb_pgbase; - data->args.count = count; data->args.stable = stable; - data->args.pages = data->pagevec; - data->res.fattr = &data->fattr; - data->res.count = count; - data->res.verf = &data->verf; - data->timestamp = jiffies; - data->lockowner = req->wb_lockowner; - if (req->wb_state) - nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); - else - memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); + data->timestamp = jiffies; /* Set the initial flags for the task. */ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ rpc_init_task(task, NFS_CLIENT(inode), nfs4_write_done, flags); - task->tk_calldata = data; - /* Release requests */ - task->tk_release = nfs_writedata_release; - rpc_call_setup(task, &msg, 0); } @@ -1670,7 +1374,7 @@ nfs4_commit_done(struct rpc_task *task) struct inode *inode = data->inode; if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { - task->tk_action = nfs4_restart_write; + rpc_restart_call(task); return; } /* Call back common NFS writeback processing */ @@ -1678,7 +1382,7 @@ nfs4_commit_done(struct rpc_task *task) } static void -nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) +nfs4_proc_commit_setup(struct nfs_write_data *data, int how) { struct rpc_task *task = &data->task; struct rpc_message msg = { @@ -1690,22 +1394,11 @@ nfs4_proc_commit_setup(struct nfs_write_ struct inode *inode = data->inode; int flags; - data->args.fh = NFS_FH(data->inode); - data->args.offset = start; - data->args.count = len; - data->res.count = len; - data->res.fattr = &data->fattr; - data->res.verf = &data->verf; - /* Set the initial flags for the task. */ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ rpc_init_task(task, NFS_CLIENT(inode), nfs4_commit_done, flags); - task->tk_calldata = data; - /* Release requests */ - task->tk_release = nfs_commit_release; - rpc_call_setup(task, &msg, 0); } diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/nfs4state.c linux-2.6.5-31-server_caps/fs/nfs/nfs4state.c --- linux-2.6.5-rc2/fs/nfs/nfs4state.c 2004-03-25 00:29:32.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/nfs4state.c 2004-03-25 00:35:26.000000000 -0500 @@ -105,7 +105,7 @@ nfs4_alloc_client(struct in_addr *addr) INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp); INIT_LIST_HEAD(&clp->cl_superblocks); init_waitqueue_head(&clp->cl_waitq); - INIT_RPC_WAITQ(&clp->cl_rpcwaitq, "NFS4 client"); + rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client"); clp->cl_state = 1 << NFS4CLNT_NEW; } return clp; diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/nfs4xdr.c linux-2.6.5-31-server_caps/fs/nfs/nfs4xdr.c --- linux-2.6.5-rc2/fs/nfs/nfs4xdr.c 2004-03-25 00:30:14.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/nfs4xdr.c 2004-03-25 14:29:51.000000000 -0500 @@ -81,21 +81,14 @@ static int nfs_stat_to_errno(int); #define decode_putrootfh_maxsz (op_decode_hdr_maxsz) #define encode_getfh_maxsz (op_encode_hdr_maxsz) #define decode_getfh_maxsz (op_decode_hdr_maxsz + 1 + \ - (NFS4_FHSIZE >> 2)) + ((3+NFS4_FHSIZE) >> 2)) #define encode_getattr_maxsz (op_encode_hdr_maxsz + 3) -#define nfs4_fattr_bitmap_maxsz (26 + 2 * ((NFS4_MAXNAMLEN +1) >> 2)) +#define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2)) +#define nfs4_fattr_bitmap_maxsz (36 + 2 * nfs4_name_maxsz) #define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \ nfs4_fattr_bitmap_maxsz) #define encode_savefh_maxsz (op_encode_hdr_maxsz) #define decode_savefh_maxsz (op_decode_hdr_maxsz) -#define encode_restorefh_maxsz (op_encode_hdr_maxsz) -#define decode_restorefh_maxsz (op_decode_hdr_maxsz) -#define encode_read_getattr_maxsz (op_encode_hdr_maxsz + 2) -#define decode_read_getattr_maxsz (op_decode_hdr_maxsz + 8) -#define encode_pre_write_getattr_maxsz (op_encode_hdr_maxsz + 2) -#define decode_pre_write_getattr_maxsz (op_decode_hdr_maxsz + 5) -#define encode_post_write_getattr_maxsz (op_encode_hdr_maxsz + 2) -#define decode_post_write_getattr_maxsz (op_decode_hdr_maxsz + 13) #define encode_fsinfo_maxsz (op_encode_hdr_maxsz + 2) #define decode_fsinfo_maxsz (op_decode_hdr_maxsz + 11) #define encode_renew_maxsz (op_encode_hdr_maxsz + 3) @@ -115,54 +108,63 @@ static int nfs_stat_to_errno(int); 3 + (NFS4_VERIFIER_SIZE >> 2)) #define decode_setclientid_confirm_maxsz \ (op_decode_hdr_maxsz) - +#define encode_lookup_maxsz (op_encode_hdr_maxsz + \ + 1 + ((3 + NFS4_FHSIZE) >> 2)) +#define encode_remove_maxsz (op_encode_hdr_maxsz + \ + nfs4_name_maxsz) +#define encode_rename_maxsz (op_encode_hdr_maxsz + \ + 2 * nfs4_name_maxsz) +#define decode_rename_maxsz (op_decode_hdr_maxsz + 5 + 5) +#define encode_link_maxsz (op_encode_hdr_maxsz + \ + nfs4_name_maxsz) +#define decode_link_maxsz (op_decode_hdr_maxsz + 5) +#define encode_create_maxsz (op_encode_hdr_maxsz + \ + 2 + 2 * nfs4_name_maxsz + \ + nfs4_fattr_bitmap_maxsz) +#define decode_create_maxsz (op_decode_hdr_maxsz + 8) #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 + \ - encode_read_getattr_maxsz + \ op_encode_hdr_maxsz + 7) #define NFS4_dec_read_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - decode_read_getattr_maxsz + \ + op_decode_hdr_maxsz + 2) +#define NFS4_enc_readlink_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + op_encode_hdr_maxsz) +#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 + \ - encode_pre_write_getattr_maxsz + \ - op_encode_hdr_maxsz + 8 + \ - encode_post_write_getattr_maxsz) + op_encode_hdr_maxsz + 8) #define NFS4_dec_write_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - decode_pre_write_getattr_maxsz + \ - op_decode_hdr_maxsz + 4 + \ - decode_post_write_getattr_maxsz) + op_decode_hdr_maxsz + 4) #define NFS4_enc_commit_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - encode_pre_write_getattr_maxsz + \ - op_encode_hdr_maxsz + 3 + \ - encode_post_write_getattr_maxsz) + op_encode_hdr_maxsz + 3) #define NFS4_dec_commit_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - decode_pre_write_getattr_maxsz + \ - op_decode_hdr_maxsz + 2 + \ - decode_post_write_getattr_maxsz) + op_decode_hdr_maxsz + 2) #define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - encode_savefh_maxsz + \ op_encode_hdr_maxsz + \ 13 + 3 + 2 + 64 + \ encode_getattr_maxsz + \ - encode_getfh_maxsz + \ - encode_restorefh_maxsz + \ - encode_getattr_maxsz) + encode_getfh_maxsz) #define NFS4_dec_open_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - decode_savefh_maxsz + \ op_decode_hdr_maxsz + 4 + 5 + 2 + 3 + \ decode_getattr_maxsz + \ - decode_getfh_maxsz + \ - decode_restorefh_maxsz + \ - decode_getattr_maxsz) + decode_getfh_maxsz) #define NFS4_enc_open_confirm_sz \ (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ @@ -255,8 +257,88 @@ static int nfs_stat_to_errno(int); decode_putfh_maxsz + \ decode_getattr_maxsz + \ op_decode_hdr_maxsz + 4) - - +#define NFS4_enc_access_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + op_encode_hdr_maxsz + 1) +#define NFS4_dec_access_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + 2) +#define NFS4_enc_getattr_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_getattr_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_getattr_maxsz) +#define NFS4_enc_lookup_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_lookup_maxsz + \ + encode_getattr_maxsz + \ + encode_getfh_maxsz) +#define NFS4_dec_lookup_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + \ + decode_getattr_maxsz + \ + decode_getfh_maxsz) +#define NFS4_enc_lookup_root_sz (compound_encode_hdr_maxsz + \ + encode_putrootfh_maxsz + \ + encode_getattr_maxsz + \ + encode_getfh_maxsz) +#define NFS4_dec_lookup_root_sz (compound_decode_hdr_maxsz + \ + decode_putrootfh_maxsz + \ + decode_getattr_maxsz + \ + decode_getfh_maxsz) +#define NFS4_enc_remove_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_remove_maxsz) +#define NFS4_dec_remove_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + 5) +#define NFS4_enc_rename_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_savefh_maxsz + \ + encode_putfh_maxsz + \ + encode_rename_maxsz) +#define NFS4_dec_rename_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_savefh_maxsz + \ + decode_putfh_maxsz + \ + decode_rename_maxsz) +#define NFS4_enc_link_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_savefh_maxsz + \ + encode_putfh_maxsz + \ + encode_link_maxsz) +#define NFS4_dec_link_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_savefh_maxsz + \ + decode_putfh_maxsz + \ + decode_link_maxsz) +#define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_create_maxsz + \ + encode_getattr_maxsz + \ + encode_getfh_maxsz) +#define NFS4_dec_create_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_create_maxsz + \ + decode_getattr_maxsz + \ + decode_getfh_maxsz) +#define NFS4_enc_pathconf_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_pathconf_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_getattr_maxsz) +#define NFS4_enc_statfs_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_statfs_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + 12) +#define NFS4_enc_server_caps_sz (compound_encode_hdr_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \ + decode_getattr_maxsz) static struct { unsigned int mode; @@ -297,7 +379,7 @@ struct compound_hdr { *p++ = htonl((uint32_t)(n)); \ } while (0) #define WRITEMEM(ptr,nbytes) do { \ - p = xdr_writemem(p, ptr, nbytes); \ + p = xdr_encode_opaque_fixed(p, ptr, nbytes); \ } while (0) #define RESERVE_SPACE(nbytes) do { \ @@ -306,19 +388,7 @@ struct compound_hdr { BUG_ON(!p); \ } while (0) -static inline -uint32_t *xdr_writemem(uint32_t *p, const void *ptr, int nbytes) -{ - int tmp = XDR_QUADLEN(nbytes); - if (!tmp) - return p; - p[tmp-1] = 0; - memcpy(p, ptr, nbytes); - return p + tmp; -} - -static int -encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) +static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) { uint32_t *p; @@ -332,9 +402,7 @@ encode_compound_hdr(struct xdr_stream *x return 0; } -static int -encode_attrs(struct xdr_stream *xdr, struct iattr *iap, - struct nfs_server *server) +static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server) { char owner_name[IDMAP_NAMESZ]; char owner_group[IDMAP_NAMESZ]; @@ -461,20 +529,18 @@ encode_attrs(struct xdr_stream *xdr, str return status; } -static int -encode_access(struct xdr_stream *xdr, struct nfs4_access *access) +static int encode_access(struct xdr_stream *xdr, u32 access) { uint32_t *p; RESERVE_SPACE(8); WRITE32(OP_ACCESS); - WRITE32(access->ac_req_access); + WRITE32(access); return 0; } -static int -encode_close(struct xdr_stream *xdr, struct nfs_closeargs *arg) +static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg) { uint32_t *p; @@ -486,8 +552,7 @@ encode_close(struct xdr_stream *xdr, str return 0; } -static int -encode_commit(struct xdr_stream *xdr, struct nfs_writeargs *args) +static int encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args) { uint32_t *p; @@ -499,42 +564,39 @@ encode_commit(struct xdr_stream *xdr, st return 0; } -static int -encode_create(struct xdr_stream *xdr, struct nfs4_create *create, - struct nfs_server *server) +static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create) { uint32_t *p; RESERVE_SPACE(8); WRITE32(OP_CREATE); - WRITE32(create->cr_ftype); + WRITE32(create->ftype); - switch (create->cr_ftype) { + switch (create->ftype) { case NF4LNK: - RESERVE_SPACE(4 + create->cr_textlen); - WRITE32(create->cr_textlen); - WRITEMEM(create->cr_text, create->cr_textlen); + RESERVE_SPACE(4 + create->u.symlink->len); + WRITE32(create->u.symlink->len); + WRITEMEM(create->u.symlink->name, create->u.symlink->len); break; case NF4BLK: case NF4CHR: RESERVE_SPACE(8); - WRITE32(create->cr_specdata1); - WRITE32(create->cr_specdata2); + WRITE32(create->u.device.specdata1); + WRITE32(create->u.device.specdata2); break; default: break; } - RESERVE_SPACE(4 + create->cr_namelen); - WRITE32(create->cr_namelen); - WRITEMEM(create->cr_name, create->cr_namelen); + RESERVE_SPACE(4 + create->name->len); + WRITE32(create->name->len); + WRITEMEM(create->name->name, create->name->len); - return encode_attrs(xdr, create->cr_attrs, server); + return encode_attrs(xdr, create->attrs, create->server); } -static int -encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap) +static int encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap) { uint32_t *p; @@ -545,8 +607,7 @@ encode_getattr_one(struct xdr_stream *xd return 0; } -static int -encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1) +static int encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1) { uint32_t *p; @@ -558,56 +619,24 @@ encode_getattr_two(struct xdr_stream *xd return 0; } -static inline int -encode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr) -{ - return encode_getattr_two(xdr, getattr->gt_bmval[0], - getattr->gt_bmval[1]); -} - -/* - * Request the change attribute in order to check attribute+cache consistency - */ -static inline int -encode_read_getattr(struct xdr_stream *xdr) +static int encode_getfattr(struct xdr_stream *xdr, const u32* bitmask) { - return encode_getattr_two(xdr, FATTR4_WORD0_CHANGE, - FATTR4_WORD1_TIME_ACCESS); -} + extern u32 nfs4_fattr_bitmap[]; -/* - * Request the change attribute prior to doing a write operation - */ -static inline int -encode_pre_write_getattr(struct xdr_stream *xdr) -{ - /* Request the change attribute */ - return encode_getattr_one(xdr, FATTR4_WORD0_CHANGE); + return encode_getattr_two(xdr, + bitmask[0] & nfs4_fattr_bitmap[0], + bitmask[1] & nfs4_fattr_bitmap[1]); } -/* - * Request the change attribute, size, and [cm]time after a write operation - */ -static inline int -encode_post_write_getattr(struct xdr_stream *xdr) +static int encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask) { - return encode_getattr_two(xdr, FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE, - FATTR4_WORD1_SPACE_USED | - FATTR4_WORD1_TIME_METADATA | - FATTR4_WORD1_TIME_MODIFY); -} + extern u32 nfs4_fsinfo_bitmap[]; -static int -encode_fsinfo(struct xdr_stream *xdr) -{ - return encode_getattr_one(xdr, FATTR4_WORD0_MAXFILESIZE - | FATTR4_WORD0_MAXREAD - | FATTR4_WORD0_MAXWRITE - | FATTR4_WORD0_LEASE_TIME); + return encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0], + bitmask[1] & nfs4_fsinfo_bitmap[1]); } -static int -encode_getfh(struct xdr_stream *xdr) +static int encode_getfh(struct xdr_stream *xdr) { uint32_t *p; @@ -617,15 +646,14 @@ encode_getfh(struct xdr_stream *xdr) return 0; } -static int -encode_link(struct xdr_stream *xdr, struct nfs4_link *link) +static int encode_link(struct xdr_stream *xdr, const struct qstr *name) { uint32_t *p; - RESERVE_SPACE(8 + link->ln_namelen); + RESERVE_SPACE(8 + name->len); WRITE32(OP_LINK); - WRITE32(link->ln_namelen); - WRITEMEM(link->ln_name, link->ln_namelen); + WRITE32(name->len); + WRITEMEM(name->name, name->len); return 0; } @@ -634,8 +662,7 @@ encode_link(struct xdr_stream *xdr, stru * opcode,type,reclaim,offset,length,new_lock_owner = 32 * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40 */ -static int -encode_lock(struct xdr_stream *xdr, struct nfs_lockargs *arg) +static int encode_lock(struct xdr_stream *xdr, const struct nfs_lockargs *arg) { uint32_t *p; struct nfs_lock_opargs *opargs = arg->u.lock; @@ -669,8 +696,7 @@ encode_lock(struct xdr_stream *xdr, stru return 0; } -static int -encode_lockt(struct xdr_stream *xdr, struct nfs_lockargs *arg) +static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockargs *arg) { uint32_t *p; struct nfs_lowner *opargs = arg->u.lockt; @@ -687,8 +713,7 @@ encode_lockt(struct xdr_stream *xdr, str return 0; } -static int -encode_locku(struct xdr_stream *xdr, struct nfs_lockargs *arg) +static int encode_locku(struct xdr_stream *xdr, const struct nfs_lockargs *arg) { uint32_t *p; struct nfs_locku_opargs *opargs = arg->u.locku; @@ -704,22 +729,20 @@ encode_locku(struct xdr_stream *xdr, str return 0; } -static int -encode_lookup(struct xdr_stream *xdr, struct nfs4_lookup *lookup) +static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name) { - int len = lookup->lo_name->len; + int len = name->len; uint32_t *p; RESERVE_SPACE(8 + len); WRITE32(OP_LOOKUP); WRITE32(len); - WRITEMEM(lookup->lo_name->name, len); + WRITEMEM(name->name, len); return 0; } -static int -encode_open(struct xdr_stream *xdr, struct nfs_openargs *arg) +static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg) { int status; uint32_t *p; @@ -778,8 +801,7 @@ encode_open(struct xdr_stream *xdr, stru return 0; } -static int -encode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmargs *arg) +static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg) { uint32_t *p; @@ -792,8 +814,7 @@ encode_open_confirm(struct xdr_stream *x } -static int -encode_open_reclaim(struct xdr_stream *xdr, struct nfs_open_reclaimargs *arg) +static int encode_open_reclaim(struct xdr_stream *xdr, const struct nfs_open_reclaimargs *arg) { uint32_t *p; @@ -827,8 +848,7 @@ encode_open_reclaim(struct xdr_stream *x return 0; } -static int -encode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeargs *arg) +static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg) { uint32_t *p; @@ -844,7 +864,7 @@ encode_open_downgrade(struct xdr_stream } static int -encode_putfh(struct xdr_stream *xdr, struct nfs_fh *fh) +encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh) { int len = fh->size; uint32_t *p; @@ -857,8 +877,7 @@ encode_putfh(struct xdr_stream *xdr, str return 0; } -static int -encode_putrootfh(struct xdr_stream *xdr) +static int encode_putrootfh(struct xdr_stream *xdr) { uint32_t *p; @@ -868,22 +887,37 @@ encode_putrootfh(struct xdr_stream *xdr) return 0; } -static int -encode_read(struct xdr_stream *xdr, struct nfs_readargs *args) +static void encode_stateid(struct xdr_stream *xdr, struct nfs4_state *state, fl_owner_t lockowner) { + extern nfs4_stateid zero_stateid; + nfs4_stateid stateid; uint32_t *p; - RESERVE_SPACE(32); + RESERVE_SPACE(16); + if (state != NULL) { + nfs4_copy_stateid(&stateid, state, lockowner); + WRITEMEM(stateid.data, sizeof(stateid.data)); + } else + WRITEMEM(zero_stateid.data, sizeof(zero_stateid.data)); +} + +static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args) +{ + uint32_t *p; + + RESERVE_SPACE(4); WRITE32(OP_READ); - WRITEMEM(args->stateid.data, sizeof(args->stateid.data)); + + encode_stateid(xdr, args->state, args->lockowner); + + RESERVE_SPACE(12); WRITE64(args->offset); WRITE32(args->count); 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; @@ -891,27 +925,26 @@ encode_readdir(struct xdr_stream *xdr, s 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; } -static int -encode_readlink(struct xdr_stream *xdr, struct nfs4_readlink *readlink, struct rpc_rqst *req) +static int encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req) { struct rpc_auth *auth = req->rq_task->tk_auth; int replen; @@ -925,43 +958,40 @@ encode_readlink(struct xdr_stream *xdr, * + OP_READLINK + status = 7 */ replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, readlink->rl_pages, 0, readlink->rl_count); + xdr_inline_pages(&req->rq_rcv_buf, replen, readlink->pages, 0, readlink->count); return 0; } -static int -encode_remove(struct xdr_stream *xdr, struct nfs4_remove *remove) +static int encode_remove(struct xdr_stream *xdr, const struct qstr *name) { uint32_t *p; - RESERVE_SPACE(8 + remove->rm_namelen); + RESERVE_SPACE(8 + name->len); WRITE32(OP_REMOVE); - WRITE32(remove->rm_namelen); - WRITEMEM(remove->rm_name, remove->rm_namelen); + WRITE32(name->len); + WRITEMEM(name->name, name->len); return 0; } -static int -encode_rename(struct xdr_stream *xdr, struct nfs4_rename *rename) +static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname) { uint32_t *p; - RESERVE_SPACE(8 + rename->rn_oldnamelen); + RESERVE_SPACE(8 + oldname->len); WRITE32(OP_RENAME); - WRITE32(rename->rn_oldnamelen); - WRITEMEM(rename->rn_oldname, rename->rn_oldnamelen); + WRITE32(oldname->len); + WRITEMEM(oldname->name, oldname->len); - RESERVE_SPACE(4 + rename->rn_newnamelen); - WRITE32(rename->rn_newnamelen); - WRITEMEM(rename->rn_newname, rename->rn_newnamelen); + RESERVE_SPACE(4 + newname->len); + WRITE32(newname->len); + WRITEMEM(newname->name, newname->len); return 0; } -static int -encode_renew(struct xdr_stream *xdr, struct nfs4_client *client_stateid) +static int encode_renew(struct xdr_stream *xdr, const struct nfs4_client *client_stateid) { uint32_t *p; @@ -973,17 +1003,6 @@ encode_renew(struct xdr_stream *xdr, str } static int -encode_restorefh(struct xdr_stream *xdr) -{ - uint32_t *p; - - RESERVE_SPACE(4); - WRITE32(OP_RESTOREFH); - - return 0; -} - -static int encode_savefh(struct xdr_stream *xdr) { uint32_t *p; @@ -994,9 +1013,7 @@ encode_savefh(struct xdr_stream *xdr) return 0; } -static int -encode_setattr(struct xdr_stream *xdr, struct nfs_setattrargs *arg, - struct nfs_server *server) +static int encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server) { int status; uint32_t *p; @@ -1011,8 +1028,7 @@ encode_setattr(struct xdr_stream *xdr, s return 0; } -static int -encode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid *setclientid) +static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid) { uint32_t total_len; uint32_t len1, len2, len3; @@ -1039,8 +1055,7 @@ encode_setclientid(struct xdr_stream *xd return 0; } -static int -encode_setclientid_confirm(struct xdr_stream *xdr, struct nfs4_client *client_state) +static int encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs4_client *client_state) { uint32_t *p; @@ -1052,14 +1067,16 @@ encode_setclientid_confirm(struct xdr_st return 0; } -static int -encode_write(struct xdr_stream *xdr, struct nfs_writeargs *args) +static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args) { uint32_t *p; - RESERVE_SPACE(36); + RESERVE_SPACE(4); WRITE32(OP_WRITE); - WRITEMEM(args->stateid.data, sizeof(args->stateid.data)); + + encode_stateid(xdr, args->state, args->lockowner); + + RESERVE_SPACE(16); WRITE64(args->offset); WRITE32(args->stable); WRITE32(args->count); @@ -1068,297 +1085,409 @@ encode_write(struct xdr_stream *xdr, str 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_ACCESS: - status = encode_access(xdr, &cp->ops[i].u.access); - break; - case OP_CREATE: - status = encode_create(xdr, &cp->ops[i].u.create, cp->server); - break; - case OP_GETATTR: - status = encode_getattr(xdr, &cp->ops[i].u.getattr); - break; - case OP_GETFH: - status = encode_getfh(xdr); - break; - case OP_LINK: - status = encode_link(xdr, &cp->ops[i].u.link); - break; - case OP_LOOKUP: - status = encode_lookup(xdr, &cp->ops[i].u.lookup); - break; - case OP_PUTFH: - status = encode_putfh(xdr, cp->ops[i].u.putfh.pf_fhandle); - break; - case OP_PUTROOTFH: - status = encode_putrootfh(xdr); - break; - case OP_READDIR: - status = encode_readdir(xdr, &cp->ops[i].u.readdir, req); - break; - case OP_READLINK: - status = encode_readlink(xdr, &cp->ops[i].u.readlink, req); - break; - case OP_REMOVE: - status = encode_remove(xdr, &cp->ops[i].u.remove); - break; - case OP_RENAME: - status = encode_rename(xdr, &cp->ops[i].u.rename); - break; - case OP_RESTOREFH: - status = encode_restorefh(xdr); - break; - case OP_SAVEFH: - status = encode_savefh(xdr); - break; - default: - BUG(); - } - if (status) - return status; - } - - return 0; -} /* * END OF "GENERIC" ENCODE ROUTINES. */ - /* - * Encode COMPOUND argument + * Encode an ACCESS request */ -static int -nfs4_xdr_enc_compound(struct rpc_rqst *req, uint32_t *p, struct nfs4_compound *cp) +static int nfs4_xdr_enc_access(struct rpc_rqst *req, uint32_t *p, const struct nfs4_accessargs *args) { struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; int status; - + xdr_init_encode(&xdr, &req->rq_snd_buf, p); - status = encode_compound(&xdr, cp, req); - cp->timestamp = jiffies; + encode_compound_hdr(&xdr, &hdr); + if ((status = encode_putfh(&xdr, args->fh)) == 0) + status = encode_access(&xdr, args->access); return status; } -/* - * Encode a CLOSE request - */ -static int -nfs4_xdr_enc_close(struct rpc_rqst *req, uint32_t *p, struct nfs_closeargs *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_close(&xdr, args); -out: - return status; -} /* - * Encode an OPEN request + * Encode LOOKUP request */ -static int -nfs4_xdr_enc_open(struct rpc_rqst *req, uint32_t *p, struct nfs_openargs *args) +static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, uint32_t *p, const struct nfs4_lookup_arg *args) { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 7, + .nops = 4, }; 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_savefh(&xdr); - if (status) - goto out; - status = encode_open(&xdr, args); - if (status) - goto out; - status = encode_getattr(&xdr, args->f_getattr); - if (status) + if ((status = encode_putfh(&xdr, args->dir_fh)) != 0) goto out; - status = encode_getfh(&xdr); - if (status) + if ((status = encode_lookup(&xdr, args->name)) != 0) goto out; - status = encode_restorefh(&xdr); - if (status) + if ((status = encode_getfh(&xdr)) != 0) goto out; - status = encode_getattr(&xdr, args->d_getattr); + status = encode_getfattr(&xdr, args->bitmask); out: return status; } /* - * Encode an OPEN_CONFIRM request + * Encode LOOKUP_ROOT request */ -static int -nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs_open_confirmargs *args) +static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, uint32_t *p, const struct nfs4_lookup_root_arg *args) { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 2, + .nops = 3, }; int status; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); - status = encode_putfh(&xdr, args->fh); - if(status) + if ((status = encode_putrootfh(&xdr)) != 0) goto out; - status = encode_open_confirm(&xdr, args); + if ((status = encode_getfh(&xdr)) == 0) + status = encode_getfattr(&xdr, args->bitmask); out: return status; } /* - * Encode an OPEN request + * Encode REMOVE request */ -static int -nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p, - struct nfs_open_reclaimargs *args) +static int nfs4_xdr_enc_remove(struct rpc_rqst *req, uint32_t *p, const struct nfs4_remove_arg *args) { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 3, + .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_open_reclaim(&xdr, args); - if (status) - goto out; - status = encode_getattr(&xdr, args->f_getattr); -out: + if ((status = encode_putfh(&xdr, args->fh)) == 0) + status = encode_remove(&xdr, args->name); return status; } /* - * Encode an OPEN_DOWNGRADE request + * Encode RENAME request */ -static int -nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, uint32_t *p, struct nfs_closeargs *args) +static int nfs4_xdr_enc_rename(struct rpc_rqst *req, uint32_t *p, const struct nfs4_rename_arg *args) { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 2, + .nops = 4, }; int status; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); - status = encode_putfh(&xdr, args->fh); - if (status) + if ((status = encode_putfh(&xdr, args->old_dir)) != 0) goto out; - status = encode_open_downgrade(&xdr, args); + if ((status = encode_savefh(&xdr)) != 0) + goto out; + if ((status = encode_putfh(&xdr, args->new_dir)) != 0) + goto out; + status = encode_rename(&xdr, args->old_name, args->new_name); out: return status; } /* - * Encode a LOCK request + * Encode LINK request */ -static int -nfs4_xdr_enc_lock(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +static int nfs4_xdr_enc_link(struct rpc_rqst *req, uint32_t *p, const struct nfs4_link_arg *args) { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 2, + .nops = 4, }; int status; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); - status = encode_putfh(&xdr, args->fh); - if(status) + if ((status = encode_putfh(&xdr, args->fh)) != 0) goto out; - status = encode_lock(&xdr, args); + if ((status = encode_savefh(&xdr)) != 0) + goto out; + if ((status = encode_putfh(&xdr, args->dir_fh)) != 0) + goto out; + status = encode_link(&xdr, args->name); out: return status; } /* - * Encode a LOCKT request + * Encode CREATE request */ -static int -nfs4_xdr_enc_lockt(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +static int nfs4_xdr_enc_create(struct rpc_rqst *req, uint32_t *p, const struct nfs4_create_arg *args) { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 2, + .nops = 4, }; int status; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); - status = encode_putfh(&xdr, args->fh); - if(status) + if ((status = encode_putfh(&xdr, args->dir_fh)) != 0) goto out; - status = encode_lockt(&xdr, args); + if ((status = encode_create(&xdr, args)) != 0) + goto out; + if ((status = encode_getfattr(&xdr, args->bitmask)) != 0) + goto out; + status = encode_getfh(&xdr); out: return status; } /* - * Encode a LOCKU request + * Encode GETATTR request */ -static int -nfs4_xdr_enc_locku(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, uint32_t *p, const struct nfs4_getattr_arg *args) { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 2, + .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_locku(&xdr, args); -out: + if ((status = encode_putfh(&xdr, args->fh)) == 0) + status = encode_getfattr(&xdr, args->bitmask); return status; } /* - * Encode a READ request + * Encode a CLOSE request */ -static int -nfs4_xdr_enc_read(struct rpc_rqst *req, uint32_t *p, struct nfs_readargs *args) +static int nfs4_xdr_enc_close(struct rpc_rqst *req, uint32_t *p, struct nfs_closeargs *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_close(&xdr, args); +out: + return status; +} + +/* + * Encode an OPEN request + */ +static int nfs4_xdr_enc_open(struct rpc_rqst *req, uint32_t *p, struct nfs_openargs *args) { - struct rpc_auth *auth = req->rq_task->tk_auth; struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 3, + .nops = 4, + }; + 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_open(&xdr, args); + if (status) + goto out; + status = encode_getfattr(&xdr, args->bitmask); + if (status) + goto out; + status = encode_getfh(&xdr); +out: + return status; +} + +/* + * Encode an OPEN_CONFIRM request + */ +static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs_open_confirmargs *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_open_confirm(&xdr, args); +out: + return status; +} + +/* + * Encode an OPEN request + */ +static int nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p, struct nfs_open_reclaimargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 3, + }; + 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_open_reclaim(&xdr, args); + if (status) + goto out; + status = encode_getfattr(&xdr, args->bitmask); +out: + return status; +} + +/* + * Encode an OPEN_DOWNGRADE request + */ +static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, uint32_t *p, struct nfs_closeargs *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_open_downgrade(&xdr, args); +out: + return status; +} + +/* + * Encode a LOCK request + */ +static int nfs4_xdr_enc_lock(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *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_lock(&xdr, args); +out: + return status; +} + +/* + * Encode a LOCKT request + */ +static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *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_lockt(&xdr, args); +out: + return status; +} + +/* + * Encode a LOCKU request + */ +static int nfs4_xdr_enc_locku(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *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_locku(&xdr, args); +out: + return status; +} + +/* + * Encode a READLINK request + */ +static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, uint32_t *p, const struct nfs4_readlink *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_readlink(&xdr, args, req); +out: + 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 + */ +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; @@ -1370,14 +1499,12 @@ nfs4_xdr_enc_read(struct rpc_rqst *req, status = encode_read(&xdr, args); if (status) goto out; - status = encode_read_getattr(&xdr); /* 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 - decode_read_getattr_maxsz) << 2; + 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: @@ -1387,8 +1514,7 @@ out: /* * Encode an SETATTR request */ -static int -nfs4_xdr_enc_setattr(struct rpc_rqst *req, uint32_t *p, struct nfs_setattrargs *args) +static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, uint32_t *p, struct nfs_setattrargs *args) { struct xdr_stream xdr; @@ -1405,7 +1531,7 @@ nfs4_xdr_enc_setattr(struct rpc_rqst *re status = encode_setattr(&xdr, args, args->server); if(status) goto out; - status = encode_getattr(&xdr, args->attr); + status = encode_getfattr(&xdr, args->bitmask); out: return status; } @@ -1413,12 +1539,11 @@ out: /* * Encode a WRITE request */ -static int -nfs4_xdr_enc_write(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args) +static int nfs4_xdr_enc_write(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args) { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 4, + .nops = 2, }; int status; @@ -1427,13 +1552,7 @@ nfs4_xdr_enc_write(struct rpc_rqst *req, status = encode_putfh(&xdr, args->fh); if (status) goto out; - status = encode_pre_write_getattr(&xdr); - if (status) - goto out; status = encode_write(&xdr, args); - if (status) - goto out; - status = encode_post_write_getattr(&xdr); out: return status; } @@ -1441,12 +1560,11 @@ out: /* * a COMMIT request */ -static int -nfs4_xdr_enc_commit(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args) +static int nfs4_xdr_enc_commit(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args) { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 4, + .nops = 2, }; int status; @@ -1455,13 +1573,7 @@ nfs4_xdr_enc_commit(struct rpc_rqst *req status = encode_putfh(&xdr, args->fh); if (status) goto out; - status = encode_pre_write_getattr(&xdr); - if (status) - goto out; status = encode_commit(&xdr, args); - if (status) - goto out; - status = encode_post_write_getattr(&xdr); out: return status; } @@ -1469,8 +1581,7 @@ out: /* * FSINFO request */ -static int -nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, uint32_t *p, void *fhandle) +static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, uint32_t *p, struct nfs4_fsinfo_arg *args) { struct xdr_stream xdr; struct compound_hdr hdr = { @@ -1480,17 +1591,81 @@ nfs4_xdr_enc_fsinfo(struct rpc_rqst *req xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); - status = encode_putfh(&xdr, fhandle); + status = encode_putfh(&xdr, args->fh); + if (!status) + status = encode_fsinfo(&xdr, args->bitmask); + return status; +} + +/* + * a PATHCONF request + */ +static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, uint32_t *p, const struct nfs4_pathconf_arg *args) +{ + extern u32 nfs4_pathconf_bitmap[2]; + 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) - status = encode_fsinfo(&xdr); + status = encode_getattr_one(&xdr, + args->bitmask[0] & nfs4_pathconf_bitmap[0]); + return status; +} + +/* + * a STATFS request + */ +static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, uint32_t *p, const struct nfs4_statfs_arg *args) +{ + extern u32 nfs4_statfs_bitmap[]; + 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 == 0) + status = encode_getattr_two(&xdr, + args->bitmask[0] & nfs4_statfs_bitmap[0], + args->bitmask[1] & nfs4_statfs_bitmap[1]); + return status; +} + +/* + * GETATTR_BITMAP request + */ +static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, uint32_t *p, const struct nfs_fh *fhandle) +{ + 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, fhandle); + if (status == 0) + status = encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS| + FATTR4_WORD0_LINK_SUPPORT| + FATTR4_WORD0_SYMLINK_SUPPORT| + FATTR4_WORD0_ACLSUPPORT); return status; } /* * a RENEW request */ -static int -nfs4_xdr_enc_renew(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) +static int nfs4_xdr_enc_renew(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) { struct xdr_stream xdr; struct compound_hdr hdr = { @@ -1505,9 +1680,7 @@ nfs4_xdr_enc_renew(struct rpc_rqst *req, /* * a SETCLIENTID request */ -static int -nfs4_xdr_enc_setclientid(struct rpc_rqst *req, uint32_t *p, - struct nfs4_setclientid *sc) +static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, uint32_t *p, struct nfs4_setclientid *sc) { struct xdr_stream xdr; struct compound_hdr hdr = { @@ -1522,14 +1695,13 @@ nfs4_xdr_enc_setclientid(struct rpc_rqst /* * a SETCLIENTID_CONFIRM request */ -static int -nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, - struct nfs4_client *clp) +static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) { struct xdr_stream xdr; struct compound_hdr hdr = { .nops = 3, }; + const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 }; int status; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1538,7 +1710,7 @@ nfs4_xdr_enc_setclientid_confirm(struct if (!status) status = encode_putrootfh(&xdr); if (!status) - status = encode_fsinfo(&xdr); + status = encode_fsinfo(&xdr, lease_bitmap); return status; } @@ -1552,15 +1724,6 @@ nfs4_xdr_enc_setclientid_confirm(struct * task to translate them into Linux-specific versions which are more * consistent with the style used in NFSv2/v3... */ -#define DECODE_TAIL \ - status = 0; \ -out: \ - return status; \ -xdr_error: \ - printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ - status = -EIO; \ - goto out - #define READ32(x) (x) = ntohl(*p++) #define READ64(x) do { \ (x) = (u64)ntohl(*p++) << 32; \ @@ -1585,8 +1748,7 @@ xdr_error: \ } \ } while (0) -static int -decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) +static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) { uint32_t *p; @@ -1601,30 +1763,616 @@ decode_compound_hdr(struct xdr_stream *x return 0; } -static int -decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected) +static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected) +{ + uint32_t *p; + uint32_t opnum; + int32_t nfserr; + + READ_BUF(8); + READ32(opnum); + if (opnum != expected) { + printk(KERN_NOTICE + "nfs4_decode_op_hdr: Server returned operation" + " %d but we issued a request for %d\n", + opnum, expected); + return -EIO; + } + READ32(nfserr); + if (nfserr != NFS_OK) + return -nfs_stat_to_errno(nfserr); + return 0; +} + +static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap) +{ + uint32_t bmlen, *p; + + READ_BUF(4); + READ32(bmlen); + + bitmap[0] = bitmap[1] = 0; + READ_BUF((bmlen << 2)); + if (bmlen > 0) { + READ32(bitmap[0]); + if (bmlen > 1) + READ32(bitmap[1]); + } + return 0; +} + +static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, uint32_t **savep) +{ + uint32_t *p; + + READ_BUF(4); + READ32(*attrlen); + *savep = xdr->p; + return 0; +} + +static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask) +{ + if (likely(bitmap[0] & FATTR4_WORD0_SUPPORTED_ATTRS)) { + decode_attr_bitmap(xdr, bitmask); + bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS; + } else + bitmask[0] = bitmask[1] = 0; + dprintk("%s: bitmask=0x%x%x\n", __FUNCTION__, bitmask[0], bitmask[1]); + return 0; +} + +static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *type) +{ + uint32_t *p; + + *type = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_TYPE - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_TYPE)) { + READ_BUF(4); + READ32(*type); + if (*type < NF4REG || *type > NF4NAMEDATTR) { + dprintk("%s: bad type %d\n", __FUNCTION__, *type); + return -EIO; + } + bitmap[0] &= ~FATTR4_WORD0_TYPE; + } + dprintk("%s: type=0%o\n", __FUNCTION__, nfs_type2fmt[*type].nfs2type); + return 0; +} + +static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *change) +{ + uint32_t *p; + + *change = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_CHANGE - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_CHANGE)) { + READ_BUF(8); + READ64(*change); + bitmap[0] &= ~FATTR4_WORD0_CHANGE; + } + dprintk("%s: change attribute=%Lu\n", __FUNCTION__, + (unsigned long long)*change); + return 0; +} + +static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *size) +{ + uint32_t *p; + + *size = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_SIZE - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_SIZE)) { + READ_BUF(8); + READ64(*size); + bitmap[0] &= ~FATTR4_WORD0_SIZE; + } + dprintk("%s: file size=%Lu\n", __FUNCTION__, (unsigned long long)*size); + return 0; +} + +static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) +{ + uint32_t *p; + + *res = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_LINK_SUPPORT - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_LINK_SUPPORT)) { + READ_BUF(4); + READ32(*res); + bitmap[0] &= ~FATTR4_WORD0_LINK_SUPPORT; + } + dprintk("%s: link support=%s\n", __FUNCTION__, *res == 0 ? "false" : "true"); + return 0; +} + +static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) +{ + uint32_t *p; + + *res = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_SYMLINK_SUPPORT - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_SYMLINK_SUPPORT)) { + READ_BUF(4); + READ32(*res); + bitmap[0] &= ~FATTR4_WORD0_SYMLINK_SUPPORT; + } + dprintk("%s: symlink support=%s\n", __FUNCTION__, *res == 0 ? "false" : "true"); + return 0; +} + +static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fsid *fsid) +{ + uint32_t *p; + + fsid->major = 0; + fsid->minor = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_FSID - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_FSID)) { + READ_BUF(16); + READ64(fsid->major); + READ64(fsid->minor); + bitmap[0] &= ~FATTR4_WORD0_FSID; + } + dprintk("%s: fsid=(0x%Lx/0x%Lx)\n", __FUNCTION__, + (unsigned long long)fsid->major, + (unsigned long long)fsid->minor); + return 0; +} + +static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) +{ + uint32_t *p; + + *res = 60; + if (unlikely(bitmap[0] & (FATTR4_WORD0_LEASE_TIME - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_LEASE_TIME)) { + READ_BUF(4); + READ32(*res); + bitmap[0] &= ~FATTR4_WORD0_LEASE_TIME; + } + dprintk("%s: file size=%u\n", __FUNCTION__, (unsigned int)*res); + return 0; +} + +static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) +{ + uint32_t *p; + + *res = ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL; + if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) { + READ_BUF(4); + READ32(*res); + bitmap[0] &= ~FATTR4_WORD0_ACLSUPPORT; + } + dprintk("%s: ACLs supported=%u\n", __FUNCTION__, (unsigned int)*res); + return 0; +} + +static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid) +{ + uint32_t *p; + + *fileid = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_FILEID - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_FILEID)) { + READ_BUF(8); + READ64(*fileid); + bitmap[0] &= ~FATTR4_WORD0_FILEID; + } + dprintk("%s: fileid=%Lu\n", __FUNCTION__, (unsigned long long)*fileid); + return 0; +} + +static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) +{ + uint32_t *p; + int status = 0; + + *res = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_AVAIL - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_FILES_AVAIL)) { + READ_BUF(8); + READ64(*res); + bitmap[0] &= ~FATTR4_WORD0_FILES_AVAIL; + } + dprintk("%s: files avail=%Lu\n", __FUNCTION__, (unsigned long long)*res); + return status; +} + +static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) +{ + uint32_t *p; + int status = 0; + + *res = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_FREE - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_FILES_FREE)) { + READ_BUF(8); + READ64(*res); + bitmap[0] &= ~FATTR4_WORD0_FILES_FREE; + } + dprintk("%s: files free=%Lu\n", __FUNCTION__, (unsigned long long)*res); + return status; +} + +static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) +{ + uint32_t *p; + int status = 0; + + *res = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_TOTAL - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_FILES_TOTAL)) { + READ_BUF(8); + READ64(*res); + bitmap[0] &= ~FATTR4_WORD0_FILES_TOTAL; + } + dprintk("%s: files total=%Lu\n", __FUNCTION__, (unsigned long long)*res); + return status; +} + +static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) +{ + uint32_t *p; + int status = 0; + + *res = 0; + if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXFILESIZE - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_MAXFILESIZE)) { + READ_BUF(8); + READ64(*res); + bitmap[0] &= ~FATTR4_WORD0_MAXFILESIZE; + } + dprintk("%s: maxfilesize=%Lu\n", __FUNCTION__, (unsigned long long)*res); + return status; +} + +static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxlink) +{ + uint32_t *p; + int status = 0; + + *maxlink = 1; + if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXLINK - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_MAXLINK)) { + READ_BUF(4); + READ32(*maxlink); + bitmap[0] &= ~FATTR4_WORD0_MAXLINK; + } + dprintk("%s: maxlink=%u\n", __FUNCTION__, *maxlink); + return status; +} + +static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxname) +{ + uint32_t *p; + int status = 0; + + *maxname = 1024; + if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXNAME - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_MAXNAME)) { + READ_BUF(4); + READ32(*maxname); + bitmap[0] &= ~FATTR4_WORD0_MAXNAME; + } + dprintk("%s: maxname=%u\n", __FUNCTION__, *maxname); + return status; +} + +static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) +{ + uint32_t *p; + int status = 0; + + *res = 1024; + if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXREAD - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_MAXREAD)) { + uint64_t maxread; + READ_BUF(8); + READ64(maxread); + if (maxread > 0x7FFFFFFF) + maxread = 0x7FFFFFFF; + *res = (uint32_t)maxread; + bitmap[0] &= ~FATTR4_WORD0_MAXREAD; + } + dprintk("%s: maxread=%lu\n", __FUNCTION__, (unsigned long)*res); + return status; +} + +static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) +{ + uint32_t *p; + int status = 0; + + *res = 1024; + if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXWRITE - 1U))) + return -EIO; + if (likely(bitmap[0] & FATTR4_WORD0_MAXWRITE)) { + uint64_t maxwrite; + READ_BUF(8); + READ64(maxwrite); + if (maxwrite > 0x7FFFFFFF) + maxwrite = 0x7FFFFFFF; + *res = (uint32_t)maxwrite; + bitmap[0] &= ~FATTR4_WORD0_MAXWRITE; + } + dprintk("%s: maxwrite=%lu\n", __FUNCTION__, (unsigned long)*res); + return status; +} + +static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *mode) +{ + uint32_t *p; + + *mode = 0; + if (unlikely(bitmap[1] & (FATTR4_WORD1_MODE - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_MODE)) { + READ_BUF(4); + READ32(*mode); + *mode &= ~S_IFMT; + bitmap[1] &= ~FATTR4_WORD1_MODE; + } + dprintk("%s: file mode=0%o\n", __FUNCTION__, (unsigned int)*mode); + return 0; +} + +static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *nlink) +{ + uint32_t *p; + + *nlink = 1; + if (unlikely(bitmap[1] & (FATTR4_WORD1_NUMLINKS - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_NUMLINKS)) { + READ_BUF(4); + READ32(*nlink); + bitmap[1] &= ~FATTR4_WORD1_NUMLINKS; + } + dprintk("%s: nlink=%u\n", __FUNCTION__, (unsigned int)*nlink); + return 0; +} + +static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_client *clp, int32_t *uid) +{ + uint32_t len, *p; + int status = 0; + + *uid = -2; + if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_OWNER)) { + READ_BUF(4); + READ32(len); + READ_BUF(len); + if (len < XDR_MAX_NETOBJ) { + status = nfs_map_name_to_uid(clp, (char *)p, len, uid); + if (status != 0) + dprintk("%s: nfs_map_name_to_uid failed!\n", + __FUNCTION__); + } else + printk(KERN_WARNING "%s: name too long (%u)!\n", + __FUNCTION__, len); + bitmap[1] &= ~FATTR4_WORD1_OWNER; + } + dprintk("%s: uid=%d\n", __FUNCTION__, (int)*uid); + return status; +} + +static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_client *clp, int32_t *gid) +{ + uint32_t len, *p; + int status = 0; + + *gid = -2; + if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER_GROUP - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_OWNER_GROUP)) { + READ_BUF(4); + READ32(len); + READ_BUF(len); + if (len < XDR_MAX_NETOBJ) { + status = nfs_map_group_to_gid(clp, (char *)p, len, gid); + if (status != 0) + dprintk("%s: nfs_map_group_to_gid failed!\n", + __FUNCTION__); + } else + printk(KERN_WARNING "%s: name too long (%u)!\n", + __FUNCTION__, len); + bitmap[1] &= ~FATTR4_WORD1_OWNER_GROUP; + } + dprintk("%s: gid=%d\n", __FUNCTION__, (int)*gid); + return status; +} + +static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rdev) +{ + uint32_t major = 0, minor = 0, *p; + + *rdev = MKDEV(0,0); + if (unlikely(bitmap[1] & (FATTR4_WORD1_RAWDEV - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_RAWDEV)) { + dev_t tmp; + + READ_BUF(8); + READ32(major); + READ32(minor); + tmp = MKDEV(major, minor); + if (MAJOR(tmp) == major && MINOR(tmp) == minor) + *rdev = tmp; + bitmap[1] &= ~ FATTR4_WORD1_RAWDEV; + } + dprintk("%s: rdev=(0x%x:0x%x)\n", __FUNCTION__, major, minor); + return 0; +} + +static int decode_attr_space_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) +{ + uint32_t *p; + int status = 0; + + *res = 0; + if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_AVAIL - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_SPACE_AVAIL)) { + READ_BUF(8); + READ64(*res); + bitmap[1] &= ~FATTR4_WORD1_SPACE_AVAIL; + } + dprintk("%s: space avail=%Lu\n", __FUNCTION__, (unsigned long long)*res); + return status; +} + +static int decode_attr_space_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) +{ + uint32_t *p; + int status = 0; + + *res = 0; + if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_FREE - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_SPACE_FREE)) { + READ_BUF(8); + READ64(*res); + bitmap[1] &= ~FATTR4_WORD1_SPACE_FREE; + } + dprintk("%s: space free=%Lu\n", __FUNCTION__, (unsigned long long)*res); + return status; +} + +static int decode_attr_space_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) +{ + uint32_t *p; + int status = 0; + + *res = 0; + if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_TOTAL - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_SPACE_TOTAL)) { + READ_BUF(8); + READ64(*res); + bitmap[1] &= ~FATTR4_WORD1_SPACE_TOTAL; + } + dprintk("%s: space total=%Lu\n", __FUNCTION__, (unsigned long long)*res); + return status; +} + +static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *used) +{ + uint32_t *p; + + *used = 0; + if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_USED - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_SPACE_USED)) { + READ_BUF(8); + READ64(*used); + bitmap[1] &= ~FATTR4_WORD1_SPACE_USED; + } + dprintk("%s: space used=%Lu\n", __FUNCTION__, + (unsigned long long)*used); + return 0; +} + +static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time) +{ + uint32_t *p; + uint64_t sec; + uint32_t nsec; + + READ_BUF(12); + READ64(sec); + READ32(nsec); + time->tv_sec = (time_t)sec; + time->tv_nsec = (long)nsec; + return 0; +} + +static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) +{ + int status = 0; + + time->tv_sec = 0; + time->tv_nsec = 0; + if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_ACCESS - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_TIME_ACCESS)) { + status = decode_attr_time(xdr, time); + bitmap[1] &= ~FATTR4_WORD1_TIME_ACCESS; + } + dprintk("%s: atime=%ld\n", __FUNCTION__, (long)time->tv_sec); + return status; +} + +static int decode_attr_time_metadata(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) +{ + int status = 0; + + time->tv_sec = 0; + time->tv_nsec = 0; + if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_METADATA - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_TIME_METADATA)) { + status = decode_attr_time(xdr, time); + bitmap[1] &= ~FATTR4_WORD1_TIME_METADATA; + } + dprintk("%s: ctime=%ld\n", __FUNCTION__, (long)time->tv_sec); + return status; +} + +static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) +{ + int status = 0; + + time->tv_sec = 0; + time->tv_nsec = 0; + if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_MODIFY - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_TIME_MODIFY)) { + status = decode_attr_time(xdr, time); + bitmap[1] &= ~FATTR4_WORD1_TIME_MODIFY; + } + dprintk("%s: mtime=%ld\n", __FUNCTION__, (long)time->tv_sec); + return status; +} + +static int verify_attr_len(struct xdr_stream *xdr, uint32_t *savep, uint32_t attrlen) { - uint32_t *p; - uint32_t opnum; - int32_t nfserr; + unsigned int attrwords = XDR_QUADLEN(attrlen); + unsigned int nwords = xdr->p - savep; - READ_BUF(8); - READ32(opnum); - if (opnum != expected) { - printk(KERN_NOTICE - "nfs4_decode_op_hdr: Server returned operation" - " %d but we issued a request for %d\n", - opnum, expected); + if (unlikely(attrwords != nwords)) { + printk(KERN_WARNING "%s: server returned incorrect attribute length: %u %c %u\n", + __FUNCTION__, + attrwords << 2, + (attrwords < nwords) ? '<' : '>', + nwords << 2); return -EIO; } - READ32(nfserr); - if (nfserr != NFS_OK) - return -nfs_stat_to_errno(nfserr); return 0; } -static int -decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) +static int decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) { uint32_t *p; @@ -1635,8 +2383,7 @@ decode_change_info(struct xdr_stream *xd return 0; } -static int -decode_access(struct xdr_stream *xdr, struct nfs4_access *access) +static int decode_access(struct xdr_stream *xdr, struct nfs4_accessres *access) { uint32_t *p; uint32_t supp, acc; @@ -1648,17 +2395,12 @@ decode_access(struct xdr_stream *xdr, st READ_BUF(8); READ32(supp); READ32(acc); - if ((supp & ~access->ac_req_access) || (acc & ~supp)) { - printk(KERN_NOTICE "NFS: server returned bad bits in access call!\n"); - return -EIO; - } - *access->ac_resp_supported = supp; - *access->ac_resp_access = acc; + access->supported = supp; + access->access = acc; return 0; } -static int -decode_close(struct xdr_stream *xdr, struct nfs_closeres *res) +static int decode_close(struct xdr_stream *xdr, struct nfs_closeres *res) { uint32_t *p; int status; @@ -1671,8 +2413,7 @@ decode_close(struct xdr_stream *xdr, str return 0; } -static int -decode_commit(struct xdr_stream *xdr, struct nfs_writeres *res) +static int decode_commit(struct xdr_stream *xdr, struct nfs_writeres *res) { uint32_t *p; int status; @@ -1685,8 +2426,7 @@ decode_commit(struct xdr_stream *xdr, st return 0; } -static int -decode_create(struct xdr_stream *xdr, struct nfs4_create *create) +static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) { uint32_t *p; uint32_t bmlen; @@ -1695,7 +2435,7 @@ decode_create(struct xdr_stream *xdr, st status = decode_op_hdr(xdr, OP_CREATE); if (status) return status; - if ((status = decode_change_info(xdr, create->cr_cinfo))) + if ((status = decode_change_info(xdr, cinfo))) return status; READ_BUF(4); READ32(bmlen); @@ -1703,443 +2443,192 @@ decode_create(struct xdr_stream *xdr, st return 0; } -extern uint32_t nfs4_fattr_bitmap[2]; -extern uint32_t nfs4_fsstat_bitmap[2]; -extern uint32_t nfs4_pathconf_bitmap[2]; - -static int -decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, - struct nfs_server *server) +static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res) { - struct nfs_fattr *nfp = getattr->gt_attrs; - struct nfs_fsstat *fsstat = getattr->gt_fsstat; - struct nfs_pathconf *pathconf = getattr->gt_pathconf; - uint32_t attrlen, dummy32, bmlen, - bmval0 = 0, - bmval1 = 0, - len = 0; - uint32_t *p; - unsigned int type; - int fmode = 0; + uint32_t *savep; + uint32_t attrlen, + bitmap[2] = {0}; int status; + + if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) + goto xdr_error; + if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) + goto xdr_error; + if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) + goto xdr_error; + if ((status = decode_attr_supported(xdr, bitmap, res->attr_bitmask)) != 0) + goto xdr_error; + if ((status = decode_attr_link_support(xdr, bitmap, &res->has_links)) != 0) + goto xdr_error; + if ((status = decode_attr_symlink_support(xdr, bitmap, &res->has_symlinks)) != 0) + goto xdr_error; + if ((status = decode_attr_aclsupport(xdr, bitmap, &res->acl_bitmask)) != 0) + goto xdr_error; + status = verify_attr_len(xdr, savep, attrlen); +xdr_error: + if (status != 0) + printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status); + return status; +} - status = decode_op_hdr(xdr, OP_GETATTR); - if (status) - return status; - - READ_BUF(4); - READ32(bmlen); - if (bmlen > 2) - goto xdr_error; +static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat) +{ + uint32_t *savep; + uint32_t attrlen, + bitmap[2] = {0}; + int status; - READ_BUF((bmlen << 2) + 4); - if (bmlen > 0) - READ32(bmval0); - if (bmlen > 1) - READ32(bmval1); - READ32(attrlen); - - if ((bmval0 & ~getattr->gt_bmval[0]) || - (bmval1 & ~getattr->gt_bmval[1])) { - dprintk("read_attrs: server returned bad attributes!\n"); + if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) + goto xdr_error; + if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) + goto xdr_error; + if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) goto xdr_error; - } - if (nfp) { - nfp->bitmap[0] = bmval0; - nfp->bitmap[1] = bmval1; - } - /* - * In case the server doesn't return some attributes, - * we initialize them here to some nominal values.. - */ - if (nfp) { - nfp->valid = NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4; - nfp->nlink = 1; - nfp->timestamp = jiffies; - } - if (bmval0 & FATTR4_WORD0_TYPE) { - READ_BUF(4); - len += 4; - READ32(type); - if (type < NF4REG || type > NF4NAMEDATTR) { - dprintk("read_attrs: bad type %d\n", type); - goto xdr_error; - } - nfp->type = nfs_type2fmt[type].nfs2type; - fmode = nfs_type2fmt[type].mode; - dprintk("read_attrs: type=%d\n", (uint32_t)nfp->type); - } - if (bmval0 & FATTR4_WORD0_CHANGE) { - READ_BUF(8); - len += 8; - READ64(nfp->change_attr); - dprintk("read_attrs: changeid=%Ld\n", (long long)nfp->change_attr); - } - if (bmval0 & FATTR4_WORD0_SIZE) { - READ_BUF(8); - len += 8; - READ64(nfp->size); - dprintk("read_attrs: size=%Ld\n", (long long)nfp->size); - } - if (bmval0 & FATTR4_WORD0_FSID) { - READ_BUF(16); - len += 16; - READ64(nfp->fsid_u.nfs4.major); - READ64(nfp->fsid_u.nfs4.minor); - dprintk("read_attrs: fsid=0x%Lx/0x%Lx\n", - (long long)nfp->fsid_u.nfs4.major, - (long long)nfp->fsid_u.nfs4.minor); - } - if (bmval0 & FATTR4_WORD0_FILEID) { - READ_BUF(8); - len += 8; - READ64(nfp->fileid); - dprintk("read_attrs: fileid=%Ld\n", (long long) nfp->fileid); - } - if (bmval0 & FATTR4_WORD0_FILES_AVAIL) { - READ_BUF(8); - len += 8; - READ64(fsstat->afiles); - dprintk("read_attrs: files_avail=0x%Lx\n", (long long) fsstat->afiles); - } - if (bmval0 & FATTR4_WORD0_FILES_FREE) { - READ_BUF(8); - len += 8; - READ64(fsstat->ffiles); - dprintk("read_attrs: files_free=0x%Lx\n", (long long) fsstat->ffiles); - } - if (bmval0 & FATTR4_WORD0_FILES_TOTAL) { - READ_BUF(8); - len += 8; - READ64(fsstat->tfiles); - dprintk("read_attrs: files_tot=0x%Lx\n", (long long) fsstat->tfiles); - } - if (bmval0 & FATTR4_WORD0_MAXLINK) { - READ_BUF(4); - len += 4; - READ32(pathconf->max_link); - dprintk("read_attrs: maxlink=%d\n", pathconf->max_link); - } - if (bmval0 & FATTR4_WORD0_MAXNAME) { - READ_BUF(4); - len += 4; - READ32(pathconf->max_namelen); - dprintk("read_attrs: maxname=%d\n", pathconf->max_namelen); - } - - if (bmval1 & FATTR4_WORD1_MODE) { - READ_BUF(4); - len += 4; - READ32(dummy32); - nfp->mode = (dummy32 & ~S_IFMT) | fmode; - dprintk("read_attrs: mode=0%o\n", nfp->mode); - } - if (bmval1 & FATTR4_WORD1_NUMLINKS) { - READ_BUF(4); - len += 4; - READ32(nfp->nlink); - dprintk("read_attrs: nlinks=0%o\n", nfp->nlink); - } - if (bmval1 & FATTR4_WORD1_OWNER) { - READ_BUF(4); - len += 4; - READ32(dummy32); /* name length */ - if (dummy32 > XDR_MAX_NETOBJ) { - dprintk("read_attrs: name too long!\n"); - goto xdr_error; - } - READ_BUF(dummy32); - len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_map_name_to_uid(server->nfs4_state, (char *)p, dummy32, - &nfp->uid)) < 0) { - dprintk("read_attrs: name-to-uid mapping failed!\n"); - nfp->uid = -2; - } - dprintk("read_attrs: uid=%d\n", (int)nfp->uid); - } - if (bmval1 & FATTR4_WORD1_OWNER_GROUP) { - READ_BUF(4); - len += 4; - READ32(dummy32); - if (dummy32 > XDR_MAX_NETOBJ) { - dprintk("read_attrs: name too long!\n"); - goto xdr_error; - } - READ_BUF(dummy32); - len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_map_group_to_gid(server->nfs4_state, (char *)p, dummy32, - &nfp->gid)) < 0) { - dprintk("read_attrs: group-to-gid mapping failed!\n"); - nfp->gid = -2; - } - dprintk("read_attrs: gid=%d\n", (int)nfp->gid); - } - if (bmval1 & FATTR4_WORD1_RAWDEV) { - uint32_t major, minor; + if ((status = decode_attr_files_avail(xdr, bitmap, &fsstat->afiles)) != 0) + goto xdr_error; + if ((status = decode_attr_files_free(xdr, bitmap, &fsstat->ffiles)) != 0) + goto xdr_error; + if ((status = decode_attr_files_total(xdr, bitmap, &fsstat->tfiles)) != 0) + goto xdr_error; + if ((status = decode_attr_space_avail(xdr, bitmap, &fsstat->abytes)) != 0) + goto xdr_error; + if ((status = decode_attr_space_free(xdr, bitmap, &fsstat->fbytes)) != 0) + goto xdr_error; + if ((status = decode_attr_space_total(xdr, bitmap, &fsstat->tbytes)) != 0) + goto xdr_error; - READ_BUF(8); - len += 8; - READ32(major); - READ32(minor); - nfp->rdev = MKDEV(major, minor); - if (MAJOR(nfp->rdev) != major || MINOR(nfp->rdev) != minor) - nfp->rdev = 0; - dprintk("read_attrs: rdev=%u:%u\n", major, minor); - } - if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) { - READ_BUF(8); - len += 8; - READ64(fsstat->abytes); - dprintk("read_attrs: savail=0x%Lx\n", (long long) fsstat->abytes); - } - if (bmval1 & FATTR4_WORD1_SPACE_FREE) { - READ_BUF(8); - len += 8; - READ64(fsstat->fbytes); - dprintk("read_attrs: sfree=0x%Lx\n", (long long) fsstat->fbytes); - } - if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) { - READ_BUF(8); - len += 8; - READ64(fsstat->tbytes); - dprintk("read_attrs: stotal=0x%Lx\n", (long long) fsstat->tbytes); - } - if (bmval1 & FATTR4_WORD1_SPACE_USED) { - READ_BUF(8); - len += 8; - READ64(nfp->du.nfs3.used); - dprintk("read_attrs: sused=0x%Lx\n", (long long) nfp->du.nfs3.used); - } - if (bmval1 & FATTR4_WORD1_TIME_ACCESS) { - READ_BUF(12); - len += 12; - READTIME(nfp->atime); - dprintk("read_attrs: atime=%ld\n", (long)nfp->atime.tv_sec); - } - if (bmval1 & FATTR4_WORD1_TIME_METADATA) { - READ_BUF(12); - len += 12; - READTIME(nfp->ctime); - dprintk("read_attrs: ctime=%ld\n", (long)nfp->ctime.tv_sec); - } - if (bmval1 & FATTR4_WORD1_TIME_MODIFY) { - READ_BUF(12); - len += 12; - READTIME(nfp->mtime); - dprintk("read_attrs: mtime=%ld\n", (long)nfp->mtime.tv_sec); - } - if (len != attrlen) - goto xdr_error; - - DECODE_TAIL; + status = verify_attr_len(xdr, savep, attrlen); +xdr_error: + if (status != 0) + printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status); + return status; } -static int -decode_change_attr(struct xdr_stream *xdr, uint64_t *change_attr) +static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf) { - uint32_t *p; - uint32_t attrlen, bmlen, bmval = 0; + uint32_t *savep; + uint32_t attrlen, + bitmap[2] = {0}; int status; + + if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) + goto xdr_error; + if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) + goto xdr_error; + if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) + goto xdr_error; - status = decode_op_hdr(xdr, OP_GETATTR); - if (status) - return status; - READ_BUF(4); - READ32(bmlen); - if (bmlen < 1) - return -EIO; - READ_BUF(bmlen << 2); - READ32(bmval); - if (bmval != FATTR4_WORD0_CHANGE) { - printk(KERN_NOTICE "decode_change_attr: server returned bad attribute bitmap 0x%x\n", - (unsigned int)bmval); - return -EIO; - } - READ_BUF(4); - READ32(attrlen); - READ_BUF(attrlen); - if (attrlen < 8) { - printk(KERN_NOTICE "decode_change_attr: server returned bad attribute length %u\n", - (unsigned int)attrlen); - return -EIO; - } - READ64(*change_attr); - return 0; + if ((status = decode_attr_maxlink(xdr, bitmap, &pathconf->max_link)) != 0) + goto xdr_error; + if ((status = decode_attr_maxname(xdr, bitmap, &pathconf->max_namelen)) != 0) + goto xdr_error; + + status = verify_attr_len(xdr, savep, attrlen); +xdr_error: + if (status != 0) + printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status); + return status; } -static int -decode_read_getattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) +static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, const struct nfs_server *server) { - uint32_t *p; - uint32_t attrlen, bmlen, bmval0 = 0, bmval1 = 0; - int status; + uint32_t *savep; + uint32_t attrlen, + bitmap[2] = {0}, + type; + int status, fmode = 0; - status = decode_op_hdr(xdr, OP_GETATTR); - if (status) - return status; - READ_BUF(4); - READ32(bmlen); - if (bmlen < 1) - return -EIO; - READ_BUF(bmlen << 2); - READ32(bmval0); - if (bmval0 != FATTR4_WORD0_CHANGE) - goto out_bad_bitmap; - if (bmlen > 1) { - READ32(bmval1); - if (bmval1 & ~(FATTR4_WORD1_TIME_ACCESS)) - goto out_bad_bitmap; - } - READ_BUF(4); - READ32(attrlen); - READ_BUF(attrlen); - if (attrlen < 16) { - printk(KERN_NOTICE "decode_post_write_getattr: server returned bad attribute length %u\n", - (unsigned int)attrlen); - return -EIO; - } - READ64(fattr->change_attr); - if (bmval1 & FATTR4_WORD1_TIME_ACCESS) - READTIME(fattr->atime); - fattr->bitmap[0] = bmval0; - fattr->bitmap[1] = bmval1; - return 0; -out_bad_bitmap: - printk(KERN_NOTICE "decode_read_getattr: server returned bad attribute bitmap 0x%x/0x%x\n", - (unsigned int)bmval0, (unsigned int)bmval1); - return -EIO; -} + if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) + goto xdr_error; + if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) + goto xdr_error; -static int -decode_pre_write_getattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) -{ - return decode_change_attr(xdr, &fattr->pre_change_attr); -} + fattr->bitmap[0] = bitmap[0]; + fattr->bitmap[1] = bitmap[1]; -static int -decode_post_write_getattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) -{ - uint32_t *p; - uint32_t attrlen, bmlen, bmval0 = 0, bmval1 = 0; - int status; + if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) + goto xdr_error; - status = decode_op_hdr(xdr, OP_GETATTR); - if (status) - return status; - READ_BUF(4); - READ32(bmlen); - if (bmlen < 1) - return -EIO; - READ_BUF(bmlen << 2); - READ32(bmval0); - if (bmval0 != (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE)) - goto out_bad_bitmap; - if (bmlen > 1) { - READ32(bmval1); - if (bmval1 & ~(FATTR4_WORD1_SPACE_USED | - FATTR4_WORD1_TIME_METADATA | - FATTR4_WORD1_TIME_MODIFY)) - goto out_bad_bitmap; + + if ((status = decode_attr_type(xdr, bitmap, &type)) != 0) + goto xdr_error; + fattr->type = nfs_type2fmt[type].nfs2type; + fmode = nfs_type2fmt[type].mode; + + if ((status = decode_attr_change(xdr, bitmap, &fattr->change_attr)) != 0) + goto xdr_error; + if ((status = decode_attr_size(xdr, bitmap, &fattr->size)) != 0) + goto xdr_error; + if ((status = decode_attr_fsid(xdr, bitmap, &fattr->fsid_u.nfs4)) != 0) + goto xdr_error; + if ((status = decode_attr_fileid(xdr, bitmap, &fattr->fileid)) != 0) + goto xdr_error; + if ((status = decode_attr_mode(xdr, bitmap, &fattr->mode)) != 0) + goto xdr_error; + fattr->mode |= fmode; + if ((status = decode_attr_nlink(xdr, bitmap, &fattr->nlink)) != 0) + goto xdr_error; + if ((status = decode_attr_owner(xdr, bitmap, server->nfs4_state, &fattr->uid)) != 0) + goto xdr_error; + if ((status = decode_attr_group(xdr, bitmap, server->nfs4_state, &fattr->gid)) != 0) + goto xdr_error; + if ((status = decode_attr_rdev(xdr, bitmap, &fattr->rdev)) != 0) + goto xdr_error; + if ((status = decode_attr_space_used(xdr, bitmap, &fattr->du.nfs3.used)) != 0) + goto xdr_error; + if ((status = decode_attr_time_access(xdr, bitmap, &fattr->atime)) != 0) + goto xdr_error; + if ((status = decode_attr_time_metadata(xdr, bitmap, &fattr->ctime)) != 0) + goto xdr_error; + if ((status = decode_attr_time_modify(xdr, bitmap, &fattr->mtime)) != 0) + goto xdr_error; + if ((status = verify_attr_len(xdr, savep, attrlen)) == 0) { + fattr->valid = NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4; + fattr->timestamp = jiffies; } - READ_BUF(4); - READ32(attrlen); - READ_BUF(attrlen); - if (attrlen < 16) { - printk(KERN_NOTICE "decode_post_write_getattr: server returned bad attribute length %u\n", - (unsigned int)attrlen); - return -EIO; - } - READ64(fattr->change_attr); - READ64(fattr->size); - if (bmval1 & FATTR4_WORD1_SPACE_USED) - READ64(fattr->du.nfs3.used); - if (bmval1 & FATTR4_WORD1_TIME_METADATA) - READTIME(fattr->ctime); - if (bmval1 & FATTR4_WORD1_TIME_MODIFY) - READTIME(fattr->mtime); - fattr->bitmap[0] = bmval0; - fattr->bitmap[1] = bmval1; - return 0; -out_bad_bitmap: - printk(KERN_NOTICE "decode_post_write_getattr: server returned bad attribute bitmap 0x%x/0x%x\n", - (unsigned int)bmval0, (unsigned int)bmval1); - return -EIO; +xdr_error: + if (status != 0) + printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status); + return status; } -static int -decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) +static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) { - uint32_t *p; - uint32_t len, attrlen, bmlen, bmval0 = 0, bmval1 = 0; + uint32_t *savep; + uint32_t attrlen, bitmap[2]; int status; - status = decode_op_hdr(xdr, OP_GETATTR); - if (status) - return status; - READ_BUF(4); - READ32(bmlen); - if (bmlen < 1) - return -EIO; - READ_BUF(bmlen << 2); - READ32(bmval0); - if (bmval0 & ~(FATTR4_WORD0_MAXFILESIZE|FATTR4_WORD0_MAXREAD| - FATTR4_WORD0_MAXWRITE|FATTR4_WORD0_LEASE_TIME)) - goto out_bad_bitmap; - if (bmlen > 1) { - READ32(bmval1); - if (bmval1 != 0 || bmlen > 2) - goto out_bad_bitmap; - } - READ_BUF(4); - READ32(attrlen); - READ_BUF(attrlen); + if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) + goto xdr_error; + if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) + goto xdr_error; + if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) + goto xdr_error; + fsinfo->rtmult = fsinfo->wtmult = 512; /* ??? */ - fsinfo->lease_time = 60; - len = attrlen; - if (bmval0 & FATTR4_WORD0_LEASE_TIME) { - len -= 4; - READ32(fsinfo->lease_time); - dprintk("read_attrs: lease_time=%d\n", fsinfo->lease_time); - } - if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { - len -= 8; - READ64(fsinfo->maxfilesize); - dprintk("read_attrs: maxfilesize=0x%Lx\n", (long long) fsinfo->maxfilesize); - } - if (bmval0 & FATTR4_WORD0_MAXREAD) { - len -= 8; - READ64(fsinfo->rtmax); - fsinfo->rtpref = fsinfo->dtpref = fsinfo->rtmax; - dprintk("read_attrs: maxread=%d\n", fsinfo->rtmax); - } - if (bmval0 & FATTR4_WORD0_MAXWRITE) { - len -= 8; - READ64(fsinfo->wtmax); - fsinfo->wtpref = fsinfo->wtmax; - dprintk("read_attrs: maxwrite=%d\n", fsinfo->wtmax); - } - if (len != 0) - goto out_bad_attrlen; - return 0; -out_bad_attrlen: - printk(KERN_NOTICE "%s: server attribute length %u does not match bitmap 0x%x/0x%x\n", - __FUNCTION__, (unsigned int)attrlen, - (unsigned int) bmval0, (unsigned int)bmval1); - return -EIO; -out_bad_bitmap: - printk(KERN_NOTICE "%s: server returned bad attribute bitmap 0x%x/0x%x\n", - __FUNCTION__, - (unsigned int)bmval0, (unsigned int)bmval1); - return -EIO; + if ((status = decode_attr_lease_time(xdr, bitmap, &fsinfo->lease_time)) != 0) + goto xdr_error; + if ((status = decode_attr_maxfilesize(xdr, bitmap, &fsinfo->maxfilesize)) != 0) + goto xdr_error; + if ((status = decode_attr_maxread(xdr, bitmap, &fsinfo->rtmax)) != 0) + goto xdr_error; + fsinfo->rtpref = fsinfo->dtpref = fsinfo->rtmax; + if ((status = decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0) + goto xdr_error; + fsinfo->wtpref = fsinfo->wtmax; + + status = verify_attr_len(xdr, savep, attrlen); +xdr_error: + if (status != 0) + printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status); + return status; } -static int -decode_getfh(struct xdr_stream *xdr, struct nfs4_getfh *getfh) +static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh) { - struct nfs_fh *fh = getfh->gf_fhandle; uint32_t *p; uint32_t len; int status; @@ -2152,7 +2641,7 @@ decode_getfh(struct xdr_stream *xdr, str READ_BUF(4); READ32(len); - if (len > NFS_MAXFHSIZE) + if (len > NFS4_FHSIZE) return -EIO; fh->size = len; READ_BUF(len); @@ -2160,22 +2649,20 @@ decode_getfh(struct xdr_stream *xdr, str return 0; } -static int -decode_link(struct xdr_stream *xdr, struct nfs4_link *link) +static int decode_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) { int status; status = decode_op_hdr(xdr, OP_LINK); if (status) return status; - return decode_change_info(xdr, link->ln_cinfo); + return decode_change_info(xdr, cinfo); } /* * We create the owner, so we know a proper owner.id length is 4. */ -static int -decode_lock_denied (struct xdr_stream *xdr, struct nfs_lock_denied *denied) +static int decode_lock_denied (struct xdr_stream *xdr, struct nfs_lock_denied *denied) { uint32_t *p; uint32_t namelen; @@ -2192,8 +2679,7 @@ decode_lock_denied (struct xdr_stream *x return -NFS4ERR_DENIED; } -static int -decode_lock(struct xdr_stream *xdr, struct nfs_lockres *res) +static int decode_lock(struct xdr_stream *xdr, struct nfs_lockres *res) { uint32_t *p; int status; @@ -2207,8 +2693,7 @@ decode_lock(struct xdr_stream *xdr, stru return status; } -static int -decode_lockt(struct xdr_stream *xdr, struct nfs_lockres *res) +static int decode_lockt(struct xdr_stream *xdr, struct nfs_lockres *res) { int status; status = decode_op_hdr(xdr, OP_LOCKT); @@ -2217,8 +2702,7 @@ decode_lockt(struct xdr_stream *xdr, str return status; } -static int -decode_locku(struct xdr_stream *xdr, struct nfs_lockres *res) +static int decode_locku(struct xdr_stream *xdr, struct nfs_lockres *res) { uint32_t *p; int status; @@ -2231,14 +2715,12 @@ decode_locku(struct xdr_stream *xdr, str return status; } -static int -decode_lookup(struct xdr_stream *xdr) +static int decode_lookup(struct xdr_stream *xdr) { return decode_op_hdr(xdr, OP_LOOKUP); } -static int -decode_open(struct xdr_stream *xdr, struct nfs_openres *res) +static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) { uint32_t *p; uint32_t bmlen, delegation_type; @@ -2250,7 +2732,7 @@ decode_open(struct xdr_stream *xdr, stru READ_BUF(sizeof(res->stateid.data)); COPYMEM(res->stateid.data, sizeof(res->stateid.data)); - decode_change_info(xdr, res->cinfo); + decode_change_info(xdr, &res->cinfo); READ_BUF(8); READ32(res->rflags); @@ -2261,14 +2743,14 @@ decode_open(struct xdr_stream *xdr, stru READ_BUF((bmlen << 2) + 4); p += bmlen; READ32(delegation_type); - if (delegation_type != NFS4_OPEN_DELEGATE_NONE) - goto xdr_error; - - DECODE_TAIL; + if (delegation_type == NFS4_OPEN_DELEGATE_NONE) + return 0; +xdr_error: + printk(KERN_NOTICE "%s: xdr error!\n", __FUNCTION__); + return -EIO; } -static int -decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res) +static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res) { uint32_t *p; int status; @@ -2281,8 +2763,7 @@ decode_open_confirm(struct xdr_stream *x return 0; } -static int -decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res) +static int decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res) { uint32_t *p; int status; @@ -2295,22 +2776,19 @@ decode_open_downgrade(struct xdr_stream return 0; } -static int -decode_putfh(struct xdr_stream *xdr) +static int decode_putfh(struct xdr_stream *xdr) { return decode_op_hdr(xdr, OP_PUTFH); } -static int -decode_putrootfh(struct xdr_stream *xdr) +static int decode_putrootfh(struct xdr_stream *xdr) { return decode_op_hdr(xdr, OP_PUTROOTFH); } -static int -decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res) +static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res) { - struct iovec *iov = req->rq_rvec; + struct iovec *iov = req->rq_rcv_buf.head; uint32_t *p; uint32_t count, eof, recvd, hdrlen; int status; @@ -2322,7 +2800,7 @@ decode_read(struct xdr_stream *xdr, stru READ32(eof); READ32(count); hdrlen = (u8 *) p - (u8 *) iov->iov_base; - recvd = req->rq_received - hdrlen; + recvd = req->rq_rcv_buf.len - hdrlen; if (count > recvd) { printk(KERN_WARNING "NFS: server cheating in read reply: " "count %u > recvd %u\n", count, recvd); @@ -2335,8 +2813,7 @@ decode_read(struct xdr_stream *xdr, stru 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; @@ -2350,17 +2827,17 @@ decode_readdir(struct xdr_stream *xdr, s 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; + recvd = rcvbuf->len - hdrlen; if (pglen > recvd) 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) @@ -2420,8 +2897,7 @@ err_unmap: return -errno_NFSERR_IO; } -static int -decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readlink *readlink) +static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) { struct xdr_buf *rcvbuf = &req->rq_rcv_buf; struct iovec *iov = rcvbuf->head; @@ -2462,43 +2938,34 @@ decode_readlink(struct xdr_stream *xdr, return 0; } -static int -decode_restorefh(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_RESTOREFH); -} - -static int -decode_remove(struct xdr_stream *xdr, struct nfs4_remove *remove) +static int decode_remove(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) { int status; status = decode_op_hdr(xdr, OP_REMOVE); if (status) goto out; - status = decode_change_info(xdr, remove->rm_cinfo); + status = decode_change_info(xdr, cinfo); out: return status; } -static int -decode_rename(struct xdr_stream *xdr, struct nfs4_rename *rename) +static int decode_rename(struct xdr_stream *xdr, struct nfs4_change_info *old_cinfo, + struct nfs4_change_info *new_cinfo) { int status; status = decode_op_hdr(xdr, OP_RENAME); if (status) goto out; - if ((status = decode_change_info(xdr, rename->rn_src_cinfo))) - goto out; - if ((status = decode_change_info(xdr, rename->rn_dst_cinfo))) + if ((status = decode_change_info(xdr, old_cinfo))) goto out; + status = decode_change_info(xdr, new_cinfo); out: return status; } -static int -decode_renew(struct xdr_stream *xdr) +static int decode_renew(struct xdr_stream *xdr) { return decode_op_hdr(xdr, OP_RENEW); } @@ -2509,8 +2976,7 @@ decode_savefh(struct xdr_stream *xdr) return decode_op_hdr(xdr, OP_SAVEFH); } -static int -decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res) +static int decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res) { uint32_t *p; uint32_t bmlen; @@ -2526,8 +2992,7 @@ decode_setattr(struct xdr_stream *xdr, s return 0; } -static int -decode_setclientid(struct xdr_stream *xdr, struct nfs4_client *clp) +static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_client *clp) { uint32_t *p; uint32_t opnum; @@ -2558,124 +3023,38 @@ decode_setclientid(struct xdr_stream *xd READ_BUF(4); READ32(len); READ_BUF(len); - return -EEXIST; - } else - return -nfs_stat_to_errno(nfserr); - - return 0; -} - -static int -decode_setclientid_confirm(struct xdr_stream *xdr) -{ - return decode_op_hdr(xdr, OP_SETCLIENTID_CONFIRM); -} - -static int -decode_write(struct xdr_stream *xdr, struct nfs_writeres *res) -{ - uint32_t *p; - int status; - - status = decode_op_hdr(xdr, OP_WRITE); - if (status) - return status; - - READ_BUF(16); - READ32(res->count); - READ32(res->verf->committed); - COPYMEM(res->verf->verifier, 8); - 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_ACCESS: - status = decode_access(xdr, &op->u.access); - break; - case OP_CREATE: - status = decode_create(xdr, &op->u.create); - break; - case OP_GETATTR: - status = decode_getattr(xdr, &op->u.getattr, cp->server); - break; - case OP_GETFH: - status = decode_getfh(xdr, &op->u.getfh); - break; - case OP_LINK: - status = decode_link(xdr, &op->u.link); - break; - case OP_LOOKUP: - status = decode_lookup(xdr); - break; - case OP_PUTFH: - status = decode_putfh(xdr); - break; - case OP_PUTROOTFH: - status = decode_putrootfh(xdr); - break; - case OP_READDIR: - status = decode_readdir(xdr, req, &op->u.readdir); - break; - case OP_READLINK: - status = decode_readlink(xdr, req, &op->u.readlink); - break; - case OP_RESTOREFH: - status = decode_restorefh(xdr); - break; - case OP_REMOVE: - status = decode_remove(xdr, &op->u.remove); - break; - case OP_RENAME: - status = decode_rename(xdr, &op->u.rename); - break; - case OP_SAVEFH: - status = decode_savefh(xdr); - break; - default: - BUG(); - return -EIO; - } - if (status) - break; - } + return -EEXIST; + } else + return -nfs_stat_to_errno(nfserr); + + return 0; +} + +static int decode_setclientid_confirm(struct xdr_stream *xdr) +{ + return decode_op_hdr(xdr, OP_SETCLIENTID_CONFIRM); +} + +static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res) +{ + uint32_t *p; + int status; + + status = decode_op_hdr(xdr, OP_WRITE); + if (status) + return status; - DECODE_TAIL; + READ_BUF(16); + READ32(res->count); + READ32(res->verf->committed); + COPYMEM(res->verf->verifier, 8); + return 0; } /* * Decode OPEN_DOWNGRADE response */ -static int -nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_closeres *res) +static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_closeres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2698,31 +3077,180 @@ out: */ /* - * Decode COMPOUND response + * Decode ACCESS response */ -static int -nfs4_xdr_dec_compound(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_compound *cp) +static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_accessres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + goto out; + if ((status = decode_putfh(&xdr)) == 0) + status = decode_access(&xdr, res); +out: + return status; +} + +/* + * Decode LOOKUP response + */ +static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_lookup_res *res) { struct xdr_stream xdr; + struct compound_hdr hdr; int status; xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); - if ((status = decode_compound(&xdr, cp, rqstp))) + if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + goto out; + if ((status = decode_putfh(&xdr)) != 0) goto out; + if ((status = decode_lookup(&xdr)) != 0) + goto out; + if ((status = decode_getfh(&xdr, res->fh)) != 0) + goto out; + status = decode_getfattr(&xdr, res->fattr, res->server); +out: + return status; +} + +/* + * Decode LOOKUP_ROOT response + */ +static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_lookup_res *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; - status = 0; - if (cp->toplevel_status) - status = -nfs_stat_to_errno(cp->toplevel_status); + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + goto out; + if ((status = decode_putrootfh(&xdr)) != 0) + goto out; + if ((status = decode_getfh(&xdr, res->fh)) == 0) + status = decode_getfattr(&xdr, res->fattr, res->server); +out: + return status; +} + +/* + * Decode REMOVE response + */ +static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_change_info *cinfo) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + goto out; + if ((status = decode_putfh(&xdr)) == 0) + status = decode_remove(&xdr, cinfo); +out: + return status; +} + +/* + * Decode RENAME response + */ +static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_rename_res *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + goto out; + if ((status = decode_putfh(&xdr)) != 0) + goto out; + if ((status = decode_savefh(&xdr)) != 0) + goto out; + if ((status = decode_putfh(&xdr)) != 0) + goto out; + status = decode_rename(&xdr, &res->old_cinfo, &res->new_cinfo); +out: + return status; +} + +/* + * Decode LINK response + */ +static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_change_info *cinfo) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + goto out; + if ((status = decode_putfh(&xdr)) != 0) + goto out; + if ((status = decode_savefh(&xdr)) != 0) + goto out; + if ((status = decode_putfh(&xdr)) != 0) + goto out; + status = decode_link(&xdr, cinfo); +out: + return status; +} + +/* + * Decode CREATE response + */ +static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_create_res *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + goto out; + if ((status = decode_putfh(&xdr)) != 0) + goto out; + if ((status = decode_create(&xdr,&res->dir_cinfo)) != 0) + goto out; + if ((status = decode_getfattr(&xdr, res->fattr, res->server)) != 0) + goto out; + status = decode_getfh(&xdr, res->fh); +out: + return status; +} +/* + * Decode GETATTR response + */ +static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_getattr_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_getfattr(&xdr, res->fattr, res->server); out: return status; + } + /* * Decode CLOSE response */ -static int -nfs4_xdr_dec_close(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_closeres *res) +static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_closeres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2743,14 +3271,10 @@ out: /* * Decode OPEN response */ -static int -nfs4_xdr_dec_open(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res) +static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res) { struct xdr_stream xdr; struct compound_hdr hdr; - struct nfs4_getfh gfh = { - .gf_fhandle = &res->fh, - }; int status; xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); @@ -2760,24 +3284,13 @@ nfs4_xdr_dec_open(struct rpc_rqst *rqstp status = decode_putfh(&xdr); if (status) goto out; - status = decode_savefh(&xdr); - if (status) - goto out; status = decode_open(&xdr, res); if (status) goto out; - status = decode_getattr(&xdr, res->f_getattr, res->server); - if (status) - goto out; - status = decode_getfh(&xdr, &gfh); - if (status) - goto out; - status = decode_restorefh(&xdr); - if (status) - goto out; - status = decode_getattr(&xdr, res->d_getattr, res->server); + status = decode_getfattr(&xdr, res->f_attr, res->server); if (status) goto out; + status = decode_getfh(&xdr, &res->fh); out: return status; } @@ -2785,8 +3298,7 @@ out: /* * Decode OPEN_CONFIRM response */ -static int -nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_open_confirmres *res) +static int nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_open_confirmres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2807,8 +3319,7 @@ out: /* * Decode OPEN_RECLAIM response */ -static int -nfs4_xdr_dec_open_reclaim(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res) +static int nfs4_xdr_dec_open_reclaim(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2824,7 +3335,7 @@ nfs4_xdr_dec_open_reclaim(struct rpc_rqs status = decode_open(&xdr, res); if (status) goto out; - status = decode_getattr(&xdr, res->f_getattr, res->server); + status = decode_getfattr(&xdr, res->f_attr, res->server); out: return status; } @@ -2832,8 +3343,7 @@ out: /* * Decode SETATTR response */ -static int -nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_setattrres *res) +static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_setattrres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2849,7 +3359,7 @@ nfs4_xdr_dec_setattr(struct rpc_rqst *rq status = decode_setattr(&xdr, res); if (status) goto out; - status = decode_getattr(&xdr, res->attr, res->server); + status = decode_getfattr(&xdr, res->fattr, res->server); out: return status; } @@ -2857,8 +3367,7 @@ out: /* * Decode LOCK response */ -static int -nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +static int nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2879,8 +3388,7 @@ out: /* * Decode LOCKT response */ -static int -nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +static int nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2901,8 +3409,7 @@ out: /* * Decode LOCKU response */ -static int -nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2921,10 +3428,9 @@ out: } /* - * Decode Read response + * Decode READLINK response */ -static int -nfs4_xdr_dec_read(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_readres *res) +static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, uint32_t *p, void *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2937,23 +3443,15 @@ nfs4_xdr_dec_read(struct rpc_rqst *rqstp status = decode_putfh(&xdr); if (status) goto out; - status = decode_read(&xdr, rqstp, res); - if (status) - goto out; - status = decode_read_getattr(&xdr, res->fattr); - if (!status) - status = -nfs_stat_to_errno(hdr.status); - if (!status) - status = res->count; + status = decode_readlink(&xdr, rqstp); out: return status; } /* - * Decode WRITE response + * Decode READDIR response */ -static int -nfs4_xdr_dec_write(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_writeres *res) +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; @@ -2966,15 +3464,28 @@ nfs4_xdr_dec_write(struct rpc_rqst *rqst status = decode_putfh(&xdr); if (status) goto out; - status = decode_pre_write_getattr(&xdr, res->fattr); + status = decode_readdir(&xdr, rqstp, res); +out: + 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_write(&xdr, res); + status = decode_putfh(&xdr); if (status) goto out; - status = decode_post_write_getattr(&xdr, res->fattr); - if (!status) - status = -nfs_stat_to_errno(hdr.status); + status = decode_read(&xdr, rqstp, res); if (!status) status = res->count; out: @@ -2982,10 +3493,9 @@ out: } /* - * Decode COMMIT response + * Decode WRITE response */ -static int -nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_writeres *res) +static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_writeres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -2998,15 +3508,30 @@ nfs4_xdr_dec_commit(struct rpc_rqst *rqs status = decode_putfh(&xdr); if (status) goto out; - status = decode_pre_write_getattr(&xdr, res->fattr); + status = decode_write(&xdr, res); + if (!status) + status = res->count; +out: + return status; +} + +/* + * Decode COMMIT response + */ +static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_writeres *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_commit(&xdr, res); + status = decode_putfh(&xdr); if (status) goto out; - status = decode_post_write_getattr(&xdr, res->fattr); - if (!status) - status = -nfs_stat_to_errno(hdr.status); + status = decode_commit(&xdr, res); out: return status; } @@ -3014,8 +3539,7 @@ out: /* * FSINFO request */ -static int -nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, uint32_t *p, struct nfs_fsinfo *fsinfo) +static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, uint32_t *p, struct nfs_fsinfo *fsinfo) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -3033,10 +3557,64 @@ nfs4_xdr_dec_fsinfo(struct rpc_rqst *req } /* + * PATHCONF request + */ +static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, uint32_t *p, struct nfs_pathconf *pathconf) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_putfh(&xdr); + if (!status) + status = decode_pathconf(&xdr, pathconf); + return status; +} + +/* + * STATFS request + */ +static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, uint32_t *p, struct nfs_fsstat *fsstat) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_putfh(&xdr); + if (!status) + status = decode_statfs(&xdr, fsstat); + return status; +} + +/* + * GETATTR_BITMAP request + */ +static int nfs4_xdr_dec_server_caps(struct rpc_rqst *req, uint32_t *p, struct nfs4_server_caps_res *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + goto out; + if ((status = decode_putfh(&xdr)) != 0) + goto out; + status = decode_server_caps(&xdr, res); +out: + return status; +} + +/* * Decode RENEW response */ -static int -nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, uint32_t *p, void *dummy) +static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, uint32_t *p, void *dummy) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -3052,8 +3630,7 @@ nfs4_xdr_dec_renew(struct rpc_rqst *rqst /* * a SETCLIENTID request */ -static int -nfs4_xdr_dec_setclientid(struct rpc_rqst *req, uint32_t *p, +static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) { struct xdr_stream xdr; @@ -3072,8 +3649,7 @@ nfs4_xdr_dec_setclientid(struct rpc_rqst /* * a SETCLIENTID_CONFIRM request */ -static int -nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs_fsinfo *fsinfo) +static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs_fsinfo *fsinfo) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -3092,8 +3668,7 @@ nfs4_xdr_dec_setclientid_confirm(struct return status; } -uint32_t * -nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) +uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) { uint32_t len; @@ -3209,7 +3784,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), @@ -3226,6 +3800,19 @@ struct rpc_procinfo nfs4_procedures[] = PROC(LOCK, enc_lock, dec_lock), PROC(LOCKT, enc_lockt, dec_lockt), PROC(LOCKU, enc_locku, dec_locku), + PROC(ACCESS, enc_access, dec_access), + PROC(GETATTR, enc_getattr, dec_getattr), + PROC(LOOKUP, enc_lookup, dec_lookup), + PROC(LOOKUP_ROOT, enc_lookup_root, dec_lookup_root), + PROC(REMOVE, enc_remove, dec_remove), + PROC(RENAME, enc_rename, dec_rename), + PROC(LINK, enc_link, dec_link), + PROC(CREATE, enc_create, dec_create), + PROC(PATHCONF, enc_pathconf, dec_pathconf), + PROC(STATFS, enc_statfs, dec_statfs), + PROC(READLINK, enc_readlink, dec_readlink), + PROC(READDIR, enc_readdir, dec_readdir), + PROC(SERVER_CAPS, enc_server_caps, dec_server_caps), }; struct rpc_version nfs_version4 = { diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/nfsroot.c linux-2.6.5-31-server_caps/fs/nfs/nfsroot.c --- linux-2.6.5-rc2/fs/nfs/nfsroot.c 2004-03-25 00:30:02.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/nfsroot.c 2004-03-25 00:35:50.000000000 -0500 @@ -66,7 +66,8 @@ * is NOT for the length of the hostname. * Hua Qin : Support for mounting root file system via * NFS over TCP. - */ + * Fabian Frederick: Option parser rebuilt (using parser lib) +*/ #include #include @@ -85,6 +86,7 @@ #include #include #include +#include /* Define this to allow debugging output */ #undef NFSROOT_DEBUG @@ -114,92 +116,158 @@ static int mount_port __initdata = 0; / ***************************************************************************/ -/* - * The following integer options are recognized - */ -static struct nfs_int_opts { - char *name; - int *val; -} root_int_opts[] __initdata = { - { "port", &nfs_port }, - { "rsize", &nfs_data.rsize }, - { "wsize", &nfs_data.wsize }, - { "timeo", &nfs_data.timeo }, - { "retrans", &nfs_data.retrans }, - { "acregmin", &nfs_data.acregmin }, - { "acregmax", &nfs_data.acregmax }, - { "acdirmin", &nfs_data.acdirmin }, - { "acdirmax", &nfs_data.acdirmax }, - { NULL, NULL } +enum { + Opt_port, Opt_rsize, Opt_wsize, Opt_timeo, Opt_retrans, Opt_acregmin, + Opt_acregmax, Opt_acdirmin, Opt_acdirmax, Opt_soft, Opt_hard, Opt_intr, + Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac, + Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp, + Opt_broken_suid, Opt_err, }; - -/* - * And now the flag options - */ -static struct nfs_bool_opts { - char *name; - int and_mask; - int or_mask; -} root_bool_opts[] __initdata = { - { "soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT }, - { "hard", ~NFS_MOUNT_SOFT, 0 }, - { "intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR }, - { "nointr", ~NFS_MOUNT_INTR, 0 }, - { "posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX }, - { "noposix", ~NFS_MOUNT_POSIX, 0 }, - { "cto", ~NFS_MOUNT_NOCTO, 0 }, - { "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO }, - { "ac", ~NFS_MOUNT_NOAC, 0 }, - { "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC }, - { "lock", ~NFS_MOUNT_NONLM, 0 }, - { "nolock", ~NFS_MOUNT_NONLM, NFS_MOUNT_NONLM }, -#ifdef CONFIG_NFS_V3 - { "v2", ~NFS_MOUNT_VER3, 0 }, - { "v3", ~NFS_MOUNT_VER3, NFS_MOUNT_VER3 }, -#endif - { "udp", ~NFS_MOUNT_TCP, 0 }, - { "tcp", ~NFS_MOUNT_TCP, NFS_MOUNT_TCP }, - { "broken_suid",~NFS_MOUNT_BROKEN_SUID, NFS_MOUNT_BROKEN_SUID }, - { NULL, 0, 0 } +static match_table_t tokens = { + {Opt_port, "port=%u"}, + {Opt_rsize, "rsize=%u"}, + {Opt_wsize, "wsize=%u"}, + {Opt_timeo, "timeo=%u"}, + {Opt_retrans, "retrans=%u"}, + {Opt_acregmin, "acregmin=%u"}, + {Opt_acregmax, "acregmax=%u"}, + {Opt_acdirmin, "acdirmin=%u"}, + {Opt_acdirmax, "acdirmax=%u"}, + {Opt_soft, "soft"}, + {Opt_hard, "hard"}, + {Opt_intr, "intr"}, + {Opt_nointr, "nointr"}, + {Opt_posix, "posix"}, + {Opt_noposix, "noposix"}, + {Opt_cto, "cto"}, + {Opt_nocto, "nocto"}, + {Opt_ac, "ac"}, + {Opt_noac, "noac"}, + {Opt_lock, "lock"}, + {Opt_nolock, "nolock"}, + {Opt_v2, "v2"}, + {Opt_v3, "v3"}, + {Opt_udp, "udp"}, + {Opt_tcp, "tcp"}, + {Opt_broken_suid, "broken_suid"}, + {Opt_err, NULL} + }; - /* * Parse option string. */ -static void __init root_nfs_parse(char *name, char *buf) + +static int __init root_nfs_parse(char *name, char *buf) { - char *options, *val, *cp; - if ((options = strchr(name, ','))) { - *options++ = 0; - while ((cp = strsep(&options, ",")) != NULL) { - if (!*cp) - continue; - if ((val = strchr(cp, '='))) { - struct nfs_int_opts *opts = root_int_opts; - *val++ = '\0'; - while (opts->name && strcmp(opts->name, cp)) - opts++; - if (opts->name) - *(opts->val) = (int) simple_strtoul(val, NULL, 10); - } else { - struct nfs_bool_opts *opts = root_bool_opts; - while (opts->name && strcmp(opts->name, cp)) - opts++; - if (opts->name) { - nfs_data.flags &= opts->and_mask; - nfs_data.flags |= opts->or_mask; - } - } + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + + if (!name) + return 1; + + if (name[0] && strcmp(name, "default")){ + strlcpy(buf, name, NFS_MAXPATHLEN); + return 1; + } + while ((p = strsep (&name, ",")) != NULL) { + int token; + if (!*p) + continue; + token = match_token(p, tokens, args); + + /* %u tokens only */ + if (match_int(&args[0], &option)) + return 0; + switch (token) { + case Opt_port: + nfs_port = option; + break; + case Opt_rsize: + nfs_data.rsize = option; + break; + case Opt_wsize: + nfs_data.wsize = option; + break; + case Opt_timeo: + nfs_data.timeo = option; + break; + case Opt_retrans: + nfs_data.retrans = option; + break; + case Opt_acregmin: + nfs_data.acregmin = option; + break; + case Opt_acregmax: + nfs_data.acregmax = option; + break; + case Opt_acdirmin: + nfs_data.acdirmin = option; + break; + case Opt_acdirmax: + nfs_data.acdirmax = option; + break; + case Opt_soft: + nfs_data.flags |= NFS_MOUNT_SOFT; + break; + case Opt_hard: + nfs_data.flags &= ~NFS_MOUNT_SOFT; + break; + case Opt_intr: + nfs_data.flags |= NFS_MOUNT_INTR; + break; + case Opt_nointr: + nfs_data.flags &= ~NFS_MOUNT_INTR; + break; + case Opt_posix: + nfs_data.flags |= NFS_MOUNT_POSIX; + break; + case Opt_noposix: + nfs_data.flags &= ~NFS_MOUNT_POSIX; + break; + case Opt_cto: + nfs_data.flags &= ~NFS_MOUNT_NOCTO; + break; + case Opt_nocto: + nfs_data.flags |= NFS_MOUNT_NOCTO; + break; + case Opt_ac: + nfs_data.flags &= ~NFS_MOUNT_NOAC; + break; + case Opt_noac: + nfs_data.flags |= NFS_MOUNT_NOAC; + break; + case Opt_lock: + nfs_data.flags &= ~NFS_MOUNT_NONLM; + break; + case Opt_nolock: + nfs_data.flags |= NFS_MOUNT_NONLM; + break; + case Opt_v2: + nfs_data.flags &= ~NFS_MOUNT_VER3; + break; + case Opt_v3: + nfs_data.flags |= NFS_MOUNT_VER3; + break; + case Opt_udp: + nfs_data.flags &= ~NFS_MOUNT_TCP; + break; + case Opt_tcp: + nfs_data.flags |= NFS_MOUNT_TCP; + break; + case Opt_broken_suid: + nfs_data.flags |= NFS_MOUNT_BROKEN_SUID; + break; + default : + return 0; } } - if (name[0] && strcmp(name, "default")) - strlcpy(buf, name, NFS_MAXPATHLEN); + return 1; } - /* * Prepare the NFS data structure and parse all options. */ diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/pagelist.c linux-2.6.5-31-server_caps/fs/nfs/pagelist.c --- linux-2.6.5-rc2/fs/nfs/pagelist.c 2004-03-25 00:30:06.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/pagelist.c 2004-03-25 00:35:16.000000000 -0500 @@ -32,7 +32,7 @@ static inline struct nfs_page * nfs_page_alloc(void) { struct nfs_page *p; - p = kmem_cache_alloc(nfs_page_cachep, SLAB_NOFS); + p = kmem_cache_alloc(nfs_page_cachep, SLAB_KERNEL); if (p) { memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->wb_list); @@ -88,6 +88,7 @@ nfs_create_request(struct file *file, st * long write-back delay. This will be adjusted in * update_nfs_request below if the region is not locked. */ req->wb_page = page; + atomic_set(&req->wb_complete, 0); req->wb_index = page->index; page_cache_get(page); req->wb_offset = offset; diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/proc.c linux-2.6.5-31-server_caps/fs/nfs/proc.c --- linux-2.6.5-rc2/fs/nfs/proc.c 2004-03-25 00:30:06.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/proc.c 2004-03-25 00:35:13.000000000 -0500 @@ -559,11 +559,10 @@ nfs_read_done(struct rpc_task *task) } static void -nfs_proc_read_setup(struct nfs_read_data *data, unsigned int count) +nfs_proc_read_setup(struct nfs_read_data *data) { struct rpc_task *task = &data->task; struct inode *inode = data->inode; - struct nfs_page *req; int flags; struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_READ], @@ -571,27 +570,13 @@ nfs_proc_read_setup(struct nfs_read_data .rpc_resp = &data->res, .rpc_cred = data->cred, }; - - req = nfs_list_entry(data->pages.next); - data->args.fh = NFS_FH(inode); - data->args.offset = req_offset(req); - data->args.pgbase = req->wb_pgbase; - 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); - task->tk_calldata = data; - /* Release requests */ - task->tk_release = nfs_readdata_release; - - rpc_call_setup(&data->task, &msg, 0); + rpc_call_setup(task, &msg, 0); } static void @@ -605,11 +590,10 @@ nfs_write_done(struct rpc_task *task) } static void -nfs_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) +nfs_proc_write_setup(struct nfs_write_data *data, int how) { struct rpc_task *task = &data->task; struct inode *inode = data->inode; - struct nfs_page *req; int flags; struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_WRITE], @@ -619,32 +603,18 @@ nfs_proc_write_setup(struct nfs_write_da }; /* Note: NFSv2 ignores @stable and always uses NFS_FILE_SYNC */ - - req = nfs_list_entry(data->pages.next); - data->args.fh = NFS_FH(inode); - data->args.offset = req_offset(req); - data->args.pgbase = req->wb_pgbase; - data->args.count = count; data->args.stable = NFS_FILE_SYNC; - data->args.pages = data->pagevec; - data->res.fattr = &data->fattr; - data->res.count = count; - data->res.verf = &data->verf; /* Set the initial flags for the task. */ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ rpc_init_task(task, NFS_CLIENT(inode), nfs_write_done, flags); - task->tk_calldata = data; - /* Release requests */ - task->tk_release = nfs_writedata_release; - - rpc_call_setup(&data->task, &msg, 0); + rpc_call_setup(task, &msg, 0); } static void -nfs_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) +nfs_proc_commit_setup(struct nfs_write_data *data, int how) { BUG(); } diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/read.c linux-2.6.5-31-server_caps/fs/nfs/read.c --- linux-2.6.5-rc2/fs/nfs/read.c 2004-03-25 00:29:30.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/read.c 2004-03-25 00:35:35.000000000 -0500 @@ -35,6 +35,8 @@ #define NFSDBG_FACILITY NFSDBG_PAGECACHE static int nfs_pagein_one(struct list_head *, struct inode *); +static void nfs_readpage_result_partial(struct nfs_read_data *, int); +static void nfs_readpage_result_full(struct nfs_read_data *, int); static kmem_cache_t *nfs_rdata_cachep; static mempool_t *nfs_rdata_mempool; @@ -57,12 +59,37 @@ static __inline__ void nfs_readdata_free mempool_free(p, nfs_rdata_mempool); } -void nfs_readdata_release(struct rpc_task *task) +static void nfs_readdata_release(struct rpc_task *task) { struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; nfs_readdata_free(data); } +static +unsigned int nfs_page_length(struct inode *inode, struct page *page) +{ + loff_t i_size = i_size_read(inode); + unsigned long idx; + + if (i_size <= 0) + return 0; + idx = (i_size - 1) >> PAGE_CACHE_SHIFT; + if (page->index > idx) + return 0; + if (page->index != idx) + return PAGE_CACHE_SIZE; + return 1 + ((i_size - 1) & (PAGE_CACHE_SIZE - 1)); +} + +static +int nfs_return_empty_page(struct page *page) +{ + memclear_highpage_flush(page, 0, PAGE_CACHE_SIZE); + SetPageUptodate(page); + unlock_page(page); + return 0; +} + /* * Read a page synchronously. */ @@ -78,6 +105,7 @@ nfs_readpage_sync(struct file *file, str .inode = inode, .args = { .fh = NFS_FH(inode), + .lockowner = current->files, .pages = &page, .pgbase = 0UL, .count = rsize, @@ -146,89 +174,209 @@ nfs_readpage_async(struct file *file, st { LIST_HEAD(one_request); struct nfs_page *new; + unsigned int len; - new = nfs_create_request(file, inode, page, 0, PAGE_CACHE_SIZE); + len = nfs_page_length(inode, page); + if (len == 0) + return nfs_return_empty_page(page); + new = nfs_create_request(file, inode, page, 0, len); if (IS_ERR(new)) { unlock_page(page); return PTR_ERR(new); } + if (len < PAGE_CACHE_SIZE) + memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len); + nfs_lock_request(new); nfs_list_add_request(new, &one_request); nfs_pagein_one(&one_request, inode); return 0; } +static void nfs_readpage_release(struct nfs_page *req) +{ + unlock_page(req->wb_page); + + nfs_clear_request(req); + nfs_release_request(req); + nfs_unlock_request(req); + + dprintk("NFS: read done (%s/%Ld %d@%Ld)\n", + req->wb_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_inode), + req->wb_bytes, + (long long)req_offset(req)); +} + /* * Set up the NFS read request struct */ -static void -nfs_read_rpcsetup(struct list_head *head, struct nfs_read_data *data) +static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, + unsigned int count, unsigned int offset) { struct inode *inode; - struct nfs_page *req; - struct page **pages; - unsigned int count; - pages = data->pagevec; - count = 0; - while (!list_empty(head)) { - req = nfs_list_entry(head->next); - nfs_list_remove_request(req); - nfs_list_add_request(req, &data->pages); - *pages++ = req->wb_page; - count += req->wb_bytes; - } - req = nfs_list_entry(data->pages.next); + data->req = req; data->inode = inode = req->wb_inode; data->cred = req->wb_cred; - NFS_PROTO(inode)->read_setup(data, count); + 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.lockowner = req->wb_lockowner; + data->args.state = req->wb_state; + + data->res.fattr = &data->fattr; + data->res.count = count; + data->res.eof = 0; + + NFS_PROTO(inode)->read_setup(data); + + data->task.tk_cookie = (unsigned long)inode; + data->task.tk_calldata = data; + /* Release requests */ + data->task.tk_release = nfs_readdata_release; - dprintk("NFS: %4d initiated read call (req %s/%Ld, %u bytes @ offset %Lu.\n", + dprintk("NFS: %4d 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)req_offset(req)); + (unsigned long long)data->args.offset); } static void nfs_async_read_error(struct list_head *head) { struct nfs_page *req; - struct page *page; while (!list_empty(head)) { req = nfs_list_entry(head->next); - page = req->wb_page; nfs_list_remove_request(req); - SetPageError(page); - unlock_page(page); - nfs_clear_request(req); - nfs_release_request(req); - nfs_unlock_request(req); + SetPageError(req->wb_page); + nfs_readpage_release(req); } } -static int -nfs_pagein_one(struct list_head *head, struct inode *inode) +/* + * 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); + lock_kernel(); + rpc_execute(&data->task); + unlock_kernel(); + 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 list_head *head, struct inode *inode) +{ + struct nfs_page *req = nfs_list_entry(head->next); + struct page *page = req->wb_page; + struct nfs_read_data *data; + unsigned int rsize = NFS_SERVER(inode)->rsize; + unsigned int nbytes, offset; + int requests = 0; + LIST_HEAD(list); + + nfs_list_remove_request(req); + + nbytes = req->wb_bytes; + for(;;) { + data = nfs_readdata_alloc(); + if (!data) + goto out_bad; + list_add(&data->pages, &list); + requests++; + if (nbytes <= rsize) + break; + nbytes -= rsize; + } + atomic_set(&req->wb_complete, requests); + + ClearPageError(page); + offset = 0; + nbytes = req->wb_bytes; + do { + data = list_entry(list.next, struct nfs_read_data, pages); + list_del_init(&data->pages); + + data->pagevec[0] = page; + data->complete = nfs_readpage_result_partial; + + if (nbytes > rsize) { + nfs_read_rpcsetup(req, data, rsize, offset); + offset += rsize; + nbytes -= rsize; + } else { + nfs_read_rpcsetup(req, data, nbytes, offset); + nbytes = 0; + } + 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 list_head *head, struct inode *inode) { - struct rpc_clnt *clnt = NFS_CLIENT(inode); + struct nfs_page *req; + struct page **pages; struct nfs_read_data *data; - sigset_t oldset; + unsigned int count; + + if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE) + return nfs_pagein_multi(head, inode); data = nfs_readdata_alloc(); if (!data) goto out_bad; - nfs_read_rpcsetup(head, data); + pages = data->pagevec; + count = 0; + while (!list_empty(head)) { + req = nfs_list_entry(head->next); + nfs_list_remove_request(req); + nfs_list_add_request(req, &data->pages); + ClearPageError(req->wb_page); + *pages++ = req->wb_page; + count += req->wb_bytes; + } + req = nfs_list_entry(data->pages.next); + + data->complete = nfs_readpage_result_full; + nfs_read_rpcsetup(req, data, count, 0); - /* Start the async call */ - rpc_clnt_sigmask(clnt, &oldset); - lock_kernel(); - rpc_execute(&data->task); - unlock_kernel(); - rpc_clnt_sigunmask(clnt, &oldset); + nfs_execute_read(data); return 0; out_bad: nfs_async_read_error(head); @@ -258,55 +406,95 @@ nfs_pagein_list(struct list_head *head, } /* + * Handle a read reply that fills part of a page. + */ +static void nfs_readpage_result_partial(struct nfs_read_data *data, int status) +{ + struct nfs_page *req = data->req; + struct page *page = req->wb_page; + + if (status >= 0) { + unsigned int request = data->args.count; + unsigned int result = data->res.count; + + if (result < request) { + memclear_highpage_flush(page, + data->args.pgbase + result, + request - result); + } + } else + SetPageError(page); + + if (atomic_dec_and_test(&req->wb_complete)) { + if (!PageError(page)) + SetPageUptodate(page); + nfs_readpage_release(req); + } +} + +/* * This is the callback from RPC telling us whether a reply was * received or some error occurred (timeout or socket shutdown). */ -void -nfs_readpage_result(struct rpc_task *task) +static void nfs_readpage_result_full(struct nfs_read_data *data, int status) { - struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; unsigned int count = data->res.count; - dprintk("NFS: %4d nfs_readpage_result, (status %d)\n", - task->tk_pid, task->tk_status); - - NFS_FLAGS(data->inode) |= NFS_INO_INVALID_ATIME; while (!list_empty(&data->pages)) { struct nfs_page *req = nfs_list_entry(data->pages.next); struct page *page = req->wb_page; nfs_list_remove_request(req); - if (task->tk_status >= 0) { + if (status >= 0) { if (count < PAGE_CACHE_SIZE) { - memclear_highpage_flush(page, + if (count < req->wb_bytes) + memclear_highpage_flush(page, req->wb_pgbase + count, req->wb_bytes - count); - count = 0; } else count -= PAGE_CACHE_SIZE; SetPageUptodate(page); } else SetPageError(page); - unlock_page(page); + nfs_readpage_release(req); + } +} - dprintk("NFS: read (%s/%Ld %d@%Ld)\n", - req->wb_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_inode), - req->wb_bytes, - (long long)req_offset(req)); - nfs_clear_request(req); - nfs_release_request(req); - nfs_unlock_request(req); +/* + * This is the callback from RPC telling us whether a reply was + * received or some error occurred (timeout or socket shutdown). + */ +void nfs_readpage_result(struct rpc_task *task) +{ + struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; + struct nfs_readargs *argp = &data->args; + struct nfs_readres *resp = &data->res; + int status = task->tk_status; + + dprintk("NFS: %4d nfs_readpage_result, (status %d)\n", + task->tk_pid, status); + + /* Is this a short read? */ + if (task->tk_status >= 0 && resp->count < argp->count && !resp->eof) { + /* Has the server at least made some progress? */ + if (resp->count != 0) { + /* Yes, so retry the read at the end of the data */ + argp->offset += resp->count; + argp->pgbase += resp->count; + argp->count -= resp->count; + rpc_restart_call(task); + return; + } + task->tk_status = -EIO; } + NFS_FLAGS(data->inode) |= NFS_INO_INVALID_ATIME; + data->complete(data, status); } /* * Read a page over NFS. - * We read the page synchronously in the following cases: - * - The NFS rsize is smaller than PAGE_CACHE_SIZE. We could kludge our way - * around this by creating several consecutive read requests, but - * that's hardly worth it. + * We read the page synchronously in the following case: * - The error flag is set for this page. This happens only when a * previous async read operation failed. */ @@ -329,7 +517,7 @@ nfs_readpage(struct file *file, struct p if (error) goto out_error; - if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE) { + if (!IS_SYNC(inode)) { error = nfs_readpage_async(file, inode, page); goto out; } @@ -351,26 +539,25 @@ struct nfs_readdesc { }; static int -readpage_sync_filler(void *data, struct page *page) -{ - struct nfs_readdesc *desc = (struct nfs_readdesc *)data; - return nfs_readpage_sync(desc->filp, page->mapping->host, page); -} - -static int readpage_async_filler(void *data, struct page *page) { struct nfs_readdesc *desc = (struct nfs_readdesc *)data; struct inode *inode = page->mapping->host; struct nfs_page *new; + unsigned int len; nfs_wb_page(inode, page); - new = nfs_create_request(desc->filp, inode, page, 0, PAGE_CACHE_SIZE); + len = nfs_page_length(inode, page); + if (len == 0) + return nfs_return_empty_page(page); + new = nfs_create_request(desc->filp, inode, page, 0, len); if (IS_ERR(new)) { SetPageError(page); unlock_page(page); return PTR_ERR(new); } + if (len < PAGE_CACHE_SIZE) + memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len); nfs_lock_request(new); nfs_list_add_request(new, desc->head); return 0; @@ -385,14 +572,16 @@ nfs_readpages(struct file *filp, struct .filp = filp, .head = &head, }; - struct nfs_server *server = NFS_SERVER(mapping->host); - int is_sync = server->rsize < PAGE_CACHE_SIZE; + struct inode *inode = mapping->host; + struct nfs_server *server = NFS_SERVER(inode); int ret; - ret = read_cache_pages(mapping, pages, - is_sync ? readpage_sync_filler : - readpage_async_filler, - &desc); + dprintk("NFS: nfs_readpages (%s/%Ld %d)\n", + inode->i_sb->s_id, + (long long)NFS_FILEID(inode), + nr_pages); + + ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); if (!list_empty(&head)) { int err = nfs_pagein_list(&head, server->rpages); if (!ret) diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/fs/nfs/write.c linux-2.6.5-31-server_caps/fs/nfs/write.c --- linux-2.6.5-rc2/fs/nfs/write.c 2004-03-25 00:30:02.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/write.c 2004-03-25 00:35:45.000000000 -0500 @@ -74,11 +74,17 @@ static struct nfs_page * nfs_update_request(struct file*, struct inode *, struct page *, unsigned int, unsigned int); +static void nfs_writeback_done_partial(struct nfs_write_data *, int); +static void nfs_writeback_done_full(struct nfs_write_data *, int); +static int nfs_wait_on_write_congestion(struct address_space *, int); +static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int); static kmem_cache_t *nfs_wdata_cachep; static mempool_t *nfs_wdata_mempool; static mempool_t *nfs_commit_mempool; +static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion); + static __inline__ struct nfs_write_data *nfs_writedata_alloc(void) { struct nfs_write_data *p; @@ -95,7 +101,7 @@ static __inline__ void nfs_writedata_fre mempool_free(p, nfs_wdata_mempool); } -void nfs_writedata_release(struct rpc_task *task) +static void nfs_writedata_release(struct rpc_task *task) { struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata; nfs_writedata_free(wdata); @@ -117,12 +123,6 @@ static __inline__ void nfs_commit_free(s mempool_free(p, nfs_commit_mempool); } -void nfs_commit_release(struct rpc_task *task) -{ - struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata; - nfs_commit_free(wdata); -} - /* Adjust the file length if we're writing beyond the end */ static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count) { @@ -173,19 +173,19 @@ static void nfs_mark_uptodate(struct pag * Write a page synchronously. * Offset is the data offset within the page. */ -static int -nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, - unsigned int offset, unsigned int count) +static int nfs_writepage_sync(struct file *file, struct inode *inode, + struct page *page, unsigned int offset, unsigned int count, + int how) { unsigned int wsize = NFS_SERVER(inode)->wsize; int result, written = 0; - int swapfile = IS_SWAPFILE(inode); struct nfs_write_data wdata = { - .flags = swapfile ? NFS_RPC_SWAPFLAGS : 0, + .flags = how, .cred = NULL, .inode = inode, .args = { .fh = NFS_FH(inode), + .lockowner = current->files, .pages = &page, .stable = NFS_FILE_SYNC, .pgbase = offset, @@ -204,7 +204,7 @@ nfs_writepage_sync(struct file *file, st nfs_begin_data_update(inode); do { - if (count < wsize && !swapfile) + if (count < wsize) wdata.args.count = count; wdata.args.offset = page_offset(page) + wdata.args.pgbase; @@ -233,7 +233,7 @@ nfs_writepage_sync(struct file *file, st ClearPageError(page); io_error: - nfs_end_data_update(inode); + nfs_end_data_update_defer(inode); if (wdata.cred) put_rpccred(wdata.cred); @@ -259,17 +259,26 @@ static int nfs_writepage_async(struct fi return status; } +static int wb_priority(struct writeback_control *wbc) +{ + if (wbc->for_reclaim) + return FLUSH_HIGHPRI; + if (wbc->for_kupdate) + return FLUSH_LOWPRI; + return 0; +} + /* * Write an mmapped page to the server. */ -int -nfs_writepage(struct page *page, struct writeback_control *wbc) +int nfs_writepage(struct page *page, struct writeback_control *wbc) { struct inode *inode = page->mapping->host; unsigned long end_index; unsigned offset = PAGE_CACHE_SIZE; loff_t i_size = i_size_read(inode); int inode_referenced = 0; + int priority = wb_priority(wbc); int err; /* @@ -285,7 +294,7 @@ nfs_writepage(struct page *page, struct end_index = i_size >> PAGE_CACHE_SHIFT; /* Ensure we've flushed out any previous writes */ - nfs_wb_page(inode,page); + nfs_wb_page_priority(inode, page, priority); /* easy case */ if (page->index < end_index) @@ -299,44 +308,60 @@ nfs_writepage(struct page *page, struct goto out; do_it: lock_kernel(); - if (NFS_SERVER(inode)->wsize >= PAGE_CACHE_SIZE && !IS_SYNC(inode) && - inode_referenced) { + if (!IS_SYNC(inode) && inode_referenced) { err = nfs_writepage_async(NULL, inode, page, 0, offset); - if (err >= 0) + if (err >= 0) { err = 0; + if (wbc->for_reclaim) + err = WRITEPAGE_ACTIVATE; + } } else { - err = nfs_writepage_sync(NULL, inode, page, 0, offset); + err = nfs_writepage_sync(NULL, inode, page, 0, offset, priority); if (err == offset) err = 0; } unlock_kernel(); out: - unlock_page(page); + if (err != WRITEPAGE_ACTIVATE) + unlock_page(page); if (inode_referenced) iput(inode); return err; } -int -nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) +/* + * Note: causes nfs_update_request() to block on the assumption + * that the writeback is generated due to memory pressure. + */ +int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) { + struct backing_dev_info *bdi = mapping->backing_dev_info; struct inode *inode = mapping->host; - int is_sync = !wbc->nonblocking; int err; err = generic_writepages(mapping, wbc); if (err) - goto out; - err = nfs_flush_inode(inode, 0, 0, 0); + return err; + while (test_and_set_bit(BDI_write_congested, &bdi->state) != 0) { + if (wbc->nonblocking) + return 0; + nfs_wait_on_write_congestion(mapping, 0); + } + err = nfs_flush_inode(inode, 0, 0, wb_priority(wbc)); if (err < 0) goto out; - if (wbc->sync_mode == WB_SYNC_HOLD) - goto out; - if (is_sync && wbc->sync_mode == WB_SYNC_ALL) { - err = nfs_wb_all(inode); - } else - nfs_commit_inode(inode, 0, 0, 0); + wbc->nr_to_write -= err; + if (!wbc->nonblocking && wbc->sync_mode == WB_SYNC_ALL) { + err = nfs_wait_on_requests(inode, 0, 0); + if (err < 0) + goto out; + } + err = nfs_commit_inode(inode, 0, 0, wb_priority(wbc)); + if (err > 0) + wbc->nr_to_write -= err; out: + clear_bit(BDI_write_congested, &bdi->state); + wake_up_all(&nfs_write_congestion); return err; } @@ -365,7 +390,7 @@ nfs_inode_add_request(struct inode *inod /* * Insert a write request into an inode */ -static inline void +static void nfs_inode_remove_request(struct nfs_page *req) { struct nfs_inode *nfsi; @@ -379,7 +404,7 @@ nfs_inode_remove_request(struct nfs_page nfsi->npages--; if (!nfsi->npages) { spin_unlock(&nfs_wreq_lock); - nfs_end_data_update(inode); + nfs_end_data_update_defer(inode); iput(inode); } else spin_unlock(&nfs_wreq_lock); @@ -416,7 +441,7 @@ nfs_find_request(struct inode *inode, un /* * Add a request to the inode's dirty list. */ -static inline void +static void nfs_mark_request_dirty(struct nfs_page *req) { struct inode *inode = req->wb_inode; @@ -444,7 +469,7 @@ nfs_dirty_request(struct nfs_page *req) /* * Add a request to the inode's commit list. */ -static inline void +static void nfs_mark_request_commit(struct nfs_page *req) { struct inode *inode = req->wb_inode; @@ -548,6 +573,38 @@ nfs_scan_commit(struct inode *inode, str } #endif +static int nfs_wait_on_write_congestion(struct address_space *mapping, int intr) +{ + struct backing_dev_info *bdi = mapping->backing_dev_info; + DEFINE_WAIT(wait); + int ret = 0; + + might_sleep(); + + if (!bdi_write_congested(bdi)) + return 0; + if (intr) { + struct rpc_clnt *clnt = NFS_CLIENT(mapping->host); + sigset_t oldset; + + rpc_clnt_sigmask(clnt, &oldset); + prepare_to_wait(&nfs_write_congestion, &wait, TASK_INTERRUPTIBLE); + if (bdi_write_congested(bdi)) { + if (signalled()) + ret = -ERESTARTSYS; + else + schedule(); + } + rpc_clnt_sigunmask(clnt, &oldset); + } else { + prepare_to_wait(&nfs_write_congestion, &wait, TASK_UNINTERRUPTIBLE); + if (bdi_write_congested(bdi)) + schedule(); + } + finish_wait(&nfs_write_congestion, &wait); + return ret; +} + /* * Try to update any existing write request, or create one if there is none. @@ -560,11 +617,14 @@ static struct nfs_page * nfs_update_request(struct file* file, struct inode *inode, struct page *page, unsigned int offset, unsigned int bytes) { + struct nfs_server *server = NFS_SERVER(inode); struct nfs_page *req, *new = NULL; unsigned long rqend, end; end = offset + bytes; + if (nfs_wait_on_write_congestion(page->mapping, server->flags & NFS_MOUNT_INTR)) + return ERR_PTR(-ERESTARTSYS); for (;;) { /* Loop over all inode entries and see if we find * A request for the page we wish to update @@ -668,8 +728,8 @@ nfs_flush_incompatible(struct file *file * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad * things with a page scheduled for an RPC call (e.g. invalidate it). */ -int -nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsigned int count) +int nfs_updatepage(struct file *file, struct page *page, + unsigned int offset, unsigned int count) { struct dentry *dentry = file->f_dentry; struct inode *inode = page->mapping->host; @@ -680,12 +740,8 @@ nfs_updatepage(struct file *file, struct dentry->d_parent->d_name.name, dentry->d_name.name, count, (long long)(page_offset(page) +offset)); - /* - * If wsize is smaller than page size, update and write - * page synchronously. - */ - if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE || IS_SYNC(inode)) { - status = nfs_writepage_sync(file, inode, page, offset, count); + if (IS_SYNC(inode)) { + status = nfs_writepage_sync(file, inode, page, offset, count, 0); if (status > 0) { if (offset == 0 && status == PAGE_CACHE_SIZE) SetPageUptodate(page); @@ -747,43 +803,159 @@ done: return status; } +static void nfs_writepage_release(struct nfs_page *req) +{ + end_page_writeback(req->wb_page); + +#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) + if (!PageError(req->wb_page)) { + if (NFS_NEED_RESCHED(req)) { + nfs_mark_request_dirty(req); + goto out; + } else if (NFS_NEED_COMMIT(req)) { + nfs_mark_request_commit(req); + goto out; + } + } + nfs_inode_remove_request(req); + +out: + nfs_clear_commit(req); + nfs_clear_reschedule(req); +#else + nfs_inode_remove_request(req); +#endif + nfs_unlock_request(req); +} + +static inline int flush_task_priority(int how) +{ + switch (how & (FLUSH_HIGHPRI|FLUSH_LOWPRI)) { + case FLUSH_HIGHPRI: + return RPC_PRIORITY_HIGH; + case FLUSH_LOWPRI: + return RPC_PRIORITY_LOW; + } + return RPC_PRIORITY_NORMAL; +} + /* * Set up the argument/result storage required for the RPC call. */ -static void -nfs_write_rpcsetup(struct list_head *head, struct nfs_write_data *data, int how) +static void nfs_write_rpcsetup(struct nfs_page *req, + struct nfs_write_data *data, + unsigned int count, unsigned int offset, + int how) { struct rpc_task *task = &data->task; struct inode *inode; - struct nfs_page *req; - struct page **pages; - unsigned int count; /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ - pages = data->pagevec; - count = 0; - while (!list_empty(head)) { - req = nfs_list_entry(head->next); - nfs_list_remove_request(req); - nfs_list_add_request(req, &data->pages); - SetPageWriteback(req->wb_page); - *pages++ = req->wb_page; - count += req->wb_bytes; - } - req = nfs_list_entry(data->pages.next); + data->req = req; data->inode = inode = req->wb_inode; data->cred = req->wb_cred; - NFS_PROTO(inode)->write_setup(data, count, how); + data->args.fh = NFS_FH(inode); + data->args.offset = req_offset(req) + offset; + data->args.pgbase = req->wb_pgbase + offset; + data->args.count = count; + data->args.pages = data->pagevec; + data->res.fattr = &data->fattr; + data->res.count = count; + data->res.verf = &data->verf; + + NFS_PROTO(inode)->write_setup(data, how); + + data->task.tk_priority = flush_task_priority(how); + data->task.tk_cookie = (unsigned long)inode; + data->task.tk_calldata = data; + /* Release requests */ + data->task.tk_release = nfs_writedata_release; dprintk("NFS: %4d initiated write call (req %s/%Ld, %u bytes @ offset %Lu)\n", task->tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), count, - (unsigned long long)req_offset(req)); + (unsigned long long)data->args.offset); +} + +static void nfs_execute_write(struct nfs_write_data *data) +{ + struct rpc_clnt *clnt = NFS_CLIENT(data->inode); + sigset_t oldset; + + rpc_clnt_sigmask(clnt, &oldset); + lock_kernel(); + rpc_execute(&data->task); + unlock_kernel(); + rpc_clnt_sigunmask(clnt, &oldset); +} + +/* + * Generate multiple small requests to write out a single + * contiguous dirty area on one page. + */ +static int nfs_flush_multi(struct list_head *head, struct inode *inode, int how) +{ + struct nfs_page *req = nfs_list_entry(head->next); + struct page *page = req->wb_page; + struct nfs_write_data *data; + unsigned int wsize = NFS_SERVER(inode)->wsize; + unsigned int nbytes, offset; + int requests = 0; + LIST_HEAD(list); + + nfs_list_remove_request(req); + + nbytes = req->wb_bytes; + for (;;) { + data = nfs_writedata_alloc(); + if (!data) + goto out_bad; + list_add(&data->pages, &list); + requests++; + if (nbytes <= wsize) + break; + nbytes -= wsize; + } + atomic_set(&req->wb_complete, requests); + + ClearPageError(page); + SetPageWriteback(page); + offset = 0; + nbytes = req->wb_bytes; + do { + data = list_entry(list.next, struct nfs_write_data, pages); + list_del_init(&data->pages); + + data->pagevec[0] = page; + data->complete = nfs_writeback_done_partial; + + if (nbytes > wsize) { + nfs_write_rpcsetup(req, data, wsize, offset, how); + offset += wsize; + nbytes -= wsize; + } else { + nfs_write_rpcsetup(req, data, nbytes, offset, how); + nbytes = 0; + } + nfs_execute_write(data); + } while (nbytes != 0); + + return 0; + +out_bad: + while (!list_empty(&list)) { + data = list_entry(list.next, struct nfs_write_data, pages); + list_del(&data->pages); + nfs_writedata_free(data); + } + nfs_mark_request_dirty(req); + nfs_unlock_request(req); + return -ENOMEM; } /* @@ -794,25 +966,38 @@ nfs_write_rpcsetup(struct list_head *hea * This is the case if nfs_updatepage detects a conflicting request * that has been written but not committed. */ -static int -nfs_flush_one(struct list_head *head, struct inode *inode, int how) +static int nfs_flush_one(struct list_head *head, struct inode *inode, int how) { - struct rpc_clnt *clnt = NFS_CLIENT(inode); + struct nfs_page *req; + struct page **pages; struct nfs_write_data *data; - sigset_t oldset; + unsigned int count; + + if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE) + return nfs_flush_multi(head, inode, how); data = nfs_writedata_alloc(); if (!data) goto out_bad; + pages = data->pagevec; + count = 0; + while (!list_empty(head)) { + req = nfs_list_entry(head->next); + nfs_list_remove_request(req); + nfs_list_add_request(req, &data->pages); + ClearPageError(req->wb_page); + SetPageWriteback(req->wb_page); + *pages++ = req->wb_page; + count += req->wb_bytes; + } + req = nfs_list_entry(data->pages.next); + + data->complete = nfs_writeback_done_full; /* Set up the argument struct */ - nfs_write_rpcsetup(head, data, how); + nfs_write_rpcsetup(req, data, count, 0, how); - rpc_clnt_sigmask(clnt, &oldset); - lock_kernel(); - rpc_execute(&data->task); - unlock_kernel(); - rpc_clnt_sigunmask(clnt, &oldset); + nfs_execute_write(data); return 0; out_bad: while (!list_empty(head)) { @@ -851,59 +1036,59 @@ nfs_flush_list(struct list_head *head, i return error; } - /* - * This function is called when the WRITE call is complete. + * Handle a write reply that flushed part of a page. */ -void -nfs_writeback_done(struct rpc_task *task) +static void nfs_writeback_done_partial(struct nfs_write_data *data, int status) { - struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; - struct nfs_writeargs *argp = &data->args; - struct nfs_writeres *resp = &data->res; - struct nfs_page *req; - struct page *page; + struct nfs_page *req = data->req; + struct page *page = req->wb_page; - dprintk("NFS: %4d nfs_writeback_done (status %d)\n", - task->tk_pid, task->tk_status); + dprintk("NFS: write (%s/%Ld %d@%Ld)", + req->wb_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_inode), + req->wb_bytes, + (long long)req_offset(req)); - /* We can't handle that yet but we check for it nevertheless */ - if (resp->count < argp->count && task->tk_status >= 0) { - static unsigned long complain; - if (time_before(complain, jiffies)) { - printk(KERN_WARNING - "NFS: Server wrote less than requested.\n"); - complain = jiffies + 300 * HZ; - } - /* Can't do anything about it right now except throw - * an error. */ - task->tk_status = -EIO; - } + if (status < 0) { + ClearPageUptodate(page); + SetPageError(page); + if (req->wb_file) + req->wb_file->f_error = status; + dprintk(", error = %d\n", status); + } else { #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) - if (data->verf.committed < argp->stable && task->tk_status >= 0) { - /* We tried a write call, but the server did not - * commit data to stable storage even though we - * requested it. - * Note: There is a known bug in Tru64 < 5.0 in which - * the server reports NFS_DATA_SYNC, but performs - * NFS_FILE_SYNC. We therefore implement this checking - * as a dprintk() in order to avoid filling syslog. - */ - static unsigned long complain; - - if (time_before(complain, jiffies)) { - dprintk("NFS: faulty NFS server %s:" - " (committed = %d) != (stable = %d)\n", - NFS_SERVER(data->inode)->hostname, - data->verf.committed, argp->stable); - complain = jiffies + 300 * HZ; - } - } + if (data->verf.committed < NFS_FILE_SYNC) { + if (!NFS_NEED_COMMIT(req)) { + nfs_defer_commit(req); + memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); + dprintk(" defer commit\n"); + } else if (memcmp(&req->wb_verf, &data->verf, sizeof(req->wb_verf))) { + nfs_defer_reschedule(req); + dprintk(" server reboot detected\n"); + } + } else #endif + dprintk(" OK\n"); + } - /* - * Process the nfs_page list - */ + if (atomic_dec_and_test(&req->wb_complete)) + nfs_writepage_release(req); +} + +/* + * Handle a write reply that flushes a whole page. + * + * FIXME: There is an inherent race with invalidate_inode_pages and + * writebacks since the page->count is kept > 1 for as long + * as the page has a write request pending. + */ +static void nfs_writeback_done_full(struct nfs_write_data *data, int status) +{ + struct nfs_page *req; + struct page *page; + + /* Update attributes as result of writeback. */ while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); @@ -915,20 +1100,20 @@ nfs_writeback_done(struct rpc_task *task req->wb_bytes, (long long)req_offset(req)); - if (task->tk_status < 0) { + if (status < 0) { ClearPageUptodate(page); SetPageError(page); if (req->wb_file) - req->wb_file->f_error = task->tk_status; + req->wb_file->f_error = status; end_page_writeback(page); nfs_inode_remove_request(req); - dprintk(", error = %d\n", task->tk_status); + dprintk(", error = %d\n", status); goto next; } end_page_writeback(page); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) - if (argp->stable != NFS_UNSTABLE || data->verf.committed == NFS_FILE_SYNC) { + if (data->args.stable != NFS_UNSTABLE || data->verf.committed == NFS_FILE_SYNC) { nfs_inode_remove_request(req); dprintk(" OK\n"); goto next; @@ -944,13 +1129,88 @@ nfs_writeback_done(struct rpc_task *task } } +/* + * This function is called when the WRITE call is complete. + */ +void nfs_writeback_done(struct rpc_task *task) +{ + struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + struct nfs_writeargs *argp = &data->args; + struct nfs_writeres *resp = &data->res; + + dprintk("NFS: %4d nfs_writeback_done (status %d)\n", + task->tk_pid, task->tk_status); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) + if (resp->verf->committed < argp->stable && task->tk_status >= 0) { + /* We tried a write call, but the server did not + * commit data to stable storage even though we + * requested it. + * Note: There is a known bug in Tru64 < 5.0 in which + * the server reports NFS_DATA_SYNC, but performs + * NFS_FILE_SYNC. We therefore implement this checking + * as a dprintk() in order to avoid filling syslog. + */ + static unsigned long complain; + + if (time_before(complain, jiffies)) { + dprintk("NFS: faulty NFS server %s:" + " (committed = %d) != (stable = %d)\n", + NFS_SERVER(data->inode)->hostname, + resp->verf->committed, argp->stable); + complain = jiffies + 300 * HZ; + } + } +#endif + /* Is this a short write? */ + if (task->tk_status >= 0 && resp->count < argp->count) { + static unsigned long complain; + + /* Has the server at least made some progress? */ + if (resp->count != 0) { + /* Was this an NFSv2 write or an NFSv3 stable write? */ + if (resp->verf->committed != NFS_UNSTABLE) { + /* Resend from where the server left off */ + argp->offset += resp->count; + argp->pgbase += resp->count; + argp->count -= resp->count; + } else { + /* Resend as a stable write in order to avoid + * headaches in the case of a server crash. + */ + argp->stable = NFS_FILE_SYNC; + } + rpc_restart_call(task); + return; + } + if (time_before(complain, jiffies)) { + printk(KERN_WARNING + "NFS: Server wrote less than requested.\n"); + complain = jiffies + 300 * HZ; + } + /* Can't do anything about it except throw an error. */ + task->tk_status = -EIO; + } + + /* + * Process the nfs_page list + */ + data->complete(data, task->tk_status); +} + + +#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +static void nfs_commit_release(struct rpc_task *task) +{ + struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata; + nfs_commit_free(wdata); +} + /* * Set up the argument/result storage required for the RPC call. */ -static void -nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data, int how) +static void nfs_commit_rpcsetup(struct list_head *head, + struct nfs_write_data *data, int how) { struct rpc_task *task = &data->task; struct nfs_page *first, *last; @@ -979,7 +1239,20 @@ nfs_commit_rpcsetup(struct list_head *he data->inode = inode; data->cred = first->wb_cred; - NFS_PROTO(inode)->commit_setup(data, start, len, how); + data->args.fh = NFS_FH(data->inode); + data->args.offset = start; + data->args.count = len; + data->res.count = len; + data->res.fattr = &data->fattr; + data->res.verf = &data->verf; + + NFS_PROTO(inode)->commit_setup(data, how); + + data->task.tk_priority = flush_task_priority(how); + data->task.tk_cookie = (unsigned long)inode; + data->task.tk_calldata = data; + /* Release requests */ + data->task.tk_release = nfs_commit_release; dprintk("NFS: %4d initiated commit call\n", task->tk_pid); } @@ -990,10 +1263,8 @@ nfs_commit_rpcsetup(struct list_head *he int nfs_commit_list(struct list_head *head, int how) { - struct rpc_clnt *clnt; struct nfs_write_data *data; struct nfs_page *req; - sigset_t oldset; data = nfs_commit_alloc(); @@ -1002,13 +1273,8 @@ nfs_commit_list(struct list_head *head, /* Set up the argument struct */ nfs_commit_rpcsetup(head, data, how); - clnt = NFS_CLIENT(data->inode); - rpc_clnt_sigmask(clnt, &oldset); - lock_kernel(); - rpc_execute(&data->task); - unlock_kernel(); - rpc_clnt_sigunmask(clnt, &oldset); + nfs_execute_write(data); return 0; out_bad: while (!list_empty(head)) { diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/include/linux/nfs4.h linux-2.6.5-31-server_caps/include/linux/nfs4.h --- linux-2.6.5-rc2/include/linux/nfs4.h 2004-03-25 00:29:50.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/nfs4.h 2004-03-25 14:31:59.000000000 -0500 @@ -47,6 +47,11 @@ #define NFS4_ACE_SYSTEM_AUDIT_ACE_TYPE 2 #define NFS4_ACE_SYSTEM_ALARM_ACE_TYPE 3 +#define ACL4_SUPPORT_ALLOW_ACL 0x01 +#define ACL4_SUPPORT_DENY_ACL 0x02 +#define ACL4_SUPPORT_AUDIT_ACL 0x04 +#define ACL4_SUPPORT_ALARM_ACL 0x08 + typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier; typedef struct { char data[16]; } nfs4_stateid; @@ -217,63 +222,63 @@ enum lock_type4 { /* Mandatory Attributes */ -#define FATTR4_WORD0_SUPPORTED_ATTRS (1) -#define FATTR4_WORD0_TYPE (1 << 1) -#define FATTR4_WORD0_FH_EXPIRE_TYPE (1 << 2) -#define FATTR4_WORD0_CHANGE (1 << 3) -#define FATTR4_WORD0_SIZE (1 << 4) -#define FATTR4_WORD0_LINK_SUPPORT (1 << 5) -#define FATTR4_WORD0_SYMLINK_SUPPORT (1 << 6) -#define FATTR4_WORD0_NAMED_ATTR (1 << 7) -#define FATTR4_WORD0_FSID (1 << 8) -#define FATTR4_WORD0_UNIQUE_HANDLES (1 << 9) -#define FATTR4_WORD0_LEASE_TIME (1 << 10) -#define FATTR4_WORD0_RDATTR_ERROR (1 << 11) +#define FATTR4_WORD0_SUPPORTED_ATTRS (1UL << 0) +#define FATTR4_WORD0_TYPE (1UL << 1) +#define FATTR4_WORD0_FH_EXPIRE_TYPE (1UL << 2) +#define FATTR4_WORD0_CHANGE (1UL << 3) +#define FATTR4_WORD0_SIZE (1UL << 4) +#define FATTR4_WORD0_LINK_SUPPORT (1UL << 5) +#define FATTR4_WORD0_SYMLINK_SUPPORT (1UL << 6) +#define FATTR4_WORD0_NAMED_ATTR (1UL << 7) +#define FATTR4_WORD0_FSID (1UL << 8) +#define FATTR4_WORD0_UNIQUE_HANDLES (1UL << 9) +#define FATTR4_WORD0_LEASE_TIME (1UL << 10) +#define FATTR4_WORD0_RDATTR_ERROR (1UL << 11) /* Recommended Attributes */ -#define FATTR4_WORD0_ACL (1 << 12) -#define FATTR4_WORD0_ACLSUPPORT (1 << 13) -#define FATTR4_WORD0_ARCHIVE (1 << 14) -#define FATTR4_WORD0_CANSETTIME (1 << 15) -#define FATTR4_WORD0_CASE_INSENSITIVE (1 << 16) -#define FATTR4_WORD0_CASE_PRESERVING (1 << 17) -#define FATTR4_WORD0_CHOWN_RESTRICTED (1 << 18) -#define FATTR4_WORD0_FILEHANDLE (1 << 19) -#define FATTR4_WORD0_FILEID (1 << 20) -#define FATTR4_WORD0_FILES_AVAIL (1 << 21) -#define FATTR4_WORD0_FILES_FREE (1 << 22) -#define FATTR4_WORD0_FILES_TOTAL (1 << 23) -#define FATTR4_WORD0_FS_LOCATIONS (1 << 24) -#define FATTR4_WORD0_HIDDEN (1 << 25) -#define FATTR4_WORD0_HOMOGENEOUS (1 << 26) -#define FATTR4_WORD0_MAXFILESIZE (1 << 27) -#define FATTR4_WORD0_MAXLINK (1 << 28) -#define FATTR4_WORD0_MAXNAME (1 << 29) -#define FATTR4_WORD0_MAXREAD (1 << 30) -#define FATTR4_WORD0_MAXWRITE (1 << 31) -#define FATTR4_WORD1_MIMETYPE (1) -#define FATTR4_WORD1_MODE (1 << 1) -#define FATTR4_WORD1_NO_TRUNC (1 << 2) -#define FATTR4_WORD1_NUMLINKS (1 << 3) -#define FATTR4_WORD1_OWNER (1 << 4) -#define FATTR4_WORD1_OWNER_GROUP (1 << 5) -#define FATTR4_WORD1_QUOTA_HARD (1 << 6) -#define FATTR4_WORD1_QUOTA_SOFT (1 << 7) -#define FATTR4_WORD1_QUOTA_USED (1 << 8) -#define FATTR4_WORD1_RAWDEV (1 << 9) -#define FATTR4_WORD1_SPACE_AVAIL (1 << 10) -#define FATTR4_WORD1_SPACE_FREE (1 << 11) -#define FATTR4_WORD1_SPACE_TOTAL (1 << 12) -#define FATTR4_WORD1_SPACE_USED (1 << 13) -#define FATTR4_WORD1_SYSTEM (1 << 14) -#define FATTR4_WORD1_TIME_ACCESS (1 << 15) -#define FATTR4_WORD1_TIME_ACCESS_SET (1 << 16) -#define FATTR4_WORD1_TIME_BACKUP (1 << 17) -#define FATTR4_WORD1_TIME_CREATE (1 << 18) -#define FATTR4_WORD1_TIME_DELTA (1 << 19) -#define FATTR4_WORD1_TIME_METADATA (1 << 20) -#define FATTR4_WORD1_TIME_MODIFY (1 << 21) -#define FATTR4_WORD1_TIME_MODIFY_SET (1 << 22) +#define FATTR4_WORD0_ACL (1UL << 12) +#define FATTR4_WORD0_ACLSUPPORT (1UL << 13) +#define FATTR4_WORD0_ARCHIVE (1UL << 14) +#define FATTR4_WORD0_CANSETTIME (1UL << 15) +#define FATTR4_WORD0_CASE_INSENSITIVE (1UL << 16) +#define FATTR4_WORD0_CASE_PRESERVING (1UL << 17) +#define FATTR4_WORD0_CHOWN_RESTRICTED (1UL << 18) +#define FATTR4_WORD0_FILEHANDLE (1UL << 19) +#define FATTR4_WORD0_FILEID (1UL << 20) +#define FATTR4_WORD0_FILES_AVAIL (1UL << 21) +#define FATTR4_WORD0_FILES_FREE (1UL << 22) +#define FATTR4_WORD0_FILES_TOTAL (1UL << 23) +#define FATTR4_WORD0_FS_LOCATIONS (1UL << 24) +#define FATTR4_WORD0_HIDDEN (1UL << 25) +#define FATTR4_WORD0_HOMOGENEOUS (1UL << 26) +#define FATTR4_WORD0_MAXFILESIZE (1UL << 27) +#define FATTR4_WORD0_MAXLINK (1UL << 28) +#define FATTR4_WORD0_MAXNAME (1UL << 29) +#define FATTR4_WORD0_MAXREAD (1UL << 30) +#define FATTR4_WORD0_MAXWRITE (1UL << 31) +#define FATTR4_WORD1_MIMETYPE (1UL << 0) +#define FATTR4_WORD1_MODE (1UL << 1) +#define FATTR4_WORD1_NO_TRUNC (1UL << 2) +#define FATTR4_WORD1_NUMLINKS (1UL << 3) +#define FATTR4_WORD1_OWNER (1UL << 4) +#define FATTR4_WORD1_OWNER_GROUP (1UL << 5) +#define FATTR4_WORD1_QUOTA_HARD (1UL << 6) +#define FATTR4_WORD1_QUOTA_SOFT (1UL << 7) +#define FATTR4_WORD1_QUOTA_USED (1UL << 8) +#define FATTR4_WORD1_RAWDEV (1UL << 9) +#define FATTR4_WORD1_SPACE_AVAIL (1UL << 10) +#define FATTR4_WORD1_SPACE_FREE (1UL << 11) +#define FATTR4_WORD1_SPACE_TOTAL (1UL << 12) +#define FATTR4_WORD1_SPACE_USED (1UL << 13) +#define FATTR4_WORD1_SYSTEM (1UL << 14) +#define FATTR4_WORD1_TIME_ACCESS (1UL << 15) +#define FATTR4_WORD1_TIME_ACCESS_SET (1UL << 16) +#define FATTR4_WORD1_TIME_BACKUP (1UL << 17) +#define FATTR4_WORD1_TIME_CREATE (1UL << 18) +#define FATTR4_WORD1_TIME_DELTA (1UL << 19) +#define FATTR4_WORD1_TIME_METADATA (1UL << 20) +#define FATTR4_WORD1_TIME_MODIFY (1UL << 21) +#define FATTR4_WORD1_TIME_MODIFY_SET (1UL << 22) #define NFSPROC4_NULL 0 #define NFSPROC4_COMPOUND 1 @@ -286,7 +291,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, @@ -303,6 +307,19 @@ enum { NFSPROC4_CLNT_LOCK, NFSPROC4_CLNT_LOCKT, NFSPROC4_CLNT_LOCKU, + NFSPROC4_CLNT_ACCESS, + NFSPROC4_CLNT_GETATTR, + NFSPROC4_CLNT_LOOKUP, + NFSPROC4_CLNT_LOOKUP_ROOT, + NFSPROC4_CLNT_REMOVE, + NFSPROC4_CLNT_RENAME, + NFSPROC4_CLNT_LINK, + NFSPROC4_CLNT_CREATE, + NFSPROC4_CLNT_PATHCONF, + NFSPROC4_CLNT_STATFS, + NFSPROC4_CLNT_READLINK, + NFSPROC4_CLNT_READDIR, + NFSPROC4_CLNT_SERVER_CAPS, }; #endif diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/include/linux/nfs_fs.h linux-2.6.5-31-server_caps/include/linux/nfs_fs.h --- linux-2.6.5-rc2/include/linux/nfs_fs.h 2004-03-25 00:29:42.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/nfs_fs.h 2004-03-25 00:35:45.000000000 -0500 @@ -69,6 +69,8 @@ #define FLUSH_SYNC 1 /* file being synced, or contention */ #define FLUSH_WAIT 2 /* wait for completion */ #define FLUSH_STABLE 4 /* commit to stable storage */ +#define FLUSH_LOWPRI 8 /* low priority background flush */ +#define FLUSH_HIGHPRI 16 /* high priority memory reclaim flush */ #ifdef __KERNEL__ @@ -275,6 +277,7 @@ extern void nfs_begin_attr_update(struct extern void nfs_end_attr_update(struct inode *); extern void nfs_begin_data_update(struct inode *); extern void nfs_end_data_update(struct inode *); +extern void nfs_end_data_update_defer(struct inode *); /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ extern u32 root_nfs_parse_addr(char *name); /*__init*/ @@ -335,10 +338,8 @@ extern int nfs_writepages(struct addres extern int nfs_flush_incompatible(struct file *file, struct page *page); extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int); extern void nfs_writeback_done(struct rpc_task *task); -extern void nfs_writedata_release(struct rpc_task *task); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -extern void nfs_commit_release(struct rpc_task *task); extern void nfs_commit_done(struct rpc_task *); #endif @@ -376,14 +377,18 @@ nfs_wb_all(struct inode *inode) /* * Write back all requests on one page - we do this before reading it. */ -static inline int -nfs_wb_page(struct inode *inode, struct page* page) +static inline int nfs_wb_page_priority(struct inode *inode, struct page* page, int how) { int error = nfs_sync_inode(inode, page->index, 1, - FLUSH_WAIT | FLUSH_STABLE); + how | FLUSH_WAIT | FLUSH_STABLE); return (error < 0) ? error : 0; } +static inline int nfs_wb_page(struct inode *inode, struct page* page) +{ + return nfs_wb_page_priority(inode, page, 0); +} + /* Hack for future NFS swap support */ #ifndef IS_SWAPFILE # define IS_SWAPFILE(inode) (0) @@ -397,7 +402,6 @@ extern int nfs_readpages(struct file *, struct list_head *, unsigned); extern int nfs_pagein_list(struct list_head *, int); extern void nfs_readpage_result(struct rpc_task *); -extern void nfs_readdata_release(struct rpc_task *); /* * linux/fs/mount_clnt.c diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/include/linux/nfs_fs_sb.h linux-2.6.5-31-server_caps/include/linux/nfs_fs_sb.h --- linux-2.6.5-rc2/include/linux/nfs_fs_sb.h 2004-03-25 00:30:15.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/nfs_fs_sb.h 2004-03-25 14:22:13.000000000 -0500 @@ -38,10 +38,19 @@ struct nfs_server { struct list_head nfs4_siblings; /* List of other nfs_server structs * that share the same clientid */ + u32 attr_bitmask[2];/* V4 bitmask representing the set + of attributes supported on this + filesystem */ + u32 acl_bitmask; /* V4 bitmask representing the ACEs + that are supported on this + filesystem */ #endif }; /* Server capabilities */ -#define NFS_CAP_READDIRPLUS (1) +#define NFS_CAP_READDIRPLUS (1U << 0) +#define NFS_CAP_HARDLINKS (1U << 1) +#define NFS_CAP_SYMLINKS (1U << 2) +#define NFS_CAP_ACLS (1U << 3) #endif diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/include/linux/nfs_page.h linux-2.6.5-31-server_caps/include/linux/nfs_page.h --- linux-2.6.5-rc2/include/linux/nfs_page.h 2004-03-25 00:29:34.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/nfs_page.h 2004-03-25 00:35:21.000000000 -0500 @@ -17,10 +17,14 @@ #include #include +#include + /* * Valid flags for a dirty buffer */ #define PG_BUSY 0 +#define PG_NEED_COMMIT 1 +#define PG_NEED_RESCHED 2 struct nfs_page { struct list_head wb_list, /* Defines state of page: */ @@ -31,6 +35,7 @@ struct nfs_page { struct rpc_cred *wb_cred; struct nfs4_state *wb_state; struct page *wb_page; /* page to read in/write out */ + atomic_t wb_complete; /* i/os we're waiting for */ wait_queue_head_t wb_wait; /* wait queue */ unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */ unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */ @@ -42,6 +47,8 @@ struct nfs_page { }; #define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) +#define NFS_NEED_COMMIT(req) (test_bit(PG_NEED_COMMIT,&(req)->wb_flags)) +#define NFS_NEED_RESCHED(req) (test_bit(PG_NEED_RESCHED,&(req)->wb_flags)) extern struct nfs_page *nfs_create_request(struct file *, struct inode *, struct page *, @@ -93,8 +100,7 @@ nfs_unlock_request(struct nfs_page *req) smp_mb__before_clear_bit(); clear_bit(PG_BUSY, &req->wb_flags); smp_mb__after_clear_bit(); - if (waitqueue_active(&req->wb_wait)) - wake_up_all(&req->wb_wait); + wake_up_all(&req->wb_wait); nfs_release_request(req); } @@ -115,6 +121,38 @@ nfs_list_remove_request(struct nfs_page req->wb_list_head = NULL; } +static inline int +nfs_defer_commit(struct nfs_page *req) +{ + if (test_and_set_bit(PG_NEED_COMMIT, &req->wb_flags)) + return 0; + return 1; +} + +static inline void +nfs_clear_commit(struct nfs_page *req) +{ + smp_mb__before_clear_bit(); + clear_bit(PG_NEED_COMMIT, &req->wb_flags); + smp_mb__after_clear_bit(); +} + +static inline int +nfs_defer_reschedule(struct nfs_page *req) +{ + if (test_and_set_bit(PG_NEED_RESCHED, &req->wb_flags)) + return 0; + return 1; +} + +static inline void +nfs_clear_reschedule(struct nfs_page *req) +{ + smp_mb__before_clear_bit(); + clear_bit(PG_NEED_RESCHED, &req->wb_flags); + smp_mb__after_clear_bit(); +} + static inline struct nfs_page * nfs_list_entry(struct list_head *head) { diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/include/linux/nfs_xdr.h linux-2.6.5-31-server_caps/include/linux/nfs_xdr.h --- linux-2.6.5-rc2/include/linux/nfs_xdr.h 2004-03-25 00:30:04.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/nfs_xdr.h 2004-03-25 14:20:48.000000000 -0500 @@ -3,6 +3,11 @@ #include +struct nfs4_fsid { + __u64 major; + __u64 minor; +}; + struct nfs_fattr { unsigned short valid; /* which fields are valid */ __u64 pre_size; /* pre_op_attr.size */ @@ -26,10 +31,7 @@ struct nfs_fattr { dev_t rdev; union { __u64 nfs3; /* also nfs2 */ - struct { - __u64 major; - __u64 minor; - } nfs4; + struct nfs4_fsid nfs4; } fsid_u; __u64 fileid; struct timespec atime; @@ -87,6 +89,12 @@ struct nfs_pathconf { __u32 max_namelen; /* max name length */ }; +struct nfs4_change_info { + u32 atomic; + u64 before; + u64 after; +}; + /* * Arguments to the open call. */ @@ -102,20 +110,18 @@ struct nfs_openargs { struct iattr * attrs; /* UNCHECKED, GUARDED */ nfs4_verifier verifier; /* EXCLUSIVE */ } u; - struct qstr * name; - struct nfs4_getattr * f_getattr; - struct nfs4_getattr * d_getattr; - struct nfs_server * server; /* Needed for ID mapping */ + const struct qstr * name; + const struct nfs_server *server; /* Needed for ID mapping */ + const u32 * bitmask; }; struct nfs_openres { nfs4_stateid stateid; struct nfs_fh fh; - struct nfs4_change_info * cinfo; + struct nfs4_change_info cinfo; __u32 rflags; - struct nfs4_getattr * f_getattr; - struct nfs4_getattr * d_getattr; - struct nfs_server * server; + struct nfs_fattr * f_attr; + const struct nfs_server *server; }; /* @@ -141,7 +147,7 @@ struct nfs_open_reclaimargs { __u32 id; __u32 share_access; __u32 claim; - struct nfs4_getattr * f_getattr; + const __u32 * bitmask; }; /* @@ -215,7 +221,7 @@ struct nfs_lockres { nfs4_stateid stateid;/* LOCK success, LOCKU */ struct nfs_lock_denied denied; /* LOCK failed, LOCKT success */ } u; - struct nfs_server * server; + const struct nfs_server * server; }; /* @@ -229,7 +235,8 @@ struct nfs_lockres { struct nfs_readargs { struct nfs_fh * fh; - nfs4_stateid stateid; + fl_owner_t lockowner; + struct nfs4_state * state; __u64 offset; __u32 count; unsigned int pgbase; @@ -252,7 +259,8 @@ struct nfs_readres { struct nfs_writeargs { struct nfs_fh * fh; - nfs4_stateid stateid; + fl_owner_t lockowner; + struct nfs4_state * state; __u64 offset; __u32 count; enum nfs3_stable_how stable; @@ -319,13 +327,13 @@ struct nfs_setattrargs { struct nfs_fh * fh; nfs4_stateid stateid; struct iattr * iap; - struct nfs4_getattr * attr; - struct nfs_server * server; /* Needed for name mapping */ + const struct nfs_server * server; /* Needed for name mapping */ + const u32 * bitmask; }; struct nfs_setattrres { - struct nfs4_getattr * attr; - struct nfs_server * server; + struct nfs_fattr * fattr; + const struct nfs_server * server; }; struct nfs_linkargs { @@ -476,124 +484,116 @@ struct nfs3_readdirres { typedef u64 clientid4; -struct nfs4_change_info { - u32 atomic; - u64 before; - u64 after; +struct nfs4_accessargs { + const struct nfs_fh * fh; + u32 access; }; -struct nfs4_access { - u32 ac_req_access; /* request */ - u32 * ac_resp_supported; /* response */ - u32 * ac_resp_access; /* response */ +struct nfs4_accessres { + u32 supported; + u32 access; }; -struct nfs4_close { - char * cl_stateid; /* request */ - u32 cl_seqid; /* request */ -}; - -struct nfs4_create { - u32 cr_ftype; /* request */ - union { /* request */ - struct { - u32 textlen; - const char * text; - } symlink; /* NF4LNK */ +struct nfs4_create_arg { + u32 ftype; + union { + struct qstr * symlink; /* NF4LNK */ struct { u32 specdata1; u32 specdata2; } device; /* NF4BLK, NF4CHR */ } u; - u32 cr_namelen; /* request */ - const char * cr_name; /* request */ - struct iattr * cr_attrs; /* request */ - struct nfs4_change_info * cr_cinfo; /* response */ + const struct qstr * name; + const struct nfs_server * server; + const struct iattr * attrs; + const struct nfs_fh * dir_fh; + const u32 * bitmask; }; -#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 */ - struct nfs_fsstat * gt_fsstat; /* response */ - struct nfs_pathconf * gt_pathconf; /* response */ +struct nfs4_create_res { + const struct nfs_server * server; + struct nfs_fh * fh; + struct nfs_fattr * fattr; + struct nfs4_change_info dir_cinfo; }; -struct nfs4_getfh { - struct nfs_fh * gf_fhandle; /* response */ +struct nfs4_fsinfo_arg { + const struct nfs_fh * fh; + const u32 * bitmask; }; -struct nfs4_link { - u32 ln_namelen; /* request */ - const char * ln_name; /* request */ - struct nfs4_change_info * ln_cinfo; /* response */ +struct nfs4_getattr_arg { + const struct nfs_fh * fh; + const u32 * bitmask; }; -struct nfs4_lookup { - struct qstr * lo_name; /* request */ +struct nfs4_getattr_res { + const struct nfs_server * server; + struct nfs_fattr * fattr; }; -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_putfh { - struct nfs_fh * pf_fhandle; /* request */ -}; - -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_link_arg { + const struct nfs_fh * fh; + const struct nfs_fh * dir_fh; + const struct qstr * name; +}; + +struct nfs4_lookup_arg { + const struct nfs_fh * dir_fh; + const struct qstr * name; + const u32 * bitmask; +}; + +struct nfs4_lookup_res { + const struct nfs_server * server; + struct nfs_fattr * fattr; + struct nfs_fh * fh; +}; + +struct nfs4_lookup_root_arg { + const u32 * bitmask; +}; + +struct nfs4_pathconf_arg { + const struct nfs_fh * fh; + const u32 * bitmask; +}; + +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_res { + nfs4_verifier verifier; + unsigned int pgbase; }; struct nfs4_readlink { - u32 rl_count; /* zero-copy data */ - struct page ** rl_pages; /* zero-copy data */ + const struct nfs_fh * fh; + u32 count; /* zero-copy data */ + struct page ** pages; /* zero-copy data */ }; -struct nfs4_remove { - u32 rm_namelen; /* request */ - const char * rm_name; /* request */ - struct nfs4_change_info * rm_cinfo; /* response */ +struct nfs4_remove_arg { + const struct nfs_fh * fh; + const struct qstr * name; }; -struct nfs4_rename { - u32 rn_oldnamelen; /* request */ - const char * rn_oldname; /* request */ - u32 rn_newnamelen; /* request */ - const char * rn_newname; /* request */ - struct nfs4_change_info * rn_src_cinfo; /* response */ - struct nfs4_change_info * rn_dst_cinfo; /* response */ +struct nfs4_rename_arg { + const struct nfs_fh * old_dir; + const struct nfs_fh * new_dir; + const struct qstr * old_name; + const struct qstr * new_name; }; -struct nfs4_setattr { - char * st_stateid; /* request */ - struct iattr * st_iap; /* request */ +struct nfs4_rename_res { + struct nfs4_change_info old_cinfo; + struct nfs4_change_info new_cinfo; }; struct nfs4_setclientid { @@ -606,70 +606,37 @@ struct nfs4_setclientid { struct nfs4_client * sc_state; /* response */ }; -struct nfs4_op { - u32 opnum; - union { - struct nfs4_access access; - struct nfs4_close close; - struct nfs4_create create; - struct nfs4_getattr getattr; - struct nfs4_getfh getfh; - struct nfs4_link link; - struct nfs4_lookup lookup; - struct nfs4_open open; - struct nfs4_open_confirm open_confirm; - struct nfs4_putfh putfh; - struct nfs4_readdir readdir; - struct nfs4_readlink readlink; - struct nfs4_remove remove; - struct nfs4_rename rename; - struct nfs4_client * renew; - struct nfs4_setattr setattr; - } u; +struct nfs4_statfs_arg { + const struct nfs_fh * fh; + const u32 * bitmask; }; -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; +struct nfs4_server_caps_res { + u32 attr_bitmask[2]; + u32 acl_bitmask; + u32 has_links; + u32 has_symlinks; }; #endif /* CONFIG_NFS_V4 */ +struct nfs_page; + struct nfs_read_data { int flags; struct rpc_task task; struct inode *inode; struct rpc_cred *cred; - fl_owner_t lockowner; struct nfs_fattr fattr; /* fattr storage */ struct list_head pages; /* Coalesced read requests */ + struct nfs_page *req; /* multi ops per nfs_page */ struct page *pagevec[NFS_READ_MAXIOV]; struct nfs_readargs args; struct nfs_readres res; #ifdef CONFIG_NFS_V4 unsigned long timestamp; /* For lease renewal */ #endif + void (*complete) (struct nfs_read_data *, int); }; struct nfs_write_data { @@ -677,20 +644,19 @@ struct nfs_write_data { struct rpc_task task; struct inode *inode; struct rpc_cred *cred; - fl_owner_t lockowner; struct nfs_fattr fattr; struct nfs_writeverf verf; struct list_head pages; /* Coalesced requests we wish to flush */ + struct nfs_page *req; /* multi ops per nfs_page */ struct page *pagevec[NFS_WRITE_MAXIOV]; struct nfs_writeargs args; /* argument struct */ struct nfs_writeres res; /* result struct */ #ifdef CONFIG_NFS_V4 unsigned long timestamp; /* For lease renewal */ #endif + void (*complete) (struct nfs_write_data *, int); }; -struct nfs_page; - /* * RPC procedure vector for NFSv2/NFSv3 demuxing */ @@ -737,9 +703,9 @@ struct nfs_rpc_ops { int (*pathconf) (struct nfs_server *, struct nfs_fh *, struct nfs_pathconf *); u32 * (*decode_dirent)(u32 *, struct nfs_entry *, int plus); - void (*read_setup) (struct nfs_read_data *, unsigned int count); - void (*write_setup) (struct nfs_write_data *, unsigned int count, int how); - void (*commit_setup) (struct nfs_write_data *, u64 start, u32 len, int how); + void (*read_setup) (struct nfs_read_data *); + void (*write_setup) (struct nfs_write_data *, int how); + void (*commit_setup) (struct nfs_write_data *, int how); int (*file_open) (struct inode *, struct file *); int (*file_release) (struct inode *, struct file *); void (*request_init)(struct nfs_page *, struct file *); diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/include/linux/sunrpc/sched.h linux-2.6.5-31-server_caps/include/linux/sunrpc/sched.h --- linux-2.6.5-rc2/include/linux/sunrpc/sched.h 2004-03-25 00:30:01.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/sunrpc/sched.h 2004-03-25 00:35:26.000000000 -0500 @@ -49,6 +49,8 @@ struct rpc_task { tk_cred_retry, tk_suid_retry; + unsigned long tk_cookie; /* Cookie for batching tasks */ + /* * timeout_fn to be executed by timer bottom half * callback to be executed after waking up @@ -72,7 +74,9 @@ struct rpc_task { unsigned long tk_timeout; /* timeout for rpc_sleep() */ unsigned short tk_flags; /* misc flags */ unsigned char tk_active : 1;/* Task has been activated */ + unsigned char tk_priority : 2;/* Task priority */ unsigned long tk_runstate; /* Task run status */ + struct list_head tk_links; /* links to related tasks */ #ifdef RPC_DEBUG unsigned short tk_pid; /* debugging aid */ #endif @@ -138,28 +142,58 @@ typedef void (*rpc_action)(struct rpc_ } while(0) /* + * Task priorities. + * Note: if you change these, you must also change + * the task initialization definitions below. + */ +#define RPC_PRIORITY_LOW 0 +#define RPC_PRIORITY_NORMAL 1 +#define RPC_PRIORITY_HIGH 2 +#define RPC_NR_PRIORITY (RPC_PRIORITY_HIGH+1) + +/* * RPC synchronization objects */ struct rpc_wait_queue { - struct list_head tasks; + struct list_head tasks[RPC_NR_PRIORITY]; /* task queue for each priority level */ + unsigned long cookie; /* cookie of last task serviced */ + unsigned char maxpriority; /* maximum priority (0 if queue is not a priority queue) */ + unsigned char priority; /* current priority */ + unsigned char count; /* # task groups remaining serviced so far */ + unsigned char nr; /* # tasks remaining for cookie */ #ifdef RPC_DEBUG - char * name; + const char * name; #endif }; +/* + * This is the # requests to send consecutively + * from a single cookie. The aim is to improve + * performance of NFS operations such as read/write. + */ +#define RPC_BATCH_COUNT 16 + #ifndef RPC_DEBUG -# define RPC_WAITQ_INIT(var,qname) ((struct rpc_wait_queue) {LIST_HEAD_INIT(var)}) -# define RPC_WAITQ(var,qname) struct rpc_wait_queue var = RPC_WAITQ_INIT(var.tasks,qname) -# define INIT_RPC_WAITQ(ptr,qname) do { \ - INIT_LIST_HEAD(&(ptr)->tasks); \ - } while(0) +# define RPC_WAITQ_INIT(var,qname) { \ + .tasks = { \ + [0] = LIST_HEAD_INIT(var.tasks[0]), \ + [1] = LIST_HEAD_INIT(var.tasks[1]), \ + [2] = LIST_HEAD_INIT(var.tasks[2]), \ + }, \ + } #else -# define RPC_WAITQ_INIT(var,qname) ((struct rpc_wait_queue) {LIST_HEAD_INIT(var.tasks), qname}) -# define RPC_WAITQ(var,qname) struct rpc_wait_queue var = RPC_WAITQ_INIT(var,qname) -# define INIT_RPC_WAITQ(ptr,qname) do { \ - INIT_LIST_HEAD(&(ptr)->tasks); (ptr)->name = qname; \ - } while(0) +# define RPC_WAITQ_INIT(var,qname) { \ + .tasks = { \ + [0] = LIST_HEAD_INIT(var.tasks[0]), \ + [1] = LIST_HEAD_INIT(var.tasks[1]), \ + [2] = LIST_HEAD_INIT(var.tasks[2]), \ + }, \ + .name = qname, \ + } #endif +# define RPC_WAITQ(var,qname) struct rpc_wait_queue var = RPC_WAITQ_INIT(var,qname) + +#define RPC_IS_PRIORITY(q) ((q)->maxpriority > 0) /* * Function prototypes @@ -175,6 +209,8 @@ void rpc_run_child(struct rpc_task *par rpc_action action); int rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *); void rpc_remove_wait_queue(struct rpc_task *); +void rpc_init_priority_wait_queue(struct rpc_wait_queue *, const char *); +void rpc_init_wait_queue(struct rpc_wait_queue *, const char *); void rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *, rpc_action action, rpc_action timer); void rpc_add_timer(struct rpc_task *, rpc_action); @@ -194,16 +230,14 @@ void rpc_show_tasks(void); int rpc_init_mempool(void); void rpc_destroy_mempool(void); -static __inline__ void -rpc_exit(struct rpc_task *task, int status) +static inline void rpc_exit(struct rpc_task *task, int status) { task->tk_status = status; task->tk_action = NULL; } #ifdef RPC_DEBUG -static __inline__ char * -rpc_qname(struct rpc_wait_queue *q) +static inline const char * rpc_qname(struct rpc_wait_queue *q) { return ((q && q->name) ? q->name : "unknown"); } diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/include/linux/sunrpc/xdr.h linux-2.6.5-31-server_caps/include/linux/sunrpc/xdr.h --- linux-2.6.5-rc2/include/linux/sunrpc/xdr.h 2004-03-25 00:30:15.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/sunrpc/xdr.h 2004-03-25 00:37:38.000000000 -0500 @@ -55,7 +55,8 @@ struct xdr_buf { unsigned int page_base, /* Start of page data */ page_len; /* Length of page data */ - unsigned int len; /* Total length of data */ + unsigned int buflen, /* Total length of storage buffer */ + len; /* Length of XDR encoded message */ }; @@ -87,7 +88,8 @@ struct xdr_buf { /* * Miscellaneous XDR helper functions */ -u32 * xdr_encode_array(u32 *p, const void *s, unsigned int len); +u32 * xdr_encode_opaque_fixed(u32 *p, const void *ptr, unsigned int len); +u32 * xdr_encode_opaque(u32 *p, const void *ptr, unsigned int len); u32 * xdr_encode_string(u32 *p, const char *s); u32 * xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen); u32 * xdr_decode_string_inplace(u32 *p, char **sp, int *lenp, int maxlen); @@ -100,6 +102,11 @@ void xdr_encode_pages(struct xdr_buf *, void xdr_inline_pages(struct xdr_buf *, unsigned int, struct page **, unsigned int, unsigned int); +static inline u32 *xdr_encode_array(u32 *p, const void *s, unsigned int len) +{ + return xdr_encode_opaque(p, s, len); +} + /* * Decode 64bit quantities (NFSv3 support) */ @@ -177,86 +184,14 @@ struct xdr_stream { struct iovec *iov; /* pointer to the current iovec */ }; -/* - * Initialize an xdr_stream for encoding data. - * - * Note: at the moment the RPC client only passes the length of our - * scratch buffer in the xdr_buf's header iovec. Previously this - * meant we needed to call xdr_adjust_iovec() after encoding the - * data. With the new scheme, the xdr_stream manages the details - * of the buffer length, and takes care of adjusting the iovec - * length for us. - */ -static inline void -xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p) -{ - struct iovec *iov = buf->head; - - xdr->buf = buf; - xdr->iov = iov; - xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); - buf->len = iov->iov_len = (char *)p - (char *)iov->iov_base; - xdr->p = p; -} - -/* - * Check that we have enough buffer space to encode 'nbytes' more - * bytes of data. If so, update the total xdr_buf length, and - * adjust the length of the current iovec. - */ -static inline uint32_t * -xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) -{ - uint32_t *p = xdr->p; - uint32_t *q; - - /* align nbytes on the next 32-bit boundary */ - nbytes += 3; - nbytes &= ~3; - q = p + (nbytes >> 2); - if (unlikely(q > xdr->end || q < p)) - return NULL; - xdr->p = q; - xdr->iov->iov_len += nbytes; - xdr->buf->len += nbytes; - return p; -} - +extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p); +extern uint32_t *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, unsigned int len); +extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p); +extern uint32_t *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len); -/* - * Initialize an xdr_stream for decoding data. - */ -static inline void -xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p) -{ - struct iovec *iov = buf->head; - xdr->buf = buf; - xdr->iov = iov; - xdr->p = p; - xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); -} - -/* - * Check if the input buffer is long enough to enable us to decode - * 'nbytes' more bytes of data starting at the current position. - * If so return the current pointer, then update the current - * position. - */ -static inline uint32_t * -xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) -{ - uint32_t *p = xdr->p; - uint32_t *q = p + XDR_QUADLEN(nbytes); - - if (unlikely(q > xdr->end || q < p)) - return NULL; - xdr->p = q; - return p; -} - #endif /* __KERNEL__ */ #endif /* _SUNRPC_XDR_H_ */ diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/include/linux/sunrpc/xprt.h linux-2.6.5-31-server_caps/include/linux/sunrpc/xprt.h --- linux-2.6.5-rc2/include/linux/sunrpc/xprt.h 2004-03-25 00:30:02.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/sunrpc/xprt.h 2004-03-25 00:37:38.000000000 -0500 @@ -120,8 +120,6 @@ struct rpc_rqst { }; #define rq_svec rq_snd_buf.head #define rq_slen rq_snd_buf.len -#define rq_rvec rq_rcv_buf.head -#define rq_rlen rq_rcv_buf.len #define XPRT_LAST_FRAG (1 << 0) #define XPRT_COPY_RECM (1 << 1) diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/net/sunrpc/auth_gss/auth_gss.c linux-2.6.5-31-server_caps/net/sunrpc/auth_gss/auth_gss.c --- linux-2.6.5-rc2/net/sunrpc/auth_gss/auth_gss.c 2004-03-25 00:29:50.000000000 -0500 +++ linux-2.6.5-31-server_caps/net/sunrpc/auth_gss/auth_gss.c 2004-03-25 00:35:54.000000000 -0500 @@ -365,7 +365,7 @@ retry: gss_msg = gss_new; memset(gss_new, 0, sizeof(*gss_new)); INIT_LIST_HEAD(&gss_new->list); - INIT_RPC_WAITQ(&gss_new->waitq, "RPCSEC_GSS upcall waitq"); + rpc_init_wait_queue(&gss_new->waitq, "RPCSEC_GSS upcall waitq"); atomic_set(&gss_new->count, 2); msg = &gss_new->msg; msg->data = &gss_new->uid; @@ -721,8 +721,7 @@ gss_marshal(struct rpc_task *task, u32 * printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat); goto out_put_ctx; } - *p++ = htonl(mic.len); - p += XDR_QUADLEN(mic.len); + p = xdr_encode_opaque(p, NULL, mic.len); gss_put_ctx(ctx); return p; out_put_ctx: @@ -857,9 +856,7 @@ gss_wrap_req(struct rpc_task *task, status = -EIO; /* XXX? */ if (maj_stat) goto out; - q = p; - *q++ = htonl(mic.len); - q += XDR_QUADLEN(mic.len); + q = xdr_encode_opaque(p, NULL, mic.len); offset = (u8 *)q - (u8 *)p; iov->iov_len += offset; diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/net/sunrpc/auth_gss/gss_krb5_crypto.c linux-2.6.5-31-server_caps/net/sunrpc/auth_gss/gss_krb5_crypto.c --- linux-2.6.5-rc2/net/sunrpc/auth_gss/gss_krb5_crypto.c 2004-03-25 00:29:32.000000000 -0500 +++ linux-2.6.5-31-server_caps/net/sunrpc/auth_gss/gss_krb5_crypto.c 2004-03-25 00:35:40.000000000 -0500 @@ -40,6 +40,7 @@ #include #include #include +#include #include #ifdef RPC_DEBUG @@ -171,22 +172,24 @@ krb5_make_checksum(s32 cksumtype, char * } len = body->page_len; - offset = body->page_base; - i = 0; - while (len) { - sg->page = body->pages[i]; - sg->offset = offset; - offset = 0; - if (PAGE_SIZE > len) - thislen = len; - else - thislen = PAGE_SIZE; - sg->length = thislen; - kmap(sg->page); /* XXX kmap_atomic? */ - crypto_digest_update(tfm, sg, 1); - kunmap(sg->page); - len -= thislen; - i++; + if (len != 0) { + offset = body->page_base & (PAGE_CACHE_SIZE - 1); + i = body->page_base >> PAGE_CACHE_SHIFT; + thislen = PAGE_CACHE_SIZE - offset; + do { + if (thislen > len) + thislen = len; + sg->page = body->pages[i]; + sg->offset = offset; + sg->length = thislen; + kmap(sg->page); /* XXX kmap_atomic? */ + crypto_digest_update(tfm, sg, 1); + kunmap(sg->page); + len -= thislen; + i++; + offset = 0; + thislen = PAGE_CACHE_SIZE; + } while(len != 0); } if (body->tail[0].iov_len) { buf_to_sg(sg, body->tail[0].iov_base, body->tail[0].iov_len); diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/net/sunrpc/clnt.c linux-2.6.5-31-server_caps/net/sunrpc/clnt.c --- linux-2.6.5-rc2/net/sunrpc/clnt.c 2004-03-25 00:29:42.000000000 -0500 +++ linux-2.6.5-31-server_caps/net/sunrpc/clnt.c 2004-03-25 00:37:38.000000000 -0500 @@ -144,7 +144,7 @@ rpc_create_client(struct rpc_xprt *xprt, clnt->cl_vers = version->number; clnt->cl_prot = xprt->prot; clnt->cl_stats = program->stats; - INIT_RPC_WAITQ(&clnt->cl_pmap_default.pm_bindwait, "bindwait"); + rpc_init_wait_queue(&clnt->cl_pmap_default.pm_bindwait, "bindwait"); if (!clnt->cl_port) clnt->cl_autobind = 1; @@ -605,11 +605,13 @@ call_encode(struct rpc_task *task) sndbuf->tail[0].iov_len = 0; sndbuf->page_len = 0; sndbuf->len = 0; + sndbuf->buflen = bufsiz; rcvbuf->head[0].iov_base = (void *)((char *)task->tk_buffer + bufsiz); rcvbuf->head[0].iov_len = bufsiz; rcvbuf->tail[0].iov_len = 0; rcvbuf->page_len = 0; - rcvbuf->len = bufsiz; + rcvbuf->len = 0; + rcvbuf->buflen = bufsiz; /* Encode header and provided arguments */ encode = task->tk_msg.rpc_proc->p_encode; @@ -849,6 +851,8 @@ call_decode(struct rpc_task *task) return; } + req->rq_rcv_buf.len = req->rq_private_buf.len; + /* Check that the softirq receive buffer is valid */ WARN_ON(memcmp(&req->rq_rcv_buf, &req->rq_private_buf, sizeof(req->rq_rcv_buf)) != 0); @@ -884,7 +888,7 @@ call_decode(struct rpc_task *task) task->tk_status); return; out_retry: - req->rq_received = 0; + req->rq_received = req->rq_private_buf.len = 0; task->tk_status = 0; } @@ -956,7 +960,7 @@ call_header(struct rpc_task *task) static u32 * call_verify(struct rpc_task *task) { - u32 *p = task->tk_rqstp->rq_rvec[0].iov_base, n; + u32 *p = task->tk_rqstp->rq_rcv_buf.head[0].iov_base, n; p += 1; /* skip XID */ diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/net/sunrpc/sched.c linux-2.6.5-31-server_caps/net/sunrpc/sched.c --- linux-2.6.5-rc2/net/sunrpc/sched.c 2004-03-25 00:29:51.000000000 -0500 +++ linux-2.6.5-31-server_caps/net/sunrpc/sched.c 2004-03-25 00:35:31.000000000 -0500 @@ -162,6 +162,26 @@ rpc_delete_timer(struct rpc_task *task) } /* + * Add new request to a priority queue. + */ +static void __rpc_add_wait_queue_priority(struct rpc_wait_queue *queue, struct rpc_task *task) +{ + struct list_head *q; + struct rpc_task *t; + + q = &queue->tasks[task->tk_priority]; + if (unlikely(task->tk_priority > queue->maxpriority)) + q = &queue->tasks[queue->maxpriority]; + list_for_each_entry(t, q, tk_list) { + if (t->tk_cookie == task->tk_cookie) { + list_add_tail(&task->tk_list, &t->tk_links); + return; + } + } + list_add_tail(&task->tk_list, q); +} + +/* * Add new request to wait queue. * * Swapper tasks always get inserted at the head of the queue. @@ -169,8 +189,7 @@ rpc_delete_timer(struct rpc_task *task) * improve overall performance. * Everyone else gets appended to the queue to ensure proper FIFO behavior. */ -static inline int -__rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task) +static int __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task) { if (task->tk_rpcwait == queue) return 0; @@ -179,10 +198,12 @@ __rpc_add_wait_queue(struct rpc_wait_que printk(KERN_WARNING "RPC: doubly enqueued task!\n"); return -EWOULDBLOCK; } - if (RPC_IS_SWAPPER(task)) - list_add(&task->tk_list, &queue->tasks); + if (RPC_IS_PRIORITY(queue)) + __rpc_add_wait_queue_priority(queue, task); + else if (RPC_IS_SWAPPER(task)) + list_add(&task->tk_list, &queue->tasks[0]); else - list_add_tail(&task->tk_list, &queue->tasks); + list_add_tail(&task->tk_list, &queue->tasks[0]); task->tk_rpcwait = queue; dprintk("RPC: %4d added to queue %p \"%s\"\n", @@ -191,8 +212,7 @@ __rpc_add_wait_queue(struct rpc_wait_que return 0; } -int -rpc_add_wait_queue(struct rpc_wait_queue *q, struct rpc_task *task) +int rpc_add_wait_queue(struct rpc_wait_queue *q, struct rpc_task *task) { int result; @@ -203,18 +223,35 @@ rpc_add_wait_queue(struct rpc_wait_queue } /* + * Remove request from a priority queue. + */ +static void __rpc_remove_wait_queue_priority(struct rpc_task *task) +{ + struct rpc_task *t; + + if (!list_empty(&task->tk_links)) { + t = list_entry(task->tk_links.next, struct rpc_task, tk_list); + list_move(&t->tk_list, &task->tk_list); + list_splice_init(&task->tk_links, &t->tk_links); + } + list_del(&task->tk_list); +} + +/* * Remove request from queue. * Note: must be called with spin lock held. */ -static inline void -__rpc_remove_wait_queue(struct rpc_task *task) +static void __rpc_remove_wait_queue(struct rpc_task *task) { struct rpc_wait_queue *queue = task->tk_rpcwait; if (!queue) return; - list_del(&task->tk_list); + if (RPC_IS_PRIORITY(queue)) + __rpc_remove_wait_queue_priority(task); + else + list_del(&task->tk_list); task->tk_rpcwait = NULL; dprintk("RPC: %4d removed from queue %p \"%s\"\n", @@ -231,6 +268,48 @@ rpc_remove_wait_queue(struct rpc_task *t spin_unlock_bh(&rpc_queue_lock); } +static inline void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority) +{ + queue->priority = priority; + queue->count = 1 << (priority * 2); +} + +static inline void rpc_set_waitqueue_cookie(struct rpc_wait_queue *queue, unsigned long cookie) +{ + queue->cookie = cookie; + queue->nr = RPC_BATCH_COUNT; +} + +static inline void rpc_reset_waitqueue_priority(struct rpc_wait_queue *queue) +{ + rpc_set_waitqueue_priority(queue, queue->maxpriority); + rpc_set_waitqueue_cookie(queue, 0); +} + +static void __rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const char *qname, int maxprio) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(queue->tasks); i++) + INIT_LIST_HEAD(&queue->tasks[i]); + queue->maxpriority = maxprio; + rpc_reset_waitqueue_priority(queue); +#ifdef RPC_DEBUG + queue->name = qname; +#endif +} + +void rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const char *qname) +{ + __rpc_init_priority_wait_queue(queue, qname, RPC_PRIORITY_HIGH); +} + +void rpc_init_wait_queue(struct rpc_wait_queue *queue, const char *qname) +{ + __rpc_init_priority_wait_queue(queue, qname, 0); +} +EXPORT_SYMBOL(rpc_init_wait_queue); + /* * Make an RPC task runnable. * @@ -255,13 +334,11 @@ rpc_make_runnable(struct rpc_task *task) return; } rpc_clear_sleeping(task); - if (waitqueue_active(&rpciod_idle)) - wake_up(&rpciod_idle); + wake_up(&rpciod_idle); } } else { rpc_clear_sleeping(task); - if (waitqueue_active(&task->tk_wait)) - wake_up(&task->tk_wait); + wake_up(&task->tk_wait); } } @@ -287,8 +364,7 @@ void rpciod_wake_up(void) { if(rpciod_pid==0) printk(KERN_ERR "rpciod: wot no daemon?\n"); - if (waitqueue_active(&rpciod_idle)) - wake_up(&rpciod_idle); + wake_up(&rpciod_idle); } /* @@ -406,17 +482,72 @@ rpc_wake_up_task(struct rpc_task *task) } /* + * Wake up the next task on a priority queue. + */ +static struct rpc_task * __rpc_wake_up_next_priority(struct rpc_wait_queue *queue) +{ + struct list_head *q; + struct rpc_task *task; + + /* + * Service a batch of tasks from a single cookie. + */ + q = &queue->tasks[queue->priority]; + if (!list_empty(q)) { + task = list_entry(q->next, struct rpc_task, tk_list); + if (queue->cookie == task->tk_cookie) { + if (--queue->nr) + goto out; + list_move_tail(&task->tk_list, q); + } + /* + * Check if we need to switch queues. + */ + if (--queue->count) + goto new_cookie; + } + + /* + * Service the next queue. + */ + do { + if (q == &queue->tasks[0]) + q = &queue->tasks[queue->maxpriority]; + else + q = q - 1; + if (!list_empty(q)) { + task = list_entry(q->next, struct rpc_task, tk_list); + goto new_queue; + } + } while (q != &queue->tasks[queue->priority]); + + rpc_reset_waitqueue_priority(queue); + return NULL; + +new_queue: + rpc_set_waitqueue_priority(queue, (unsigned int)(q - &queue->tasks[0])); +new_cookie: + rpc_set_waitqueue_cookie(queue, task->tk_cookie); +out: + __rpc_wake_up_task(task); + return task; +} + +/* * Wake up the next task on the wait queue. */ -struct rpc_task * -rpc_wake_up_next(struct rpc_wait_queue *queue) +struct rpc_task * rpc_wake_up_next(struct rpc_wait_queue *queue) { struct rpc_task *task = NULL; dprintk("RPC: wake_up_next(%p \"%s\")\n", queue, rpc_qname(queue)); spin_lock_bh(&rpc_queue_lock); - task_for_first(task, &queue->tasks) - __rpc_wake_up_task(task); + if (RPC_IS_PRIORITY(queue)) + task = __rpc_wake_up_next_priority(queue); + else { + task_for_first(task, &queue->tasks[0]) + __rpc_wake_up_task(task); + } spin_unlock_bh(&rpc_queue_lock); return task; @@ -428,15 +559,22 @@ rpc_wake_up_next(struct rpc_wait_queue * * * Grabs rpc_queue_lock */ -void -rpc_wake_up(struct rpc_wait_queue *queue) +void rpc_wake_up(struct rpc_wait_queue *queue) { struct rpc_task *task; + struct list_head *head; spin_lock_bh(&rpc_queue_lock); - while (!list_empty(&queue->tasks)) - task_for_first(task, &queue->tasks) + head = &queue->tasks[queue->maxpriority]; + for (;;) { + while (!list_empty(head)) { + task = list_entry(head->next, struct rpc_task, tk_list); __rpc_wake_up_task(task); + } + if (head == &queue->tasks[0]) + break; + head--; + } spin_unlock_bh(&rpc_queue_lock); } @@ -447,17 +585,22 @@ rpc_wake_up(struct rpc_wait_queue *queue * * Grabs rpc_queue_lock */ -void -rpc_wake_up_status(struct rpc_wait_queue *queue, int status) +void rpc_wake_up_status(struct rpc_wait_queue *queue, int status) { + struct list_head *head; struct rpc_task *task; spin_lock_bh(&rpc_queue_lock); - while (!list_empty(&queue->tasks)) { - task_for_first(task, &queue->tasks) { + head = &queue->tasks[queue->maxpriority]; + for (;;) { + while (!list_empty(head)) { + task = list_entry(head->next, struct rpc_task, tk_list); task->tk_status = status; __rpc_wake_up_task(task); } + if (head == &queue->tasks[0]) + break; + head--; } spin_unlock_bh(&rpc_queue_lock); } @@ -643,21 +786,22 @@ __rpc_schedule(void) dprintk("RPC: rpc_schedule enter\n"); while (1) { - spin_lock_bh(&rpc_queue_lock); - task_for_first(task, &schedq.tasks) { + task_for_first(task, &schedq.tasks[0]) { __rpc_remove_wait_queue(task); spin_unlock_bh(&rpc_queue_lock); __rpc_execute(task); + spin_lock_bh(&rpc_queue_lock); } else { - spin_unlock_bh(&rpc_queue_lock); break; } if (++count >= 200 || need_resched()) { count = 0; + spin_unlock_bh(&rpc_queue_lock); schedule(); + spin_lock_bh(&rpc_queue_lock); } } dprintk("RPC: rpc_schedule leave\n"); @@ -709,9 +853,7 @@ rpc_free(struct rpc_task *task) /* * Creation and deletion of RPC task structures */ -inline void -rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, - rpc_action callback, int flags) +void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, rpc_action callback, int flags) { memset(task, 0, sizeof(*task)); init_timer(&task->tk_timer); @@ -729,6 +871,10 @@ rpc_init_task(struct rpc_task *task, str task->tk_cred_retry = 2; task->tk_suid_retry = 1; + task->tk_priority = RPC_PRIORITY_NORMAL; + task->tk_cookie = (unsigned long)current; + INIT_LIST_HEAD(&task->tk_links); + /* Add to global list of all tasks */ spin_lock(&rpc_sched_lock); list_add(&task->tk_task, &all_tasks); @@ -866,7 +1012,7 @@ rpc_find_parent(struct rpc_task *child) struct list_head *le; parent = (struct rpc_task *) child->tk_calldata; - task_for_each(task, le, &childq.tasks) + task_for_each(task, le, &childq.tasks[0]) if (task == parent) return parent; @@ -946,7 +1092,7 @@ static DECLARE_MUTEX_LOCKED(rpciod_runni static inline int rpciod_task_pending(void) { - return !list_empty(&schedq.tasks); + return !list_empty(&schedq.tasks[0]); } @@ -969,27 +1115,41 @@ rpciod(void *ptr) allow_signal(SIGKILL); dprintk("RPC: rpciod starting (pid %d)\n", rpciod_pid); + spin_lock_bh(&rpc_queue_lock); while (rpciod_users) { + DEFINE_WAIT(wait); if (signalled()) { + spin_unlock_bh(&rpc_queue_lock); rpciod_killall(); flush_signals(current); + spin_lock_bh(&rpc_queue_lock); } __rpc_schedule(); - if (current->flags & PF_FREEZE) + if (current->flags & PF_FREEZE) { + spin_unlock_bh(&rpc_queue_lock); refrigerator(PF_IOTHREAD); + spin_lock_bh(&rpc_queue_lock); + } if (++rounds >= 64) { /* safeguard */ + spin_unlock_bh(&rpc_queue_lock); schedule(); rounds = 0; + spin_lock_bh(&rpc_queue_lock); } - if (!rpciod_task_pending()) { - dprintk("RPC: rpciod back to sleep\n"); - wait_event_interruptible(rpciod_idle, rpciod_task_pending()); - dprintk("RPC: switch to rpciod\n"); + dprintk("RPC: rpciod back to sleep\n"); + prepare_to_wait(&rpciod_idle, &wait, TASK_INTERRUPTIBLE); + if (!rpciod_task_pending() && !signalled()) { + spin_unlock_bh(&rpc_queue_lock); + schedule(); rounds = 0; + spin_lock_bh(&rpc_queue_lock); } + finish_wait(&rpciod_idle, &wait); + dprintk("RPC: switch to rpciod\n"); } + spin_unlock_bh(&rpc_queue_lock); dprintk("RPC: rpciod shutdown commences\n"); if (!list_empty(&all_tasks)) { @@ -1013,7 +1173,9 @@ rpciod_killall(void) while (!list_empty(&all_tasks)) { clear_thread_flag(TIF_SIGPENDING); rpc_killall_tasks(NULL); + spin_lock_bh(&rpc_queue_lock); __rpc_schedule(); + spin_unlock_bh(&rpc_queue_lock); if (!list_empty(&all_tasks)) { dprintk("rpciod_killall: waiting for tasks to exit\n"); yield(); diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/net/sunrpc/sunrpc_syms.c linux-2.6.5-31-server_caps/net/sunrpc/sunrpc_syms.c --- linux-2.6.5-rc2/net/sunrpc/sunrpc_syms.c 2004-03-25 00:30:05.000000000 -0500 +++ linux-2.6.5-31-server_caps/net/sunrpc/sunrpc_syms.c 2004-03-25 00:37:38.000000000 -0500 @@ -120,7 +120,6 @@ EXPORT_SYMBOL(svcauth_unix_purge); EXPORT_SYMBOL(unix_domain_find); /* Generic XDR */ -EXPORT_SYMBOL(xdr_encode_array); EXPORT_SYMBOL(xdr_encode_string); EXPORT_SYMBOL(xdr_decode_string); EXPORT_SYMBOL(xdr_decode_string_inplace); @@ -129,8 +128,6 @@ EXPORT_SYMBOL(xdr_encode_netobj); EXPORT_SYMBOL(xdr_encode_pages); EXPORT_SYMBOL(xdr_inline_pages); EXPORT_SYMBOL(xdr_shift_buf); -EXPORT_SYMBOL(xdr_write_pages); -EXPORT_SYMBOL(xdr_read_pages); EXPORT_SYMBOL(xdr_buf_from_iov); EXPORT_SYMBOL(xdr_buf_subsegment); EXPORT_SYMBOL(xdr_buf_read_netobj); diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/net/sunrpc/xdr.c linux-2.6.5-31-server_caps/net/sunrpc/xdr.c --- linux-2.6.5-rc2/net/sunrpc/xdr.c 2004-03-25 00:30:01.000000000 -0500 +++ linux-2.6.5-31-server_caps/net/sunrpc/xdr.c 2004-03-25 00:37:38.000000000 -0500 @@ -53,16 +53,50 @@ xdr_decode_netobj(u32 *p, struct xdr_net return p + XDR_QUADLEN(len); } -u32 * -xdr_encode_array(u32 *p, const void *array, unsigned int len) +/** + * xdr_encode_opaque_fixed - Encode fixed length opaque data + * @p - pointer to current position in XDR buffer. + * @ptr - pointer to data to encode (or NULL) + * @nbytes - size of data. + * + * Copy the array of data of length nbytes at ptr to the XDR buffer + * at position p, then align to the next 32-bit boundary by padding + * with zero bytes (see RFC1832). + * Note: if ptr is NULL, only the padding is performed. + * + * Returns the updated current XDR buffer position + * + */ +u32 *xdr_encode_opaque_fixed(u32 *p, const void *ptr, unsigned int nbytes) { - int quadlen = XDR_QUADLEN(len); - - p[quadlen] = 0; - *p++ = htonl(len); - memcpy(p, array, len); - return p + quadlen; + if (likely(nbytes != 0)) { + unsigned int quadlen = XDR_QUADLEN(nbytes); + unsigned int padding = (quadlen << 2) - nbytes; + + if (ptr != NULL) + memcpy(p, ptr, nbytes); + if (padding != 0) + memset((char *)p + nbytes, 0, padding); + p += quadlen; + } + return p; +} +EXPORT_SYMBOL(xdr_encode_opaque_fixed); + +/** + * xdr_encode_opaque - Encode variable length opaque data + * @p - pointer to current position in XDR buffer. + * @ptr - pointer to data to encode (or NULL) + * @nbytes - size of data. + * + * Returns the updated current XDR buffer position + */ +u32 *xdr_encode_opaque(u32 *p, const void *ptr, unsigned int nbytes) +{ + *p++ = htonl(nbytes); + return xdr_encode_opaque_fixed(p, ptr, nbytes); } +EXPORT_SYMBOL(xdr_encode_opaque); u32 * xdr_encode_string(u32 *p, const char *string) @@ -126,6 +160,7 @@ xdr_encode_pages(struct xdr_buf *xdr, st tail->iov_len = pad; len += pad; } + xdr->buflen += len; xdr->len += len; } @@ -147,7 +182,7 @@ xdr_inline_pages(struct xdr_buf *xdr, un tail->iov_base = buf + offset; tail->iov_len = buflen - offset; - xdr->len += len; + xdr->buflen += len; } /* @@ -641,7 +676,10 @@ xdr_shrink_bufhead(struct xdr_buf *buf, copy); } head->iov_len -= len; - buf->len -= len; + buf->buflen -= len; + /* Have we truncated the message? */ + if (buf->len > buf->buflen) + buf->len = buf->buflen; } /* @@ -671,7 +709,7 @@ xdr_shrink_pagelen(struct xdr_buf *buf, copy = tail->iov_len - len; memmove(p, tail->iov_base, copy); } else - buf->len -= len; + buf->buflen -= len; /* Copy from the inlined pages into the tail */ copy = len; if (copy > tail->iov_len) @@ -681,7 +719,10 @@ xdr_shrink_pagelen(struct xdr_buf *buf, copy); } buf->page_len -= len; - buf->len -= len; + buf->buflen -= len; + /* Have we truncated the message? */ + if (buf->len > buf->buflen) + buf->len = buf->buflen; } void @@ -690,8 +731,67 @@ xdr_shift_buf(struct xdr_buf *buf, size_ xdr_shrink_bufhead(buf, len); } -void -xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, +/** + * xdr_init_encode - Initialize a struct xdr_stream for sending data. + * @xdr: pointer to xdr_stream struct + * @buf: pointer to XDR buffer in which to encode data + * @p: current pointer inside XDR buffer + * + * Note: at the moment the RPC client only passes the length of our + * scratch buffer in the xdr_buf's header iovec. Previously this + * meant we needed to call xdr_adjust_iovec() after encoding the + * data. With the new scheme, the xdr_stream manages the details + * of the buffer length, and takes care of adjusting the iovec + * length for us. + */ +void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p) +{ + struct iovec *iov = buf->head; + + xdr->buf = buf; + xdr->iov = iov; + xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); + buf->len = iov->iov_len = (char *)p - (char *)iov->iov_base; + xdr->p = p; +} +EXPORT_SYMBOL(xdr_init_encode); + +/** + * xdr_reserve_space - Reserve buffer space for sending + * @xdr: pointer to xdr_stream + * @nbytes: number of bytes to reserve + * + * Checks that we have enough buffer space to encode 'nbytes' more + * bytes of data. If so, update the total xdr_buf length, and + * adjust the length of the current iovec. + */ +uint32_t * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) +{ + uint32_t *p = xdr->p; + uint32_t *q; + + /* align nbytes on the next 32-bit boundary */ + nbytes += 3; + nbytes &= ~3; + q = p + (nbytes >> 2); + if (unlikely(q > xdr->end || q < p)) + return NULL; + xdr->p = q; + xdr->iov->iov_len += nbytes; + xdr->buf->len += nbytes; + return p; +} +EXPORT_SYMBOL(xdr_reserve_space); + +/** + * xdr_write_pages - Insert a list of pages into an XDR buffer for sending + * @xdr: pointer to xdr_stream + * @pages: list of pages + * @base: offset of first byte + * @len: length of data in bytes + * + */ +void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, unsigned int len) { struct xdr_buf *buf = xdr->buf; @@ -713,15 +813,69 @@ xdr_write_pages(struct xdr_stream *xdr, len += pad; *xdr->p++ = 0; } + buf->buflen += len; buf->len += len; } +EXPORT_SYMBOL(xdr_write_pages); -void -xdr_read_pages(struct xdr_stream *xdr, unsigned int len) +/** + * xdr_init_decode - Initialize an xdr_stream for decoding data. + * @xdr: pointer to xdr_stream struct + * @buf: pointer to XDR buffer from which to decode data + * @p: current pointer inside XDR buffer + */ +void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p) +{ + struct iovec *iov = buf->head; + unsigned int len = iov->iov_len; + + if (len > buf->len) + len = buf->len; + xdr->buf = buf; + xdr->iov = iov; + xdr->p = p; + xdr->end = (uint32_t *)((char *)iov->iov_base + len); +} +EXPORT_SYMBOL(xdr_init_decode); + +/** + * xdr_inline_decode - Retrieve non-page XDR data to decode + * @xdr: pointer to xdr_stream struct + * @nbytes: number of bytes of data to decode + * + * Check if the input buffer is long enough to enable us to decode + * 'nbytes' more bytes of data starting at the current position. + * If so return the current pointer, then update the current + * pointer position. + */ +uint32_t * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) +{ + uint32_t *p = xdr->p; + uint32_t *q = p + XDR_QUADLEN(nbytes); + + if (unlikely(q > xdr->end || q < p)) + return NULL; + xdr->p = q; + return p; +} +EXPORT_SYMBOL(xdr_inline_decode); + +/** + * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position + * @xdr: pointer to xdr_stream struct + * @len: number of bytes of page data + * + * Moves data beyond the current pointer position from the XDR head[] buffer + * into the page list. Any data that lies beyond current position + "len" + * bytes is moved into the XDR tail[]. The current pointer is then + * repositioned at the beginning of the XDR tail. + */ +void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) { struct xdr_buf *buf = xdr->buf; struct iovec *iov; ssize_t shift; + unsigned int end; int padding; /* Realign pages to current pointer position */ @@ -735,9 +889,21 @@ xdr_read_pages(struct xdr_stream *xdr, u xdr_shrink_pagelen(buf, buf->page_len - len); padding = (XDR_QUADLEN(len) << 2) - len; xdr->iov = iov = buf->tail; + /* Compute remaining message length. */ + end = iov->iov_len; + shift = buf->buflen - buf->len; + if (shift < end) + end -= shift; + else if (shift > 0) + end = 0; + /* + * Position current pointer at beginning of tail, and + * set remaining message length. + */ xdr->p = (uint32_t *)((char *)iov->iov_base + padding); - xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); + xdr->end = (uint32_t *)((char *)iov->iov_base + end); } +EXPORT_SYMBOL(xdr_read_pages); static struct iovec empty_iov = {.iov_base = NULL, .iov_len = 0}; @@ -747,7 +913,7 @@ xdr_buf_from_iov(struct iovec *iov, stru buf->head[0] = *iov; buf->tail[0] = empty_iov; buf->page_len = 0; - buf->len = iov->iov_len; + buf->buflen = buf->len = iov->iov_len; } /* Sets subiov to the intersection of iov with the buffer of length len @@ -777,7 +943,7 @@ xdr_buf_subsegment(struct xdr_buf *buf, { int i; - subbuf->len = len; + subbuf->buflen = subbuf->len = len; iov_subsegment(buf->head, subbuf->head, &base, &len); if (base < buf->page_len) { diff -u --recursive --new-file --show-c-function linux-2.6.5-rc2/net/sunrpc/xprt.c linux-2.6.5-31-server_caps/net/sunrpc/xprt.c --- linux-2.6.5-rc2/net/sunrpc/xprt.c 2004-03-25 00:30:01.000000000 -0500 +++ linux-2.6.5-31-server_caps/net/sunrpc/xprt.c 2004-03-25 00:37:38.000000000 -0500 @@ -647,8 +647,8 @@ xprt_complete_rqst(struct rpc_xprt *xprt #endif dprintk("RPC: %4d has input (%d bytes)\n", task->tk_pid, copied); - req->rq_received = copied; list_del_init(&req->rq_list); + req->rq_received = req->rq_private_buf.len = copied; /* ... and wake up the process. */ rpc_wake_up_task(task); @@ -765,7 +765,7 @@ udp_data_ready(struct sock *sk, int len) dprintk("RPC: %4d received reply\n", task->tk_pid); - if ((copied = rovr->rq_private_buf.len) > repsize) + if ((copied = rovr->rq_private_buf.buflen) > repsize) copied = repsize; /* Suck it into the iovec, verify checksum if not done by hw. */ @@ -782,8 +782,6 @@ udp_data_ready(struct sock *sk, int len) dropit: skb_free_datagram(sk, skb); out: - if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) - wake_up_interruptible(sk->sk_sleep); read_unlock(&sk->sk_callback_lock); } @@ -910,7 +908,7 @@ tcp_read_request(struct rpc_xprt *xprt, xprt->tcp_copied += len; xprt->tcp_offset += len; - if (xprt->tcp_copied == req->rq_private_buf.len) + if (xprt->tcp_copied == req->rq_private_buf.buflen) xprt->tcp_flags &= ~XPRT_COPY_DATA; else if (xprt->tcp_offset == xprt->tcp_reclen) { if (xprt->tcp_flags & XPRT_LAST_FRAG) @@ -1043,8 +1041,6 @@ tcp_state_change(struct sock *sk) break; } out: - if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) - wake_up_interruptible_all(sk->sk_sleep); read_unlock(&sk->sk_callback_lock); } @@ -1084,8 +1080,6 @@ xprt_write_space(struct sock *sk) if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->pending) rpc_wake_up_task(xprt->snd_task); spin_unlock_bh(&xprt->sock_lock); - if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) - wake_up_interruptible(sk->sk_sleep); out: read_unlock(&sk->sk_callback_lock); } @@ -1464,10 +1458,10 @@ xprt_setup(int proto, struct sockaddr_in } else xprt_default_timeout(&xprt->timeout, xprt->prot); - INIT_RPC_WAITQ(&xprt->pending, "xprt_pending"); - INIT_RPC_WAITQ(&xprt->sending, "xprt_sending"); - INIT_RPC_WAITQ(&xprt->resend, "xprt_resend"); - INIT_RPC_WAITQ(&xprt->backlog, "xprt_backlog"); + rpc_init_wait_queue(&xprt->pending, "xprt_pending"); + rpc_init_wait_queue(&xprt->sending, "xprt_sending"); + rpc_init_wait_queue(&xprt->resend, "xprt_resend"); + rpc_init_priority_wait_queue(&xprt->backlog, "xprt_backlog"); /* initialize free list */ for (req = &xprt->slot[entries-1]; req >= &xprt->slot[0]; req--) @@ -1626,8 +1620,7 @@ xprt_shutdown(struct rpc_xprt *xprt) rpc_wake_up(&xprt->resend); rpc_wake_up(&xprt->pending); rpc_wake_up(&xprt->backlog); - if (waitqueue_active(&xprt->cong_wait)) - wake_up(&xprt->cong_wait); + wake_up(&xprt->cong_wait); del_timer_sync(&xprt->timer); } @@ -1637,8 +1630,7 @@ xprt_shutdown(struct rpc_xprt *xprt) int xprt_clear_backlog(struct rpc_xprt *xprt) { rpc_wake_up_next(&xprt->backlog); - if (waitqueue_active(&xprt->cong_wait)) - wake_up(&xprt->cong_wait); + wake_up(&xprt->cong_wait); return 1; }