diff --git a/lib/iscsi/conn.c b/lib/iscsi/conn.c index 259d18ed2..f9531d696 100644 --- a/lib/iscsi/conn.c +++ b/lib/iscsi/conn.c @@ -1166,7 +1166,11 @@ spdk_iscsi_conn_flush_pdus_internal(struct spdk_iscsi_conn *conn) /* * Build up a list of iovecs for the first few PDUs in the - * connection's write_pdu_list. + * connection's write_pdu_list. For the first PDU, check if it was + * partially written out the last time this function was called, and + * if so adjust the iovec array accordingly. This check is done in + * spdk_iscsi_build_iovs() and so applied to remaining PDUs too. + * But extra overhead is negligible. */ while (pdu != NULL && ((num_iovs - iovcnt) >= 5)) { pdu_length = spdk_iscsi_get_pdu_length(pdu, @@ -1177,24 +1181,8 @@ spdk_iscsi_conn_flush_pdus_internal(struct spdk_iscsi_conn *conn) pdu = TAILQ_NEXT(pdu, tailq); } - /* - * Check if the first PDU was partially written out the last time - * this function was called, and if so adjust the iovec array - * accordingly. - */ writev_offset = TAILQ_FIRST(&conn->write_pdu_list)->writev_offset; total_length -= writev_offset; - while (writev_offset > 0) { - if (writev_offset >= iov->iov_len) { - writev_offset -= iov->iov_len; - iov++; - iovcnt--; - } else { - iov->iov_len -= writev_offset; - iov->iov_base = (char *)iov->iov_base + writev_offset; - writev_offset = 0; - } - } spdk_trace_record(TRACE_ISCSI_FLUSH_WRITEBUF_START, conn->id, total_length, 0, iovcnt); diff --git a/lib/iscsi/iscsi.c b/lib/iscsi/iscsi.c index 8661cc839..2c1bab467 100644 --- a/lib/iscsi/iscsi.c +++ b/lib/iscsi/iscsi.c @@ -569,11 +569,13 @@ spdk_iscsi_build_iovs(struct spdk_iscsi_conn *conn, struct iovec *iovs, { int iovcnt = 0; int enable_digest; - int total_ahs_len; - int data_len; + uint32_t total_ahs_len; + uint32_t data_len; + uint32_t iov_offset; total_ahs_len = pdu->bhs.total_ahs_len; data_len = DGET24(pdu->bhs.data_segment_len); + data_len = ISCSI_ALIGN(data_len); enable_digest = 1; if (pdu->bhs.opcode == ISCSI_OP_LOGIN_RSP) { @@ -581,37 +583,60 @@ spdk_iscsi_build_iovs(struct spdk_iscsi_conn *conn, struct iovec *iovs, enable_digest = 0; } - /* BHS */ - iovs[iovcnt].iov_base = &pdu->bhs; - iovs[iovcnt].iov_len = ISCSI_BHS_LEN; - iovcnt++; + iov_offset = pdu->writev_offset; + /* BHS */ + if (iov_offset >= ISCSI_BHS_LEN) { + iov_offset -= ISCSI_BHS_LEN; + } else { + iovs[iovcnt].iov_base = (uint8_t *)&pdu->bhs + iov_offset; + iovs[iovcnt].iov_len = ISCSI_BHS_LEN - iov_offset; + iov_offset = 0; + iovcnt++; + } /* AHS */ if (total_ahs_len > 0) { - iovs[iovcnt].iov_base = pdu->ahs; - iovs[iovcnt].iov_len = 4 * total_ahs_len; - iovcnt++; + if (iov_offset >= 4 * total_ahs_len) { + iov_offset -= 4 * total_ahs_len; + } else { + iovs[iovcnt].iov_base = pdu->ahs + iov_offset; + iovs[iovcnt].iov_len = 4 * total_ahs_len - iov_offset; + iov_offset = 0; + iovcnt++; + } } /* Header Digest */ if (enable_digest && conn->header_digest) { - iovs[iovcnt].iov_base = pdu->header_digest; - iovs[iovcnt].iov_len = ISCSI_DIGEST_LEN; - iovcnt++; + if (iov_offset >= ISCSI_DIGEST_LEN) { + iov_offset -= ISCSI_DIGEST_LEN; + } else { + iovs[iovcnt].iov_base = pdu->header_digest + iov_offset; + iovs[iovcnt].iov_len = ISCSI_DIGEST_LEN - iov_offset; + iov_offset = 0; + iovcnt++; + } } /* Data Segment */ if (data_len > 0) { - iovs[iovcnt].iov_base = pdu->data; - iovs[iovcnt].iov_len = ISCSI_ALIGN(data_len); - iovcnt++; + if (iov_offset >= data_len) { + iov_offset -= data_len; + } else { + iovs[iovcnt].iov_base = pdu->data + iov_offset; + iovs[iovcnt].iov_len = data_len - iov_offset; + iov_offset = 0; + iovcnt++; + } } /* Data Digest */ if (enable_digest && conn->data_digest && data_len != 0) { - iovs[iovcnt].iov_base = pdu->data_digest; - iovs[iovcnt].iov_len = ISCSI_DIGEST_LEN; - iovcnt++; + if (iov_offset < ISCSI_DIGEST_LEN) { + iovs[iovcnt].iov_base = pdu->data_digest + iov_offset; + iovs[iovcnt].iov_len = ISCSI_DIGEST_LEN - iov_offset; + iovcnt++; + } } return iovcnt; diff --git a/test/unit/lib/iscsi/iscsi.c/iscsi_ut.c b/test/unit/lib/iscsi/iscsi.c/iscsi_ut.c index 202e1a3d3..5180f7545 100644 --- a/test/unit/lib/iscsi/iscsi.c/iscsi_ut.c +++ b/test/unit/lib/iscsi/iscsi.c/iscsi_ut.c @@ -1202,6 +1202,97 @@ abort_queued_datain_tasks_test(void) spdk_put_pdu(pdu1); } +static void +build_iovs_test(void) +{ + struct spdk_iscsi_conn conn = {}; + struct spdk_iscsi_pdu pdu = {}; + struct iovec iovs[5] = {}; + uint8_t *data; + int rc; + + conn.header_digest = true; + conn.data_digest = true; + + DSET24(&pdu.bhs.data_segment_len, 512); + data = calloc(1, 512); + SPDK_CU_ASSERT_FATAL(data != NULL); + pdu.data = data; + + pdu.bhs.total_ahs_len = 0; + pdu.bhs.opcode = ISCSI_OP_SCSI; + + pdu.writev_offset = 0; + rc = spdk_iscsi_build_iovs(&conn, iovs, &pdu); + CU_ASSERT(rc == 4); + CU_ASSERT(iovs[0].iov_base == (void *)&pdu.bhs); + CU_ASSERT(iovs[0].iov_len == ISCSI_BHS_LEN); + CU_ASSERT(iovs[1].iov_base == (void *)pdu.header_digest); + CU_ASSERT(iovs[1].iov_len == ISCSI_DIGEST_LEN); + CU_ASSERT(iovs[2].iov_base == (void *)pdu.data); + CU_ASSERT(iovs[2].iov_len == 512); + CU_ASSERT(iovs[3].iov_base == (void *)pdu.data_digest); + CU_ASSERT(iovs[3].iov_len == ISCSI_DIGEST_LEN); + + pdu.writev_offset = ISCSI_BHS_LEN / 2; + rc = spdk_iscsi_build_iovs(&conn, iovs, &pdu); + CU_ASSERT(rc == 4); + CU_ASSERT(iovs[0].iov_base == (void *)((uint8_t *)&pdu.bhs + ISCSI_BHS_LEN / 2)); + CU_ASSERT(iovs[0].iov_len == ISCSI_BHS_LEN / 2); + CU_ASSERT(iovs[1].iov_base == (void *)pdu.header_digest); + CU_ASSERT(iovs[1].iov_len == ISCSI_DIGEST_LEN); + CU_ASSERT(iovs[2].iov_base == (void *)pdu.data); + CU_ASSERT(iovs[2].iov_len == 512); + CU_ASSERT(iovs[3].iov_base == (void *)pdu.data_digest); + CU_ASSERT(iovs[3].iov_len == ISCSI_DIGEST_LEN); + + pdu.writev_offset = ISCSI_BHS_LEN; + rc = spdk_iscsi_build_iovs(&conn, iovs, &pdu); + CU_ASSERT(rc == 3); + CU_ASSERT(iovs[0].iov_base == (void *)pdu.header_digest); + CU_ASSERT(iovs[0].iov_len == ISCSI_DIGEST_LEN); + CU_ASSERT(iovs[1].iov_base == (void *)pdu.data); + CU_ASSERT(iovs[1].iov_len == 512); + CU_ASSERT(iovs[2].iov_base == (void *)pdu.data_digest); + CU_ASSERT(iovs[2].iov_len == ISCSI_DIGEST_LEN); + + pdu.writev_offset = ISCSI_BHS_LEN + ISCSI_DIGEST_LEN / 2; + rc = spdk_iscsi_build_iovs(&conn, iovs, &pdu); + CU_ASSERT(rc == 3); + CU_ASSERT(iovs[0].iov_base == (void *)((uint8_t *)pdu.header_digest + ISCSI_DIGEST_LEN / 2)); + CU_ASSERT(iovs[0].iov_len == ISCSI_DIGEST_LEN / 2); + CU_ASSERT(iovs[1].iov_base == (void *)pdu.data); + CU_ASSERT(iovs[1].iov_len == 512); + CU_ASSERT(iovs[2].iov_base == (void *)pdu.data_digest); + CU_ASSERT(iovs[2].iov_len == ISCSI_DIGEST_LEN); + + pdu.writev_offset = ISCSI_BHS_LEN + ISCSI_DIGEST_LEN; + rc = spdk_iscsi_build_iovs(&conn, iovs, &pdu); + CU_ASSERT(rc == 2); + CU_ASSERT(iovs[0].iov_base == (void *)pdu.data); + CU_ASSERT(iovs[0].iov_len == 512); + CU_ASSERT(iovs[1].iov_base == (void *)pdu.data_digest); + CU_ASSERT(iovs[1].iov_len == ISCSI_DIGEST_LEN); + + pdu.writev_offset = ISCSI_BHS_LEN + ISCSI_DIGEST_LEN + 512; + rc = spdk_iscsi_build_iovs(&conn, iovs, &pdu); + CU_ASSERT(rc == 1); + CU_ASSERT(iovs[0].iov_base == (void *)pdu.data_digest); + CU_ASSERT(iovs[0].iov_len == ISCSI_DIGEST_LEN); + + pdu.writev_offset = ISCSI_BHS_LEN + ISCSI_DIGEST_LEN + 512 + ISCSI_DIGEST_LEN / 2; + rc = spdk_iscsi_build_iovs(&conn, iovs, &pdu); + CU_ASSERT(rc == 1); + CU_ASSERT(iovs[0].iov_base == (void *)((uint8_t *)pdu.data_digest + ISCSI_DIGEST_LEN / 2)); + CU_ASSERT(iovs[0].iov_len == ISCSI_DIGEST_LEN / 2); + + pdu.writev_offset = ISCSI_BHS_LEN + ISCSI_DIGEST_LEN + 512 + ISCSI_DIGEST_LEN; + rc = spdk_iscsi_build_iovs(&conn, iovs, &pdu); + CU_ASSERT(rc == 0); + + free(data); +} + int main(int argc, char **argv) { @@ -1238,6 +1329,7 @@ main(int argc, char **argv) abort_queued_datain_task_test) == NULL || CU_add_test(suite, "abort_queued_datain_tasks_test", abort_queued_datain_tasks_test) == NULL + || CU_add_test(suite, "build_iovs_test", build_iovs_test) == NULL ) { CU_cleanup_registry(); return CU_get_error();