Protect outqueue and waitpdu with a spinlock

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
This commit is contained in:
Ronnie Sahlberg
2025-04-25 11:05:37 +10:00
parent 4593746363
commit 91cc1e4197
9 changed files with 271 additions and 160 deletions

307
lib/pdu.c
View File

@@ -458,11 +458,13 @@ int iscsi_process_reject(struct iscsi_context *iscsi,
iscsi_dump_pdu_header(iscsi, in->data);
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
if (pdu->itt == itt) {
break;
}
}
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
if (pdu == NULL) {
iscsi_set_error(iscsi, "Can not match REJECT with"
@@ -476,7 +478,9 @@ int iscsi_process_reject(struct iscsi_context *iscsi,
pdu->private_data);
}
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
iscsi->drv->free_pdu(iscsi, pdu);
return 0;
}
@@ -525,6 +529,8 @@ iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in)
enum iscsi_opcode opcode = in->hdr[0] & 0x3f;
uint8_t ahslen = in->hdr[4];
struct iscsi_pdu *pdu;
enum iscsi_opcode expected_response;
int is_finished = 1;
/* verify header checksum */
if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE) {
@@ -623,125 +629,148 @@ iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in)
return 0;
}
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
enum iscsi_opcode expected_response = pdu->response_opcode;
int is_finished = 1;
if (pdu->itt != itt) {
continue;
if (pdu->itt == itt) {
break;
}
}
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
if (pdu == NULL) {
iscsi_set_error(iscsi, "Got unsolicited response with "
"itt:%d",
itt);
return -1;
}
/* we have a special case with scsi-command opcodes,
* they are replied to by either a scsi-response
* or a data-in, or a combination of both.
*/
expected_response = pdu->response_opcode;
if (opcode == ISCSI_PDU_DATA_IN
&& expected_response == ISCSI_PDU_SCSI_RESPONSE) {
expected_response = ISCSI_PDU_DATA_IN;
}
/* we have a special case with scsi-command opcodes,
* they are replied to by either a scsi-response
* or a data-in, or a combination of both.
*/
if (opcode == ISCSI_PDU_DATA_IN
&& expected_response == ISCSI_PDU_SCSI_RESPONSE) {
expected_response = ISCSI_PDU_DATA_IN;
}
/* Another special case is if we get a R2T.
* In this case we should find the original request and just send an additional
* DATAOUT segment for this task.
*/
if (opcode == ISCSI_PDU_R2T) {
expected_response = ISCSI_PDU_R2T;
}
/* Another special case is if we get a R2T.
* In this case we should find the original request and just send an additional
* DATAOUT segment for this task.
*/
if (opcode == ISCSI_PDU_R2T) {
expected_response = ISCSI_PDU_R2T;
}
if (opcode != expected_response) {
iscsi_set_error(iscsi, "Got wrong opcode back for "
"itt:%d got:%d expected %d",
itt, opcode, pdu->response_opcode);
return -1;
}
switch (opcode) {
case ISCSI_PDU_LOGIN_RESPONSE:
if (iscsi_process_login_reply(iscsi, pdu, in) != 0) {
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi login reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_TEXT_RESPONSE:
if (iscsi_process_text_reply(iscsi, pdu, in) != 0) {
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi text reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_LOGOUT_RESPONSE:
if (iscsi_process_logout_reply(iscsi, pdu, in) != 0) {
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi logout reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_SCSI_RESPONSE:
if (iscsi_process_scsi_reply(iscsi, pdu, in) != 0) {
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi response reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_DATA_IN:
if (iscsi_process_scsi_data_in(iscsi, pdu, in,
&is_finished) != 0) {
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi data in "
"failed");
return -1;
}
break;
case ISCSI_PDU_NOP_IN:
if (iscsi_process_nop_out_reply(iscsi, pdu, in) != 0) {
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi nop-in failed");
return -1;
}
break;
case ISCSI_PDU_SCSI_TASK_MANAGEMENT_RESPONSE:
if (iscsi_process_task_mgmt_reply(iscsi, pdu,
in) != 0) {
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi task-mgmt failed");
return -1;
}
break;
case ISCSI_PDU_R2T:
if (iscsi_process_r2t(iscsi, pdu, in) != 0) {
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi r2t "
"failed");
return -1;
}
is_finished = 0;
break;
default:
iscsi_set_error(iscsi, "Don't know how to handle "
"opcode 0x%02x", opcode);
return -1;
}
if (opcode != expected_response) {
iscsi_set_error(iscsi, "Got wrong opcode back for "
"itt:%d got:%d expected %d",
itt, opcode, pdu->response_opcode);
return -1;
}
switch (opcode) {
case ISCSI_PDU_LOGIN_RESPONSE:
if (iscsi_process_login_reply(iscsi, pdu, in) != 0) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi login reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_TEXT_RESPONSE:
if (iscsi_process_text_reply(iscsi, pdu, in) != 0) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi text reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_LOGOUT_RESPONSE:
if (iscsi_process_logout_reply(iscsi, pdu, in) != 0) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi logout reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_SCSI_RESPONSE:
if (iscsi_process_scsi_reply(iscsi, pdu, in) != 0) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi response reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_DATA_IN:
if (iscsi_process_scsi_data_in(iscsi, pdu, in,
&is_finished) != 0) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi data in "
"failed");
return -1;
}
break;
case ISCSI_PDU_NOP_IN:
if (iscsi_process_nop_out_reply(iscsi, pdu, in) != 0) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi nop-in failed");
return -1;
}
break;
case ISCSI_PDU_SCSI_TASK_MANAGEMENT_RESPONSE:
if (iscsi_process_task_mgmt_reply(iscsi, pdu,
in) != 0) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi task-mgmt failed");
return -1;
}
break;
case ISCSI_PDU_R2T:
if (iscsi_process_r2t(iscsi, pdu, in) != 0) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi r2t "
"failed");
return -1;
}
is_finished = 0;
break;
default:
iscsi_set_error(iscsi, "Don't know how to handle "
"opcode 0x%02x", opcode);
return -1;
}
if (is_finished && iscsi->waitpdu != NULL) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
}
return 0;
}
return 0;
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
if (is_finished && iscsi->waitpdu != NULL) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi->drv->free_pdu(iscsi, pdu);
}
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
return 0;
}
void
@@ -842,7 +871,8 @@ static int iscsi_pdu_data_out_inprocess(struct iscsi_context *iscsi, struct iscs
{
struct iscsi_pdu *tmp_pdu, *next_pdu;
enum scsi_opcode opcode = pdu->outdata.data[32];
int ret = 1;
/* only care DATA OUT command here */
if ((pdu->outdata.data[0] & 0x3f) != ISCSI_PDU_SCSI_REQUEST) {
return 0;
@@ -857,9 +887,10 @@ static int iscsi_pdu_data_out_inprocess(struct iscsi_context *iscsi, struct iscs
return 0;
};
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
/* current outgoing one is part of the PDU? */
if (iscsi->outqueue_current && (iscsi->outqueue_current->scsi_cbdata.task == pdu->scsi_cbdata.task)) {
return 1;
goto finished;
}
/* any child DATAOUT PDU in outqueue? */
@@ -867,21 +898,26 @@ static int iscsi_pdu_data_out_inprocess(struct iscsi_context *iscsi, struct iscs
next_pdu = tmp_pdu->next;
if (tmp_pdu->scsi_cbdata.task == pdu->scsi_cbdata.task) {
return 1;
goto finished;
}
}
return 0;
ret = 0;
finished:
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
return ret;
}
void
iscsi_timeout_scan(struct iscsi_context *iscsi)
{
struct iscsi_pdu *pdu;
struct iscsi_pdu *pdu, *tmp;
struct iscsi_pdu *next_pdu;
time_t t = time(NULL);
uint32_t cmdsn_gap = 0;
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
tmp = NULL;
for (pdu = iscsi->outqueue; pdu; pdu = next_pdu) {
next_pdu = pdu->next;
@@ -904,6 +940,11 @@ iscsi_timeout_scan(struct iscsi_context *iscsi)
continue;
}
ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu);
ISCSI_LIST_ADD_END(&tmp, pdu);
}
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
for (pdu = tmp; pdu; pdu = next_pdu) {
ISCSI_LIST_REMOVE(&tmp, pdu);
iscsi_set_error(iscsi, "command timed out from outqueue");
iscsi_dump_pdu_header(iscsi, pdu->outdata.data);
if (pdu->callback) {
@@ -912,6 +953,9 @@ iscsi_timeout_scan(struct iscsi_context *iscsi)
}
iscsi->drv->free_pdu(iscsi, pdu);
}
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
tmp = NULL;
for (pdu = iscsi->waitpdu; pdu; pdu = next_pdu) {
next_pdu = pdu->next;
@@ -927,6 +971,11 @@ iscsi_timeout_scan(struct iscsi_context *iscsi)
continue;
}
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
ISCSI_LIST_ADD_END(&tmp, pdu);
}
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
for (pdu = tmp; pdu; pdu = next_pdu) {
ISCSI_LIST_REMOVE(&tmp, pdu);
iscsi_set_error(iscsi, "command timed out from waitqueue");
iscsi_dump_pdu_header(iscsi, pdu->outdata.data);
if (pdu->callback) {
@@ -946,8 +995,9 @@ iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
void
iscsi_cancel_pdus(struct iscsi_context *iscsi)
{
struct iscsi_pdu *pdu;
struct iscsi_pdu *pdu, *tmp;
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
while ((pdu = iscsi->outqueue)) {
ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu);
if (pdu->callback) {
@@ -960,8 +1010,12 @@ iscsi_cancel_pdus(struct iscsi_context *iscsi)
}
iscsi->drv->free_pdu(iscsi, pdu);
}
while ((pdu = iscsi->waitpdu)) {
ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu);
tmp = iscsi->waitpdu;
iscsi->waitpdu = NULL;
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
while ((pdu = tmp)) {
ISCSI_LIST_REMOVE(&tmp, pdu);
if (pdu->callback) {
pdu->callback(iscsi, SCSI_STATUS_CANCELLED,
NULL, pdu->private_data);
@@ -973,11 +1027,13 @@ iscsi_cancel_pdus(struct iscsi_context *iscsi)
void
iscsi_cancel_lun_pdus(struct iscsi_context *iscsi, uint32_t lun)
{
struct iscsi_pdu *pdu;
struct iscsi_pdu *pdu, *tmp;
struct iscsi_pdu *next_pdu;
uint32_t cmdsn_gap = 0;
struct scsi_task * task = NULL;
iscsi_mt_spin_lock(&iscsi->iscsi_lock);
tmp = NULL;
for (pdu = iscsi->outqueue; pdu; pdu = next_pdu) {
next_pdu = pdu->next;
task = iscsi_scsi_get_task_from_pdu(pdu);
@@ -994,6 +1050,11 @@ iscsi_cancel_lun_pdus(struct iscsi_context *iscsi, uint32_t lun)
cmdsn_gap++;
}
ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu);
ISCSI_LIST_ADD_END(&tmp, pdu);
}
iscsi_mt_spin_unlock(&iscsi->iscsi_lock);
for (pdu = tmp; pdu; pdu = next_pdu) {
ISCSI_LIST_REMOVE(&tmp, pdu);
iscsi_set_error(iscsi, "command cancelled");
if (pdu->callback) {
pdu->callback(iscsi, SCSI_STATUS_CANCELLED,