diff options
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r-- | fs/nfs/nfs4proc.c | 110 |
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 */ | ||
193 | static 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 | |||
192 | static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) | 207 | static 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 | ||
828 | static 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 | |||
813 | static void nfs4_close_done(struct rpc_task *task) | 842 | static 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 | ||
849 | static inline int nfs4_close_call(struct rpc_clnt *clnt, struct nfs4_closedata *calldata) | 872 | static 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 * | |||
873 | int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode) | 922 | int 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 | 945 | out_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. | 947 | out: |
903 | */ | 948 | return status; |
904 | return (status == 0) ? -EINPROGRESS : status; | ||
905 | } | 949 | } |
906 | 950 | ||
907 | struct inode * | 951 | struct inode * |