From 5d55d32adbe7d40b0f2a91d0ef01f10cb763f216 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 13 Jun 2014 12:59:17 +0200 Subject: [PATCH 01/12] socket: do not stop sending for immediate PDUs Signed-off-by: Peter Lieven --- lib/socket.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/socket.c b/lib/socket.c index 4b1c569..e9e499f 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -611,8 +611,9 @@ iscsi_write_to_socket(struct iscsi_context *iscsi) while (iscsi->outqueue != NULL || iscsi->outqueue_current != NULL) { if (iscsi->outqueue_current == NULL) { - if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) > 0) { - /* stop sending. maxcmdsn is reached */ + if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) > 0 + && !(iscsi->outqueue->outdata.data[0] & ISCSI_PDU_IMMEDIATE)) { + /* stop sending for non-immediate PDUs. maxcmdsn is reached */ return 0; } /* pop first element of the outqueue */ From 99585b69968df1934415c6af3ad38fd1e35e6040 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 13 Jun 2014 13:07:39 +0200 Subject: [PATCH 02/12] pdu: introduce ISCSI_PDU_CORK_WHEN_SENT Signed-off-by: Peter Lieven --- include/iscsi-private.h | 3 +++ lib/socket.c | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 93d98fc..b433fdf 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -80,6 +80,7 @@ struct iscsi_context { int fd; int is_connected; + int is_corked; int tcp_user_timeout; int tcp_keepcnt; @@ -209,6 +210,8 @@ struct iscsi_pdu { * This includes any DATA-OUT PDU as well as all NOPs. */ #define ISCSI_PDU_DROP_ON_RECONNECT 0x00000004 +/* stop sending after this PDU has been sent */ +#define ISCSI_PDU_CORK_WHEN_SENT 0x00000008 uint32_t flags; diff --git a/lib/socket.c b/lib/socket.c index e9e499f..b161447 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -611,6 +611,12 @@ iscsi_write_to_socket(struct iscsi_context *iscsi) while (iscsi->outqueue != NULL || iscsi->outqueue_current != NULL) { if (iscsi->outqueue_current == NULL) { + if (iscsi->is_corked) { + /* connection is corked we are not allowed to send + * additional PDUs */ + return 0; + } + if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) > 0 && !(iscsi->outqueue->outdata.data[0] & ISCSI_PDU_IMMEDIATE)) { /* stop sending for non-immediate PDUs. maxcmdsn is reached */ @@ -699,10 +705,12 @@ iscsi_write_to_socket(struct iscsi_context *iscsi) if (pdu->payload_written != total) { return 0; } - if (pdu->flags & ISCSI_PDU_DELETE_WHEN_SENT) { iscsi_free_pdu(iscsi, pdu); } + if (pdu->flags & ISCSI_PDU_CORK_WHEN_SENT) { + iscsi->is_corked = 1; + } iscsi->outqueue_current = NULL; } return 0; From e940f957693dc285710492f992181525fd6bc0c8 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 13 Jun 2014 13:10:18 +0200 Subject: [PATCH 03/12] logout: cork socket after logout has been sent Signed-off-by: Peter Lieven --- lib/login.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/login.c b/lib/login.c index 32c3c8c..492df3b 100644 --- a/lib/login.c +++ b/lib/login.c @@ -1154,6 +1154,7 @@ iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, pdu->callback = cb; pdu->private_data = private_data; + pdu->flags |= ISCSI_PDU_CORK_WHEN_SENT; if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " From 344b21d37d1eae3b64238d8b96a9c72eeda9e91d Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 16 Jun 2014 16:23:28 +0200 Subject: [PATCH 04/12] logout: allow additional PDU flags to be passed internally Signed-off-by: Peter Lieven --- include/iscsi-private.h | 4 ++++ lib/login.c | 14 ++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index b433fdf..d03a4ba 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -350,6 +350,10 @@ iscsi_itt_post_increment(struct iscsi_context *iscsi); void iscsi_timeout_scan(struct iscsi_context *iscsi); +int +iscsi_logout_async_internal(struct iscsi_context *iscsi, iscsi_command_cb cb, + void *private_data, uint32_t flags); + #ifdef __cplusplus } #endif diff --git a/lib/login.c b/lib/login.c index 492df3b..493b2bc 100644 --- a/lib/login.c +++ b/lib/login.c @@ -1117,10 +1117,9 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, return 0; } - int -iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, - void *private_data) +iscsi_logout_async_internal(struct iscsi_context *iscsi, iscsi_command_cb cb, + void *private_data, uint32_t flags) { struct iscsi_pdu *pdu; @@ -1154,7 +1153,7 @@ iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, pdu->callback = cb; pdu->private_data = private_data; - pdu->flags |= ISCSI_PDU_CORK_WHEN_SENT; + pdu->flags |= ISCSI_PDU_CORK_WHEN_SENT | flags; if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " @@ -1166,6 +1165,13 @@ iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, return 0; } +int +iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, + void *private_data) +{ + return iscsi_logout_async_internal(iscsi, cb, private_data, 0); +} + int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) From 98bbeab6646038fdabb8aea614b32b66ac5901b1 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 17 Jun 2014 14:50:41 +0200 Subject: [PATCH 05/12] pdu: introduce ISCSI_PDU_URGENT_DELIVERY Signed-off-by: Peter Lieven --- include/iscsi-private.h | 5 +++++ lib/socket.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index d03a4ba..658dc98 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -212,6 +212,11 @@ struct iscsi_pdu { #define ISCSI_PDU_DROP_ON_RECONNECT 0x00000004 /* stop sending after this PDU has been sent */ #define ISCSI_PDU_CORK_WHEN_SENT 0x00000008 +/* put this immediate delivery PDU in front of outqueue. + * This is currently only used for immediate logout requests + * as answer to an async logout event. */ +#define ISCSI_PDU_URGENT_DELIVERY 0x00000010 + uint32_t flags; diff --git a/lib/socket.c b/lib/socket.c index b161447..2a79538 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -94,7 +94,8 @@ iscsi_add_to_outqueue(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) * ensure that pakets with the same CmdSN are kept in FIFO order. */ do { - if (iscsi_serial32_compare(pdu->cmdsn, current->cmdsn) < 0) { + if (iscsi_serial32_compare(pdu->cmdsn, current->cmdsn) < 0 || + pdu->flags & ISCSI_PDU_URGENT_DELIVERY) { /* insert PDU before the current */ if (last != NULL) { last->next=pdu; From 81921e3762589a6a30960ebe68b032592f7c9e54 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 17 Jun 2014 14:52:27 +0200 Subject: [PATCH 06/12] pdu: handle async target requests logout events Signed-off-by: Peter Lieven --- include/iscsi-private.h | 4 +++- lib/pdu.c | 27 +++++++++++++++++++++++++++ lib/socket.c | 4 ++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 658dc98..c74390c 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -126,6 +126,7 @@ struct iscsi_context { int no_auto_reconnect; int reconnect_deferred; int reconnect_max_retries; + int pending_reconnect; int log_level; iscsi_log_fn log_fn; @@ -189,8 +190,9 @@ enum iscsi_opcode { ISCSI_PDU_DATA_IN = 0x25, ISCSI_PDU_LOGOUT_RESPONSE = 0x26, ISCSI_PDU_R2T = 0x31, + ISCSI_PDU_ASYNC_MSG = 0x32, ISCSI_PDU_REJECT = 0x3f, - ISCSI_PDU_NO_PDU = 0xff + ISCSI_PDU_NO_PDU = 0xff }; struct iscsi_scsi_cbdata { diff --git a/lib/pdu.c b/lib/pdu.c index 89b15c2..0b0ce1f 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -359,6 +359,17 @@ int iscsi_process_reject(struct iscsi_context *iscsi, } +static void iscsi_reconnect_after_logout(struct iscsi_context *iscsi, int status, + void *command_data _U_, void *opaque _U_) +{ + if (status) { + ISCSI_LOG(iscsi, 1, "logout failed: %s", iscsi_get_error(iscsi)); + } else { + ISCSI_LOG(iscsi, 2, "logout was successful"); + } + iscsi->pending_reconnect = 1; +} + int iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { @@ -376,6 +387,22 @@ iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) return -1; } + if (opcode == ISCSI_PDU_ASYNC_MSG) { + uint8_t event = in->hdr[36]; + uint16_t param1 = scsi_get_uint16(&in->hdr[38]); + uint16_t param2 = scsi_get_uint16(&in->hdr[40]); + uint16_t param3 = scsi_get_uint16(&in->hdr[42]); + switch (event) { + case 0x1: + ISCSI_LOG(iscsi, 2, "target requests logout within %u seconds", param3); + iscsi_logout_async_internal(iscsi, iscsi_reconnect_after_logout, NULL, ISCSI_PDU_DROP_ON_RECONNECT|ISCSI_PDU_URGENT_DELIVERY); + return 0; + default: + ISCSI_LOG(iscsi, 1, "unhandled async event %u: param1 %u param2 %u param3 %u", event, param1, param2, param3); + return -1; + } + } + if (opcode == ISCSI_PDU_REJECT) { iscsi_set_error(iscsi, "Request was rejected with reason: 0x%02x (%s)", in->hdr[2], iscsi_reject_reason_str(in->hdr[2])); diff --git a/lib/socket.c b/lib/socket.c index 2a79538..9844084 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -735,6 +735,10 @@ iscsi_service(struct iscsi_context *iscsi, int revents) return 0; } + if (iscsi->pending_reconnect) { + iscsi_reconnect(iscsi); + } + if (revents & POLLERR) { int err = 0; socklen_t err_size = sizeof(err); From 21e02c20785d173a7c74f89cba5cd481dd2126ac Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 17 Jun 2014 14:54:05 +0200 Subject: [PATCH 07/12] pdu: catch rejected packets while target waits for logout after a target has send the async target requests logout event it may reject each request with a waiting for logout reason. Catch these rejects if an outstanding request and the logout event overlap. Signed-off-by: Peter Lieven --- lib/pdu.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/lib/pdu.c b/lib/pdu.c index 0b0ce1f..ddc7711 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -317,12 +317,25 @@ int iscsi_process_target_nop_in(struct iscsi_context *iscsi, } +static void iscsi_reconnect_after_logout(struct iscsi_context *iscsi, int status, + void *command_data _U_, void *opaque _U_) +{ + if (status) { + ISCSI_LOG(iscsi, 1, "logout failed: %s", iscsi_get_error(iscsi)); + } else { + ISCSI_LOG(iscsi, 2, "logout was successful"); + } + iscsi->pending_reconnect = 1; +} + + int iscsi_process_reject(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { int size = in->data_pos; uint32_t itt; struct iscsi_pdu *pdu; + uint8_t reason = in->hdr[2]; if (size < ISCSI_RAW_HEADER_SIZE) { iscsi_set_error(iscsi, "size of REJECT payload is too small." @@ -331,6 +344,14 @@ int iscsi_process_reject(struct iscsi_context *iscsi, return -1; } + if (reason == ISCSI_REJECT_WAITING_FOR_LOGOUT) { + ISCSI_LOG(iscsi, 1, "target rejects request with reason: %s", iscsi_reject_reason_str(reason)); + iscsi_logout_async_internal(iscsi, iscsi_reconnect_after_logout, NULL, ISCSI_PDU_DROP_ON_RECONNECT|ISCSI_PDU_URGENT_DELIVERY); + return 0; + } + + iscsi_set_error(iscsi, "Request was rejected with reason: 0x%02x (%s)", reason, iscsi_reject_reason_str(reason)); + itt = scsi_get_uint32(&in->data[16]); if (iscsi->log_level > 1) { @@ -359,17 +380,6 @@ int iscsi_process_reject(struct iscsi_context *iscsi, } -static void iscsi_reconnect_after_logout(struct iscsi_context *iscsi, int status, - void *command_data _U_, void *opaque _U_) -{ - if (status) { - ISCSI_LOG(iscsi, 1, "logout failed: %s", iscsi_get_error(iscsi)); - } else { - ISCSI_LOG(iscsi, 2, "logout was successful"); - } - iscsi->pending_reconnect = 1; -} - int iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { @@ -404,12 +414,7 @@ iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) } if (opcode == ISCSI_PDU_REJECT) { - iscsi_set_error(iscsi, "Request was rejected with reason: 0x%02x (%s)", in->hdr[2], iscsi_reject_reason_str(in->hdr[2])); - - if (iscsi_process_reject(iscsi, in) != 0) { - return -1; - } - return 0; + return iscsi_process_reject(iscsi, in); } if (opcode == ISCSI_PDU_NOP_IN && itt == 0xffffffff) { From f14f3ef6fe43c46716e729a3b443a111740a60c2 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 17 Jun 2014 14:56:43 +0200 Subject: [PATCH 08/12] pdu: handle async events 2,3,4 Signed-off-by: Peter Lieven --- lib/pdu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/pdu.c b/lib/pdu.c index ddc7711..36c1df9 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -407,6 +407,18 @@ iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) ISCSI_LOG(iscsi, 2, "target requests logout within %u seconds", param3); iscsi_logout_async_internal(iscsi, iscsi_reconnect_after_logout, NULL, ISCSI_PDU_DROP_ON_RECONNECT|ISCSI_PDU_URGENT_DELIVERY); return 0; + case 0x2: + ISCSI_LOG(iscsi, 2, "target will drop this connection. Time2Wait is %u seconds", param2); + iscsi->last_reconnect = time(NULL) + param2; + return 0; + case 0x3: + ISCSI_LOG(iscsi, 2, "target will drop all connections of this session. Time2Wait is %u seconds", param2); + iscsi->last_reconnect = time(NULL) + param2; + return 0; + case 0x4: + ISCSI_LOG(iscsi, 2, "target requests parameter renogitiation."); + iscsi_logout_async_internal(iscsi, iscsi_reconnect_after_logout, NULL, ISCSI_PDU_DROP_ON_RECONNECT); + return 0; default: ISCSI_LOG(iscsi, 1, "unhandled async event %u: param1 %u param2 %u param3 %u", event, param1, param2, param3); return -1; From e6894fd73ad003e739f28d2757bbefd911b7481e Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 16 Jun 2014 18:18:19 +0200 Subject: [PATCH 09/12] iscsi-command: do not fail commands while reconnect is pending Signed-off-by: Peter Lieven --- lib/iscsi-command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/iscsi-command.c b/lib/iscsi-command.c index 95187a1..e4f6e8e 100644 --- a/lib/iscsi-command.c +++ b/lib/iscsi-command.c @@ -208,7 +208,7 @@ iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, return -1; } - if (iscsi->is_loggedin == 0) { + if (iscsi->is_loggedin == 0 && !iscsi->pending_reconnect) { iscsi_set_error(iscsi, "Trying to send command while " "not logged in."); return -1; From 52c6b0d397fddec46b75a5a9a3bec72fbcfa2d25 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 17 Jun 2014 12:50:26 +0200 Subject: [PATCH 10/12] pdu: check callback on iscsi_process_reject if the rejected packet is a NOP-Out it is legal that it has no callback. In this case we end up in a segfault. Signed-off-by: Peter Lieven --- lib/pdu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pdu.c b/lib/pdu.c index 36c1df9..72a6e3f 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -371,8 +371,10 @@ int iscsi_process_reject(struct iscsi_context *iscsi, return -1; } - pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, - pdu->private_data); + if (pdu->callback) { + pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, + pdu->private_data); + } ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); From 4e129d385c27e7b26172ed1f93c688eabca11654 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 17 Jun 2014 10:56:18 +0200 Subject: [PATCH 11/12] pdu: fix statsn and factor out sn comparision Signed-off-by: Peter Lieven --- include/iscsi-private.h | 8 ++++---- lib/iscsi-command.c | 45 ++++++++--------------------------------- lib/login.c | 25 ++++------------------- lib/pdu.c | 31 +++++++++++++++++++++++----- lib/task_mgmt.c | 11 ++-------- 5 files changed, 44 insertions(+), 76 deletions(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index c74390c..e69fdce 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -349,11 +349,11 @@ iscsi_log_message(struct iscsi_context *iscsi, int level, const char *format, .. void iscsi_add_to_outqueue(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); -int -iscsi_serial32_compare(uint32_t s1, uint32_t s2); +int iscsi_serial32_compare(uint32_t s1, uint32_t s2); +void iscsi_adjust_statsn(struct iscsi_context *iscsi, struct iscsi_in_pdu *in); +void iscsi_adjust_maxexpcmdsn(struct iscsi_context *iscsi, struct iscsi_in_pdu *in); -uint32_t -iscsi_itt_post_increment(struct iscsi_context *iscsi); +uint32_t iscsi_itt_post_increment(struct iscsi_context *iscsi); void iscsi_timeout_scan(struct iscsi_context *iscsi); diff --git a/lib/iscsi-command.c b/lib/iscsi-command.c index e4f6e8e..6a7bec7 100644 --- a/lib/iscsi-command.c +++ b/lib/iscsi-command.c @@ -347,23 +347,12 @@ int iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { - uint32_t statsn, maxcmdsn, expcmdsn, flags, status; + uint32_t flags, status; struct iscsi_scsi_cbdata *scsi_cbdata = &pdu->scsi_cbdata; struct scsi_task *task = scsi_cbdata->task; - statsn = scsi_get_uint32(&in->hdr[24]); - if (statsn > iscsi->statsn) { - iscsi->statsn = statsn; - } - - maxcmdsn = scsi_get_uint32(&in->hdr[32]); - if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { - iscsi->maxcmdsn = maxcmdsn; - } - expcmdsn = scsi_get_uint32(&in->hdr[28]); - if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { - iscsi->expcmdsn = expcmdsn; - } + iscsi_adjust_statsn(iscsi, in); + iscsi_adjust_maxexpcmdsn(iscsi, in); flags = in->hdr[1]; if ((flags&ISCSI_PDU_DATA_FINAL) == 0) { @@ -489,24 +478,13 @@ int iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in, int *is_finished) { - uint32_t statsn, maxcmdsn, expcmdsn, flags, status; + uint32_t flags, status; struct iscsi_scsi_cbdata *scsi_cbdata = &pdu->scsi_cbdata; struct scsi_task *task = scsi_cbdata->task; int dsl; - statsn = scsi_get_uint32(&in->hdr[24]); - if (statsn > iscsi->statsn) { - iscsi->statsn = statsn; - } - - maxcmdsn = scsi_get_uint32(&in->hdr[32]); - if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { - iscsi->maxcmdsn = maxcmdsn; - } - expcmdsn = scsi_get_uint32(&in->hdr[28]); - if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { - iscsi->expcmdsn = expcmdsn; - } + iscsi_adjust_statsn(iscsi, in); + iscsi_adjust_maxexpcmdsn(iscsi, in); flags = in->hdr[1]; if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) { @@ -579,20 +557,13 @@ int iscsi_process_r2t(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { - uint32_t ttt, offset, len, maxcmdsn, expcmdsn; + uint32_t ttt, offset, len; ttt = scsi_get_uint32(&in->hdr[20]); offset = scsi_get_uint32(&in->hdr[40]); len = scsi_get_uint32(&in->hdr[44]); - maxcmdsn = scsi_get_uint32(&in->hdr[32]); - if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { - iscsi->maxcmdsn = maxcmdsn; - } - expcmdsn = scsi_get_uint32(&in->hdr[28]); - if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { - iscsi->expcmdsn = expcmdsn; - } + iscsi_adjust_maxexpcmdsn(iscsi, in); pdu->datasn = 0; iscsi_send_data_out(iscsi, pdu, ttt, offset, len); diff --git a/lib/login.c b/lib/login.c index 493b2bc..59c55d2 100644 --- a/lib/login.c +++ b/lib/login.c @@ -965,22 +965,14 @@ int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { - uint32_t status, maxcmdsn, expcmdsn; + uint32_t status; char *ptr = (char *)in->data; int size = in->data_pos; status = scsi_get_uint16(&in->hdr[36]); - iscsi->statsn = scsi_get_uint16(&in->hdr[24]); - - maxcmdsn = scsi_get_uint32(&in->hdr[32]); - if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { - iscsi->maxcmdsn = maxcmdsn; - } - expcmdsn = scsi_get_uint32(&in->hdr[28]); - if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { - iscsi->expcmdsn = expcmdsn; - } + iscsi_adjust_statsn(iscsi, in); + iscsi_adjust_maxexpcmdsn(iscsi, in); /* XXX here we should parse the data returned in case the target * renegotiated some some parameters. @@ -1176,16 +1168,7 @@ int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { - uint32_t maxcmdsn, expcmdsn; - - maxcmdsn = scsi_get_uint32(&in->hdr[32]); - if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { - iscsi->maxcmdsn = maxcmdsn; - } - expcmdsn = scsi_get_uint32(&in->hdr[28]); - if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { - iscsi->expcmdsn = expcmdsn; - } + iscsi_adjust_maxexpcmdsn(iscsi, in); iscsi->is_loggedin = 0; pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data); diff --git a/lib/pdu.c b/lib/pdu.c index 72a6e3f..292e6e5 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -69,6 +69,31 @@ iscsi_itt_post_increment(struct iscsi_context *iscsi) { return old_itt; } +void iscsi_adjust_statsn(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { + uint32_t statsn = scsi_get_uint32(&in->hdr[24]); + uint32_t itt = scsi_get_uint32(&in->hdr[16]); + + if (itt == 0xffffffff) { + /* target will not increase statsn if itt == 0xffffffff */ + statsn--; + } + + if (iscsi_serial32_compare(statsn, iscsi->statsn) > 0) { + iscsi->statsn = statsn; + } +} + +void iscsi_adjust_maxexpcmdsn(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { + uint32_t maxcmdsn = scsi_get_uint32(&in->hdr[32]); + if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { + iscsi->maxcmdsn = maxcmdsn; + } + uint32_t expcmdsn = scsi_get_uint32(&in->hdr[28]); + if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { + iscsi->expcmdsn = expcmdsn; + } +} + void iscsi_dump_pdu_header(struct iscsi_context *iscsi, unsigned char *data) { char dump[ISCSI_RAW_HEADER_SIZE*3+1]={0}; int i; @@ -297,14 +322,10 @@ int iscsi_process_target_nop_in(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t ttt; - uint32_t statsn; ttt = scsi_get_uint32(&in->hdr[20]); - statsn = scsi_get_uint32(&in->hdr[24]); - if (statsn > iscsi->statsn) { - iscsi->statsn = statsn; - } + iscsi_adjust_statsn(iscsi, in); /* if the server does not want a response */ if (ttt == 0xffffffff) { diff --git a/lib/task_mgmt.c b/lib/task_mgmt.c index 68889a4..7a3d749 100644 --- a/lib/task_mgmt.c +++ b/lib/task_mgmt.c @@ -92,18 +92,11 @@ int iscsi_process_task_mgmt_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { - uint32_t response, maxcmdsn, expcmdsn; + uint32_t response; response = in->hdr[2]; - maxcmdsn = scsi_get_uint32(&in->hdr[32]); - if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { - iscsi->maxcmdsn = maxcmdsn; - } - expcmdsn = scsi_get_uint32(&in->hdr[28]); - if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { - iscsi->expcmdsn = expcmdsn; - } + iscsi_adjust_maxexpcmdsn(iscsi, in); pdu->callback(iscsi, SCSI_STATUS_GOOD, &response, pdu->private_data); From ce0723320cf485ffce4e497d6e0ca30946c83580 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 2 Apr 2014 12:44:04 +0200 Subject: [PATCH 12/12] libiscsi: fix dangling pointer for outqueue_current The outqueue_current PDU might also be in waitpdu if it does not have ISCSI_PDU_DELETE_WHEN_SENT. outqueue_current is freed after the waitpdu list (for reconnect or defer_reconnect), or sometimes not considered at all (for cancel), and this can cause a dangling pointer. Keep outqueue_current up to date when a PDU is freed. A bit hacky, but it avoids touching code all over the place. Signed-off-by: Paolo Bonzini --- lib/pdu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pdu.c b/lib/pdu.c index 292e6e5..66a24e8 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -172,6 +172,10 @@ iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) } pdu->indata.data = NULL; + if (iscsi->outqueue_current == pdu) { + iscsi->outqueue_current = NULL; + } + iscsi_sfree(iscsi, pdu); }