]> git.hungrycats.org Git - linux/commitdiff
NFSv4 state model update
authorTrond Myklebust <trond.myklebust@fys.uio.no>
Tue, 7 Oct 2003 16:51:33 +0000 (12:51 -0400)
committerTrond Myklebust <trond.myklebust@fys.uio.no>
Tue, 7 Oct 2003 16:51:33 +0000 (12:51 -0400)
  - 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.

14 files changed:
fs/nfs/dir.c
fs/nfs/file.c
fs/nfs/inode.c
fs/nfs/nfs3proc.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/pagelist.c
fs/nfs/proc.c
fs/nfs/read.c
fs/nfs/write.c
include/linux/nfs_fs.h
include/linux/nfs_page.h
include/linux/nfs_xdr.h

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