aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2006-01-03 03:55:11 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2006-01-06 14:58:42 -0500
commit24ac23ab88df5b21b5b2df8cde748bf99b289099 (patch)
treee5e599fc55fc1744284077b320f3541a3658b958 /fs/nfs
parente60859ac0e50f660d23b72e42e05f58757dcfeff (diff)
NFSv4: Convert open() into an asynchronous RPC call
OPEN is a stateful operation, so we must ensure that it always completes. In order to allow users to interrupt the operation, we need to make the RPC call asynchronous, and then wait on completion (or cancel). Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/nfs4proc.c193
-rw-r--r--fs/nfs/nfs4xdr.c3
2 files changed, 130 insertions, 66 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 4a5cc840211..aed8701c1a3 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -195,6 +195,7 @@ static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinf
195} 195}
196 196
197struct nfs4_opendata { 197struct nfs4_opendata {
198 atomic_t count;
198 struct nfs_openargs o_arg; 199 struct nfs_openargs o_arg;
199 struct nfs_openres o_res; 200 struct nfs_openres o_res;
200 struct nfs_fattr f_attr; 201 struct nfs_fattr f_attr;
@@ -203,6 +204,8 @@ struct nfs4_opendata {
203 struct dentry *dir; 204 struct dentry *dir;
204 struct nfs4_state_owner *owner; 205 struct nfs4_state_owner *owner;
205 struct iattr attrs; 206 struct iattr attrs;
207 int rpc_status;
208 int cancelled;
206}; 209};
207 210
208static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, 211static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
@@ -220,6 +223,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
220 p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid); 223 p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid);
221 if (p->o_arg.seqid == NULL) 224 if (p->o_arg.seqid == NULL)
222 goto err_free; 225 goto err_free;
226 atomic_set(&p->count, 1);
223 p->dentry = dget(dentry); 227 p->dentry = dget(dentry);
224 p->dir = parent; 228 p->dir = parent;
225 p->owner = sp; 229 p->owner = sp;
@@ -255,7 +259,7 @@ err:
255 259
256static void nfs4_opendata_free(struct nfs4_opendata *p) 260static void nfs4_opendata_free(struct nfs4_opendata *p)
257{ 261{
258 if (p != NULL) { 262 if (p != NULL && atomic_dec_and_test(&p->count)) {
259 nfs_free_seqid(p->o_arg.seqid); 263 nfs_free_seqid(p->o_arg.seqid);
260 nfs4_put_state_owner(p->owner); 264 nfs4_put_state_owner(p->owner);
261 dput(p->dir); 265 dput(p->dir);
@@ -305,6 +309,26 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid,
305 spin_unlock(&state->owner->so_lock); 309 spin_unlock(&state->owner->so_lock);
306} 310}
307 311
312static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
313{
314 struct inode *inode;
315 struct nfs4_state *state = NULL;
316
317 if (!(data->f_attr.valid & NFS_ATTR_FATTR))
318 goto out;
319 inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr);
320 if (inode == NULL)
321 goto out;
322 state = nfs4_get_open_state(inode, data->owner);
323 if (state == NULL)
324 goto put_inode;
325 update_open_stateid(state, &data->o_res.stateid, data->o_arg.open_flags);
326put_inode:
327 iput(inode);
328out:
329 return state;
330}
331
308/* 332/*
309 * OPEN_RECLAIM: 333 * OPEN_RECLAIM:
310 * reclaim state on the server after a reboot. 334 * reclaim state on the server after a reboot.
@@ -473,41 +497,105 @@ static int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *f
473 return status; 497 return status;
474} 498}
475 499
476static int _nfs4_proc_open(struct inode *dir, struct nfs4_state_owner *sp, struct nfs_openargs *o_arg, struct nfs_openres *o_res) 500static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
477{ 501{
478 struct nfs_server *server = NFS_SERVER(dir); 502 struct nfs4_opendata *data = calldata;
503 struct nfs4_state_owner *sp = data->owner;
479 struct rpc_message msg = { 504 struct rpc_message msg = {
480 .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN], 505 .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN],
481 .rpc_argp = o_arg, 506 .rpc_argp = &data->o_arg,
482 .rpc_resp = o_res, 507 .rpc_resp = &data->o_res,
483 .rpc_cred = sp->so_cred, 508 .rpc_cred = sp->so_cred,
484 }; 509 };
485 int status; 510
511 if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
512 return;
513 /* Update sequence id. */
514 data->o_arg.id = sp->so_id;
515 data->o_arg.clientid = sp->so_client->cl_clientid;
516 rpc_call_setup(task, &msg, 0);
517}
486 518
487 /* Update sequence id. The caller must serialize! */ 519static void nfs4_open_done(struct rpc_task *task, void *calldata)
488 o_arg->id = sp->so_id; 520{
489 o_arg->clientid = sp->so_client->cl_clientid; 521 struct nfs4_opendata *data = calldata;
490 522
491 status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); 523 data->rpc_status = task->tk_status;
492 if (status == 0) { 524 if (RPC_ASSASSINATED(task))
493 /* OPEN on anything except a regular file is disallowed in NFSv4 */ 525 return;
494 switch (o_res->f_attr->mode & S_IFMT) { 526 if (task->tk_status == 0) {
527 switch (data->o_res.f_attr->mode & S_IFMT) {
495 case S_IFREG: 528 case S_IFREG:
496 break; 529 break;
497 case S_IFLNK: 530 case S_IFLNK:
498 status = -ELOOP; 531 data->rpc_status = -ELOOP;
499 break; 532 break;
500 case S_IFDIR: 533 case S_IFDIR:
501 status = -EISDIR; 534 data->rpc_status = -EISDIR;
502 break; 535 break;
503 default: 536 default:
504 status = -ENOTDIR; 537 data->rpc_status = -ENOTDIR;
505 } 538 }
506 } 539 }
540 nfs_increment_open_seqid(data->rpc_status, data->o_arg.seqid);
541}
542
543static void nfs4_open_release(void *calldata)
544{
545 struct nfs4_opendata *data = calldata;
546 struct nfs4_state *state = NULL;
547
548 /* If this request hasn't been cancelled, do nothing */
549 if (data->cancelled == 0)
550 goto out_free;
551 /* In case of error, no cleanup! */
552 if (data->rpc_status != 0)
553 goto out_free;
554 /* In case we need an open_confirm, no cleanup! */
555 if (data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM)
556 goto out_free;
557 nfs_confirm_seqid(&data->owner->so_seqid, 0);
558 state = nfs4_opendata_to_nfs4_state(data);
559 if (state != NULL)
560 nfs4_close_state(state, data->o_arg.open_flags);
561out_free:
562 nfs4_opendata_free(data);
563}
564
565static const struct rpc_call_ops nfs4_open_ops = {
566 .rpc_call_prepare = nfs4_open_prepare,
567 .rpc_call_done = nfs4_open_done,
568 .rpc_release = nfs4_open_release,
569};
507 570
508 nfs_increment_open_seqid(status, o_arg->seqid); 571/*
572 * Note: On error, nfs4_proc_open will free the struct nfs4_opendata
573 */
574static int _nfs4_proc_open(struct nfs4_opendata *data)
575{
576 struct inode *dir = data->dir->d_inode;
577 struct nfs_server *server = NFS_SERVER(dir);
578 struct nfs_openargs *o_arg = &data->o_arg;
579 struct nfs_openres *o_res = &data->o_res;
580 struct rpc_task *task;
581 int status;
582
583 atomic_inc(&data->count);
584 task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_ops, data);
585 if (IS_ERR(task)) {
586 nfs4_opendata_free(data);
587 return PTR_ERR(task);
588 }
589 status = nfs4_wait_for_completion_rpc_task(task);
590 if (status != 0) {
591 data->cancelled = 1;
592 smp_wmb();
593 } else
594 status = data->rpc_status;
595 rpc_release_task(task);
509 if (status != 0) 596 if (status != 0)
510 goto out; 597 return status;
598
511 if (o_arg->open_flags & O_CREAT) { 599 if (o_arg->open_flags & O_CREAT) {
512 update_changeattr(dir, &o_res->cinfo); 600 update_changeattr(dir, &o_res->cinfo);
513 nfs_post_op_update_inode(dir, o_res->dir_attr); 601 nfs_post_op_update_inode(dir, o_res->dir_attr);
@@ -515,15 +603,14 @@ static int _nfs4_proc_open(struct inode *dir, struct nfs4_state_owner *sp, stru
515 nfs_refresh_inode(dir, o_res->dir_attr); 603 nfs_refresh_inode(dir, o_res->dir_attr);
516 if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) { 604 if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
517 status = _nfs4_proc_open_confirm(server->client, &o_res->fh, 605 status = _nfs4_proc_open_confirm(server->client, &o_res->fh,
518 sp, &o_res->stateid, o_arg->seqid); 606 data->owner, &o_res->stateid, o_arg->seqid);
519 if (status != 0) 607 if (status != 0)
520 goto out; 608 return status;
521 } 609 }
522 nfs_confirm_seqid(&sp->so_seqid, 0); 610 nfs_confirm_seqid(&data->owner->so_seqid, 0);
523 if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) 611 if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
524 status = server->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr); 612 return server->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr);
525out: 613 return 0;
526 return status;
527} 614}
528 615
529static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags) 616static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags)
@@ -562,14 +649,15 @@ out:
562static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) 649static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry)
563{ 650{
564 struct dentry *parent = dget_parent(dentry); 651 struct dentry *parent = dget_parent(dentry);
565 struct inode *dir = parent->d_inode;
566 struct inode *inode = state->inode; 652 struct inode *inode = state->inode;
567 struct nfs_delegation *delegation = NFS_I(inode)->delegation; 653 struct nfs_delegation *delegation = NFS_I(inode)->delegation;
568 struct nfs4_opendata *opendata; 654 struct nfs4_opendata *opendata;
655 struct nfs4_state *newstate;
656 int openflags = state->state & (FMODE_READ|FMODE_WRITE);
569 int status = 0; 657 int status = 0;
570 658
571 if (delegation != NULL && !(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) { 659 if (delegation != NULL && !(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) {
572 status = _nfs4_do_access(inode, sp->so_cred, state->state); 660 status = _nfs4_do_access(inode, sp->so_cred, openflags);
573 if (status < 0) 661 if (status < 0)
574 goto out; 662 goto out;
575 memcpy(&state->stateid, &delegation->stateid, sizeof(state->stateid)); 663 memcpy(&state->stateid, &delegation->stateid, sizeof(state->stateid));
@@ -577,27 +665,15 @@ static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
577 goto out; 665 goto out;
578 } 666 }
579 status = -ENOMEM; 667 status = -ENOMEM;
580 opendata = nfs4_opendata_alloc(dentry, sp, state->state, NULL); 668 opendata = nfs4_opendata_alloc(dentry, sp, openflags, NULL);
581 if (opendata == NULL) 669 if (opendata == NULL)
582 goto out; 670 goto out;
583 status = _nfs4_proc_open(dir, sp, &opendata->o_arg, &opendata->o_res); 671 status = _nfs4_proc_open(opendata);
584 if (status != 0) 672 if (status != 0)
585 goto out_nodeleg; 673 goto out_nodeleg;
586 /* Check if files differ */ 674 newstate = nfs4_opendata_to_nfs4_state(opendata);
587 if ((opendata->f_attr.mode & S_IFMT) != (inode->i_mode & S_IFMT)) 675 if (newstate != state)
588 goto out_stale; 676 goto out_stale;
589 /* Has the file handle changed? */
590 if (nfs_compare_fh(&opendata->o_res.fh, NFS_FH(inode)) != 0) {
591 /* Verify if the change attributes are the same */
592 if (opendata->f_attr.change_attr != NFS_I(inode)->change_attr)
593 goto out_stale;
594 if (nfs_size_to_loff_t(opendata->f_attr.size) != inode->i_size)
595 goto out_stale;
596 /* Lets just pretend that this is the same file */
597 nfs_copy_fh(NFS_FH(inode), &opendata->o_res.fh);
598 NFS_I(inode)->fileid = opendata->f_attr.fileid;
599 }
600 memcpy(&state->stateid, &opendata->o_res.stateid, sizeof(state->stateid));
601 if (opendata->o_res.delegation_type != 0) { 677 if (opendata->o_res.delegation_type != 0) {
602 if (!(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) 678 if (!(delegation->flags & NFS_DELEGATION_NEED_RECLAIM))
603 nfs_inode_set_delegation(inode, sp->so_cred, 679 nfs_inode_set_delegation(inode, sp->so_cred,
@@ -606,6 +682,8 @@ static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
606 nfs_inode_reclaim_delegation(inode, sp->so_cred, 682 nfs_inode_reclaim_delegation(inode, sp->so_cred,
607 &opendata->o_res); 683 &opendata->o_res);
608 } 684 }
685out_close_state:
686 nfs4_close_state(newstate, openflags);
609out_nodeleg: 687out_nodeleg:
610 nfs4_opendata_free(opendata); 688 nfs4_opendata_free(opendata);
611 clear_bit(NFS_DELEGATED_STATE, &state->flags); 689 clear_bit(NFS_DELEGATED_STATE, &state->flags);
@@ -618,7 +696,7 @@ out_stale:
618 nfs4_drop_state_owner(sp); 696 nfs4_drop_state_owner(sp);
619 d_drop(dentry); 697 d_drop(dentry);
620 /* Should we be trying to close that stateid? */ 698 /* Should we be trying to close that stateid? */
621 goto out_nodeleg; 699 goto out_close_state;
622} 700}
623 701
624static inline int nfs4_do_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) 702static inline int nfs4_do_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry)
@@ -656,7 +734,7 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
656} 734}
657 735
658/* 736/*
659 * Returns an nfs4_state + an extra reference to the inode 737 * Returns a referenced nfs4_state if there is an open delegation on the file
660 */ 738 */
661static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res) 739static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res)
662{ 740{
@@ -709,7 +787,6 @@ out_ok:
709 nfs4_put_state_owner(sp); 787 nfs4_put_state_owner(sp);
710 up_read(&nfsi->rwsem); 788 up_read(&nfsi->rwsem);
711 up_read(&clp->cl_sem); 789 up_read(&clp->cl_sem);
712 igrab(inode);
713 *res = state; 790 *res = state;
714 return 0; 791 return 0;
715out_err: 792out_err:
@@ -742,7 +819,7 @@ static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, st
742} 819}
743 820
744/* 821/*
745 * Returns an nfs4_state + an referenced inode 822 * Returns a referenced nfs4_state
746 */ 823 */
747static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res) 824static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
748{ 825{
@@ -750,7 +827,6 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
750 struct nfs4_state *state = NULL; 827 struct nfs4_state *state = NULL;
751 struct nfs_server *server = NFS_SERVER(dir); 828 struct nfs_server *server = NFS_SERVER(dir);
752 struct nfs4_client *clp = server->nfs4_state; 829 struct nfs4_client *clp = server->nfs4_state;
753 struct inode *inode = NULL;
754 struct nfs4_opendata *opendata; 830 struct nfs4_opendata *opendata;
755 int status; 831 int status;
756 832
@@ -765,20 +841,16 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
765 if (opendata == NULL) 841 if (opendata == NULL)
766 goto err_put_state_owner; 842 goto err_put_state_owner;
767 843
768 status = _nfs4_proc_open(dir, sp, &opendata->o_arg, &opendata->o_res); 844 status = _nfs4_proc_open(opendata);
769 if (status != 0) 845 if (status != 0)
770 goto err_opendata_free; 846 goto err_opendata_free;
771 847
772 status = -ENOMEM; 848 status = -ENOMEM;
773 inode = nfs_fhget(dir->i_sb, &opendata->o_res.fh, &opendata->f_attr); 849 state = nfs4_opendata_to_nfs4_state(opendata);
774 if (!inode) 850 if (state == NULL)
775 goto err_opendata_free;
776 state = nfs4_get_open_state(inode, sp);
777 if (!state)
778 goto err_opendata_free; 851 goto err_opendata_free;
779 update_open_stateid(state, &opendata->o_res.stateid, flags);
780 if (opendata->o_res.delegation_type != 0) 852 if (opendata->o_res.delegation_type != 0)
781 nfs_inode_set_delegation(inode, cred, &opendata->o_res); 853 nfs_inode_set_delegation(state->inode, cred, &opendata->o_res);
782 nfs4_opendata_free(opendata); 854 nfs4_opendata_free(opendata);
783 nfs4_put_state_owner(sp); 855 nfs4_put_state_owner(sp);
784 up_read(&clp->cl_sem); 856 up_read(&clp->cl_sem);
@@ -791,8 +863,6 @@ err_put_state_owner:
791out_err: 863out_err:
792 /* Note: clp->cl_sem must be released before nfs4_put_open_state()! */ 864 /* Note: clp->cl_sem must be released before nfs4_put_open_state()! */
793 up_read(&clp->cl_sem); 865 up_read(&clp->cl_sem);
794 if (inode != NULL)
795 iput(inode);
796 *res = NULL; 866 *res = NULL;
797 return status; 867 return status;
798} 868}
@@ -1066,7 +1136,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
1066 d_add(dentry, NULL); 1136 d_add(dentry, NULL);
1067 return (struct dentry *)state; 1137 return (struct dentry *)state;
1068 } 1138 }
1069 res = d_add_unique(dentry, state->inode); 1139 res = d_add_unique(dentry, igrab(state->inode));
1070 if (res != NULL) 1140 if (res != NULL)
1071 dentry = res; 1141 dentry = res;
1072 nfs4_intent_set_file(nd, dentry, state); 1142 nfs4_intent_set_file(nd, dentry, state);
@@ -1078,7 +1148,6 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
1078{ 1148{
1079 struct rpc_cred *cred; 1149 struct rpc_cred *cred;
1080 struct nfs4_state *state; 1150 struct nfs4_state *state;
1081 struct inode *inode;
1082 1151
1083 cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); 1152 cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
1084 if (IS_ERR(cred)) 1153 if (IS_ERR(cred))
@@ -1102,9 +1171,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
1102 } 1171 }
1103 goto out_drop; 1172 goto out_drop;
1104 } 1173 }
1105 inode = state->inode; 1174 if (state->inode == dentry->d_inode) {
1106 iput(inode);
1107 if (inode == dentry->d_inode) {
1108 nfs4_intent_set_file(nd, dentry, state); 1175 nfs4_intent_set_file(nd, dentry, state);
1109 return 1; 1176 return 1;
1110 } 1177 }
@@ -1633,7 +1700,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
1633 status = PTR_ERR(state); 1700 status = PTR_ERR(state);
1634 goto out; 1701 goto out;
1635 } 1702 }
1636 d_instantiate(dentry, state->inode); 1703 d_instantiate(dentry, igrab(state->inode));
1637 if (flags & O_EXCL) { 1704 if (flags & O_EXCL) {
1638 struct nfs_fattr fattr; 1705 struct nfs_fattr fattr;
1639 status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, 1706 status = nfs4_do_setattr(NFS_SERVER(dir), &fattr,
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index fbbace8a30c..db2bcf722f9 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1499,9 +1499,6 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, uint32_t *p, struct nfs_opena
1499 }; 1499 };
1500 int status; 1500 int status;
1501 1501
1502 status = nfs_wait_on_sequence(args->seqid, req->rq_task);
1503 if (status != 0)
1504 goto out;
1505 xdr_init_encode(&xdr, &req->rq_snd_buf, p); 1502 xdr_init_encode(&xdr, &req->rq_snd_buf, p);
1506 encode_compound_hdr(&xdr, &hdr); 1503 encode_compound_hdr(&xdr, &hdr);
1507 status = encode_putfh(&xdr, args->fh); 1504 status = encode_putfh(&xdr, args->fh);