SOCKET queue cmd PDUs directly in waitpdu queue

A storage might sent an R2T response for a WRITE command while
we still sending out the WRITE command PDU. This is especially
the case when the command PDU carries immediata data.

Without this patch the R2T response will get lost as
the cmdpdu for the R2T cannot be found in iscsi_process_pdu()
leading to a deadlock.

Signed-off-by: Peter Lieven <pl@kamp.de>
This commit is contained in:
Peter Lieven
2012-12-03 11:01:30 +01:00
parent 39f42dbd2f
commit dbe9a1e73a
4 changed files with 29 additions and 12 deletions

View File

@@ -103,6 +103,7 @@ struct iscsi_context {
void *connect_data;
struct iscsi_pdu *outqueue;
struct iscsi_pdu *outqueue_current;
struct iscsi_pdu *waitpdu;
struct iscsi_in_pdu *incoming;

View File

@@ -268,6 +268,10 @@ iscsi_destroy_context(struct iscsi_context *iscsi)
iscsi_free_pdu(iscsi, pdu);
}
if (iscsi->outqueue_current != NULL && iscsi->outqueue_current->flags & ISCSI_PDU_DELETE_WHEN_SENT) {
iscsi_free_pdu(iscsi, iscsi->outqueue_current);
}
if (iscsi->incoming != NULL) {
iscsi_free_iscsi_in_pdu(iscsi, iscsi->incoming);
}

View File

@@ -369,7 +369,7 @@ iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in)
*/
if (opcode == ISCSI_PDU_R2T) {
expected_response = ISCSI_PDU_R2T;
}
}
if (opcode != expected_response) {
iscsi_set_error(iscsi, "Got wrong opcode back for "

View File

@@ -65,8 +65,8 @@ iscsi_add_to_outqueue(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
* queue pdus with itt = 0xffffffff (SNACK / DataACK) in order but at head of queue.
*/
do {
if (current->written == 0 && (iscsi_serial32_compare(pdu->itt, current->itt) < 0
|| (pdu->itt == 0xffffffff && current->itt != 0xffffffff))) {
if (iscsi_serial32_compare(pdu->itt, current->itt) < 0
|| (pdu->itt == 0xffffffff && current->itt != 0xffffffff)) {
/* insert PDU before the current */
if (last != NULL) {
last->next=pdu;
@@ -332,7 +332,7 @@ iscsi_which_events(struct iscsi_context *iscsi)
{
int events = iscsi->is_connected ? POLLIN : POLLOUT;
if (iscsi->outqueue && iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) <= 0) {
if (iscsi->outqueue_current != NULL || (iscsi->outqueue != NULL && iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) <= 0)) {
events |= POLLOUT;
}
return events;
@@ -470,14 +470,28 @@ iscsi_write_to_socket(struct iscsi_context *iscsi)
return -1;
}
while ((pdu = iscsi->outqueue) != NULL) {
while (iscsi->outqueue != NULL || iscsi->outqueue_current != NULL) {
ssize_t total;
if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) > 0) {
/* stop sending. maxcmdsn is reached */
return 0;
if (iscsi->outqueue_current == NULL) {
if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) > 0) {
/* stop sending. maxcmdsn is reached */
return 0;
}
/* pop first element of the outqueue */
iscsi->outqueue_current = iscsi->outqueue;
SLIST_REMOVE(&iscsi->outqueue, iscsi->outqueue_current);
if (!(iscsi->outqueue_current->flags & ISCSI_PDU_DELETE_WHEN_SENT)) {
/* we have to add the pdu to the waitqueue already here
since the storage might sent a R2T as soon as it has
received the header. if we sent immediate data in a
cmd PDU the R2T might get lost otherwise. */
SLIST_ADD_END(&iscsi->waitpdu, iscsi->outqueue_current);
}
}
pdu = iscsi->outqueue_current;
total = pdu->outdata.size;
total = (total + 3) & 0xfffffffc;
@@ -530,12 +544,10 @@ iscsi_write_to_socket(struct iscsi_context *iscsi)
}
if (pdu->written == total) {
SLIST_REMOVE(&iscsi->outqueue, pdu);
if (pdu->flags & ISCSI_PDU_DELETE_WHEN_SENT) {
iscsi_free_pdu(iscsi, pdu);
} else {
SLIST_ADD_END(&iscsi->waitpdu, pdu);
}
iscsi->outqueue_current = NULL;
}
}
return 0;
@@ -625,7 +637,7 @@ iscsi_service(struct iscsi_context *iscsi, int revents)
return 0;
}
if (revents & POLLOUT && iscsi->outqueue != NULL) {
if (revents & POLLOUT && (iscsi->outqueue != NULL || iscsi->outqueue_current != NULL)) {
if (iscsi_write_to_socket(iscsi) != 0) {
return iscsi_service_reconnect_if_loggedin(iscsi);
}