aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2007-07-05 19:02:21 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2007-07-10 23:40:43 -0400
commitaac00a8d0a53097063da532cbdf0b8775a4dcd53 (patch)
tree4a83d1a25dd23d63cbcb0fe5c40b2f857ebcc19e /fs
parent3e309914a15333a5493058e4927e979c7434ae44 (diff)
NFSv4: Check for the existence of a delegation in nfs4_open_prepare()
We should not be calling open() on an inode that has a delegation unless we're doing a reclaim. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfs/nfs4proc.c195
1 files changed, 107 insertions, 88 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 05afb7ba3bc4..ea332e831d75 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -65,6 +65,7 @@ static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *)
65static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); 65static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
66static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception); 66static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
67static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp); 67static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp);
68static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags);
68 69
69/* Prevent leaks of NFSv4 errors into userland */ 70/* Prevent leaks of NFSv4 errors into userland */
70int nfs4_map_errors(int err) 71int nfs4_map_errors(int err)
@@ -224,6 +225,7 @@ struct nfs4_opendata {
224 struct path path; 225 struct path path;
225 struct dentry *dir; 226 struct dentry *dir;
226 struct nfs4_state_owner *owner; 227 struct nfs4_state_owner *owner;
228 struct nfs4_state *state;
227 struct iattr attrs; 229 struct iattr attrs;
228 unsigned long timestamp; 230 unsigned long timestamp;
229 unsigned int rpc_done : 1; 231 unsigned int rpc_done : 1;
@@ -296,6 +298,8 @@ static void nfs4_opendata_free(struct kref *kref)
296 struct nfs4_opendata, kref); 298 struct nfs4_opendata, kref);
297 299
298 nfs_free_seqid(p->o_arg.seqid); 300 nfs_free_seqid(p->o_arg.seqid);
301 if (p->state != NULL)
302 nfs4_put_open_state(p->state);
299 nfs4_put_state_owner(p->owner); 303 nfs4_put_state_owner(p->owner);
300 dput(p->dir); 304 dput(p->dir);
301 dput(p->path.dentry); 305 dput(p->path.dentry);
@@ -320,6 +324,15 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
320 return ret; 324 return ret;
321} 325}
322 326
327static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags)
328{
329 if ((delegation->type & open_flags) != open_flags)
330 return 0;
331 if (delegation->flags & NFS_DELEGATION_NEED_RECLAIM)
332 return 0;
333 return 1;
334}
335
323static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags) 336static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
324{ 337{
325 switch (open_flags) { 338 switch (open_flags) {
@@ -380,6 +393,65 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_sta
380 spin_unlock(&state->owner->so_lock); 393 spin_unlock(&state->owner->so_lock);
381} 394}
382 395
396static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open_flags)
397{
398 struct nfs_delegation *delegation;
399
400 rcu_read_lock();
401 delegation = rcu_dereference(NFS_I(inode)->delegation);
402 if (delegation == NULL || (delegation->type & open_flags) == open_flags) {
403 rcu_read_unlock();
404 return;
405 }
406 rcu_read_unlock();
407 nfs_inode_return_delegation(inode);
408}
409
410static struct nfs4_state *nfs4_try_open_delegated(struct nfs4_opendata *opendata)
411{
412 struct nfs4_state *state = opendata->state;
413 struct nfs_inode *nfsi = NFS_I(state->inode);
414 struct nfs_delegation *delegation;
415 int open_mode = opendata->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL);
416 nfs4_stateid stateid;
417 int ret = -EAGAIN;
418
419 rcu_read_lock();
420 delegation = rcu_dereference(nfsi->delegation);
421 if (delegation == NULL)
422 goto out_unlock;
423 for (;;) {
424 if (!can_open_delegated(delegation, open_mode))
425 break;
426 /* Save the delegation */
427 memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data));
428 rcu_read_unlock();
429 lock_kernel();
430 ret = _nfs4_do_access(state->inode, state->owner->so_cred, open_mode);
431 unlock_kernel();
432 if (ret != 0)
433 goto out;
434 ret = -EAGAIN;
435 rcu_read_lock();
436 delegation = rcu_dereference(nfsi->delegation);
437 if (delegation == NULL)
438 break;
439 /* Is the delegation still valid? */
440 if (memcmp(stateid.data, delegation->stateid.data, sizeof(stateid.data)) != 0)
441 continue;
442 rcu_read_unlock();
443 update_open_stateid(state, NULL, &stateid, open_mode);
444 goto out_return_state;
445 }
446out_unlock:
447 rcu_read_unlock();
448out:
449 return ERR_PTR(ret);
450out_return_state:
451 atomic_inc(&state->count);
452 return state;
453}
454
383static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data) 455static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
384{ 456{
385 struct inode *inode; 457 struct inode *inode;
@@ -388,6 +460,11 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
388 nfs4_stateid *deleg_stateid = NULL; 460 nfs4_stateid *deleg_stateid = NULL;
389 int ret; 461 int ret;
390 462
463 if (!data->rpc_done) {
464 state = nfs4_try_open_delegated(data);
465 goto out;
466 }
467
391 ret = -EAGAIN; 468 ret = -EAGAIN;
392 if (!(data->f_attr.valid & NFS_ATTR_FATTR)) 469 if (!(data->f_attr.valid & NFS_ATTR_FATTR))
393 goto err; 470 goto err;
@@ -423,6 +500,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
423 update_open_stateid(state, &data->o_res.stateid, deleg_stateid, data->o_arg.open_flags); 500 update_open_stateid(state, &data->o_res.stateid, deleg_stateid, data->o_arg.open_flags);
424 rcu_read_unlock(); 501 rcu_read_unlock();
425 iput(inode); 502 iput(inode);
503out:
426 return state; 504 return state;
427err_put_inode: 505err_put_inode:
428 iput(inode); 506 iput(inode);
@@ -690,6 +768,23 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
690 768
691 if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0) 769 if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
692 return; 770 return;
771 /*
772 * Check if we still need to send an OPEN call, or if we can use
773 * a delegation instead.
774 */
775 if (data->state != NULL) {
776 struct nfs_delegation *delegation;
777
778 rcu_read_lock();
779 delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
780 if (delegation != NULL &&
781 (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) {
782 rcu_read_unlock();
783 task->tk_action = NULL;
784 return;
785 }
786 rcu_read_unlock();
787 }
693 /* Update sequence id. */ 788 /* Update sequence id. */
694 data->o_arg.id = sp->so_owner_id.id; 789 data->o_arg.id = sp->so_owner_id.id;
695 data->o_arg.clientid = sp->so_client->cl_clientid; 790 data->o_arg.clientid = sp->so_client->cl_clientid;
@@ -907,90 +1002,6 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
907} 1002}
908 1003
909/* 1004/*
910 * Returns a referenced nfs4_state if there is an open delegation on the file
911 */
912static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res)
913{
914 struct nfs_delegation *delegation;
915 struct nfs_server *server = NFS_SERVER(inode);
916 struct nfs_client *clp = server->nfs_client;
917 struct nfs_inode *nfsi = NFS_I(inode);
918 struct nfs4_state_owner *sp = NULL;
919 struct nfs4_state *state = NULL;
920 int open_flags = flags & (FMODE_READ|FMODE_WRITE);
921 int err;
922
923 err = -ENOMEM;
924 if (!(sp = nfs4_get_state_owner(server, cred))) {
925 dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__);
926 return err;
927 }
928 err = nfs4_recover_expired_lease(server);
929 if (err != 0)
930 goto out_put_state_owner;
931 /* Protect against reboot recovery - NOTE ORDER! */
932 down_read(&clp->cl_sem);
933 /* Protect against delegation recall */
934 down_read(&nfsi->rwsem);
935 delegation = NFS_I(inode)->delegation;
936 err = -ENOENT;
937 if (delegation == NULL || (delegation->type & open_flags) != open_flags)
938 goto out_err;
939 err = -ENOMEM;
940 state = nfs4_get_open_state(inode, sp);
941 if (state == NULL)
942 goto out_err;
943
944 err = -ENOENT;
945 if ((state->state & open_flags) == open_flags) {
946 spin_lock(&inode->i_lock);
947 update_open_stateflags(state, open_flags);
948 spin_unlock(&inode->i_lock);
949 goto out_ok;
950 } else if (state->state != 0)
951 goto out_put_open_state;
952
953 lock_kernel();
954 err = _nfs4_do_access(inode, cred, open_flags);
955 unlock_kernel();
956 if (err != 0)
957 goto out_put_open_state;
958 update_open_stateid(state, NULL, &delegation->stateid, open_flags);
959out_ok:
960 nfs4_put_state_owner(sp);
961 up_read(&nfsi->rwsem);
962 up_read(&clp->cl_sem);
963 *res = state;
964 return 0;
965out_put_open_state:
966 nfs4_put_open_state(state);
967out_err:
968 up_read(&nfsi->rwsem);
969 up_read(&clp->cl_sem);
970 if (err != -EACCES)
971 nfs_inode_return_delegation(inode);
972out_put_state_owner:
973 nfs4_put_state_owner(sp);
974 return err;
975}
976
977static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred)
978{
979 struct nfs4_exception exception = { };
980 struct nfs4_state *res = ERR_PTR(-EIO);
981 int err;
982
983 do {
984 err = _nfs4_open_delegated(inode, flags, cred, &res);
985 if (err == 0)
986 break;
987 res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(inode),
988 err, &exception));
989 } while (exception.retry);
990 return res;
991}
992
993/*
994 * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-* 1005 * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-*
995 * fields corresponding to attributes that were used to store the verifier. 1006 * fields corresponding to attributes that were used to store the verifier.
996 * Make sure we clobber those fields in the later setattr call 1007 * Make sure we clobber those fields in the later setattr call
@@ -1016,7 +1027,7 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
1016 struct nfs_server *server = NFS_SERVER(dir); 1027 struct nfs_server *server = NFS_SERVER(dir);
1017 struct nfs_client *clp = server->nfs_client; 1028 struct nfs_client *clp = server->nfs_client;
1018 struct nfs4_opendata *opendata; 1029 struct nfs4_opendata *opendata;
1019 int status; 1030 int status;
1020 1031
1021 /* Protect against reboot recovery conflicts */ 1032 /* Protect against reboot recovery conflicts */
1022 status = -ENOMEM; 1033 status = -ENOMEM;
@@ -1027,12 +1038,17 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
1027 status = nfs4_recover_expired_lease(server); 1038 status = nfs4_recover_expired_lease(server);
1028 if (status != 0) 1039 if (status != 0)
1029 goto err_put_state_owner; 1040 goto err_put_state_owner;
1041 if (path->dentry->d_inode != NULL)
1042 nfs4_return_incompatible_delegation(path->dentry->d_inode, flags & (FMODE_READ|FMODE_WRITE));
1030 down_read(&clp->cl_sem); 1043 down_read(&clp->cl_sem);
1031 status = -ENOMEM; 1044 status = -ENOMEM;
1032 opendata = nfs4_opendata_alloc(path, sp, flags, sattr); 1045 opendata = nfs4_opendata_alloc(path, sp, flags, sattr);
1033 if (opendata == NULL) 1046 if (opendata == NULL)
1034 goto err_release_rwsem; 1047 goto err_release_rwsem;
1035 1048
1049 if (path->dentry->d_inode != NULL)
1050 opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp);
1051
1036 status = _nfs4_proc_open(opendata); 1052 status = _nfs4_proc_open(opendata);
1037 if (status != 0) 1053 if (status != 0)
1038 goto err_opendata_put; 1054 goto err_opendata_put;
@@ -1099,6 +1115,11 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int
1099 exception.retry = 1; 1115 exception.retry = 1;
1100 continue; 1116 continue;
1101 } 1117 }
1118 if (status == -EAGAIN) {
1119 /* We must have found a delegation */
1120 exception.retry = 1;
1121 continue;
1122 }
1102 res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir), 1123 res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
1103 status, &exception)); 1124 status, &exception));
1104 } while (exception.retry); 1125 } while (exception.retry);
@@ -1390,9 +1411,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
1390 cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); 1411 cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
1391 if (IS_ERR(cred)) 1412 if (IS_ERR(cred))
1392 return PTR_ERR(cred); 1413 return PTR_ERR(cred);
1393 state = nfs4_open_delegated(dentry->d_inode, openflags, cred); 1414 state = nfs4_do_open(dir, &path, openflags, NULL, cred);
1394 if (IS_ERR(state))
1395 state = nfs4_do_open(dir, &path, openflags, NULL, cred);
1396 put_rpccred(cred); 1415 put_rpccred(cred);
1397 if (IS_ERR(state)) { 1416 if (IS_ERR(state)) {
1398 switch (PTR_ERR(state)) { 1417 switch (PTR_ERR(state)) {