From 1a7bc91499344d6be96098622829e297206669eb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 7 Oct 2003 12:51:33 -0400 Subject: [PATCH] NFSv4 state model update - Hierarchy of state attached to nfs4_client in order to simplify state recovery. state_owners hang off nfs4_client, whereas state hangs off both open_owners and the nfs_inode. - Model tries to minimize the number of open_owners on the server by recycling unused open_owners into a pool. - NFSv4 state attached to file->private_data. Previously this was used by credentials (and still is for NFSv2/v3) Abstract out setup/release of struct file and nfs_page structure initialization in order to cope with these conflicting uses of private_data. --- fs/nfs/dir.c | 19 ++- fs/nfs/file.c | 12 +- fs/nfs/inode.c | 10 +- fs/nfs/nfs3proc.c | 84 +++++++++-- fs/nfs/nfs4proc.c | 289 ++++++++++++++++++++++--------------- fs/nfs/nfs4state.c | 302 +++++++++++++++++++++++++-------------- fs/nfs/nfs4xdr.c | 9 +- fs/nfs/pagelist.c | 10 +- fs/nfs/proc.c | 67 +++++++-- fs/nfs/read.c | 10 +- fs/nfs/write.c | 14 +- include/linux/nfs_fs.h | 68 ++++++--- include/linux/nfs_page.h | 11 +- include/linux/nfs_xdr.h | 13 +- 14 files changed, 592 insertions(+), 326 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 6587f4ede169d..33c2aa940c920 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -785,6 +785,9 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, struct inode *inode; int error = -EACCES; + /* We may have been initialized further down */ + if (dentry->d_inode) + return 0; if (fhandle->size == 0 || !(fattr->valid & NFS_ATTR_FATTR)) { struct inode *dir = dentry->d_parent->d_inode; error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); @@ -813,8 +816,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { struct iattr attr; - struct nfs_fattr fattr; - struct nfs_fh fhandle; + struct inode *inode; int error; int open_flags = 0; @@ -835,12 +837,15 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, */ lock_kernel(); nfs_zap_caches(dir); - error = NFS_PROTO(dir)->create(dir, &dentry->d_name, - &attr, open_flags, &fhandle, &fattr); - if (!error) - error = nfs_instantiate(dentry, &fhandle, &fattr); - else + inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags); + if (!IS_ERR(inode)) { + d_instantiate(dentry, inode); + nfs_renew_times(dentry); + error = 0; + } else { + error = PTR_ERR(inode); d_drop(dentry); + } unlock_kernel(); return error; } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 7375f369e5174..8c5b1c626df45 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -35,6 +35,7 @@ #define NFSDBG_FACILITY NFSDBG_FILE static int nfs_file_open(struct inode *, struct file *); +static int nfs_file_release(struct inode *, struct file *); static int nfs_file_mmap(struct file *, struct vm_area_struct *); static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *); static ssize_t nfs_file_read(struct kiocb *, char *, size_t, loff_t); @@ -51,7 +52,7 @@ struct file_operations nfs_file_operations = { .mmap = nfs_file_mmap, .open = nfs_file_open, .flush = nfs_file_flush, - .release = nfs_release, + .release = nfs_file_release, .fsync = nfs_fsync, .lock = nfs_lock, .sendfile = nfs_file_sendfile, @@ -82,13 +83,16 @@ nfs_file_open(struct inode *inode, struct file *filp) /* Do NFSv4 open() call */ if ((open = server->rpc_ops->file_open) != NULL) res = open(inode, filp); - /* Call generic open code in order to cache credentials */ - if (!res) - res = nfs_open(inode, filp); unlock_kernel(); return res; } +static int +nfs_file_release(struct inode *inode, struct file *filp) +{ + return NFS_PROTO(inode)->file_release(inode, filp); +} + /* * Flush all dirty pages, and check for write errors. * diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index ec54a826cd177..a049c69951da3 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -151,10 +151,6 @@ nfs_clear_inode(struct inode *inode) cred = nfsi->cache_access.cred; if (cred) put_rpccred(cred); - /* Clean up the V4 state */ - nfs4_put_state_owner(inode, nfsi->wo_owner); - nfs4_put_state_owner(inode, nfsi->ro_owner); - nfs4_put_state_owner(inode, nfsi->rw_owner); } void @@ -891,7 +887,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) * Ensure that mmap has a recent RPC credential for use when writing out * shared pages */ -static inline void +void nfs_set_mmcred(struct inode *inode, struct rpc_cred *cred) { struct rpc_cred **p = &NFS_I(inode)->mm_cred, @@ -1573,9 +1569,7 @@ static struct file_system_type nfs4_fs_type = { #define nfs4_zero_state(nfsi) \ do { \ - (nfsi)->wo_owner = NULL; \ - (nfsi)->ro_owner = NULL; \ - (nfsi)->rw_owner = NULL; \ + INIT_LIST_HEAD(&(nfsi)->open_states); \ } while(0) #define register_nfs4fs() register_filesystem(&nfs4_fs_type) #define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type) diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index e3d37fbc8e519..bfea0dbeb5d13 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -81,6 +81,18 @@ nfs3_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) } } +static struct rpc_cred * +nfs_cred(struct inode *inode, struct file *filp) +{ + struct rpc_cred *cred = NULL; + + if (filp) + cred = (struct rpc_cred *)filp->private_data; + if (!cred) + cred = NFS_I(inode)->mm_cred; + return cred; +} + /* * Bare-bones access to getattr: this is for nfs_read_super. */ @@ -225,7 +237,7 @@ nfs3_proc_readlink(struct inode *inode, struct page *page) } static int -nfs3_proc_read(struct nfs_read_data *rdata) +nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp) { int flags = rdata->flags; struct inode * inode = rdata->inode; @@ -234,13 +246,13 @@ nfs3_proc_read(struct nfs_read_data *rdata) .rpc_proc = &nfs3_procedures[NFS3PROC_READ], .rpc_argp = &rdata->args, .rpc_resp = &rdata->res, - .rpc_cred = rdata->cred, }; int status; dprintk("NFS call read %d @ %Ld\n", rdata->args.count, (long long) rdata->args.offset); fattr->valid = 0; + msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); if (status >= 0) nfs_refresh_inode(inode, fattr); @@ -249,7 +261,7 @@ nfs3_proc_read(struct nfs_read_data *rdata) } static int -nfs3_proc_write(struct nfs_write_data *wdata) +nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp) { int rpcflags = wdata->flags; struct inode * inode = wdata->inode; @@ -258,13 +270,13 @@ nfs3_proc_write(struct nfs_write_data *wdata) .rpc_proc = &nfs3_procedures[NFS3PROC_WRITE], .rpc_argp = &wdata->args, .rpc_resp = &wdata->res, - .rpc_cred = wdata->cred, }; int status; dprintk("NFS call write %d @ %Ld\n", wdata->args.count, (long long) wdata->args.offset); fattr->valid = 0; + msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags); if (status >= 0) nfs3_write_refresh_inode(inode, fattr); @@ -276,10 +288,12 @@ nfs3_proc_write(struct nfs_write_data *wdata) * Create a regular file. * For now, we don't implement O_EXCL. */ -static int +static struct inode * nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, - int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr) + int flags) { + struct nfs_fh fhandle; + struct nfs_fattr fattr; struct nfs_fattr dir_attr; struct nfs3_createargs arg = { .fh = NFS_FH(dir), @@ -289,8 +303,8 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, }; struct nfs3_diropres res = { .dir_attr = &dir_attr, - .fh = fhandle, - .fattr = fattr + .fh = &fhandle, + .fattr = &fattr }; int status; @@ -304,7 +318,7 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, again: dir_attr.valid = 0; - fattr->valid = 0; + fattr.valid = 0; status = rpc_call(NFS_CLIENT(dir), NFS3PROC_CREATE, &arg, &res, 0); nfs_refresh_inode(dir, &dir_attr); @@ -329,11 +343,19 @@ again: exit: dprintk("NFS reply create: %d\n", status); + if (status != 0) + goto out; + if (fhandle.size == 0 || !(fattr.valid & NFS_ATTR_FATTR)) { + status = nfs3_proc_lookup(dir, name, &fhandle, &fattr); + if (status != 0) + goto out; + } + /* When we created the file with exclusive semantics, make * sure we set the attributes afterwards. */ - if (status == 0 && arg.createmode == NFS3_CREATE_EXCLUSIVE) { + if (arg.createmode == NFS3_CREATE_EXCLUSIVE) { struct nfs3_sattrargs arg = { - .fh = fhandle, + .fh = &fhandle, .sattr = sattr, }; dprintk("NFS call setattr (post-create)\n"); @@ -341,13 +363,20 @@ exit: /* Note: we could use a guarded setattr here, but I'm * not sure this buys us anything (and I'd have * to revamp the NFSv3 XDR code) */ - fattr->valid = 0; + fattr.valid = 0; status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SETATTR, - &arg, fattr, 0); + &arg, &fattr, 0); dprintk("NFS reply setattr (post-create): %d\n", status); } - - return status; + if (status == 0) { + struct inode *inode; + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (inode) + return inode; + status = -ENOMEM; + } +out: + return ERR_PTR(status); } static int @@ -823,6 +852,27 @@ nfs3_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) rpc_call_setup(&data->task, &msg, 0); } +/* + * Set up the nfspage struct with the right credentials + */ +void +nfs3_request_init(struct nfs_page *req, struct file *filp) +{ + req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp)); +} + +static int +nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *page) +{ + if (req->wb_file != filp) + return 0; + if (req->wb_page != page) + return 0; + if (req->wb_cred != nfs_file_cred(filp)) + return 0; + return 1; +} + struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .getroot = nfs3_proc_get_root, @@ -851,4 +901,8 @@ struct nfs_rpc_ops nfs_v3_clientops = { .read_setup = nfs3_proc_read_setup, .write_setup = nfs3_proc_write_setup, .commit_setup = nfs3_proc_commit_setup, + .file_open = nfs_open, + .file_release = nfs_release, + .request_init = nfs3_request_init, + .request_compatible = nfs3_request_compatible, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 134bd78f56868..8b742557c1e0f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -553,13 +553,13 @@ process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr) } } -int -nfs4_do_open(struct inode *dir, struct qstr *name, int flags, - struct iattr *sattr, struct nfs_fattr *fattr, - struct nfs_fh *fhandle, struct nfs4_state_owner **spp) +struct nfs4_state * +nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred) { struct nfs4_state_owner *sp; + 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 = { @@ -570,7 +570,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, }; struct nfs4_getattr f_getattr = { .gt_bmval = nfs4_fattr_bitmap, - .gt_attrs = (fattr == NULL ? &f_attr: fattr), + .gt_attrs = &f_attr, }; struct nfs4_getattr d_getattr = { .gt_bmval = nfs4_fattr_bitmap, @@ -578,7 +578,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, }; struct nfs_openargs o_arg = { .fh = NFS_FH(dir), - .share_access = flags & O_ACCMODE, + .share_access = flags & (FMODE_READ|FMODE_WRITE), .clientid = NFS_SERVER(dir)->nfs4_state->cl_clientid, .opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE, .createmode = (flags & O_EXCL) ? NFS4_CREATE_EXCLUSIVE : NFS4_CREATE_UNCHECKED, @@ -597,10 +597,11 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN], .rpc_argp = &o_arg, .rpc_resp = &o_res, + .rpc_cred = cred, }; status = -ENOMEM; - if (!(sp = nfs4_get_state_owner(dir))) { + if (!(sp = nfs4_get_state_owner(NFS_SERVER(dir), cred))) { dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n"); goto out; } @@ -624,11 +625,13 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, process_cinfo(&d_cinfo, &d_attr); nfs_refresh_inode(dir, &d_attr); - if (fhandle) { - memset(fhandle, 0, sizeof(*fhandle)); - fhandle->size = (o_res.fh.size < NFS_MAXFHSIZE ? o_res.fh.size : NFS_MAXFHSIZE); - memcpy(fhandle->data, o_res.fh.data, fhandle->size); - } + status = -ENOMEM; + inode = nfs_fhget(dir->i_sb, &o_res.fh, &f_attr); + if (!inode) + goto out_up; + state = nfs4_get_open_state(inode, sp); + if (!state) + goto out_up; if(o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) { struct nfs_open_confirmargs oc_arg = { @@ -642,6 +645,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM], .rpc_argp = &oc_arg, .rpc_resp = &oc_res, + .rpc_cred = cred, }; memcpy(oc_arg.stateid, o_res.stateid, sizeof(nfs4_stateid)); @@ -649,22 +653,32 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, if (status) goto out_up; nfs4_increment_seqid(status, sp); - memcpy(sp->so_stateid, oc_res.stateid, sizeof(nfs4_stateid)); + memcpy(state->stateid, oc_res.stateid, sizeof(state->stateid)); } else - memcpy(sp->so_stateid, o_res.stateid, sizeof(nfs4_stateid)); - sp->so_flags = flags & O_ACCMODE; + memcpy(state->stateid, o_res.stateid, sizeof(state->stateid)); + state->state |= flags & (FMODE_READ|FMODE_WRITE); + state->pid = current->pid; + + up(&sp->so_sema); + nfs4_put_state_owner(sp); + iput(inode); + return state; out_up: up(&sp->so_sema); + nfs4_put_state_owner(sp); + if (state) + nfs4_put_open_state(state); + if (inode) + iput(inode); out: - *spp = sp; - return status; + return ERR_PTR(status); } int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, struct nfs_fh *fhandle, struct iattr *sattr, - struct nfs4_state_owner *sp) + struct nfs4_state *state) { struct nfs4_getattr getattr = { .gt_bmval = nfs4_fattr_bitmap, @@ -688,10 +702,10 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, fattr->valid = 0; - if (sp) - memcpy(arg.stateid, sp->so_stateid, sizeof(nfs4_stateid)); + if (state) + memcpy(arg.stateid, state->stateid, sizeof(arg.stateid)); else - memcpy(arg.stateid, zero_stateid, sizeof(nfs4_stateid)); + memcpy(arg.stateid, zero_stateid, sizeof(arg.stateid)); return(rpc_call_sync(server->client, &msg, 0)); } @@ -704,10 +718,13 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, * NFSv4 citizens - we do not indicate to the server to update the file's * share state even when we are done with one of the three share * stateid's in the inode. + * + * NOTE: Caller must be holding the sp->so_owner semaphore! */ int -nfs4_do_close(struct inode *inode, struct nfs4_state_owner *sp) +nfs4_do_close(struct inode *inode, struct nfs4_state *state) { + struct nfs4_state_owner *sp = state->owner; int status = 0; struct nfs_closeargs arg = { .fh = NFS_FH(inode), @@ -721,9 +738,8 @@ nfs4_do_close(struct inode *inode, struct nfs4_state_owner *sp) .rpc_resp = &res, }; - memcpy(arg.stateid, sp->so_stateid, sizeof(nfs4_stateid)); + memcpy(arg.stateid, state->stateid, sizeof(arg.stateid)); /* Serialization for the sequence id */ - down(&sp->so_sema); arg.seqid = sp->so_seqid, status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0); @@ -731,7 +747,6 @@ nfs4_do_close(struct inode *inode, struct nfs4_state_owner *sp) * the state_owner. we keep this around to process errors */ nfs4_increment_seqid(status, sp); - up(&sp->so_sema); return status; } @@ -837,7 +852,6 @@ no_setclientid: out_unlock: up_write(&clp->cl_sem); nfs4_put_client(clp); - server->nfs4_state = NULL; return status; } @@ -878,31 +892,29 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, { struct inode * inode = dentry->d_inode; int size_change = sattr->ia_valid & ATTR_SIZE; - struct nfs4_state_owner *sp = NULL; + struct nfs4_state *state = NULL; int status; fattr->valid = 0; if (size_change) { - if (NFS_I(inode)->wo_owner) { - /* file is already open for O_WRONLY */ - sp = NFS_I(inode)->wo_owner; - goto no_open; - } - status = nfs4_do_open(dentry->d_parent->d_inode, - &dentry->d_name, O_WRONLY, NULL, fattr, - NULL, &sp); - if (status) - return status; - - if (fattr->fileid != NFS_FILEID(inode)) { + struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + state = nfs4_do_open(dentry->d_parent->d_inode, + &dentry->d_name, FMODE_WRITE, NULL, cred); + put_rpccred(cred); + if (IS_ERR(state)) + return PTR_ERR(state); + + if (state->inode != inode) { printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n"); + nfs4_put_open_state(state); return -EIO; } } -no_open: status = nfs4_do_setattr(NFS_SERVER(inode), fattr, - NFS_FH(inode), sattr, sp); + NFS_FH(inode), sattr, state); + if (state) + nfs4_put_open_state(state); return status; } @@ -1017,14 +1029,13 @@ nfs4_proc_readlink(struct inode *inode, struct page *page) } static int -nfs4_proc_read(struct nfs_read_data *rdata) +nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) { int flags = rdata->flags; struct inode *inode = rdata->inode; struct nfs_fattr *fattr = rdata->res.fattr; nfs4_stateid *stateid = &rdata->args.stateid; struct nfs_server *server = NFS_SERVER(inode); - struct nfs4_state_owner *sp; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ], .rpc_argp = &rdata->args, @@ -1040,13 +1051,12 @@ nfs4_proc_read(struct nfs_read_data *rdata) /* * Try first to use O_RDONLY, then O_RDWR stateid. */ - sp = nfs4_get_inode_share(inode, O_RDONLY); - if (!sp) - sp = nfs4_get_inode_share(inode, O_RDWR); - if (sp) - memcpy(stateid, sp->so_stateid, sizeof(nfs4_stateid)); - else - memcpy(stateid, zero_stateid, sizeof(nfs4_stateid)); + if (filp) { + struct nfs4_state *state; + state = (struct nfs4_state *)filp->private_data; + memcpy(stateid, state->stateid, sizeof(stateid)); + } else + memcpy(stateid, zero_stateid, sizeof(stateid)); fattr->valid = 0; status = rpc_call_sync(server->client, &msg, flags); @@ -1061,14 +1071,13 @@ nfs4_proc_read(struct nfs_read_data *rdata) } static int -nfs4_proc_write(struct nfs_write_data *wdata) +nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp) { int rpcflags = wdata->flags; struct inode *inode = wdata->inode; struct nfs_fattr *fattr = wdata->res.fattr; nfs4_stateid *stateid = &wdata->args.stateid; struct nfs_server *server = NFS_SERVER(inode); - struct nfs4_state_owner *sp; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE], .rpc_argp = &wdata->args, @@ -1083,14 +1092,12 @@ nfs4_proc_write(struct nfs_write_data *wdata) /* * Try first to use O_WRONLY, then O_RDWR stateid. */ - sp = nfs4_get_inode_share(inode, O_WRONLY); - if (!sp) - sp = nfs4_get_inode_share(inode, O_RDWR); - - if (sp) - memcpy(stateid, sp->so_stateid, sizeof(nfs4_stateid)); - else - memcpy(stateid, zero_stateid, sizeof(nfs4_stateid)); + if (filp) { + struct nfs4_state *state; + state = (struct nfs4_state *)filp->private_data; + memcpy(stateid, state->stateid, sizeof(stateid)); + } else + memcpy(stateid, zero_stateid, sizeof(stateid)); fattr->valid = 0; status = rpc_call_sync(server->client, &msg, rpcflags); @@ -1113,24 +1120,33 @@ nfs4_proc_write(struct nfs_write_data *wdata) * opens the file O_RDONLY. This will all be resolved with the VFS changes. */ -static int +static struct inode * nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, - int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr) + int flags) { - int oflags; - struct nfs4_state_owner *sp = NULL; - int status; + struct inode *inode; + struct nfs4_state *state = NULL; + struct rpc_cred *cred; - oflags = O_RDONLY | O_CREAT | (flags & O_EXCL); - status = nfs4_do_open(dir, name, oflags, sattr, fattr, fhandle, &sp); - if (!status) { + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + state = nfs4_do_open(dir, name, flags, sattr, cred); + put_rpccred(cred); + if (!IS_ERR(state)) { + inode = igrab(state->inode); if (flags & O_EXCL) { - status = nfs4_do_setattr(NFS_SERVER(dir), fattr, - fhandle, sattr, sp); - /* XXX should i bother closing the file? */ + struct nfs_fattr fattr; + int status; + status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, + NFS_FH(inode), sattr, state); + if (status != 0) { + iput(inode); + inode = ERR_PTR(status); + } } - } - return status; + nfs4_put_open_state(state); + } else + inode = (struct inode *)state; + return inode; } static int @@ -1447,7 +1463,6 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) }; struct inode *inode = data->inode; struct nfs_page *req = nfs_list_entry(data->pages.next); - struct nfs4_state_owner *sp; int flags; data->args.fh = NFS_FH(inode); @@ -1460,18 +1475,10 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) data->res.eof = 0; data->timestamp = jiffies; - if(req->wb_file) { - unsigned int oflags = req->wb_file->f_flags; - sp = nfs4_get_inode_share(inode, oflags); - } else { - sp = nfs4_get_inode_share(inode, O_RDONLY); - if (!sp) - sp = nfs4_get_inode_share(inode, O_RDWR); - } - if (sp) - memcpy(data->args.stateid,sp->so_stateid, sizeof(nfs4_stateid)); + if (req->wb_state) + memcpy(data->args.stateid, req->wb_state->stateid, sizeof(data->args.stateid)); else - memcpy(data->args.stateid, zero_stateid, sizeof(nfs4_stateid)); + 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); @@ -1525,7 +1532,6 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) }; struct inode *inode = data->inode; struct nfs_page *req = nfs_list_entry(data->pages.next); - struct nfs4_state_owner *sp; int stable; int flags; @@ -1548,19 +1554,10 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) data->res.verf = &data->verf; data->timestamp = jiffies; - if(req->wb_file) { - unsigned int oflags = req->wb_file->f_flags; - sp = nfs4_get_inode_share(inode, oflags); - } else { - sp = nfs4_get_inode_share(inode, O_WRONLY); - if (!sp) - sp = nfs4_get_inode_share(inode, O_RDWR); - } - - if (sp) - memcpy(data->args.stateid,sp->so_stateid, sizeof(nfs4_stateid)); + if (req->wb_state) + memcpy(data->args.stateid, req->wb_state->stateid, sizeof(data->args.stateid)); else - memcpy(data->args.stateid, zero_stateid, sizeof(nfs4_stateid)); + memcpy(data->args.stateid, zero_stateid, sizeof(data->args.stateid)); /* Set the initial flags for the task. */ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; @@ -1674,48 +1671,99 @@ nfs4_proc_renew(struct nfs_server *server) * due to the lookup, potential create, and open VFS calls from sys_open() * placed on the wire. */ -int +static int nfs4_proc_file_open(struct inode *inode, struct file *filp) { struct dentry *dentry = filp->f_dentry; struct inode *dir = dentry->d_parent->d_inode; - int flags, status = 0; + struct rpc_cred *cred; + struct nfs4_state *state; + int flags = filp->f_flags; + int status = 0; dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n", (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int)dentry->d_name.len, dentry->d_name.name); - lock_kernel(); - - /* isn't this done in open_namei? */ - if (!S_ISREG(inode->i_mode)) { - status = -EISDIR; - goto out; - } + if ((flags + 1) & O_ACCMODE) + flags++; - flags = filp->f_flags & O_ACCMODE; + lock_kernel(); /* -* Got race?? * We have already opened the file "O_EXCL" in nfs4_proc_create!! * This ugliness will go away with lookup-intent... */ - while (!nfs4_get_inode_share(inode, flags)) { - struct nfs4_state_owner *sp = NULL; - status = nfs4_do_open(dir, &dentry->d_name, flags, NULL, NULL, NULL, &sp); - if (status) { - nfs4_put_state_owner(inode,sp); - break; - } - if (nfs4_set_inode_share(inode, sp, flags)) - nfs4_put_state_owner(inode,sp); + cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + state = nfs4_do_open(dir, &dentry->d_name, flags, NULL, cred); + if (IS_ERR(state)) { + status = PTR_ERR(state); + state = NULL; + } else if (filp->f_mode & FMODE_WRITE) + nfs_set_mmcred(inode, cred); + if (inode != filp->f_dentry->d_inode) { + printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__); + status = -EIO; /* ERACE actually */ + nfs4_put_open_state(state); + state = NULL; } -out: + filp->private_data = state; + put_rpccred(cred); unlock_kernel(); return status; } +/* + * Release our state + */ +static int +nfs4_proc_file_release(struct inode *inode, struct file *filp) +{ + struct nfs4_state *state = (struct nfs4_state *)filp->private_data; + + if (state) + nfs4_put_open_state(state); + return 0; +} + +/* + * Set up the nfspage struct with the right state info and credentials + */ +static void +nfs4_request_init(struct nfs_page *req, struct file *filp) +{ + struct nfs4_state *state; + + if (!filp) { + req->wb_cred = get_rpccred(NFS_I(req->wb_inode)->mm_cred); + req->wb_state = NULL; + return; + } + state = (struct nfs4_state *)filp->private_data; + req->wb_state = state; + req->wb_cred = get_rpccred(state->owner->so_cred); +} + + +static int +nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *page) +{ + struct nfs4_state *state = NULL; + struct rpc_cred *cred = NULL; + + if (req->wb_file != filp) + return 0; + if (req->wb_page != page) + return 0; + state = (struct nfs4_state *)filp->private_data; + if (req->wb_state != state) + return 0; + cred = state->owner->so_cred; + if (req->wb_cred != cred) + return 0; + return 1; +} struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ @@ -1747,6 +1795,9 @@ struct nfs_rpc_ops nfs_v4_clientops = { .write_setup = nfs4_proc_write_setup, .commit_setup = nfs4_proc_commit_setup, .file_open = nfs4_proc_file_open, + .file_release = nfs4_proc_file_release, + .request_init = nfs4_request_init, + .request_compatible = nfs4_request_compatible, }; /* diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index d1dac0c0a25c8..fd5a93d948e58 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -42,8 +42,9 @@ #include #include -/* This protects most of the client-side state. */ -static spinlock_t state_spinlock = SPIN_LOCK_UNLOCKED; +#define OPENOWNER_POOL_SIZE 8 + +static spinlock_t state_spinlock = SPIN_LOCK_UNLOCKED; nfs4_stateid zero_stateid = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -60,7 +61,7 @@ static LIST_HEAD(nfs4_clientid_list); * Since these are allocated/deallocated very rarely, we don't * bother putting them in a slab cache... */ -struct nfs4_client * +static struct nfs4_client * nfs4_alloc_client(struct in_addr *addr) { struct nfs4_client *clp; @@ -70,6 +71,7 @@ nfs4_alloc_client(struct in_addr *addr) memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr)); init_rwsem(&clp->cl_sem); INIT_LIST_HEAD(&clp->cl_state_owners); + INIT_LIST_HEAD(&clp->cl_unused); spin_lock_init(&clp->cl_lock); atomic_set(&clp->cl_count, 1); clp->cl_state = NFS4CLNT_NEW; @@ -77,10 +79,19 @@ nfs4_alloc_client(struct in_addr *addr) return clp; } -void +static void nfs4_free_client(struct nfs4_client *clp) { - BUG_ON(!clp); + struct nfs4_state_owner *sp; + + while (!list_empty(&clp->cl_unused)) { + sp = list_entry(clp->cl_unused.next, + struct nfs4_state_owner, + so_list); + list_del(&sp->so_list); + kfree(sp); + } + BUG_ON(!list_empty(&clp->cl_state_owners)); kfree(clp); } @@ -120,149 +131,224 @@ nfs4_put_client(struct nfs4_client *clp) static inline u32 nfs4_alloc_lockowner_id(struct nfs4_client *clp) { - u32 res; + return clp->cl_lockowner_id ++; +} - spin_lock(&state_spinlock); - res = clp->cl_lockowner_id ++; - spin_unlock(&state_spinlock); - return res; +static struct nfs4_state_owner * +nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred) +{ + struct nfs4_state_owner *sp = NULL; + + if (!list_empty(&clp->cl_unused)) { + sp = list_entry(clp->cl_unused.next, struct nfs4_state_owner, so_list); + atomic_inc(&sp->so_count); + sp->so_cred = cred; + list_move(&sp->so_list, &clp->cl_state_owners); + clp->cl_nunused--; + } + return sp; } /* - * nfs4_get_state_owner(): this is called on the OPEN or CREATE path to - * obtain a new state_owner. - * - * There are three state_owners (open_owner4 in rfc3010) per inode, - * one for each possible combination of share lock access. Since - * Linux does not support the deny access type, there are - * three (not 9) referenced by the nfs_inode: - * - * O_WRONLY: inode->wo_owner - * O_RDONLY: inode->ro_owner - * O_RDWR: inode->rw_owner - * - * We create a new state_owner the first time a file is OPENed with - * one of the above shares. All other OPENs with a similar - * share use the single stateid associated with the inode. + * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to + * create a new state_owner. * */ -struct nfs4_state_owner * -nfs4_get_state_owner(struct inode *dir) +static struct nfs4_state_owner * +nfs4_alloc_state_owner(void) { - struct nfs4_client *clp; struct nfs4_state_owner *sp; sp = kmalloc(sizeof(*sp),GFP_KERNEL); if (!sp) return NULL; - clp = (NFS_SB(dir->i_sb))->nfs4_state; - BUG_ON(!clp); init_MUTEX(&sp->so_sema); sp->so_seqid = 0; /* arbitrary */ - memset(sp->so_stateid, 0, sizeof(nfs4_stateid)); - sp->so_id = nfs4_alloc_lockowner_id(clp); + INIT_LIST_HEAD(&sp->so_states); + atomic_set(&sp->so_count, 1); return sp; } -/* - * Called for each non-null inode state_owner in nfs_clear_inode, - * or if nfs4_do_open fails. - */ -void -nfs4_put_state_owner(struct inode *inode, struct nfs4_state_owner *sp) +struct nfs4_state_owner * +nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) { + struct nfs4_client *clp = server->nfs4_state; + struct nfs4_state_owner *sp, *new; + + get_rpccred(cred); + new = nfs4_alloc_state_owner(); + spin_lock(&clp->cl_lock); + sp = nfs4_client_grab_unused(clp, cred); + if (sp == NULL && new != NULL) { + list_add(&new->so_list, &clp->cl_state_owners); + new->so_client = clp; + new->so_id = nfs4_alloc_lockowner_id(clp); + new->so_cred = cred; + sp = new; + new = NULL; + } + spin_unlock(&clp->cl_lock); + if (new) + kfree(new); if (!sp) - return; - if (sp->so_flags & O_ACCMODE) - nfs4_do_close(inode, sp); - kfree(sp); + put_rpccred(cred); + return sp; } -/* -* Called with sp->so_sema held. -* -* Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or -* failed with a seqid incrementing error - -* see comments nfs_fs.h:seqid_mutating_error() -*/ void -nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp) +nfs4_put_state_owner(struct nfs4_state_owner *sp) { - if (status == NFS_OK || seqid_mutating_err(status)) - sp->so_seqid++; + struct nfs4_client *clp = sp->so_client; + struct rpc_cred *cred = sp->so_cred; + + if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) + return; + if (clp->cl_nunused >= OPENOWNER_POOL_SIZE) + goto out_free; + list_move(&sp->so_list, &clp->cl_unused); + clp->cl_nunused++; + spin_unlock(&clp->cl_lock); + put_rpccred(cred); + cred = NULL; + return; +out_free: + list_del(&sp->so_list); + spin_unlock(&clp->cl_lock); + put_rpccred(cred); + kfree(sp); } -/* -* Called by nfs4_proc_open to set the appropriate stateid -*/ -int -nfs4_set_inode_share(struct inode * inode, struct nfs4_state_owner *sp, unsigned int open_flags) +static struct nfs4_state * +nfs4_alloc_open_state(void) +{ + struct nfs4_state *state; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + state->pid = current->pid; + state->state = 0; + memset(state->stateid, 0, sizeof(state->stateid)); + atomic_set(&state->count, 1); + return state; +} + +static struct nfs4_state * +__nfs4_find_state_bypid(struct inode *inode, pid_t pid) { struct nfs_inode *nfsi = NFS_I(inode); + struct nfs4_state *state; - switch (open_flags & O_ACCMODE) { - case O_RDONLY: - if (!nfsi->ro_owner) { - nfsi->ro_owner = sp; - return 0; - } - break; - case O_WRONLY: - if (!nfsi->wo_owner) { - nfsi->wo_owner = sp; - return 0; - } - break; - case O_RDWR: - if (!nfsi->rw_owner) { - nfsi->rw_owner = sp; - return 0; - } + list_for_each_entry(state, &nfsi->open_states, inode_states) { + if (state->pid == pid) { + atomic_inc(&state->count); + return state; + } } - return -EBUSY; + return NULL; } -/* -* Boolean test to determine if an OPEN call goes on the wire. -* -* Called by nfs4_proc_open. -*/ -int -nfs4_test_state_owner(struct inode *inode, unsigned int open_flags) +static struct nfs4_state * +__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) { struct nfs_inode *nfsi = NFS_I(inode); + struct nfs4_state *state; - switch (open_flags & O_ACCMODE) { - case O_RDONLY: - if(nfsi->ro_owner) - return 0; - break; - case O_WRONLY: - if(nfsi->wo_owner) - return 0; - break; - case O_RDWR: - if(nfsi->rw_owner) - return 0; - } - return 1; + list_for_each_entry(state, &nfsi->open_states, inode_states) { + if (state->owner == owner) { + atomic_inc(&state->count); + return state; + } + } + return NULL; } -struct nfs4_state_owner * -nfs4_get_inode_share(struct inode * inode, unsigned int open_flags) +struct nfs4_state * +nfs4_find_state_bypid(struct inode *inode, pid_t pid) +{ + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs4_state *state; + + spin_lock(&inode->i_lock); + state = __nfs4_find_state_bypid(inode, pid); + /* Add the state to the tail of the inode's list */ + if (state) + list_move_tail(&state->inode_states, &nfsi->open_states); + spin_unlock(&inode->i_lock); + return state; +} + +static void +nfs4_free_open_state(struct nfs4_state *state) { + kfree(state); +} + +struct nfs4_state * +nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner) +{ + struct nfs4_state *state, *new; struct nfs_inode *nfsi = NFS_I(inode); - switch (open_flags & O_ACCMODE) { - case O_RDONLY: - return nfsi->ro_owner; - case O_WRONLY: - return nfsi->wo_owner; - case O_RDWR: - return nfsi->rw_owner; + spin_lock(&inode->i_lock); + state = __nfs4_find_state_byowner(inode, owner); + spin_unlock(&inode->i_lock); + if (state) + goto out; + new = nfs4_alloc_open_state(); + spin_lock(&inode->i_lock); + state = __nfs4_find_state_byowner(inode, owner); + if (state == NULL && new != NULL) { + state = new; + /* Caller *must* be holding owner->so_sem */ + list_add(&state->open_states, &owner->so_states); + state->owner = owner; + atomic_inc(&owner->so_count); + list_add(&state->inode_states, &nfsi->open_states); + state->inode = inode; + atomic_inc(&inode->i_count); + spin_unlock(&inode->i_lock); + } else { + spin_unlock(&inode->i_lock); + if (new) + nfs4_free_open_state(new); } - /* Duh gcc warning if we don't... */ - return NULL; +out: + return state; +} + +void +nfs4_put_open_state(struct nfs4_state *state) +{ + struct inode *inode = state->inode; + struct nfs4_state_owner *owner = state->owner; + + if (!atomic_dec_and_lock(&state->count, &inode->i_lock)) + return; + list_del(&state->inode_states); + spin_unlock(&inode->i_lock); + down(&owner->so_sema); + list_del(&state->open_states); + if (state->state != 0) + nfs4_do_close(inode, state); + up(&owner->so_sema); + iput(inode); + nfs4_free_open_state(state); + nfs4_put_state_owner(owner); +} + +/* +* Called with sp->so_sema held. +* +* Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or +* failed with a seqid incrementing error - +* see comments nfs_fs.h:seqid_mutating_error() +*/ +void +nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp) +{ + if (status == NFS_OK || seqid_mutating_err(status)) + sp->so_seqid++; } /* diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 2aecef42aa6da..ac89bf9ac3362 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -553,14 +553,17 @@ encode_open(struct xdr_stream *xdr, struct nfs_openargs *arg) WRITE32(OP_OPEN); WRITE32(arg->seqid); switch (arg->share_access) { - case O_RDONLY: + case FMODE_READ: WRITE32(NFS4_SHARE_ACCESS_READ); break; - case O_WRONLY: + case FMODE_WRITE: WRITE32(NFS4_SHARE_ACCESS_WRITE); break; - case O_RDWR: + case FMODE_READ|FMODE_WRITE: WRITE32(NFS4_SHARE_ACCESS_BOTH); + break; + default: + BUG(); } WRITE32(0); /* for linux, share_deny = 0 always */ WRITE64(arg->clientid); diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 9514f7eabf89c..70c86f06abd93 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -49,7 +49,7 @@ nfs_page_free(struct nfs_page *p) /** * nfs_create_request - Create an NFS read/write request. - * @cred: RPC credential to use + * @file: file descriptor to use * @inode: inode to which the request is attached * @page: page to write * @offset: starting offset within the page for the write @@ -62,7 +62,7 @@ nfs_page_free(struct nfs_page *p) * User should ensure it is safe to sleep in this function. */ struct nfs_page * -nfs_create_request(struct rpc_cred *cred, struct inode *inode, +nfs_create_request(struct file *file, struct inode *inode, struct page *page, unsigned int offset, unsigned int count) { @@ -93,11 +93,9 @@ nfs_create_request(struct rpc_cred *cred, struct inode *inode, req->wb_offset = offset; req->wb_pgbase = offset; req->wb_bytes = count; - - if (cred) - req->wb_cred = get_rpccred(cred); req->wb_inode = inode; req->wb_count = 1; + server->rpc_ops->request_init(req, file); return req; } @@ -111,6 +109,8 @@ nfs_create_request(struct rpc_cred *cred, struct inode *inode, */ void nfs_clear_request(struct nfs_page *req) { + if (req->wb_state) + req->wb_state = NULL; /* Release struct file or cached credential */ if (req->wb_file) { fput(req->wb_file); diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index d6791db622cac..6570e719ee543 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -60,6 +60,18 @@ nfs_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) nfs_refresh_inode(inode, fattr); } +static struct rpc_cred * +nfs_cred(struct inode *inode, struct file *filp) +{ + struct rpc_cred *cred = NULL; + + if (filp) + cred = (struct rpc_cred *)filp->private_data; + if (!cred) + cred = NFS_I(inode)->mm_cred; + return cred; +} + /* * Bare-bones access to getattr: this is for nfs_read_super. */ @@ -149,7 +161,7 @@ nfs_proc_readlink(struct inode *inode, struct page *page) } static int -nfs_proc_read(struct nfs_read_data *rdata) +nfs_proc_read(struct nfs_read_data *rdata, struct file *filp) { int flags = rdata->flags; struct inode * inode = rdata->inode; @@ -158,13 +170,13 @@ nfs_proc_read(struct nfs_read_data *rdata) .rpc_proc = &nfs_procedures[NFSPROC_READ], .rpc_argp = &rdata->args, .rpc_resp = &rdata->res, - .rpc_cred = rdata->cred, }; int status; dprintk("NFS call read %d @ %Ld\n", rdata->args.count, (long long) rdata->args.offset); fattr->valid = 0; + msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); if (status >= 0) @@ -174,7 +186,7 @@ nfs_proc_read(struct nfs_read_data *rdata) } static int -nfs_proc_write(struct nfs_write_data *wdata) +nfs_proc_write(struct nfs_write_data *wdata, struct file *filp) { int flags = wdata->flags; struct inode * inode = wdata->inode; @@ -183,13 +195,13 @@ nfs_proc_write(struct nfs_write_data *wdata) .rpc_proc = &nfs_procedures[NFSPROC_WRITE], .rpc_argp = &wdata->args, .rpc_resp = &wdata->res, - .rpc_cred = wdata->cred }; int status; dprintk("NFS call write %d @ %Ld\n", wdata->args.count, (long long) wdata->args.offset); fattr->valid = 0; + msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); if (status >= 0) { nfs_write_refresh_inode(inode, fattr); @@ -200,10 +212,12 @@ nfs_proc_write(struct nfs_write_data *wdata) return status < 0? status : wdata->res.count; } -static int +static struct inode * nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, - int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr) + int flags) { + struct nfs_fh fhandle; + struct nfs_fattr fattr; struct nfs_createargs arg = { .fh = NFS_FH(dir), .name = name->name, @@ -211,16 +225,23 @@ nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, .sattr = sattr }; struct nfs_diropok res = { - .fh = fhandle, - .fattr = fattr + .fh = &fhandle, + .fattr = &fattr }; int status; - fattr->valid = 0; + fattr.valid = 0; dprintk("NFS call create %s\n", name->name); status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0); dprintk("NFS reply create: %d\n", status); - return status; + if (status == 0) { + struct inode *inode; + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (inode) + return inode; + status = -ENOMEM; + } + return ERR_PTR(status); } /* @@ -611,6 +632,28 @@ nfs_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) BUG(); } +/* + * Set up the nfspage struct with the right credentials + */ +static void +nfs_request_init(struct nfs_page *req, struct file *filp) +{ + req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp)); +} + +static int +nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *page) +{ + if (req->wb_file != filp) + return 0; + if (req->wb_page != page) + return 0; + if (req->wb_cred != nfs_file_cred(filp)) + return 0; + return 1; +} + + struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ .getroot = nfs_proc_get_root, @@ -640,4 +683,8 @@ struct nfs_rpc_ops nfs_v2_clientops = { .read_setup = nfs_proc_read_setup, .write_setup = nfs_proc_write_setup, .commit_setup = nfs_proc_commit_setup, + .file_open = nfs_open, + .file_release = nfs_release, + .request_init = nfs_request_init, + .request_compatible = nfs_request_compatible, }; diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 2f64fc7e5e4c4..eaa650d24d91b 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -89,9 +89,6 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) dprintk("NFS: nfs_readpage_sync(%p)\n", page); - if (file) - rdata.cred = nfs_file_cred(file); - /* * This works now because the socket layer never tries to DMA * into this buffer directly. @@ -110,7 +107,7 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) rdata.args.count); lock_kernel(); - result = NFS_PROTO(inode)->read(&rdata); + result = NFS_PROTO(inode)->read(&rdata, file); unlock_kernel(); /* @@ -146,7 +143,7 @@ nfs_readpage_async(struct file *file, struct inode *inode, struct page *page) LIST_HEAD(one_request); struct nfs_page *new; - new = nfs_create_request(nfs_file_cred(file), inode, page, 0, PAGE_CACHE_SIZE); + new = nfs_create_request(file, inode, page, 0, PAGE_CACHE_SIZE); if (IS_ERR(new)) { unlock_page(page); return PTR_ERR(new); @@ -363,8 +360,7 @@ readpage_async_filler(void *data, struct page *page) struct nfs_page *new; nfs_wb_page(inode, page); - new = nfs_create_request(nfs_file_cred(desc->filp), inode, page, - 0, PAGE_CACHE_SIZE); + new = nfs_create_request(desc->filp, inode, page, 0, PAGE_CACHE_SIZE); if (IS_ERR(new)) { SetPageError(page); unlock_page(page); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 52aec4862ec8d..5f4403e2f2363 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -152,11 +152,6 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, }, }; - if (file) - wdata.cred = get_rpccred(nfs_file_cred(file)); - if (!wdata.cred) - wdata.cred = get_rpccred(NFS_I(inode)->mm_cred); - dprintk("NFS: nfs_writepage_sync(%s/%Ld %d@%Ld)\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode), @@ -167,7 +162,7 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, wdata.args.count = count; wdata.args.offset = page_offset(page) + wdata.args.pgbase; - result = NFS_PROTO(inode)->write(&wdata); + result = NFS_PROTO(inode)->write(&wdata, file); if (result < 0) { /* Must mark the page invalid after I/O error */ @@ -213,8 +208,6 @@ nfs_writepage_async(struct file *file, struct inode *inode, struct page *page, status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status < 0) goto out; - if (!req->wb_cred) - req->wb_cred = get_rpccred(NFS_I(inode)->mm_cred); nfs_unlock_request(req); nfs_strategy(inode); end = ((loff_t)page->index<mapping->host; - struct rpc_cred *cred = nfs_file_cred(file); struct nfs_page *req; int status = 0; /* @@ -650,7 +642,7 @@ nfs_flush_incompatible(struct file *file, struct page *page) */ req = nfs_find_request(inode, page->index); if (req) { - if (req->wb_file != file || req->wb_cred != cred || req->wb_page != page) + if (!NFS_PROTO(inode)->request_compatible(req, file, page)) status = nfs_wb_page(inode, page); nfs_release_request(req); } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index f57c6df50785d..4520308410df6 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -27,7 +27,6 @@ #include #include #include -#include #include /* @@ -158,9 +157,7 @@ struct nfs_inode { #ifdef CONFIG_NFS_V4 /* NFSv4 state */ - struct nfs4_state_owner *ro_owner; - struct nfs4_state_owner *wo_owner; - struct nfs4_state_owner *rw_owner; + struct list_head open_states; #endif /* CONFIG_NFS_V4*/ struct inode vfs_inode; @@ -229,12 +226,6 @@ loff_t page_offset(struct page *page) return ((loff_t)page->index) << PAGE_CACHE_SHIFT; } -static inline -loff_t req_offset(struct nfs_page *req) -{ - return (((loff_t)req->wb_index) << PAGE_CACHE_SHIFT) + req->wb_offset; -} - /* * linux/fs/nfs/inode.c */ @@ -244,6 +235,7 @@ extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *, extern int __nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int nfs_permission(struct inode *, int, struct nameidata *); +extern void nfs_set_mmcred(struct inode *, struct rpc_cred *); extern int nfs_open(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *); extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); @@ -494,29 +486,60 @@ struct nfs4_client { struct rw_semaphore cl_sem; struct list_head cl_state_owners; + struct list_head cl_unused; + int cl_nunused; spinlock_t cl_lock; atomic_t cl_count; }; /* -* The ->so_sema is held during all state_owner seqid-mutating operations: -* OPEN, OPEN_DOWNGRADE, and CLOSE. -* Its purpose is to properly serialize so_seqid, as mandated by -* the protocol. -*/ + * NFS4 state_owners and lock_owners are simply labels for ordered + * sequences of RPC calls. Their sole purpose is to provide once-only + * semantics by allowing the server to identify replayed requests. + * + * The ->so_sema is held during all state_owner seqid-mutating operations: + * OPEN, OPEN_DOWNGRADE, and CLOSE. Its purpose is to properly serialize + * so_seqid. + */ struct nfs4_state_owner { struct list_head so_list; /* per-clientid list of state_owners */ + struct nfs4_client *so_client; u32 so_id; /* 32-bit identifier, unique */ struct semaphore so_sema; u32 so_seqid; /* protected by so_sema */ - nfs4_stateid so_stateid; /* protected by so_sema */ unsigned int so_flags; /* protected by so_sema */ + atomic_t so_count; + + struct rpc_cred *so_cred; /* Associated cred */ + struct list_head so_states; +}; + +/* + * struct nfs4_state maintains the client-side state for a given + * (state_owner,inode) tuple. + * + * In order to know when to OPEN_DOWNGRADE or CLOSE the state on the server, + * we need to know how many files are open for reading or writing on a + * given inode. This information too is stored here. + */ +struct nfs4_state { + struct list_head open_states; /* List of states for the same state_owner */ + struct list_head inode_states; /* List of states for the same inode */ + + struct nfs4_state_owner *owner; /* Pointer to the open owner */ + struct inode *inode; /* Pointer to the inode */ + pid_t pid; /* Thread that called OPEN */ + + nfs4_stateid stateid; + + int state; /* State on the server (R,W, or RW) */ + atomic_t count; }; /* nfs4proc.c */ extern int nfs4_proc_renew(struct nfs_server *server); -extern int nfs4_do_close(struct inode *inode, struct nfs4_state_owner *sp); +extern int nfs4_do_close(struct inode *, struct nfs4_state *); /* nfs4renewd.c */ extern int nfs4_init_renewd(struct nfs_server *server); @@ -524,13 +547,11 @@ extern int nfs4_init_renewd(struct nfs_server *server); /* nfs4state.c */ extern struct nfs4_client *nfs4_get_client(struct in_addr *); extern void nfs4_put_client(struct nfs4_client *clp); -extern struct nfs4_state_owner * nfs4_get_state_owner(struct inode *inode); -void nfs4_put_state_owner(struct inode *inode, struct nfs4_state_owner *sp); -extern int nfs4_set_inode_share(struct inode * inode, - struct nfs4_state_owner *sp, unsigned int flags); +extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); +extern void nfs4_put_state_owner(struct nfs4_state_owner *); +extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); +extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); -extern int nfs4_test_state_owner(struct inode *inode, unsigned int open_flags); -struct nfs4_state_owner * nfs4_get_inode_share(struct inode * inode, unsigned int open_flags); @@ -561,6 +582,7 @@ destroy_nfsv4_state(struct nfs_server *server) #define create_nfsv4_state(server, data) 0 #define destroy_nfsv4_state(server) do { } while (0) #define nfs4_put_state_owner(inode, owner) do { } while (0) +#define nfs4_put_open_state(state) do { } while (0) #endif #endif /* __KERNEL__ */ diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index fb0a8e39e6697..0ad0f78e9afe9 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -11,7 +11,7 @@ #include -#include +#include #include #include #include @@ -28,6 +28,7 @@ struct nfs_page { struct file *wb_file; struct inode *wb_inode; struct rpc_cred *wb_cred; + struct nfs4_state *wb_state; struct page *wb_page; /* page to read in/write out */ wait_queue_head_t wb_wait; /* wait queue */ unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */ @@ -41,7 +42,7 @@ struct nfs_page { #define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) -extern struct nfs_page *nfs_create_request(struct rpc_cred *, struct inode *, +extern struct nfs_page *nfs_create_request(struct file *, struct inode *, struct page *, unsigned int, unsigned int); extern void nfs_clear_request(struct nfs_page *req); @@ -121,4 +122,10 @@ nfs_list_entry(struct list_head *head) return list_entry(head, struct nfs_page, wb_list); } +static inline +loff_t req_offset(struct nfs_page *req) +{ + return (((loff_t)req->wb_index) << PAGE_CACHE_SHIFT) + req->wb_offset; +} + #endif /* _LINUX_NFS_PAGE_H */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index bf8fe562d073e..ca0549e9ee4d2 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -620,6 +620,8 @@ struct nfs_write_data { #endif }; +struct nfs_page; + /* * RPC procedure vector for NFSv2/NFSv3 demuxing */ @@ -635,12 +637,12 @@ struct nfs_rpc_ops { struct nfs_fh *, struct nfs_fattr *); int (*access) (struct inode *, struct rpc_cred *, int); int (*readlink)(struct inode *, struct page *); - int (*read) (struct nfs_read_data *); - int (*write) (struct nfs_write_data *); + int (*read) (struct nfs_read_data *, struct file *); + int (*write) (struct nfs_write_data *, struct file *); int (*commit) (struct inode *, struct nfs_fattr *, unsigned long, unsigned int); - int (*create) (struct inode *, struct qstr *, struct iattr *, - int, struct nfs_fh *, struct nfs_fattr *); + struct inode * (*create) (struct inode *, struct qstr *, + struct iattr *, int); int (*remove) (struct inode *, struct qstr *); int (*unlink_setup) (struct rpc_message *, struct dentry *, struct qstr *); @@ -669,6 +671,9 @@ struct nfs_rpc_ops { 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); int (*file_open) (struct inode *, struct file *); + int (*file_release) (struct inode *, struct file *); + void (*request_init)(struct nfs_page *, struct file *); + int (*request_compatible)(struct nfs_page *, struct file *, struct page *); }; /* -- 2.53.0