Commit 1a7bc914 authored by Trond Myklebust's avatar Trond Myklebust

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.
parent 3e97d5ee
...@@ -785,6 +785,9 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, ...@@ -785,6 +785,9 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
struct inode *inode; struct inode *inode;
int error = -EACCES; 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)) { if (fhandle->size == 0 || !(fattr->valid & NFS_ATTR_FATTR)) {
struct inode *dir = dentry->d_parent->d_inode; struct inode *dir = dentry->d_parent->d_inode;
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); 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, ...@@ -813,8 +816,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
struct nameidata *nd) struct nameidata *nd)
{ {
struct iattr attr; struct iattr attr;
struct nfs_fattr fattr; struct inode *inode;
struct nfs_fh fhandle;
int error; int error;
int open_flags = 0; int open_flags = 0;
...@@ -835,12 +837,15 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, ...@@ -835,12 +837,15 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
*/ */
lock_kernel(); lock_kernel();
nfs_zap_caches(dir); nfs_zap_caches(dir);
error = NFS_PROTO(dir)->create(dir, &dentry->d_name, inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags);
&attr, open_flags, &fhandle, &fattr); if (!IS_ERR(inode)) {
if (!error) d_instantiate(dentry, inode);
error = nfs_instantiate(dentry, &fhandle, &fattr); nfs_renew_times(dentry);
else error = 0;
} else {
error = PTR_ERR(inode);
d_drop(dentry); d_drop(dentry);
}
unlock_kernel(); unlock_kernel();
return error; return error;
} }
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#define NFSDBG_FACILITY NFSDBG_FILE #define NFSDBG_FACILITY NFSDBG_FILE
static int nfs_file_open(struct inode *, struct 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 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_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *);
static ssize_t nfs_file_read(struct kiocb *, char *, size_t, loff_t); static ssize_t nfs_file_read(struct kiocb *, char *, size_t, loff_t);
...@@ -51,7 +52,7 @@ struct file_operations nfs_file_operations = { ...@@ -51,7 +52,7 @@ struct file_operations nfs_file_operations = {
.mmap = nfs_file_mmap, .mmap = nfs_file_mmap,
.open = nfs_file_open, .open = nfs_file_open,
.flush = nfs_file_flush, .flush = nfs_file_flush,
.release = nfs_release, .release = nfs_file_release,
.fsync = nfs_fsync, .fsync = nfs_fsync,
.lock = nfs_lock, .lock = nfs_lock,
.sendfile = nfs_file_sendfile, .sendfile = nfs_file_sendfile,
...@@ -82,13 +83,16 @@ nfs_file_open(struct inode *inode, struct file *filp) ...@@ -82,13 +83,16 @@ nfs_file_open(struct inode *inode, struct file *filp)
/* Do NFSv4 open() call */ /* Do NFSv4 open() call */
if ((open = server->rpc_ops->file_open) != NULL) if ((open = server->rpc_ops->file_open) != NULL)
res = open(inode, filp); res = open(inode, filp);
/* Call generic open code in order to cache credentials */
if (!res)
res = nfs_open(inode, filp);
unlock_kernel(); unlock_kernel();
return res; 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. * Flush all dirty pages, and check for write errors.
* *
......
...@@ -151,10 +151,6 @@ nfs_clear_inode(struct inode *inode) ...@@ -151,10 +151,6 @@ nfs_clear_inode(struct inode *inode)
cred = nfsi->cache_access.cred; cred = nfsi->cache_access.cred;
if (cred) if (cred)
put_rpccred(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 void
...@@ -891,7 +887,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) ...@@ -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 * Ensure that mmap has a recent RPC credential for use when writing out
* shared pages * shared pages
*/ */
static inline void void
nfs_set_mmcred(struct inode *inode, struct rpc_cred *cred) nfs_set_mmcred(struct inode *inode, struct rpc_cred *cred)
{ {
struct rpc_cred **p = &NFS_I(inode)->mm_cred, struct rpc_cred **p = &NFS_I(inode)->mm_cred,
...@@ -1573,9 +1569,7 @@ static struct file_system_type nfs4_fs_type = { ...@@ -1573,9 +1569,7 @@ static struct file_system_type nfs4_fs_type = {
#define nfs4_zero_state(nfsi) \ #define nfs4_zero_state(nfsi) \
do { \ do { \
(nfsi)->wo_owner = NULL; \ INIT_LIST_HEAD(&(nfsi)->open_states); \
(nfsi)->ro_owner = NULL; \
(nfsi)->rw_owner = NULL; \
} while(0) } while(0)
#define register_nfs4fs() register_filesystem(&nfs4_fs_type) #define register_nfs4fs() register_filesystem(&nfs4_fs_type)
#define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type) #define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type)
......
...@@ -81,6 +81,18 @@ nfs3_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -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. * Bare-bones access to getattr: this is for nfs_read_super.
*/ */
...@@ -225,7 +237,7 @@ nfs3_proc_readlink(struct inode *inode, struct page *page) ...@@ -225,7 +237,7 @@ nfs3_proc_readlink(struct inode *inode, struct page *page)
} }
static int 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; int flags = rdata->flags;
struct inode * inode = rdata->inode; struct inode * inode = rdata->inode;
...@@ -234,13 +246,13 @@ nfs3_proc_read(struct nfs_read_data *rdata) ...@@ -234,13 +246,13 @@ nfs3_proc_read(struct nfs_read_data *rdata)
.rpc_proc = &nfs3_procedures[NFS3PROC_READ], .rpc_proc = &nfs3_procedures[NFS3PROC_READ],
.rpc_argp = &rdata->args, .rpc_argp = &rdata->args,
.rpc_resp = &rdata->res, .rpc_resp = &rdata->res,
.rpc_cred = rdata->cred,
}; };
int status; int status;
dprintk("NFS call read %d @ %Ld\n", rdata->args.count, dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset); (long long) rdata->args.offset);
fattr->valid = 0; fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) if (status >= 0)
nfs_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
...@@ -249,7 +261,7 @@ nfs3_proc_read(struct nfs_read_data *rdata) ...@@ -249,7 +261,7 @@ nfs3_proc_read(struct nfs_read_data *rdata)
} }
static int 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; int rpcflags = wdata->flags;
struct inode * inode = wdata->inode; struct inode * inode = wdata->inode;
...@@ -258,13 +270,13 @@ nfs3_proc_write(struct nfs_write_data *wdata) ...@@ -258,13 +270,13 @@ nfs3_proc_write(struct nfs_write_data *wdata)
.rpc_proc = &nfs3_procedures[NFS3PROC_WRITE], .rpc_proc = &nfs3_procedures[NFS3PROC_WRITE],
.rpc_argp = &wdata->args, .rpc_argp = &wdata->args,
.rpc_resp = &wdata->res, .rpc_resp = &wdata->res,
.rpc_cred = wdata->cred,
}; };
int status; int status;
dprintk("NFS call write %d @ %Ld\n", wdata->args.count, dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset); (long long) wdata->args.offset);
fattr->valid = 0; fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
if (status >= 0) if (status >= 0)
nfs3_write_refresh_inode(inode, fattr); nfs3_write_refresh_inode(inode, fattr);
...@@ -276,10 +288,12 @@ nfs3_proc_write(struct nfs_write_data *wdata) ...@@ -276,10 +288,12 @@ nfs3_proc_write(struct nfs_write_data *wdata)
* Create a regular file. * Create a regular file.
* For now, we don't implement O_EXCL. * 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, 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 nfs_fattr dir_attr;
struct nfs3_createargs arg = { struct nfs3_createargs arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
...@@ -289,8 +303,8 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, ...@@ -289,8 +303,8 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
}; };
struct nfs3_diropres res = { struct nfs3_diropres res = {
.dir_attr = &dir_attr, .dir_attr = &dir_attr,
.fh = fhandle, .fh = &fhandle,
.fattr = fattr .fattr = &fattr
}; };
int status; int status;
...@@ -304,7 +318,7 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, ...@@ -304,7 +318,7 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
again: again:
dir_attr.valid = 0; dir_attr.valid = 0;
fattr->valid = 0; fattr.valid = 0;
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_CREATE, &arg, &res, 0); status = rpc_call(NFS_CLIENT(dir), NFS3PROC_CREATE, &arg, &res, 0);
nfs_refresh_inode(dir, &dir_attr); nfs_refresh_inode(dir, &dir_attr);
...@@ -329,11 +343,19 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, ...@@ -329,11 +343,19 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
exit: exit:
dprintk("NFS reply create: %d\n", status); 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 /* When we created the file with exclusive semantics, make
* sure we set the attributes afterwards. */ * sure we set the attributes afterwards. */
if (status == 0 && arg.createmode == NFS3_CREATE_EXCLUSIVE) { if (arg.createmode == NFS3_CREATE_EXCLUSIVE) {
struct nfs3_sattrargs arg = { struct nfs3_sattrargs arg = {
.fh = fhandle, .fh = &fhandle,
.sattr = sattr, .sattr = sattr,
}; };
dprintk("NFS call setattr (post-create)\n"); dprintk("NFS call setattr (post-create)\n");
...@@ -341,13 +363,20 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, ...@@ -341,13 +363,20 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
/* Note: we could use a guarded setattr here, but I'm /* Note: we could use a guarded setattr here, but I'm
* not sure this buys us anything (and I'd have * not sure this buys us anything (and I'd have
* to revamp the NFSv3 XDR code) */ * to revamp the NFSv3 XDR code) */
fattr->valid = 0; fattr.valid = 0;
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SETATTR, status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SETATTR,
&arg, fattr, 0); &arg, &fattr, 0);
dprintk("NFS reply setattr (post-create): %d\n", status); dprintk("NFS reply setattr (post-create): %d\n", status);
} }
if (status == 0) {
return status; struct inode *inode;
inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
if (inode)
return inode;
status = -ENOMEM;
}
out:
return ERR_PTR(status);
} }
static int static int
...@@ -823,6 +852,27 @@ nfs3_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) ...@@ -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); 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 = { struct nfs_rpc_ops nfs_v3_clientops = {
.version = 3, /* protocol version */ .version = 3, /* protocol version */
.getroot = nfs3_proc_get_root, .getroot = nfs3_proc_get_root,
...@@ -851,4 +901,8 @@ struct nfs_rpc_ops nfs_v3_clientops = { ...@@ -851,4 +901,8 @@ struct nfs_rpc_ops nfs_v3_clientops = {
.read_setup = nfs3_proc_read_setup, .read_setup = nfs3_proc_read_setup,
.write_setup = nfs3_proc_write_setup, .write_setup = nfs3_proc_write_setup,
.commit_setup = nfs3_proc_commit_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,
}; };
...@@ -553,13 +553,13 @@ process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr) ...@@ -553,13 +553,13 @@ process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr)
} }
} }
int struct nfs4_state *
nfs4_do_open(struct inode *dir, struct qstr *name, int flags, nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred)
struct iattr *sattr, struct nfs_fattr *fattr,
struct nfs_fh *fhandle, struct nfs4_state_owner **spp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL;
struct nfs_server *server = NFS_SERVER(dir); struct nfs_server *server = NFS_SERVER(dir);
struct inode *inode = NULL;
struct nfs4_change_info d_cinfo; struct nfs4_change_info d_cinfo;
int status; int status;
struct nfs_fattr d_attr = { struct nfs_fattr d_attr = {
...@@ -570,7 +570,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, ...@@ -570,7 +570,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags,
}; };
struct nfs4_getattr f_getattr = { struct nfs4_getattr f_getattr = {
.gt_bmval = nfs4_fattr_bitmap, .gt_bmval = nfs4_fattr_bitmap,
.gt_attrs = (fattr == NULL ? &f_attr: fattr), .gt_attrs = &f_attr,
}; };
struct nfs4_getattr d_getattr = { struct nfs4_getattr d_getattr = {
.gt_bmval = nfs4_fattr_bitmap, .gt_bmval = nfs4_fattr_bitmap,
...@@ -578,7 +578,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, ...@@ -578,7 +578,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags,
}; };
struct nfs_openargs o_arg = { struct nfs_openargs o_arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.share_access = flags & O_ACCMODE, .share_access = flags & (FMODE_READ|FMODE_WRITE),
.clientid = NFS_SERVER(dir)->nfs4_state->cl_clientid, .clientid = NFS_SERVER(dir)->nfs4_state->cl_clientid,
.opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE, .opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE,
.createmode = (flags & O_EXCL) ? NFS4_CREATE_EXCLUSIVE : NFS4_CREATE_UNCHECKED, .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, ...@@ -597,10 +597,11 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags,
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN],
.rpc_argp = &o_arg, .rpc_argp = &o_arg,
.rpc_resp = &o_res, .rpc_resp = &o_res,
.rpc_cred = cred,
}; };
status = -ENOMEM; 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"); dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n");
goto out; goto out;
} }
...@@ -624,11 +625,13 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, ...@@ -624,11 +625,13 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags,
process_cinfo(&d_cinfo, &d_attr); process_cinfo(&d_cinfo, &d_attr);
nfs_refresh_inode(dir, &d_attr); nfs_refresh_inode(dir, &d_attr);
if (fhandle) { status = -ENOMEM;
memset(fhandle, 0, sizeof(*fhandle)); inode = nfs_fhget(dir->i_sb, &o_res.fh, &f_attr);
fhandle->size = (o_res.fh.size < NFS_MAXFHSIZE ? o_res.fh.size : NFS_MAXFHSIZE); if (!inode)
memcpy(fhandle->data, o_res.fh.data, fhandle->size); goto out_up;
} state = nfs4_get_open_state(inode, sp);
if (!state)
goto out_up;
if(o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) { if(o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) {
struct nfs_open_confirmargs oc_arg = { struct nfs_open_confirmargs oc_arg = {
...@@ -642,6 +645,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, ...@@ -642,6 +645,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags,
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM],
.rpc_argp = &oc_arg, .rpc_argp = &oc_arg,
.rpc_resp = &oc_res, .rpc_resp = &oc_res,
.rpc_cred = cred,
}; };
memcpy(oc_arg.stateid, o_res.stateid, sizeof(nfs4_stateid)); 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, ...@@ -649,22 +653,32 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags,
if (status) if (status)
goto out_up; goto out_up;
nfs4_increment_seqid(status, sp); 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 } else
memcpy(sp->so_stateid, o_res.stateid, sizeof(nfs4_stateid)); memcpy(state->stateid, o_res.stateid, sizeof(state->stateid));
sp->so_flags = flags & O_ACCMODE; 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: out_up:
up(&sp->so_sema); up(&sp->so_sema);
nfs4_put_state_owner(sp);
if (state)
nfs4_put_open_state(state);
if (inode)
iput(inode);
out: out:
*spp = sp; return ERR_PTR(status);
return status;
} }
int int
nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
struct nfs_fh *fhandle, struct iattr *sattr, struct nfs_fh *fhandle, struct iattr *sattr,
struct nfs4_state_owner *sp) struct nfs4_state *state)
{ {
struct nfs4_getattr getattr = { struct nfs4_getattr getattr = {
.gt_bmval = nfs4_fattr_bitmap, .gt_bmval = nfs4_fattr_bitmap,
...@@ -688,10 +702,10 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, ...@@ -688,10 +702,10 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
fattr->valid = 0; fattr->valid = 0;
if (sp) if (state)
memcpy(arg.stateid, sp->so_stateid, sizeof(nfs4_stateid)); memcpy(arg.stateid, state->stateid, sizeof(arg.stateid));
else 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)); return(rpc_call_sync(server->client, &msg, 0));
} }
...@@ -704,10 +718,13 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, ...@@ -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 * 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 * share state even when we are done with one of the three share
* stateid's in the inode. * stateid's in the inode.
*
* NOTE: Caller must be holding the sp->so_owner semaphore!
*/ */
int 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; int status = 0;
struct nfs_closeargs arg = { struct nfs_closeargs arg = {
.fh = NFS_FH(inode), .fh = NFS_FH(inode),
...@@ -721,9 +738,8 @@ nfs4_do_close(struct inode *inode, struct nfs4_state_owner *sp) ...@@ -721,9 +738,8 @@ nfs4_do_close(struct inode *inode, struct nfs4_state_owner *sp)
.rpc_resp = &res, .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 */ /* Serialization for the sequence id */
down(&sp->so_sema);
arg.seqid = sp->so_seqid, arg.seqid = sp->so_seqid,
status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0); 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) ...@@ -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 * the state_owner. we keep this around to process errors
*/ */
nfs4_increment_seqid(status, sp); nfs4_increment_seqid(status, sp);
up(&sp->so_sema);
return status; return status;
} }
...@@ -837,7 +852,6 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -837,7 +852,6 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
out_unlock: out_unlock:
up_write(&clp->cl_sem); up_write(&clp->cl_sem);
nfs4_put_client(clp); nfs4_put_client(clp);
server->nfs4_state = NULL;
return status; return status;
} }
...@@ -878,31 +892,29 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, ...@@ -878,31 +892,29 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
{ {
struct inode * inode = dentry->d_inode; struct inode * inode = dentry->d_inode;
int size_change = sattr->ia_valid & ATTR_SIZE; int size_change = sattr->ia_valid & ATTR_SIZE;
struct nfs4_state_owner *sp = NULL; struct nfs4_state *state = NULL;
int status; int status;
fattr->valid = 0; fattr->valid = 0;
if (size_change) { if (size_change) {
if (NFS_I(inode)->wo_owner) { struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
/* file is already open for O_WRONLY */ state = nfs4_do_open(dentry->d_parent->d_inode,
sp = NFS_I(inode)->wo_owner; &dentry->d_name, FMODE_WRITE, NULL, cred);
goto no_open; put_rpccred(cred);
} if (IS_ERR(state))
status = nfs4_do_open(dentry->d_parent->d_inode, return PTR_ERR(state);
&dentry->d_name, O_WRONLY, NULL, fattr,
NULL, &sp); if (state->inode != inode) {
if (status)
return status;
if (fattr->fileid != NFS_FILEID(inode)) {
printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n"); printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n");
nfs4_put_open_state(state);
return -EIO; return -EIO;
} }
} }
no_open:
status = nfs4_do_setattr(NFS_SERVER(inode), fattr, 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; return status;
} }
...@@ -1017,14 +1029,13 @@ nfs4_proc_readlink(struct inode *inode, struct page *page) ...@@ -1017,14 +1029,13 @@ nfs4_proc_readlink(struct inode *inode, struct page *page)
} }
static int 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; int flags = rdata->flags;
struct inode *inode = rdata->inode; struct inode *inode = rdata->inode;
struct nfs_fattr *fattr = rdata->res.fattr; struct nfs_fattr *fattr = rdata->res.fattr;
nfs4_stateid *stateid = &rdata->args.stateid; nfs4_stateid *stateid = &rdata->args.stateid;
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_state_owner *sp;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ],
.rpc_argp = &rdata->args, .rpc_argp = &rdata->args,
...@@ -1040,13 +1051,12 @@ nfs4_proc_read(struct nfs_read_data *rdata) ...@@ -1040,13 +1051,12 @@ nfs4_proc_read(struct nfs_read_data *rdata)
/* /*
* Try first to use O_RDONLY, then O_RDWR stateid. * Try first to use O_RDONLY, then O_RDWR stateid.
*/ */
sp = nfs4_get_inode_share(inode, O_RDONLY); if (filp) {
if (!sp) struct nfs4_state *state;
sp = nfs4_get_inode_share(inode, O_RDWR); state = (struct nfs4_state *)filp->private_data;
if (sp) memcpy(stateid, state->stateid, sizeof(stateid));
memcpy(stateid, sp->so_stateid, sizeof(nfs4_stateid)); } else
else memcpy(stateid, zero_stateid, sizeof(stateid));
memcpy(stateid, zero_stateid, sizeof(nfs4_stateid));
fattr->valid = 0; fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, flags); status = rpc_call_sync(server->client, &msg, flags);
...@@ -1061,14 +1071,13 @@ nfs4_proc_read(struct nfs_read_data *rdata) ...@@ -1061,14 +1071,13 @@ nfs4_proc_read(struct nfs_read_data *rdata)
} }
static int 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; int rpcflags = wdata->flags;
struct inode *inode = wdata->inode; struct inode *inode = wdata->inode;
struct nfs_fattr *fattr = wdata->res.fattr; struct nfs_fattr *fattr = wdata->res.fattr;
nfs4_stateid *stateid = &wdata->args.stateid; nfs4_stateid *stateid = &wdata->args.stateid;
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_state_owner *sp;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE],
.rpc_argp = &wdata->args, .rpc_argp = &wdata->args,
...@@ -1083,14 +1092,12 @@ nfs4_proc_write(struct nfs_write_data *wdata) ...@@ -1083,14 +1092,12 @@ nfs4_proc_write(struct nfs_write_data *wdata)
/* /*
* Try first to use O_WRONLY, then O_RDWR stateid. * Try first to use O_WRONLY, then O_RDWR stateid.
*/ */
sp = nfs4_get_inode_share(inode, O_WRONLY); if (filp) {
if (!sp) struct nfs4_state *state;
sp = nfs4_get_inode_share(inode, O_RDWR); state = (struct nfs4_state *)filp->private_data;
memcpy(stateid, state->stateid, sizeof(stateid));
if (sp) } else
memcpy(stateid, sp->so_stateid, sizeof(nfs4_stateid)); memcpy(stateid, zero_stateid, sizeof(stateid));
else
memcpy(stateid, zero_stateid, sizeof(nfs4_stateid));
fattr->valid = 0; fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, rpcflags); status = rpc_call_sync(server->client, &msg, rpcflags);
...@@ -1113,24 +1120,33 @@ nfs4_proc_write(struct nfs_write_data *wdata) ...@@ -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. * 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, 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 inode *inode;
struct nfs4_state_owner *sp = NULL; struct nfs4_state *state = NULL;
int status; struct rpc_cred *cred;
oflags = O_RDONLY | O_CREAT | (flags & O_EXCL); cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
status = nfs4_do_open(dir, name, oflags, sattr, fattr, fhandle, &sp); state = nfs4_do_open(dir, name, flags, sattr, cred);
if (!status) { put_rpccred(cred);
if (!IS_ERR(state)) {
inode = igrab(state->inode);
if (flags & O_EXCL) { if (flags & O_EXCL) {
status = nfs4_do_setattr(NFS_SERVER(dir), fattr, struct nfs_fattr fattr;
fhandle, sattr, sp); int status;
/* XXX should i bother closing the file? */ 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 static int
...@@ -1447,7 +1463,6 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) ...@@ -1447,7 +1463,6 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count)
}; };
struct inode *inode = data->inode; struct inode *inode = data->inode;
struct nfs_page *req = nfs_list_entry(data->pages.next); struct nfs_page *req = nfs_list_entry(data->pages.next);
struct nfs4_state_owner *sp;
int flags; int flags;
data->args.fh = NFS_FH(inode); data->args.fh = NFS_FH(inode);
...@@ -1460,18 +1475,10 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) ...@@ -1460,18 +1475,10 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count)
data->res.eof = 0; data->res.eof = 0;
data->timestamp = jiffies; data->timestamp = jiffies;
if(req->wb_file) { if (req->wb_state)
unsigned int oflags = req->wb_file->f_flags; memcpy(data->args.stateid, req->wb_state->stateid, sizeof(data->args.stateid));
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));
else 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 */ /* N.B. Do we need to test? Never called for swapfile inode */
flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); 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) ...@@ -1525,7 +1532,6 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
}; };
struct inode *inode = data->inode; struct inode *inode = data->inode;
struct nfs_page *req = nfs_list_entry(data->pages.next); struct nfs_page *req = nfs_list_entry(data->pages.next);
struct nfs4_state_owner *sp;
int stable; int stable;
int flags; int flags;
...@@ -1548,19 +1554,10 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) ...@@ -1548,19 +1554,10 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
data->res.verf = &data->verf; data->res.verf = &data->verf;
data->timestamp = jiffies; data->timestamp = jiffies;
if(req->wb_file) { if (req->wb_state)
unsigned int oflags = req->wb_file->f_flags; memcpy(data->args.stateid, req->wb_state->stateid, sizeof(data->args.stateid));
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));
else 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. */ /* Set the initial flags for the task. */
flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
...@@ -1674,48 +1671,99 @@ nfs4_proc_renew(struct nfs_server *server) ...@@ -1674,48 +1671,99 @@ nfs4_proc_renew(struct nfs_server *server)
* due to the lookup, potential create, and open VFS calls from sys_open() * due to the lookup, potential create, and open VFS calls from sys_open()
* placed on the wire. * placed on the wire.
*/ */
int static int
nfs4_proc_file_open(struct inode *inode, struct file *filp) nfs4_proc_file_open(struct inode *inode, struct file *filp)
{ {
struct dentry *dentry = filp->f_dentry; struct dentry *dentry = filp->f_dentry;
struct inode *dir = dentry->d_parent->d_inode; 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", dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n",
(int)dentry->d_parent->d_name.len, (int)dentry->d_parent->d_name.len,
dentry->d_parent->d_name.name, dentry->d_parent->d_name.name,
(int)dentry->d_name.len, dentry->d_name.name); (int)dentry->d_name.len, dentry->d_name.name);
lock_kernel(); if ((flags + 1) & O_ACCMODE)
flags++;
/* isn't this done in open_namei? */
if (!S_ISREG(inode->i_mode)) {
status = -EISDIR;
goto out;
}
flags = filp->f_flags & O_ACCMODE; lock_kernel();
/* /*
* Got race??
* We have already opened the file "O_EXCL" in nfs4_proc_create!! * We have already opened the file "O_EXCL" in nfs4_proc_create!!
* This ugliness will go away with lookup-intent... * This ugliness will go away with lookup-intent...
*/ */
while (!nfs4_get_inode_share(inode, flags)) { cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
struct nfs4_state_owner *sp = NULL; state = nfs4_do_open(dir, &dentry->d_name, flags, NULL, cred);
status = nfs4_do_open(dir, &dentry->d_name, flags, NULL, NULL, NULL, &sp); if (IS_ERR(state)) {
if (status) { status = PTR_ERR(state);
nfs4_put_state_owner(inode,sp); state = NULL;
break; } else if (filp->f_mode & FMODE_WRITE)
} nfs_set_mmcred(inode, cred);
if (nfs4_set_inode_share(inode, sp, flags)) if (inode != filp->f_dentry->d_inode) {
nfs4_put_state_owner(inode,sp); 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(); unlock_kernel();
return status; 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 = { struct nfs_rpc_ops nfs_v4_clientops = {
.version = 4, /* protocol version */ .version = 4, /* protocol version */
...@@ -1747,6 +1795,9 @@ struct nfs_rpc_ops nfs_v4_clientops = { ...@@ -1747,6 +1795,9 @@ struct nfs_rpc_ops nfs_v4_clientops = {
.write_setup = nfs4_proc_write_setup, .write_setup = nfs4_proc_write_setup,
.commit_setup = nfs4_proc_commit_setup, .commit_setup = nfs4_proc_commit_setup,
.file_open = nfs4_proc_file_open, .file_open = nfs4_proc_file_open,
.file_release = nfs4_proc_file_release,
.request_init = nfs4_request_init,
.request_compatible = nfs4_request_compatible,
}; };
/* /*
......
...@@ -42,7 +42,8 @@ ...@@ -42,7 +42,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
/* This protects most of the client-side state. */ #define OPENOWNER_POOL_SIZE 8
static spinlock_t state_spinlock = SPIN_LOCK_UNLOCKED; static spinlock_t state_spinlock = SPIN_LOCK_UNLOCKED;
nfs4_stateid zero_stateid = nfs4_stateid zero_stateid =
...@@ -60,7 +61,7 @@ static LIST_HEAD(nfs4_clientid_list); ...@@ -60,7 +61,7 @@ static LIST_HEAD(nfs4_clientid_list);
* Since these are allocated/deallocated very rarely, we don't * Since these are allocated/deallocated very rarely, we don't
* bother putting them in a slab cache... * bother putting them in a slab cache...
*/ */
struct nfs4_client * static struct nfs4_client *
nfs4_alloc_client(struct in_addr *addr) nfs4_alloc_client(struct in_addr *addr)
{ {
struct nfs4_client *clp; struct nfs4_client *clp;
...@@ -70,6 +71,7 @@ nfs4_alloc_client(struct in_addr *addr) ...@@ -70,6 +71,7 @@ nfs4_alloc_client(struct in_addr *addr)
memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr)); memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
init_rwsem(&clp->cl_sem); init_rwsem(&clp->cl_sem);
INIT_LIST_HEAD(&clp->cl_state_owners); INIT_LIST_HEAD(&clp->cl_state_owners);
INIT_LIST_HEAD(&clp->cl_unused);
spin_lock_init(&clp->cl_lock); spin_lock_init(&clp->cl_lock);
atomic_set(&clp->cl_count, 1); atomic_set(&clp->cl_count, 1);
clp->cl_state = NFS4CLNT_NEW; clp->cl_state = NFS4CLNT_NEW;
...@@ -77,10 +79,19 @@ nfs4_alloc_client(struct in_addr *addr) ...@@ -77,10 +79,19 @@ nfs4_alloc_client(struct in_addr *addr)
return clp; return clp;
} }
void static void
nfs4_free_client(struct nfs4_client *clp) 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); kfree(clp);
} }
...@@ -120,149 +131,224 @@ nfs4_put_client(struct nfs4_client *clp) ...@@ -120,149 +131,224 @@ nfs4_put_client(struct nfs4_client *clp)
static inline u32 static inline u32
nfs4_alloc_lockowner_id(struct nfs4_client *clp) nfs4_alloc_lockowner_id(struct nfs4_client *clp)
{ {
u32 res; return clp->cl_lockowner_id ++;
}
spin_lock(&state_spinlock); static struct nfs4_state_owner *
res = clp->cl_lockowner_id ++; nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred)
spin_unlock(&state_spinlock); {
return res; 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 * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to
* obtain a new state_owner. * create 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.
* *
*/ */
struct nfs4_state_owner * static struct nfs4_state_owner *
nfs4_get_state_owner(struct inode *dir) nfs4_alloc_state_owner(void)
{ {
struct nfs4_client *clp;
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
sp = kmalloc(sizeof(*sp),GFP_KERNEL); sp = kmalloc(sizeof(*sp),GFP_KERNEL);
if (!sp) if (!sp)
return NULL; return NULL;
clp = (NFS_SB(dir->i_sb))->nfs4_state;
BUG_ON(!clp);
init_MUTEX(&sp->so_sema); init_MUTEX(&sp->so_sema);
sp->so_seqid = 0; /* arbitrary */ sp->so_seqid = 0; /* arbitrary */
memset(sp->so_stateid, 0, sizeof(nfs4_stateid)); INIT_LIST_HEAD(&sp->so_states);
sp->so_id = nfs4_alloc_lockowner_id(clp); atomic_set(&sp->so_count, 1);
return sp; return sp;
} }
/* struct nfs4_state_owner *
* Called for each non-null inode state_owner in nfs_clear_inode, nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
* or if nfs4_do_open fails.
*/
void
nfs4_put_state_owner(struct inode *inode, struct nfs4_state_owner *sp)
{ {
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) if (!sp)
put_rpccred(cred);
return sp;
}
void
nfs4_put_state_owner(struct nfs4_state_owner *sp)
{
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; return;
if (sp->so_flags & O_ACCMODE) out_free:
nfs4_do_close(inode, sp); list_del(&sp->so_list);
spin_unlock(&clp->cl_lock);
put_rpccred(cred);
kfree(sp); kfree(sp);
} }
/* static struct nfs4_state *
* Called with sp->so_sema held. nfs4_alloc_open_state(void)
*
* 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)) struct nfs4_state *state;
sp->so_seqid++;
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 *
* Called by nfs4_proc_open to set the appropriate stateid __nfs4_find_state_bypid(struct inode *inode, pid_t pid)
*/
int
nfs4_set_inode_share(struct inode * inode, struct nfs4_state_owner *sp, unsigned int open_flags)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs4_state *state;
switch (open_flags & O_ACCMODE) { list_for_each_entry(state, &nfsi->open_states, inode_states) {
case O_RDONLY: if (state->pid == pid) {
if (!nfsi->ro_owner) { atomic_inc(&state->count);
nfsi->ro_owner = sp; return state;
return 0;
} }
break;
case O_WRONLY:
if (!nfsi->wo_owner) {
nfsi->wo_owner = sp;
return 0;
} }
break; return NULL;
case O_RDWR: }
if (!nfsi->rw_owner) {
nfsi->rw_owner = sp; static struct nfs4_state *
return 0; __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs4_state *state;
list_for_each_entry(state, &nfsi->open_states, inode_states) {
if (state->owner == owner) {
atomic_inc(&state->count);
return state;
} }
} }
return -EBUSY; return NULL;
} }
/* struct nfs4_state *
* Boolean test to determine if an OPEN call goes on the wire. nfs4_find_state_bypid(struct inode *inode, pid_t pid)
*
* Called by nfs4_proc_open.
*/
int
nfs4_test_state_owner(struct inode *inode, unsigned int open_flags)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs4_state *state;
switch (open_flags & O_ACCMODE) { spin_lock(&inode->i_lock);
case O_RDONLY: state = __nfs4_find_state_bypid(inode, pid);
if(nfsi->ro_owner) /* Add the state to the tail of the inode's list */
return 0; if (state)
break; list_move_tail(&state->inode_states, &nfsi->open_states);
case O_WRONLY: spin_unlock(&inode->i_lock);
if(nfsi->wo_owner) return state;
return 0;
break;
case O_RDWR:
if(nfsi->rw_owner)
return 0;
}
return 1;
} }
struct nfs4_state_owner * static void
nfs4_get_inode_share(struct inode * inode, unsigned int open_flags) 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); struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) { spin_lock(&inode->i_lock);
case O_RDONLY: state = __nfs4_find_state_byowner(inode, owner);
return nfsi->ro_owner; spin_unlock(&inode->i_lock);
case O_WRONLY: if (state)
return nfsi->wo_owner; goto out;
case O_RDWR: new = nfs4_alloc_open_state();
return nfsi->rw_owner; 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... */ out:
return NULL; 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++;
} }
/* /*
......
...@@ -553,14 +553,17 @@ encode_open(struct xdr_stream *xdr, struct nfs_openargs *arg) ...@@ -553,14 +553,17 @@ encode_open(struct xdr_stream *xdr, struct nfs_openargs *arg)
WRITE32(OP_OPEN); WRITE32(OP_OPEN);
WRITE32(arg->seqid); WRITE32(arg->seqid);
switch (arg->share_access) { switch (arg->share_access) {
case O_RDONLY: case FMODE_READ:
WRITE32(NFS4_SHARE_ACCESS_READ); WRITE32(NFS4_SHARE_ACCESS_READ);
break; break;
case O_WRONLY: case FMODE_WRITE:
WRITE32(NFS4_SHARE_ACCESS_WRITE); WRITE32(NFS4_SHARE_ACCESS_WRITE);
break; break;
case O_RDWR: case FMODE_READ|FMODE_WRITE:
WRITE32(NFS4_SHARE_ACCESS_BOTH); WRITE32(NFS4_SHARE_ACCESS_BOTH);
break;
default:
BUG();
} }
WRITE32(0); /* for linux, share_deny = 0 always */ WRITE32(0); /* for linux, share_deny = 0 always */
WRITE64(arg->clientid); WRITE64(arg->clientid);
......
...@@ -49,7 +49,7 @@ nfs_page_free(struct nfs_page *p) ...@@ -49,7 +49,7 @@ nfs_page_free(struct nfs_page *p)
/** /**
* nfs_create_request - Create an NFS read/write request. * 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 * @inode: inode to which the request is attached
* @page: page to write * @page: page to write
* @offset: starting offset within the page for the write * @offset: starting offset within the page for the write
...@@ -62,7 +62,7 @@ nfs_page_free(struct nfs_page *p) ...@@ -62,7 +62,7 @@ nfs_page_free(struct nfs_page *p)
* User should ensure it is safe to sleep in this function. * User should ensure it is safe to sleep in this function.
*/ */
struct nfs_page * 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, struct page *page,
unsigned int offset, unsigned int count) unsigned int offset, unsigned int count)
{ {
...@@ -93,11 +93,9 @@ nfs_create_request(struct rpc_cred *cred, struct inode *inode, ...@@ -93,11 +93,9 @@ nfs_create_request(struct rpc_cred *cred, struct inode *inode,
req->wb_offset = offset; req->wb_offset = offset;
req->wb_pgbase = offset; req->wb_pgbase = offset;
req->wb_bytes = count; req->wb_bytes = count;
if (cred)
req->wb_cred = get_rpccred(cred);
req->wb_inode = inode; req->wb_inode = inode;
req->wb_count = 1; req->wb_count = 1;
server->rpc_ops->request_init(req, file);
return req; return req;
} }
...@@ -111,6 +109,8 @@ nfs_create_request(struct rpc_cred *cred, struct inode *inode, ...@@ -111,6 +109,8 @@ nfs_create_request(struct rpc_cred *cred, struct inode *inode,
*/ */
void nfs_clear_request(struct nfs_page *req) void nfs_clear_request(struct nfs_page *req)
{ {
if (req->wb_state)
req->wb_state = NULL;
/* Release struct file or cached credential */ /* Release struct file or cached credential */
if (req->wb_file) { if (req->wb_file) {
fput(req->wb_file); fput(req->wb_file);
......
...@@ -60,6 +60,18 @@ nfs_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -60,6 +60,18 @@ nfs_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
nfs_refresh_inode(inode, 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. * Bare-bones access to getattr: this is for nfs_read_super.
*/ */
...@@ -149,7 +161,7 @@ nfs_proc_readlink(struct inode *inode, struct page *page) ...@@ -149,7 +161,7 @@ nfs_proc_readlink(struct inode *inode, struct page *page)
} }
static int 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; int flags = rdata->flags;
struct inode * inode = rdata->inode; struct inode * inode = rdata->inode;
...@@ -158,13 +170,13 @@ nfs_proc_read(struct nfs_read_data *rdata) ...@@ -158,13 +170,13 @@ nfs_proc_read(struct nfs_read_data *rdata)
.rpc_proc = &nfs_procedures[NFSPROC_READ], .rpc_proc = &nfs_procedures[NFSPROC_READ],
.rpc_argp = &rdata->args, .rpc_argp = &rdata->args,
.rpc_resp = &rdata->res, .rpc_resp = &rdata->res,
.rpc_cred = rdata->cred,
}; };
int status; int status;
dprintk("NFS call read %d @ %Ld\n", rdata->args.count, dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset); (long long) rdata->args.offset);
fattr->valid = 0; fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) if (status >= 0)
...@@ -174,7 +186,7 @@ nfs_proc_read(struct nfs_read_data *rdata) ...@@ -174,7 +186,7 @@ nfs_proc_read(struct nfs_read_data *rdata)
} }
static int 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; int flags = wdata->flags;
struct inode * inode = wdata->inode; struct inode * inode = wdata->inode;
...@@ -183,13 +195,13 @@ nfs_proc_write(struct nfs_write_data *wdata) ...@@ -183,13 +195,13 @@ nfs_proc_write(struct nfs_write_data *wdata)
.rpc_proc = &nfs_procedures[NFSPROC_WRITE], .rpc_proc = &nfs_procedures[NFSPROC_WRITE],
.rpc_argp = &wdata->args, .rpc_argp = &wdata->args,
.rpc_resp = &wdata->res, .rpc_resp = &wdata->res,
.rpc_cred = wdata->cred
}; };
int status; int status;
dprintk("NFS call write %d @ %Ld\n", wdata->args.count, dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset); (long long) wdata->args.offset);
fattr->valid = 0; fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) { if (status >= 0) {
nfs_write_refresh_inode(inode, fattr); nfs_write_refresh_inode(inode, fattr);
...@@ -200,10 +212,12 @@ nfs_proc_write(struct nfs_write_data *wdata) ...@@ -200,10 +212,12 @@ nfs_proc_write(struct nfs_write_data *wdata)
return status < 0? status : wdata->res.count; return status < 0? status : wdata->res.count;
} }
static int static struct inode *
nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, 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 = { struct nfs_createargs arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.name = name->name, .name = name->name,
...@@ -211,16 +225,23 @@ nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, ...@@ -211,16 +225,23 @@ nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
.sattr = sattr .sattr = sattr
}; };
struct nfs_diropok res = { struct nfs_diropok res = {
.fh = fhandle, .fh = &fhandle,
.fattr = fattr .fattr = &fattr
}; };
int status; int status;
fattr->valid = 0; fattr.valid = 0;
dprintk("NFS call create %s\n", name->name); dprintk("NFS call create %s\n", name->name);
status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0); status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
dprintk("NFS reply create: %d\n", status); 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) ...@@ -611,6 +632,28 @@ nfs_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how)
BUG(); 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 = { struct nfs_rpc_ops nfs_v2_clientops = {
.version = 2, /* protocol version */ .version = 2, /* protocol version */
.getroot = nfs_proc_get_root, .getroot = nfs_proc_get_root,
...@@ -640,4 +683,8 @@ struct nfs_rpc_ops nfs_v2_clientops = { ...@@ -640,4 +683,8 @@ struct nfs_rpc_ops nfs_v2_clientops = {
.read_setup = nfs_proc_read_setup, .read_setup = nfs_proc_read_setup,
.write_setup = nfs_proc_write_setup, .write_setup = nfs_proc_write_setup,
.commit_setup = nfs_proc_commit_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,
}; };
...@@ -89,9 +89,6 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) ...@@ -89,9 +89,6 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
dprintk("NFS: nfs_readpage_sync(%p)\n", 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 * This works now because the socket layer never tries to DMA
* into this buffer directly. * into this buffer directly.
...@@ -110,7 +107,7 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) ...@@ -110,7 +107,7 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
rdata.args.count); rdata.args.count);
lock_kernel(); lock_kernel();
result = NFS_PROTO(inode)->read(&rdata); result = NFS_PROTO(inode)->read(&rdata, file);
unlock_kernel(); unlock_kernel();
/* /*
...@@ -146,7 +143,7 @@ nfs_readpage_async(struct file *file, struct inode *inode, struct page *page) ...@@ -146,7 +143,7 @@ nfs_readpage_async(struct file *file, struct inode *inode, struct page *page)
LIST_HEAD(one_request); LIST_HEAD(one_request);
struct nfs_page *new; 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)) { if (IS_ERR(new)) {
unlock_page(page); unlock_page(page);
return PTR_ERR(new); return PTR_ERR(new);
...@@ -363,8 +360,7 @@ readpage_async_filler(void *data, struct page *page) ...@@ -363,8 +360,7 @@ readpage_async_filler(void *data, struct page *page)
struct nfs_page *new; struct nfs_page *new;
nfs_wb_page(inode, page); nfs_wb_page(inode, page);
new = nfs_create_request(nfs_file_cred(desc->filp), inode, page, new = nfs_create_request(desc->filp, inode, page, 0, PAGE_CACHE_SIZE);
0, PAGE_CACHE_SIZE);
if (IS_ERR(new)) { if (IS_ERR(new)) {
SetPageError(page); SetPageError(page);
unlock_page(page); unlock_page(page);
......
...@@ -152,11 +152,6 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, ...@@ -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", dprintk("NFS: nfs_writepage_sync(%s/%Ld %d@%Ld)\n",
inode->i_sb->s_id, inode->i_sb->s_id,
(long long)NFS_FILEID(inode), (long long)NFS_FILEID(inode),
...@@ -167,7 +162,7 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, ...@@ -167,7 +162,7 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page,
wdata.args.count = count; wdata.args.count = count;
wdata.args.offset = page_offset(page) + wdata.args.pgbase; 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) { if (result < 0) {
/* Must mark the page invalid after I/O error */ /* 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, ...@@ -213,8 +208,6 @@ nfs_writepage_async(struct file *file, struct inode *inode, struct page *page,
status = (IS_ERR(req)) ? PTR_ERR(req) : 0; status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
if (status < 0) if (status < 0)
goto out; goto out;
if (!req->wb_cred)
req->wb_cred = get_rpccred(NFS_I(inode)->mm_cred);
nfs_unlock_request(req); nfs_unlock_request(req);
nfs_strategy(inode); nfs_strategy(inode);
end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count); end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
...@@ -556,7 +549,7 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, ...@@ -556,7 +549,7 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
} }
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfs_wreq_lock);
new = nfs_create_request(nfs_file_cred(file), inode, page, offset, bytes); new = nfs_create_request(file, inode, page, offset, bytes);
if (IS_ERR(new)) if (IS_ERR(new))
return new; return new;
if (file) { if (file) {
...@@ -637,7 +630,6 @@ int ...@@ -637,7 +630,6 @@ int
nfs_flush_incompatible(struct file *file, struct page *page) nfs_flush_incompatible(struct file *file, struct page *page)
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct rpc_cred *cred = nfs_file_cred(file);
struct nfs_page *req; struct nfs_page *req;
int status = 0; int status = 0;
/* /*
...@@ -650,7 +642,7 @@ nfs_flush_incompatible(struct file *file, struct page *page) ...@@ -650,7 +642,7 @@ nfs_flush_incompatible(struct file *file, struct page *page)
*/ */
req = nfs_find_request(inode, page->index); req = nfs_find_request(inode, page->index);
if (req) { 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); status = nfs_wb_page(inode, page);
nfs_release_request(req); nfs_release_request(req);
} }
......
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
#include <linux/nfs2.h> #include <linux/nfs2.h>
#include <linux/nfs3.h> #include <linux/nfs3.h>
#include <linux/nfs4.h> #include <linux/nfs4.h>
#include <linux/nfs_page.h>
#include <linux/nfs_xdr.h> #include <linux/nfs_xdr.h>
/* /*
...@@ -158,9 +157,7 @@ struct nfs_inode { ...@@ -158,9 +157,7 @@ struct nfs_inode {
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
/* NFSv4 state */ /* NFSv4 state */
struct nfs4_state_owner *ro_owner; struct list_head open_states;
struct nfs4_state_owner *wo_owner;
struct nfs4_state_owner *rw_owner;
#endif /* CONFIG_NFS_V4*/ #endif /* CONFIG_NFS_V4*/
struct inode vfs_inode; struct inode vfs_inode;
...@@ -229,12 +226,6 @@ loff_t page_offset(struct page *page) ...@@ -229,12 +226,6 @@ loff_t page_offset(struct page *page)
return ((loff_t)page->index) << PAGE_CACHE_SHIFT; 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 * linux/fs/nfs/inode.c
*/ */
...@@ -244,6 +235,7 @@ extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *, ...@@ -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_refresh_inode(struct inode *, struct nfs_fattr *);
extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int nfs_permission(struct inode *, int, struct nameidata *); 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_open(struct inode *, struct file *);
extern int nfs_release(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *);
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
...@@ -494,29 +486,60 @@ struct nfs4_client { ...@@ -494,29 +486,60 @@ struct nfs4_client {
struct rw_semaphore cl_sem; struct rw_semaphore cl_sem;
struct list_head cl_state_owners; struct list_head cl_state_owners;
struct list_head cl_unused;
int cl_nunused;
spinlock_t cl_lock; spinlock_t cl_lock;
atomic_t cl_count; atomic_t cl_count;
}; };
/* /*
* The ->so_sema is held during all state_owner seqid-mutating operations: * NFS4 state_owners and lock_owners are simply labels for ordered
* OPEN, OPEN_DOWNGRADE, and CLOSE. * sequences of RPC calls. Their sole purpose is to provide once-only
* Its purpose is to properly serialize so_seqid, as mandated by * semantics by allowing the server to identify replayed requests.
* the protocol. *
*/ * 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 nfs4_state_owner {
struct list_head so_list; /* per-clientid list of state_owners */ struct list_head so_list; /* per-clientid list of state_owners */
struct nfs4_client *so_client;
u32 so_id; /* 32-bit identifier, unique */ u32 so_id; /* 32-bit identifier, unique */
struct semaphore so_sema; struct semaphore so_sema;
u32 so_seqid; /* protected by 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 */ 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 */ /* nfs4proc.c */
extern int nfs4_proc_renew(struct nfs_server *server); 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 */ /* nfs4renewd.c */
extern int nfs4_init_renewd(struct nfs_server *server); extern int nfs4_init_renewd(struct nfs_server *server);
...@@ -524,13 +547,11 @@ extern int nfs4_init_renewd(struct nfs_server *server); ...@@ -524,13 +547,11 @@ extern int nfs4_init_renewd(struct nfs_server *server);
/* nfs4state.c */ /* nfs4state.c */
extern struct nfs4_client *nfs4_get_client(struct in_addr *); extern struct nfs4_client *nfs4_get_client(struct in_addr *);
extern void nfs4_put_client(struct nfs4_client *clp); extern void nfs4_put_client(struct nfs4_client *clp);
extern struct nfs4_state_owner * nfs4_get_state_owner(struct inode *inode); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
void nfs4_put_state_owner(struct inode *inode, struct nfs4_state_owner *sp); extern void nfs4_put_state_owner(struct nfs4_state_owner *);
extern int nfs4_set_inode_share(struct inode * inode, extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
struct nfs4_state_owner *sp, unsigned int flags); extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); 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) ...@@ -561,6 +582,7 @@ destroy_nfsv4_state(struct nfs_server *server)
#define create_nfsv4_state(server, data) 0 #define create_nfsv4_state(server, data) 0
#define destroy_nfsv4_state(server) do { } while (0) #define destroy_nfsv4_state(server) do { } while (0)
#define nfs4_put_state_owner(inode, owner) do { } while (0) #define nfs4_put_state_owner(inode, owner) do { } while (0)
#define nfs4_put_open_state(state) do { } while (0)
#endif #endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/mm.h> #include <linux/pagemap.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/nfs_fs_sb.h> #include <linux/nfs_fs_sb.h>
#include <linux/sunrpc/auth.h> #include <linux/sunrpc/auth.h>
...@@ -28,6 +28,7 @@ struct nfs_page { ...@@ -28,6 +28,7 @@ struct nfs_page {
struct file *wb_file; struct file *wb_file;
struct inode *wb_inode; struct inode *wb_inode;
struct rpc_cred *wb_cred; struct rpc_cred *wb_cred;
struct nfs4_state *wb_state;
struct page *wb_page; /* page to read in/write out */ struct page *wb_page; /* page to read in/write out */
wait_queue_head_t wb_wait; /* wait queue */ wait_queue_head_t wb_wait; /* wait queue */
unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */ unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */
...@@ -41,7 +42,7 @@ struct nfs_page { ...@@ -41,7 +42,7 @@ struct nfs_page {
#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) #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 *, struct page *,
unsigned int, unsigned int); unsigned int, unsigned int);
extern void nfs_clear_request(struct nfs_page *req); extern void nfs_clear_request(struct nfs_page *req);
...@@ -121,4 +122,10 @@ nfs_list_entry(struct list_head *head) ...@@ -121,4 +122,10 @@ nfs_list_entry(struct list_head *head)
return list_entry(head, struct nfs_page, wb_list); 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 */ #endif /* _LINUX_NFS_PAGE_H */
...@@ -620,6 +620,8 @@ struct nfs_write_data { ...@@ -620,6 +620,8 @@ struct nfs_write_data {
#endif #endif
}; };
struct nfs_page;
/* /*
* RPC procedure vector for NFSv2/NFSv3 demuxing * RPC procedure vector for NFSv2/NFSv3 demuxing
*/ */
...@@ -635,12 +637,12 @@ struct nfs_rpc_ops { ...@@ -635,12 +637,12 @@ struct nfs_rpc_ops {
struct nfs_fh *, struct nfs_fattr *); struct nfs_fh *, struct nfs_fattr *);
int (*access) (struct inode *, struct rpc_cred *, int); int (*access) (struct inode *, struct rpc_cred *, int);
int (*readlink)(struct inode *, struct page *); int (*readlink)(struct inode *, struct page *);
int (*read) (struct nfs_read_data *); int (*read) (struct nfs_read_data *, struct file *);
int (*write) (struct nfs_write_data *); int (*write) (struct nfs_write_data *, struct file *);
int (*commit) (struct inode *, struct nfs_fattr *, int (*commit) (struct inode *, struct nfs_fattr *,
unsigned long, unsigned int); unsigned long, unsigned int);
int (*create) (struct inode *, struct qstr *, struct iattr *, struct inode * (*create) (struct inode *, struct qstr *,
int, struct nfs_fh *, struct nfs_fattr *); struct iattr *, int);
int (*remove) (struct inode *, struct qstr *); int (*remove) (struct inode *, struct qstr *);
int (*unlink_setup) (struct rpc_message *, int (*unlink_setup) (struct rpc_message *,
struct dentry *, struct qstr *); struct dentry *, struct qstr *);
...@@ -669,6 +671,9 @@ struct nfs_rpc_ops { ...@@ -669,6 +671,9 @@ struct nfs_rpc_ops {
void (*write_setup) (struct nfs_write_data *, unsigned int count, int how); 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 (*commit_setup) (struct nfs_write_data *, u64 start, u32 len, int how);
int (*file_open) (struct inode *, struct file *); 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 *);
}; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment