aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs4proc.c
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2005-10-18 17:20:12 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-10-18 17:20:12 -0400
commit9512135df14f8293b9bc5e8fb22d4279dee5ff66 (patch)
tree83fb778ed53ba10c46734968f538aa0a57d376a1 /fs/nfs/nfs4proc.c
parentcee54fc944422c44e476736c045a9e8053cb0644 (diff)
NFSv4: Fix a potential CLOSE race
Once the state_owner and lock_owner semaphores get removed, it will be possible for other OPEN requests to reopen the same file if they have lower sequence ids than our CLOSE call. This patch ensures that we recheck the file state once nfs_wait_on_sequence() has completed waiting. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r--fs/nfs/nfs4proc.c110
1 files changed, 77 insertions, 33 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 9ba89e7cdd28..5154ddf6d9a5 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -189,6 +189,21 @@ static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinf
189 nfsi->change_attr = cinfo->after; 189 nfsi->change_attr = cinfo->after;
190} 190}
191 191
192/* Helper for asynchronous RPC calls */
193static int nfs4_call_async(struct rpc_clnt *clnt, rpc_action tk_begin,
194 rpc_action tk_exit, void *calldata)
195{
196 struct rpc_task *task;
197
198 if (!(task = rpc_new_task(clnt, tk_exit, RPC_TASK_ASYNC)))
199 return -ENOMEM;
200
201 task->tk_calldata = calldata;
202 task->tk_action = tk_begin;
203 rpc_execute(task);
204 return 0;
205}
206
192static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) 207static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
193{ 208{
194 struct inode *inode = state->inode; 209 struct inode *inode = state->inode;
@@ -810,11 +825,24 @@ struct nfs4_closedata {
810 struct nfs_closeres res; 825 struct nfs_closeres res;
811}; 826};
812 827
828static void nfs4_free_closedata(struct nfs4_closedata *calldata)
829{
830 struct nfs4_state *state = calldata->state;
831 struct nfs4_state_owner *sp = state->owner;
832 struct nfs_server *server = NFS_SERVER(calldata->inode);
833
834 nfs4_put_open_state(calldata->state);
835 nfs_free_seqid(calldata->arg.seqid);
836 up(&sp->so_sema);
837 nfs4_put_state_owner(sp);
838 up_read(&server->nfs4_state->cl_sem);
839 kfree(calldata);
840}
841
813static void nfs4_close_done(struct rpc_task *task) 842static void nfs4_close_done(struct rpc_task *task)
814{ 843{
815 struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata; 844 struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata;
816 struct nfs4_state *state = calldata->state; 845 struct nfs4_state *state = calldata->state;
817 struct nfs4_state_owner *sp = state->owner;
818 struct nfs_server *server = NFS_SERVER(calldata->inode); 846 struct nfs_server *server = NFS_SERVER(calldata->inode);
819 847
820 /* hmm. we are done with the inode, and in the process of freeing 848 /* hmm. we are done with the inode, and in the process of freeing
@@ -838,25 +866,46 @@ static void nfs4_close_done(struct rpc_task *task)
838 } 866 }
839 } 867 }
840 state->state = calldata->arg.open_flags; 868 state->state = calldata->arg.open_flags;
841 nfs4_put_open_state(state); 869 nfs4_free_closedata(calldata);
842 nfs_free_seqid(calldata->arg.seqid);
843 up(&sp->so_sema);
844 nfs4_put_state_owner(sp);
845 up_read(&server->nfs4_state->cl_sem);
846 kfree(calldata);
847} 870}
848 871
849static inline int nfs4_close_call(struct rpc_clnt *clnt, struct nfs4_closedata *calldata) 872static void nfs4_close_begin(struct rpc_task *task)
850{ 873{
874 struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata;
875 struct nfs4_state *state = calldata->state;
851 struct rpc_message msg = { 876 struct rpc_message msg = {
852 .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE], 877 .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
853 .rpc_argp = &calldata->arg, 878 .rpc_argp = &calldata->arg,
854 .rpc_resp = &calldata->res, 879 .rpc_resp = &calldata->res,
855 .rpc_cred = calldata->state->owner->so_cred, 880 .rpc_cred = state->owner->so_cred,
856 }; 881 };
857 if (calldata->arg.open_flags != 0) 882 int mode = 0;
883 int status;
884
885 status = nfs_wait_on_sequence(calldata->arg.seqid, task);
886 if (status != 0)
887 return;
888 /* Don't reorder reads */
889 smp_rmb();
890 /* Recalculate the new open mode in case someone reopened the file
891 * while we were waiting in line to be scheduled.
892 */
893 if (state->nreaders != 0)
894 mode |= FMODE_READ;
895 if (state->nwriters != 0)
896 mode |= FMODE_WRITE;
897 if (test_bit(NFS_DELEGATED_STATE, &state->flags))
898 state->state = mode;
899 if (mode == state->state) {
900 nfs4_free_closedata(calldata);
901 task->tk_exit = NULL;
902 rpc_exit(task, 0);
903 return;
904 }
905 if (mode != 0)
858 msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; 906 msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
859 return rpc_call_async(clnt, &msg, 0, nfs4_close_done, calldata); 907 calldata->arg.open_flags = mode;
908 rpc_call_setup(task, &msg, 0);
860} 909}
861 910
862/* 911/*
@@ -873,35 +922,30 @@ static inline int nfs4_close_call(struct rpc_clnt *clnt, struct nfs4_closedata *
873int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode) 922int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode)
874{ 923{
875 struct nfs4_closedata *calldata; 924 struct nfs4_closedata *calldata;
876 int status; 925 int status = -ENOMEM;
877 926
878 /* Tell caller we're done */ 927 calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
879 if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
880 state->state = mode;
881 return 0;
882 }
883 calldata = (struct nfs4_closedata *)kmalloc(sizeof(*calldata), GFP_KERNEL);
884 if (calldata == NULL) 928 if (calldata == NULL)
885 return -ENOMEM; 929 goto out;
886 calldata->inode = inode; 930 calldata->inode = inode;
887 calldata->state = state; 931 calldata->state = state;
888 calldata->arg.fh = NFS_FH(inode); 932 calldata->arg.fh = NFS_FH(inode);
933 calldata->arg.stateid = &state->stateid;
889 /* Serialization for the sequence id */ 934 /* Serialization for the sequence id */
890 calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid); 935 calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
891 if (calldata->arg.seqid == NULL) { 936 if (calldata->arg.seqid == NULL)
892 kfree(calldata); 937 goto out_free_calldata;
893 return -ENOMEM; 938
894 } 939 status = nfs4_call_async(NFS_SERVER(inode)->client, nfs4_close_begin,
895 calldata->arg.open_flags = mode; 940 nfs4_close_done, calldata);
896 memcpy(&calldata->arg.stateid, &state->stateid, 941 if (status == 0)
897 sizeof(calldata->arg.stateid)); 942 goto out;
898 status = nfs4_close_call(NFS_SERVER(inode)->client, calldata); 943
899 /* 944 nfs_free_seqid(calldata->arg.seqid);
900 * Return -EINPROGRESS on success in order to indicate to the 945out_free_calldata:
901 * caller that an asynchronous RPC call has been launched, and 946 kfree(calldata);
902 * that it will release the semaphores on completion. 947out:
903 */ 948 return status;
904 return (status == 0) ? -EINPROGRESS : status;
905} 949}
906 950
907struct inode * 951struct inode *