diff options
author | Andy Adamson <andros@netapp.com> | 2011-02-28 20:34:20 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-03-11 15:38:43 -0500 |
commit | cbdabc7f8bf14ca1d40ab1cb86f64b3bc09716e8 (patch) | |
tree | 16939b96996e8380d91d7e2743e811cf5c49f629 | |
parent | dc70d7b3189597f313df7bd2da849cfc39063b15 (diff) |
NFSv4.1: filelayout async error handler
Use our own async error handler.
Mark the layout as failed and retry i/o through the MDS on specified errors.
Update the mds_offset in nfs_readpage_retry so that a failed short-read retry
to a DS gets correctly resent through the MDS.
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/internal.h | 1 | ||||
-rw-r--r-- | fs/nfs/nfs4filelayout.c | 81 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 35 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 1 | ||||
-rw-r--r-- | fs/nfs/read.c | 1 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 1 | ||||
-rw-r--r-- | include/linux/sunrpc/clnt.h | 1 | ||||
-rw-r--r-- | net/sunrpc/clnt.c | 8 |
8 files changed, 123 insertions, 6 deletions
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 5e9df992cd73..1a3228e9ea22 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -285,6 +285,7 @@ extern int nfs_migrate_page(struct address_space *, | |||
285 | #endif | 285 | #endif |
286 | 286 | ||
287 | /* nfs4proc.c */ | 287 | /* nfs4proc.c */ |
288 | extern void nfs4_reset_read(struct rpc_task *task, struct nfs_read_data *data); | ||
288 | extern int nfs4_init_client(struct nfs_client *clp, | 289 | extern int nfs4_init_client(struct nfs_client *clp, |
289 | const struct rpc_timeout *timeparms, | 290 | const struct rpc_timeout *timeparms, |
290 | const char *ip_addr, | 291 | const char *ip_addr, |
diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 3608411653dc..6a424c19abea 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c | |||
@@ -40,6 +40,8 @@ MODULE_LICENSE("GPL"); | |||
40 | MODULE_AUTHOR("Dean Hildebrand <dhildebz@umich.edu>"); | 40 | MODULE_AUTHOR("Dean Hildebrand <dhildebz@umich.edu>"); |
41 | MODULE_DESCRIPTION("The NFSv4 file layout driver"); | 41 | MODULE_DESCRIPTION("The NFSv4 file layout driver"); |
42 | 42 | ||
43 | #define FILELAYOUT_POLL_RETRY_MAX (15*HZ) | ||
44 | |||
43 | static int | 45 | static int |
44 | filelayout_set_layoutdriver(struct nfs_server *nfss) | 46 | filelayout_set_layoutdriver(struct nfs_server *nfss) |
45 | { | 47 | { |
@@ -100,6 +102,83 @@ filelayout_get_dserver_offset(struct pnfs_layout_segment *lseg, loff_t offset) | |||
100 | BUG(); | 102 | BUG(); |
101 | } | 103 | } |
102 | 104 | ||
105 | /* For data server errors we don't recover from */ | ||
106 | static void | ||
107 | filelayout_set_lo_fail(struct pnfs_layout_segment *lseg) | ||
108 | { | ||
109 | if (lseg->pls_range.iomode == IOMODE_RW) { | ||
110 | dprintk("%s Setting layout IOMODE_RW fail bit\n", __func__); | ||
111 | set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags); | ||
112 | } else { | ||
113 | dprintk("%s Setting layout IOMODE_READ fail bit\n", __func__); | ||
114 | set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static int filelayout_async_handle_error(struct rpc_task *task, | ||
119 | struct nfs4_state *state, | ||
120 | struct nfs_client *clp, | ||
121 | int *reset) | ||
122 | { | ||
123 | if (task->tk_status >= 0) | ||
124 | return 0; | ||
125 | |||
126 | *reset = 0; | ||
127 | |||
128 | switch (task->tk_status) { | ||
129 | case -NFS4ERR_BADSESSION: | ||
130 | case -NFS4ERR_BADSLOT: | ||
131 | case -NFS4ERR_BAD_HIGH_SLOT: | ||
132 | case -NFS4ERR_DEADSESSION: | ||
133 | case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: | ||
134 | case -NFS4ERR_SEQ_FALSE_RETRY: | ||
135 | case -NFS4ERR_SEQ_MISORDERED: | ||
136 | dprintk("%s ERROR %d, Reset session. Exchangeid " | ||
137 | "flags 0x%x\n", __func__, task->tk_status, | ||
138 | clp->cl_exchange_flags); | ||
139 | nfs4_schedule_session_recovery(clp->cl_session); | ||
140 | break; | ||
141 | case -NFS4ERR_DELAY: | ||
142 | case -NFS4ERR_GRACE: | ||
143 | case -EKEYEXPIRED: | ||
144 | rpc_delay(task, FILELAYOUT_POLL_RETRY_MAX); | ||
145 | break; | ||
146 | default: | ||
147 | dprintk("%s DS error. Retry through MDS %d\n", __func__, | ||
148 | task->tk_status); | ||
149 | *reset = 1; | ||
150 | break; | ||
151 | } | ||
152 | task->tk_status = 0; | ||
153 | return -EAGAIN; | ||
154 | } | ||
155 | |||
156 | /* NFS_PROTO call done callback routines */ | ||
157 | |||
158 | static int filelayout_read_done_cb(struct rpc_task *task, | ||
159 | struct nfs_read_data *data) | ||
160 | { | ||
161 | struct nfs_client *clp = data->ds_clp; | ||
162 | int reset = 0; | ||
163 | |||
164 | dprintk("%s DS read\n", __func__); | ||
165 | |||
166 | if (filelayout_async_handle_error(task, data->args.context->state, | ||
167 | data->ds_clp, &reset) == -EAGAIN) { | ||
168 | dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n", | ||
169 | __func__, data->ds_clp, data->ds_clp->cl_session); | ||
170 | if (reset) { | ||
171 | filelayout_set_lo_fail(data->lseg); | ||
172 | nfs4_reset_read(task, data); | ||
173 | clp = NFS_SERVER(data->inode)->nfs_client; | ||
174 | } | ||
175 | nfs_restart_rpc(task, clp); | ||
176 | return -EAGAIN; | ||
177 | } | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
103 | /* | 182 | /* |
104 | * Call ops for the async read/write cases | 183 | * Call ops for the async read/write cases |
105 | * In the case of dense layouts, the offset needs to be reset to its | 184 | * In the case of dense layouts, the offset needs to be reset to its |
@@ -109,6 +188,8 @@ static void filelayout_read_prepare(struct rpc_task *task, void *data) | |||
109 | { | 188 | { |
110 | struct nfs_read_data *rdata = (struct nfs_read_data *)data; | 189 | struct nfs_read_data *rdata = (struct nfs_read_data *)data; |
111 | 190 | ||
191 | rdata->read_done_cb = filelayout_read_done_cb; | ||
192 | |||
112 | if (nfs41_setup_sequence(rdata->ds_clp->cl_session, | 193 | if (nfs41_setup_sequence(rdata->ds_clp->cl_session, |
113 | &rdata->args.seq_args, &rdata->res.seq_res, | 194 | &rdata->args.seq_args, &rdata->res.seq_res, |
114 | 0, task)) | 195 | 0, task)) |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d09623933302..1dc809039448 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -3074,15 +3074,10 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, | |||
3074 | return err; | 3074 | return err; |
3075 | } | 3075 | } |
3076 | 3076 | ||
3077 | static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) | 3077 | static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data) |
3078 | { | 3078 | { |
3079 | struct nfs_server *server = NFS_SERVER(data->inode); | 3079 | struct nfs_server *server = NFS_SERVER(data->inode); |
3080 | 3080 | ||
3081 | dprintk("--> %s\n", __func__); | ||
3082 | |||
3083 | if (!nfs4_sequence_done(task, &data->res.seq_res)) | ||
3084 | return -EAGAIN; | ||
3085 | |||
3086 | if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) { | 3081 | if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) { |
3087 | nfs_restart_rpc(task, server->nfs_client); | 3082 | nfs_restart_rpc(task, server->nfs_client); |
3088 | return -EAGAIN; | 3083 | return -EAGAIN; |
@@ -3094,12 +3089,40 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) | |||
3094 | return 0; | 3089 | return 0; |
3095 | } | 3090 | } |
3096 | 3091 | ||
3092 | static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) | ||
3093 | { | ||
3094 | |||
3095 | dprintk("--> %s\n", __func__); | ||
3096 | |||
3097 | if (!nfs4_sequence_done(task, &data->res.seq_res)) | ||
3098 | return -EAGAIN; | ||
3099 | |||
3100 | return data->read_done_cb(task, data); | ||
3101 | } | ||
3102 | |||
3097 | static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message *msg) | 3103 | static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message *msg) |
3098 | { | 3104 | { |
3099 | data->timestamp = jiffies; | 3105 | data->timestamp = jiffies; |
3106 | data->read_done_cb = nfs4_read_done_cb; | ||
3100 | msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ]; | 3107 | msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ]; |
3101 | } | 3108 | } |
3102 | 3109 | ||
3110 | /* Reset the the nfs_read_data to send the read to the MDS. */ | ||
3111 | void nfs4_reset_read(struct rpc_task *task, struct nfs_read_data *data) | ||
3112 | { | ||
3113 | dprintk("%s Reset task for i/o through\n", __func__); | ||
3114 | put_lseg(data->lseg); | ||
3115 | data->lseg = NULL; | ||
3116 | /* offsets will differ in the dense stripe case */ | ||
3117 | data->args.offset = data->mds_offset; | ||
3118 | data->ds_clp = NULL; | ||
3119 | data->args.fh = NFS_FH(data->inode); | ||
3120 | data->read_done_cb = nfs4_read_done_cb; | ||
3121 | task->tk_ops = data->mds_ops; | ||
3122 | rpc_task_reset_client(task, NFS_CLIENT(data->inode)); | ||
3123 | } | ||
3124 | EXPORT_SYMBOL_GPL(nfs4_reset_read); | ||
3125 | |||
3103 | static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) | 3126 | static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) |
3104 | { | 3127 | { |
3105 | struct inode *inode = data->inode; | 3128 | struct inode *inode = data->inode; |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 69c836373124..ab1bf5bb021f 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -1453,6 +1453,7 @@ void nfs4_schedule_session_recovery(struct nfs4_session *session) | |||
1453 | { | 1453 | { |
1454 | nfs4_schedule_lease_recovery(session->clp); | 1454 | nfs4_schedule_lease_recovery(session->clp); |
1455 | } | 1455 | } |
1456 | EXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery); | ||
1456 | 1457 | ||
1457 | void nfs41_handle_recall_slot(struct nfs_client *clp) | 1458 | void nfs41_handle_recall_slot(struct nfs_client *clp) |
1458 | { | 1459 | { |
diff --git a/fs/nfs/read.c b/fs/nfs/read.c index f4d0fcffcb5a..f40c7f4dc16b 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c | |||
@@ -391,6 +391,7 @@ static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data | |||
391 | return; | 391 | return; |
392 | 392 | ||
393 | /* Yes, so retry the read at the end of the data */ | 393 | /* Yes, so retry the read at the end of the data */ |
394 | data->mds_offset += resp->count; | ||
394 | argp->offset += resp->count; | 395 | argp->offset += resp->count; |
395 | argp->pgbase += resp->count; | 396 | argp->pgbase += resp->count; |
396 | argp->count -= resp->count; | 397 | argp->count -= resp->count; |
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index b63faef5052e..eb0e87084353 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h | |||
@@ -1020,6 +1020,7 @@ struct nfs_read_data { | |||
1020 | struct pnfs_layout_segment *lseg; | 1020 | struct pnfs_layout_segment *lseg; |
1021 | struct nfs_client *ds_clp; /* pNFS data server */ | 1021 | struct nfs_client *ds_clp; /* pNFS data server */ |
1022 | const struct rpc_call_ops *mds_ops; | 1022 | const struct rpc_call_ops *mds_ops; |
1023 | int (*read_done_cb) (struct rpc_task *task, struct nfs_read_data *data); | ||
1023 | __u64 mds_offset; | 1024 | __u64 mds_offset; |
1024 | struct page *page_array[NFS_PAGEVEC_SIZE]; | 1025 | struct page *page_array[NFS_PAGEVEC_SIZE]; |
1025 | }; | 1026 | }; |
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index ef9476a36ff7..db7bcaf7c5bd 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h | |||
@@ -129,6 +129,7 @@ struct rpc_create_args { | |||
129 | struct rpc_clnt *rpc_create(struct rpc_create_args *args); | 129 | struct rpc_clnt *rpc_create(struct rpc_create_args *args); |
130 | struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *, | 130 | struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *, |
131 | struct rpc_program *, u32); | 131 | struct rpc_program *, u32); |
132 | void rpc_task_reset_client(struct rpc_task *task, struct rpc_clnt *clnt); | ||
132 | struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); | 133 | struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); |
133 | void rpc_shutdown_client(struct rpc_clnt *); | 134 | void rpc_shutdown_client(struct rpc_clnt *); |
134 | void rpc_release_client(struct rpc_clnt *); | 135 | void rpc_release_client(struct rpc_clnt *); |
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 8b5a6b40d37c..edaf56e2ed29 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -597,6 +597,14 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt) | |||
597 | } | 597 | } |
598 | } | 598 | } |
599 | 599 | ||
600 | void rpc_task_reset_client(struct rpc_task *task, struct rpc_clnt *clnt) | ||
601 | { | ||
602 | rpc_task_release_client(task); | ||
603 | rpc_task_set_client(task, clnt); | ||
604 | } | ||
605 | EXPORT_SYMBOL_GPL(rpc_task_reset_client); | ||
606 | |||
607 | |||
600 | static void | 608 | static void |
601 | rpc_task_set_rpc_message(struct rpc_task *task, const struct rpc_message *msg) | 609 | rpc_task_set_rpc_message(struct rpc_task *task, const struct rpc_message *msg) |
602 | { | 610 | { |