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);
struct nameidata *nd)
{
struct iattr attr;
- struct nfs_fattr fattr;
- struct nfs_fh fhandle;
+ struct inode *inode;
int error;
int open_flags = 0;
*/
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;
}
#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);
.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,
/* 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.
*
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
* 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,
#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)
}
}
+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.
*/
}
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;
.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);
}
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;
.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);
* 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),
};
struct nfs3_diropres res = {
.dir_attr = &dir_attr,
- .fh = fhandle,
- .fattr = fattr
+ .fh = &fhandle,
+ .fattr = &fattr
};
int status;
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);
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");
/* 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
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,
.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,
};
}
}
-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 = {
};
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,
};
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,
.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;
}
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 = {
.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));
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,
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));
}
* 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),
.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);
* the state_owner. we keep this around to process errors
*/
nfs4_increment_seqid(status, sp);
- up(&sp->so_sema);
return status;
}
out_unlock:
up_write(&clp->cl_sem);
nfs4_put_client(clp);
- server->nfs4_state = NULL;
return status;
}
{
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;
}
}
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,
/*
* 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);
}
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,
/*
* 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);
* 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
};
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);
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);
};
struct inode *inode = data->inode;
struct nfs_page *req = nfs_list_entry(data->pages.next);
- struct nfs4_state_owner *sp;
int stable;
int flags;
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;
* 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 */
.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,
};
/*
#include <linux/slab.h>
#include <linux/nfs_fs.h>
-/* 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 };
* 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;
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;
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);
}
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++;
}
/*
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);
/**
* 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
* 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)
{
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;
}
*/
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);
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.
*/
}
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;
.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)
}
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;
.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);
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,
.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);
}
/*
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,
.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,
};
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.
rdata.args.count);
lock_kernel();
- result = NFS_PROTO(inode)->read(&rdata);
+ result = NFS_PROTO(inode)->read(&rdata, file);
unlock_kernel();
/*
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);
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);
},
};
- 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),
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 */
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<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
}
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))
return new;
if (file) {
nfs_flush_incompatible(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
- struct rpc_cred *cred = nfs_file_cred(file);
struct nfs_page *req;
int status = 0;
/*
*/
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);
}
#include <linux/nfs2.h>
#include <linux/nfs3.h>
#include <linux/nfs4.h>
-#include <linux/nfs_page.h>
#include <linux/nfs_xdr.h>
/*
#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;
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
*/
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 *);
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);
/* 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);
#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__ */
#include <linux/list.h>
-#include <linux/mm.h>
+#include <linux/pagemap.h>
#include <linux/wait.h>
#include <linux/nfs_fs_sb.h>
#include <linux/sunrpc/auth.h>
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 */
#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);
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
};
+struct nfs_page;
+
/*
* RPC procedure vector for NFSv2/NFSv3 demuxing
*/
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 *);
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 *);
};
/*