diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-03-21 16:19:41 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-04-19 16:53:20 -0400 |
commit | 1e799b673c6b82b336ab13c48b5651d511ca3000 (patch) | |
tree | 9954155b2a9bdd72e49a078418ceea6c47bcc609 /net/sunrpc/clnt.c | |
parent | c1d519312dcdf11532fed9f99a8ecc3547ffd9d6 (diff) |
SUNRPC: Fix read ordering problems with req->rq_private_buf.len
We want to ensure that req->rq_private_buf.len is updated before
req->rq_received, so that call_decode() doesn't use an old value for
req->rq_rcv_buf.len.
In 'call_decode()' itself, instead of using task->tk_status (which is set
using req->rq_received) must use the actual value of
req->rq_private_buf.len when deciding whether or not the received RPC reply
is too short.
Finally ensure that we set req->rq_rcv_buf.len to zero when retrying a
request. A typo meant that we were resetting req->rq_private_buf.len in
call_decode(), and then clobbering that value with the old rq_rcv_buf.len
again in xprt_transmit().
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net/sunrpc/clnt.c')
-rw-r--r-- | net/sunrpc/clnt.c | 26 |
1 files changed, 13 insertions, 13 deletions
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 522b06849f86..3ae560464513 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -1199,18 +1199,6 @@ call_decode(struct rpc_task *task) | |||
1199 | task->tk_flags &= ~RPC_CALL_MAJORSEEN; | 1199 | task->tk_flags &= ~RPC_CALL_MAJORSEEN; |
1200 | } | 1200 | } |
1201 | 1201 | ||
1202 | if (task->tk_status < 12) { | ||
1203 | if (!RPC_IS_SOFT(task)) { | ||
1204 | task->tk_action = call_bind; | ||
1205 | clnt->cl_stats->rpcretrans++; | ||
1206 | goto out_retry; | ||
1207 | } | ||
1208 | dprintk("RPC: %s: too small RPC reply size (%d bytes)\n", | ||
1209 | clnt->cl_protname, task->tk_status); | ||
1210 | task->tk_action = call_timeout; | ||
1211 | goto out_retry; | ||
1212 | } | ||
1213 | |||
1214 | /* | 1202 | /* |
1215 | * Ensure that we see all writes made by xprt_complete_rqst() | 1203 | * Ensure that we see all writes made by xprt_complete_rqst() |
1216 | * before it changed req->rq_received. | 1204 | * before it changed req->rq_received. |
@@ -1222,6 +1210,18 @@ call_decode(struct rpc_task *task) | |||
1222 | WARN_ON(memcmp(&req->rq_rcv_buf, &req->rq_private_buf, | 1210 | WARN_ON(memcmp(&req->rq_rcv_buf, &req->rq_private_buf, |
1223 | sizeof(req->rq_rcv_buf)) != 0); | 1211 | sizeof(req->rq_rcv_buf)) != 0); |
1224 | 1212 | ||
1213 | if (req->rq_rcv_buf.len < 12) { | ||
1214 | if (!RPC_IS_SOFT(task)) { | ||
1215 | task->tk_action = call_bind; | ||
1216 | clnt->cl_stats->rpcretrans++; | ||
1217 | goto out_retry; | ||
1218 | } | ||
1219 | dprintk("RPC: %s: too small RPC reply size (%d bytes)\n", | ||
1220 | clnt->cl_protname, task->tk_status); | ||
1221 | task->tk_action = call_timeout; | ||
1222 | goto out_retry; | ||
1223 | } | ||
1224 | |||
1225 | /* Verify the RPC header */ | 1225 | /* Verify the RPC header */ |
1226 | p = call_verify(task); | 1226 | p = call_verify(task); |
1227 | if (IS_ERR(p)) { | 1227 | if (IS_ERR(p)) { |
@@ -1243,7 +1243,7 @@ out_retry: | |||
1243 | task->tk_status = 0; | 1243 | task->tk_status = 0; |
1244 | /* Note: call_verify() may have freed the RPC slot */ | 1244 | /* Note: call_verify() may have freed the RPC slot */ |
1245 | if (task->tk_rqstp == req) { | 1245 | if (task->tk_rqstp == req) { |
1246 | req->rq_received = req->rq_private_buf.len = 0; | 1246 | req->rq_received = req->rq_rcv_buf.len = 0; |
1247 | if (task->tk_client->cl_discrtry) | 1247 | if (task->tk_client->cl_discrtry) |
1248 | xprt_force_disconnect(task->tk_xprt); | 1248 | xprt_force_disconnect(task->tk_xprt); |
1249 | } | 1249 | } |