aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--fs/nfs/nfs4proc.c110
-rw-r--r--fs/nfs/nfs4state.c6
-rw-r--r--fs/nfs/nfs4xdr.c14
-rw-r--r--include/linux/nfs_xdr.h2
4 files changed, 87 insertions, 45 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 *
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index f535c219cf3a..59c93f37e1b2 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -518,7 +518,11 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
518 newstate |= FMODE_WRITE; 518 newstate |= FMODE_WRITE;
519 if (state->state == newstate) 519 if (state->state == newstate)
520 goto out; 520 goto out;
521 if (nfs4_do_close(inode, state, newstate) == -EINPROGRESS) 521 if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
522 state->state = newstate;
523 goto out;
524 }
525 if (nfs4_do_close(inode, state, newstate) == 0)
522 return; 526 return;
523 } 527 }
524out: 528out:
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index fcd28a29a2f8..934ec50ea6bf 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -602,10 +602,10 @@ static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
602{ 602{
603 uint32_t *p; 603 uint32_t *p;
604 604
605 RESERVE_SPACE(8+sizeof(arg->stateid.data)); 605 RESERVE_SPACE(8+sizeof(arg->stateid->data));
606 WRITE32(OP_CLOSE); 606 WRITE32(OP_CLOSE);
607 WRITE32(arg->seqid->sequence->counter); 607 WRITE32(arg->seqid->sequence->counter);
608 WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data)); 608 WRITEMEM(arg->stateid->data, sizeof(arg->stateid->data));
609 609
610 return 0; 610 return 0;
611} 611}
@@ -950,9 +950,9 @@ static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closea
950{ 950{
951 uint32_t *p; 951 uint32_t *p;
952 952
953 RESERVE_SPACE(8+sizeof(arg->stateid.data)); 953 RESERVE_SPACE(8+sizeof(arg->stateid->data));
954 WRITE32(OP_OPEN_DOWNGRADE); 954 WRITE32(OP_OPEN_DOWNGRADE);
955 WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data)); 955 WRITEMEM(arg->stateid->data, sizeof(arg->stateid->data));
956 WRITE32(arg->seqid->sequence->counter); 956 WRITE32(arg->seqid->sequence->counter);
957 encode_share_access(xdr, arg->open_flags); 957 encode_share_access(xdr, arg->open_flags);
958 return 0; 958 return 0;
@@ -1416,9 +1416,6 @@ static int nfs4_xdr_enc_close(struct rpc_rqst *req, uint32_t *p, struct nfs_clos
1416 }; 1416 };
1417 int status; 1417 int status;
1418 1418
1419 status = nfs_wait_on_sequence(args->seqid, req->rq_task);
1420 if (status != 0)
1421 goto out;
1422 xdr_init_encode(&xdr, &req->rq_snd_buf, p); 1419 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1423 encode_compound_hdr(&xdr, &hdr); 1420 encode_compound_hdr(&xdr, &hdr);
1424 status = encode_putfh(&xdr, args->fh); 1421 status = encode_putfh(&xdr, args->fh);
@@ -1518,9 +1515,6 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, uint32_t *p, struct
1518 }; 1515 };
1519 int status; 1516 int status;
1520 1517
1521 status = nfs_wait_on_sequence(args->seqid, req->rq_task);
1522 if (status != 0)
1523 goto out;
1524 xdr_init_encode(&xdr, &req->rq_snd_buf, p); 1518 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1525 encode_compound_hdr(&xdr, &hdr); 1519 encode_compound_hdr(&xdr, &hdr);
1526 status = encode_putfh(&xdr, args->fh); 1520 status = encode_putfh(&xdr, args->fh);
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index d578912bf9a9..cac0df950c66 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -149,7 +149,7 @@ struct nfs_open_confirmres {
149 */ 149 */
150struct nfs_closeargs { 150struct nfs_closeargs {
151 struct nfs_fh * fh; 151 struct nfs_fh * fh;
152 nfs4_stateid stateid; 152 nfs4_stateid * stateid;
153 struct nfs_seqid * seqid; 153 struct nfs_seqid * seqid;
154 int open_flags; 154 int open_flags;
155}; 155};