Merge pull request #123 from plieven/asyncv3

add support for async messages
This commit is contained in:
Ronnie Sahlberg
2014-07-13 16:46:27 -07:00
6 changed files with 145 additions and 92 deletions

View File

@@ -80,6 +80,7 @@ struct iscsi_context {
int fd;
int is_connected;
int is_corked;
int tcp_user_timeout;
int tcp_keepcnt;
@@ -125,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;
@@ -188,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 {
@@ -209,6 +212,13 @@ 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
/* 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;
@@ -339,14 +349,18 @@ 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);
int
iscsi_logout_async_internal(struct iscsi_context *iscsi, iscsi_command_cb cb,
void *private_data, uint32_t flags);
#ifdef __cplusplus
}
#endif

View File

@@ -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;
@@ -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);

View File

@@ -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.
@@ -1117,10 +1109,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,6 +1145,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 | flags;
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
@@ -1165,20 +1157,18 @@ 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)
{
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);

View File

@@ -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;
@@ -147,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);
}
@@ -297,14 +326,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) {
@@ -317,12 +342,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 +369,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) {
@@ -350,8 +396,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);
@@ -376,13 +424,36 @@ iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in)
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]));
if (iscsi_process_reject(iscsi, in) != 0) {
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;
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;
}
return 0;
}
if (opcode == ISCSI_PDU_REJECT) {
return iscsi_process_reject(iscsi, in);
}
if (opcode == ISCSI_PDU_NOP_IN && itt == 0xffffffff) {

View File

@@ -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;
@@ -611,8 +612,15 @@ 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->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 */
return 0;
}
/* pop first element of the outqueue */
@@ -698,10 +706,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;
@@ -725,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);

View File

@@ -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);