Commit 8544f4aa authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Steve French

CIFS: Fix credit computation for compounded requests

In SMB3 protocol every part of the compound chain consumes credits
individually, so we need to call wait_for_free_credits() for each
of the PDUs in the chain. If an operation is interrupted, we must
ensure we return all credits taken from the server structure back.

Without this patch server can sometimes disconnect the session
due to credit mismatches, especially when first operation(s)
are large writes.
Signed-off-by: default avatarPavel Shilovsky <pshilov@microsoft.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
CC: Stable <stable@vger.kernel.org>
parent 33fa5c8b
...@@ -795,7 +795,8 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -795,7 +795,8 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
int i, j, rc = 0; int i, j, rc = 0;
int timeout, optype; int timeout, optype;
struct mid_q_entry *midQ[MAX_COMPOUND]; struct mid_q_entry *midQ[MAX_COMPOUND];
unsigned int credits = 0; bool cancelled_mid[MAX_COMPOUND] = {false};
unsigned int credits[MAX_COMPOUND] = {0};
char *buf; char *buf;
timeout = flags & CIFS_TIMEOUT_MASK; timeout = flags & CIFS_TIMEOUT_MASK;
...@@ -813,13 +814,31 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -813,13 +814,31 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
return -ENOENT; return -ENOENT;
/* /*
* Ensure that we do not send more than 50 overlapping requests * Ensure we obtain 1 credit per request in the compound chain.
* to the same server. We may make this configurable later or * It can be optimized further by waiting for all the credits
* use ses->maxReq. * at once but this can wait long enough if we don't have enough
* credits due to some heavy operations in progress or the server
* not granting us much, so a fallback to the current approach is
* needed anyway.
*/ */
rc = wait_for_free_request(ses->server, timeout, optype); for (i = 0; i < num_rqst; i++) {
if (rc) rc = wait_for_free_request(ses->server, timeout, optype);
return rc; if (rc) {
/*
* We haven't sent an SMB packet to the server yet but
* we already obtained credits for i requests in the
* compound chain - need to return those credits back
* for future use. Note that we need to call add_credits
* multiple times to match the way we obtained credits
* in the first place and to account for in flight
* requests correctly.
*/
for (j = 0; j < i; j++)
add_credits(ses->server, 1, optype);
return rc;
}
credits[i] = 1;
}
/* /*
* Make sure that we sign in the same order that we send on this socket * Make sure that we sign in the same order that we send on this socket
...@@ -835,8 +854,10 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -835,8 +854,10 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
cifs_delete_mid(midQ[j]); cifs_delete_mid(midQ[j]);
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&ses->server->srv_mutex);
/* Update # of requests on wire to server */ /* Update # of requests on wire to server */
add_credits(ses->server, 1, optype); for (j = 0; j < num_rqst; j++)
add_credits(ses->server, credits[j], optype);
return PTR_ERR(midQ[i]); return PTR_ERR(midQ[i]);
} }
...@@ -883,17 +904,16 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -883,17 +904,16 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) {
midQ[i]->mid_flags |= MID_WAIT_CANCELLED; midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
midQ[i]->callback = DeleteMidQEntry; midQ[i]->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock); cancelled_mid[i] = true;
add_credits(ses->server, 1, optype);
return rc;
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
} }
} }
for (i = 0; i < num_rqst; i++) for (i = 0; i < num_rqst; i++)
if (midQ[i]->resp_buf) if (!cancelled_mid[i] && midQ[i]->resp_buf
credits += ses->server->ops->get_credits(midQ[i]); && (midQ[i]->mid_state == MID_RESPONSE_RECEIVED))
credits[i] = ses->server->ops->get_credits(midQ[i]);
for (i = 0; i < num_rqst; i++) { for (i = 0; i < num_rqst; i++) {
if (rc < 0) if (rc < 0)
...@@ -901,8 +921,9 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -901,8 +921,9 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
rc = cifs_sync_mid_result(midQ[i], ses->server); rc = cifs_sync_mid_result(midQ[i], ses->server);
if (rc != 0) { if (rc != 0) {
add_credits(ses->server, credits, optype); /* mark this mid as cancelled to not free it below */
return rc; cancelled_mid[i] = true;
goto out;
} }
if (!midQ[i]->resp_buf || if (!midQ[i]->resp_buf ||
...@@ -949,9 +970,11 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -949,9 +970,11 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
* This is prevented above by using a noop callback that will not * This is prevented above by using a noop callback that will not
* wake this thread except for the very last PDU. * wake this thread except for the very last PDU.
*/ */
for (i = 0; i < num_rqst; i++) for (i = 0; i < num_rqst; i++) {
cifs_delete_mid(midQ[i]); if (!cancelled_mid[i])
add_credits(ses->server, credits, optype); cifs_delete_mid(midQ[i]);
add_credits(ses->server, credits[i], optype);
}
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment