PDUの受信処理。
int
iscsi_handle_incoming_pdus(struct spdk_iscsi_conn *conn)
{
int i, rc;
/* Read new PDUs from network */
for (i = 0; i < GET_PDU_LOOP_COUNT; i++) {
rc = iscsi_read_pdu(conn); ★本体
if (rc == 0) {
break;
} else if (rc < 0) {
return rc;
}
if (conn->is_stopped) {
break;
}
}
return i;
}
iscsi_read_pduはconnのステートに応じて処理。
static int
iscsi_read_pdu(struct spdk_iscsi_conn *conn)
{
enum iscsi_pdu_recv_state prev_state;
struct spdk_iscsi_pdu *pdu;
uint32_t crc32c;
int ahs_len;
int rc;
do {
prev_state = conn->pdu_recv_state;
pdu = conn->pdu_in_progress;
switch (conn->pdu_recv_state) {
case ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY:
assert(conn->pdu_in_progress == NULL);
conn->pdu_in_progress = iscsi_get_pdu(conn);
if (conn->pdu_in_progress == NULL) {
return SPDK_ISCSI_CONNECTION_FATAL;
}
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_HDR;
break;
case ISCSI_PDU_RECV_STATE_AWAIT_PDU_HDR:
if (pdu->bhs_valid_bytes < ISCSI_BHS_LEN) {
rc = iscsi_conn_read_data(conn,
ISCSI_BHS_LEN - pdu->bhs_valid_bytes,
(uint8_t *)&pdu->bhs + pdu->bhs_valid_bytes);
if (rc < 0) {
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
break;
}
pdu->bhs_valid_bytes += rc;
if (pdu->bhs_valid_bytes < ISCSI_BHS_LEN) {
return 0;
}
}
/* conn->is_logged_out must be checked after completing to process
* logout request, i.e., before processing PDU header in this state
* machine, otherwise logout response may not be sent to initiator
* and initiator may get logout timeout.
*/
if (spdk_unlikely(conn->is_logged_out)) {
SPDK_DEBUGLOG(iscsi, "pdu received after logout\n");
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
break;
}
pdu->data_segment_len = ISCSI_ALIGN(DGET24(pdu->bhs.data_segment_len));
pdu->data_buf_len = pdu->data_segment_len;
/* AHS */
ahs_len = pdu->bhs.total_ahs_len * 4;
if (ahs_len > ISCSI_AHS_LEN) {
SPDK_DEBUGLOG(iscsi, "pdu ahs length %d is invalid\n", ahs_len);
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
break;
}
if (pdu->ahs_valid_bytes < ahs_len) {
rc = iscsi_conn_read_data(conn,
ahs_len - pdu->ahs_valid_bytes,
pdu->ahs + pdu->ahs_valid_bytes);
if (rc < 0) {
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
break;
}
pdu->ahs_valid_bytes += rc;
if (pdu->ahs_valid_bytes < ahs_len) {
return 0;
}
}
/* Header Digest */
if (conn->header_digest &&
pdu->hdigest_valid_bytes < ISCSI_DIGEST_LEN) {
rc = iscsi_conn_read_data(conn,
ISCSI_DIGEST_LEN - pdu->hdigest_valid_bytes,
pdu->header_digest + pdu->hdigest_valid_bytes);
if (rc < 0) {
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
break;
}
pdu->hdigest_valid_bytes += rc;
if (pdu->hdigest_valid_bytes < ISCSI_DIGEST_LEN) {
return 0;
}
}
if (conn->header_digest) {
crc32c = iscsi_pdu_calc_header_digest(pdu);
rc = MATCH_DIGEST_WORD(pdu->header_digest, crc32c);
if (rc == 0) {
SPDK_ERRLOG("header digest error (%s)\n", conn->initiator_name);
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
break;
}
}
rc = iscsi_pdu_hdr_handle(conn, pdu);
if (rc < 0) {
SPDK_ERRLOG("Critical error is detected. Close the connection\n");
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
break;
}
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD;
break;
case ISCSI_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD:
if (pdu->data_segment_len != 0) {
rc = iscsi_pdu_payload_read(conn, pdu);
if (rc > 0) {
return 0;
} else if (rc < 0) {
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
break;
}
}
/* All data for this PDU has now been read from the socket. */
spdk_trace_record(TRACE_ISCSI_READ_PDU, conn->id, pdu->data_valid_bytes,
(uintptr_t)pdu, pdu->bhs.opcode);
if (!pdu->is_rejected) {
rc = iscsi_pdu_payload_handle(conn, pdu);
} else {
rc = 0;
}
if (rc == 0) {
spdk_trace_record(TRACE_ISCSI_TASK_EXECUTED, 0, 0, (uintptr_t)pdu);
iscsi_put_pdu(pdu);
conn->pdu_in_progress = NULL;
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY;
return 1;
} else {
conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
}
break;
case ISCSI_PDU_RECV_STATE_ERROR:
return SPDK_ISCSI_CONNECTION_FATAL;
default:
assert(false);
SPDK_ERRLOG("code should not come here\n");
break;
}
} while (prev_state != conn->pdu_recv_state);
return 0;
}