--- embedaddon/istgt/src/istgt_iscsi.c 2012/10/09 09:13:23 1.1.1.2 +++ embedaddon/istgt/src/istgt_iscsi.c 2013/07/21 23:49:22 1.1.1.3 @@ -117,6 +117,14 @@ static void istgt_remove_conn(CONN_Ptr conn); static int istgt_iscsi_drop_all_conns(CONN_Ptr conn); static int istgt_iscsi_drop_old_conns(CONN_Ptr conn); +/* Switch to use readv/writev (assume blocking) */ +#define ISTGT_USE_IOVEC + +#if defined (ISTGT_USE_IOVEC) +#include +#endif + +#if !defined (ISTGT_USE_IOVEC) #if 0 #define ISTGT_USE_RECVBLOCK #define ISTGT_USE_SENDBLOCK @@ -376,6 +384,7 @@ istgt_iscsi_write(CONN_Ptr conn, const void *buf, size #endif /* ISTGT_USE_SENDBLOCK */ return nbytes; } +#endif /* !defined (ISTGT_USE_IOVEC) */ #define MATCH_DIGEST_WORD(BUF, CRC32C) \ ( ((((uint32_t) *((uint8_t *)(BUF)+0)) << 0) \ @@ -417,6 +426,7 @@ istgt_make_digest_word(uint8_t *buf, size_t len, uint3 } #endif +#if !defined (ISTGT_USE_IOVEC) static int istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { @@ -447,13 +457,14 @@ istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) conn->initiator_name); conn->state = CONN_STATE_EXITING; } else { - ISTGT_ERRLOG("iscsi_read() failed (errno=%d)\n", - errno); + ISTGT_ERRLOG("iscsi_read() failed (errno=%d,%s)\n", + errno, conn->initiator_name); } return -1; } if (rc == 0) { - ISTGT_TRACELOG(ISTGT_TRACE_NET, "iscsi_read() EOF\n"); + ISTGT_TRACELOG(ISTGT_TRACE_NET, "iscsi_read() EOF (%s)\n", + conn->initiator_name); conn->state = CONN_STATE_EXITING; return -1; } @@ -471,7 +482,8 @@ istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) (4 * total_ahs_len)); rc = istgt_iscsi_read(conn, pdu->ahs, (4 * total_ahs_len)); if (rc < 0) { - ISTGT_ERRLOG("iscsi_read() failed\n"); + ISTGT_ERRLOG("iscsi_read() failed (errno=%d,%s)\n", + errno, conn->initiator_name); return -1; } if (rc == 0) { @@ -497,7 +509,8 @@ istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) rc = istgt_iscsi_read(conn, pdu->header_digest, ISCSI_DIGEST_LEN); if (rc < 0) { - ISTGT_ERRLOG("iscsi_read() failed\n"); + ISTGT_ERRLOG("iscsi_read() failed (errno=%d,%s)\n", + errno, conn->initiator_name); { int opcode = BGET8W(&pdu->bhs.opcode, 5, 6); ISTGT_ERRLOG("Header Digest read error (opcode = 0x%x)\n", @@ -540,8 +553,8 @@ istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) data_len); rc = istgt_iscsi_read(conn, pdu->data, data_len); if (rc < 0) { - ISTGT_ERRLOG("iscsi_read() failed (%d,errno=%d)\n", - rc, errno); + ISTGT_ERRLOG("iscsi_read() failed (%d,errno=%d,%s)\n", + rc, errno, conn->initiator_name); return -1; } if (rc == 0) { @@ -577,7 +590,8 @@ istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) rc = istgt_iscsi_read(conn, pdu->data_digest, ISCSI_DIGEST_LEN); if (rc < 0) { - ISTGT_ERRLOG("iscsi_read() failed\n"); + ISTGT_ERRLOG("iscsi_read() failed (errno=%d,%s)\n", + errno, conn->initiator_name); return -1; } if (rc == 0) { @@ -611,7 +625,7 @@ istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) } rc = MATCH_DIGEST_WORD(pdu->header_digest, crc32c); if (rc == 0) { - ISTGT_ERRLOG("header digest error\n"); + ISTGT_ERRLOG("header digest error (%s)\n", conn->initiator_name); return -1; } } @@ -619,14 +633,198 @@ istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) crc32c = istgt_crc32c(pdu->data, data_len); rc = MATCH_DIGEST_WORD(pdu->data_digest, crc32c); if (rc == 0) { - ISTGT_ERRLOG("data digest error\n"); + ISTGT_ERRLOG("data digest error (%s)\n", conn->initiator_name); return -1; } } return total; } +#else /* defined (ISTGT_USE_IOVEC) */ +static int +istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) +{ + struct iovec iovec[4]; /* AHS+HD+DATA+DD */ + uint32_t crc32c; + time_t start, now; + int nbytes; + int total_ahs_len; + int data_len; + int segment_len; + int total; + int rc; + int i; + pdu->ahs = NULL; + pdu->total_ahs_len = 0; + pdu->data = NULL; + pdu->data_segment_len = 0; + total = 0; + + /* BHS (require for all PDU) */ + ISTGT_TRACELOG(ISTGT_TRACE_NET, "BHS read %d\n", + ISCSI_BHS_LEN); + errno = 0; + start = time(NULL); + rc = recv(conn->sock, &pdu->bhs, ISCSI_BHS_LEN, MSG_WAITALL); + if (rc < 0) { + now = time(NULL); + if (errno == ECONNRESET) { + ISTGT_WARNLOG("Connection reset by peer (%s,time=%d)\n", + conn->initiator_name, (int)difftime(now, start)); + conn->state = CONN_STATE_EXITING; + } else if (errno == ETIMEDOUT) { + ISTGT_WARNLOG("Operation timed out (%s,time=%d)\n", + conn->initiator_name, (int)difftime(now, start)); + conn->state = CONN_STATE_EXITING; + } else { + ISTGT_ERRLOG("iscsi_read() failed (errno=%d,%s,time=%d)\n", + errno, conn->initiator_name, (int)difftime(now, start)); + } + return -1; + } + if (rc == 0) { + ISTGT_TRACELOG(ISTGT_TRACE_NET, "recv() EOF (%s)\n", + conn->initiator_name); + conn->state = CONN_STATE_EXITING; + return -1; + } + if (rc != ISCSI_BHS_LEN) { + ISTGT_ERRLOG("invalid BHS length (%d,%s)\n", rc, conn->initiator_name); + return -1; + } + total += ISCSI_BHS_LEN; + + /* AHS */ + total_ahs_len = DGET8(&pdu->bhs.total_ahs_len); + if (total_ahs_len != 0) { + pdu->ahs = xmalloc(ISCSI_ALIGN((4 * total_ahs_len))); + pdu->total_ahs_len = total_ahs_len; + total += (4 * total_ahs_len); + } else { + pdu->ahs = NULL; + pdu->total_ahs_len = 0; + } + iovec[0].iov_base = pdu->ahs; + iovec[0].iov_len = 4 * pdu->total_ahs_len; + + /* Header Digest */ + iovec[1].iov_base = pdu->header_digest; + if (conn->header_digest) { + iovec[1].iov_len = ISCSI_DIGEST_LEN; + total += ISCSI_DIGEST_LEN; + } else { + iovec[1].iov_len = 0; + } + + /* Data Segment */ + data_len = DGET24(&pdu->bhs.data_segment_len[0]); + if (data_len != 0) { + if (conn->sess == NULL) { + segment_len = DEFAULT_FIRSTBURSTLENGTH; + } else { + segment_len = conn->MaxRecvDataSegmentLength; + } + if (data_len > segment_len) { + ISTGT_ERRLOG("Data(%d) > Segment(%d)\n", + data_len, segment_len); + return -1; + } + if (ISCSI_ALIGN(data_len) <= ISTGT_SHORTDATASIZE) { + pdu->data = pdu->shortdata; + } else { + pdu->data = xmalloc(ISCSI_ALIGN(segment_len)); + } + pdu->data_segment_len = data_len; + total += ISCSI_ALIGN(data_len); + } else { + pdu->data = NULL; + pdu->data_segment_len = 0; + } + iovec[2].iov_base = pdu->data; + iovec[2].iov_len = ISCSI_ALIGN(pdu->data_segment_len); + + /* Data Digest */ + iovec[3].iov_base = pdu->data_digest; + if (conn->data_digest && data_len != 0) { + iovec[3].iov_len = ISCSI_DIGEST_LEN; + total += ISCSI_DIGEST_LEN; + } else { + iovec[3].iov_len = 0; + } + + /* read all bytes to iovec */ + nbytes = total - ISCSI_BHS_LEN; + ISTGT_TRACELOG(ISTGT_TRACE_NET, "PDU read %d\n", nbytes); + errno = 0; + start = time(NULL); + while (nbytes > 0) { + rc = readv(conn->sock, &iovec[0], 4); + if (rc < 0) { + now = time(NULL); + ISTGT_ERRLOG("readv() failed (%d,errno=%d,%s,time=%d)\n", + rc, errno, conn->initiator_name, (int)difftime(now, start)); + return -1; + } + if (rc == 0) { + ISTGT_TRACELOG(ISTGT_TRACE_NET, "readv() EOF (%s)\n", + conn->initiator_name); + conn->state = CONN_STATE_EXITING; + return -1; + } + nbytes -= rc; + if (nbytes == 0) + break; + /* adjust iovec length */ + for (i = 0; i < 4; i++) { + if (iovec[i].iov_len != 0 && iovec[i].iov_len > (size_t)rc) { + iovec[i].iov_base + = (void *) (((uintptr_t)iovec[i].iov_base) + rc); + iovec[i].iov_len -= rc; + break; + } else { + rc -= iovec[i].iov_len; + iovec[i].iov_len = 0; + } + } + } + + /* check digest */ + if (conn->header_digest) { + if (total_ahs_len == 0) { + crc32c = istgt_crc32c((uint8_t *) &pdu->bhs, + ISCSI_BHS_LEN); + } else { + int upd_total = 0; + crc32c = ISTGT_CRC32C_INITIAL; + crc32c = istgt_update_crc32c((uint8_t *) &pdu->bhs, + ISCSI_BHS_LEN, crc32c); + upd_total += ISCSI_BHS_LEN; + crc32c = istgt_update_crc32c((uint8_t *) pdu->ahs, + (4 * total_ahs_len), crc32c); + upd_total += (4 * total_ahs_len); + crc32c = istgt_fixup_crc32c(upd_total, crc32c); + crc32c = crc32c ^ ISTGT_CRC32C_XOR; + } + rc = MATCH_DIGEST_WORD(pdu->header_digest, crc32c); + if (rc == 0) { + ISTGT_ERRLOG("header digest error (%s)\n", conn->initiator_name); + return -1; + } + } + if (conn->data_digest && data_len != 0) { + crc32c = istgt_crc32c(pdu->data, ISCSI_ALIGN(data_len)); + rc = MATCH_DIGEST_WORD(pdu->data_digest, crc32c); + if (rc == 0) { + ISTGT_ERRLOG("data digest error (%s)\n", conn->initiator_name); + return -1; + } + } + + return total; +} +#endif /* defined (ISTGT_USE_IOVEC) */ + static int istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PDU_Ptr pdu); static int istgt_iscsi_write_pdu_queue(CONN_Ptr conn, ISCSI_PDU_Ptr pdu, int req_type, int I_bit); @@ -787,6 +985,7 @@ istgt_iscsi_write_pdu_queue(CONN_Ptr conn, ISCSI_PDU_P return rc; } +#if !defined (ISTGT_USE_IOVEC) static int istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { @@ -843,8 +1042,8 @@ istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PD total); rc = istgt_iscsi_write(conn, spp, total); if (rc < 0) { - ISTGT_ERRLOG("iscsi_write() failed (errno=%d)\n", - errno); + ISTGT_ERRLOG("iscsi_write() failed (errno=%d,%s)\n", + errno, conn->initiator_name); return -1; } if (rc != total) { @@ -860,7 +1059,8 @@ istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PD ISCSI_BHS_LEN); rc = istgt_iscsi_write(conn, &pdu->bhs, ISCSI_BHS_LEN); if (rc < 0) { - ISTGT_ERRLOG("iscsi_write() failed (errno=%d)\n", errno); + ISTGT_ERRLOG("iscsi_write() failed (errno=%d,%s)\n", errno, + conn->initiator_name); return -1; } if (rc != ISCSI_BHS_LEN) { @@ -875,7 +1075,8 @@ istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PD (4 * total_ahs_len)); rc = istgt_iscsi_write(conn, pdu->ahs, (4 * total_ahs_len)); if (rc < 0) { - ISTGT_ERRLOG("iscsi_write() failed\n"); + ISTGT_ERRLOG("iscsi_write() failed (errno=%d,%s)\n", + errno, conn->initiator_name); return -1; } if (rc != (4 * total_ahs_len)) { @@ -909,7 +1110,8 @@ istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PD rc = istgt_iscsi_write(conn, pdu->header_digest, ISCSI_DIGEST_LEN); if (rc < 0) { - ISTGT_ERRLOG("iscsi_write() failed\n"); + ISTGT_ERRLOG("iscsi_write() failed (errno=%d,%s)\n", + errno, conn->initiator_name); return -1; } if (rc != ISCSI_DIGEST_LEN) { @@ -926,7 +1128,8 @@ istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PD data_len); rc = istgt_iscsi_write(conn, pdu->data, data_len); if (rc < 0) { - ISTGT_ERRLOG("iscsi_write() failed\n"); + ISTGT_ERRLOG("iscsi_write() failed (errno=%d,%s)\n", + errno, conn->initiator_name); return -1; } if (rc != data_len) { @@ -949,7 +1152,8 @@ istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PD rc = istgt_iscsi_write(conn, pdu->data_digest, ISCSI_DIGEST_LEN); if (rc < 0) { - ISTGT_ERRLOG("iscsi_write() failed\n"); + ISTGT_ERRLOG("iscsi_write() failed (errno=%d,%s)\n", + errno, conn->initiator_name); return -1; } if (rc != ISCSI_DIGEST_LEN) { @@ -962,7 +1166,122 @@ istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PD return total; } +#else /* defined (ISTGT_USE_IOVEC) */ +static int +istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) +{ + struct iovec iovec[5]; /* BHS+AHS+HD+DATA+DD */ + uint8_t *cp; + uint32_t crc32c; + time_t start, now; + int nbytes; + int enable_digest; + int opcode; + int total_ahs_len; + int data_len; + int total; + int rc; + int i; + cp = (uint8_t *) &pdu->bhs; + total_ahs_len = DGET8(&cp[4]); + data_len = DGET24(&cp[5]); + total = 0; + + enable_digest = 1; + opcode = BGET8W(&cp[0], 5, 6); + if (opcode == ISCSI_OP_LOGIN_RSP) { + /* this PDU should be sent without digest */ + enable_digest = 0; + } + + /* BHS */ + iovec[0].iov_base = &pdu->bhs; + iovec[0].iov_len = ISCSI_BHS_LEN; + total += ISCSI_BHS_LEN; + + /* AHS */ + iovec[1].iov_base = pdu->ahs; + iovec[1].iov_len = 4 * total_ahs_len; + total += (4 * total_ahs_len); + + /* Header Digest */ + iovec[2].iov_base = pdu->header_digest; + if (enable_digest && conn->header_digest) { + if (total_ahs_len == 0) { + crc32c = istgt_crc32c((uint8_t *) &pdu->bhs, + ISCSI_BHS_LEN); + } else { + int upd_total = 0; + crc32c = ISTGT_CRC32C_INITIAL; + crc32c = istgt_update_crc32c((uint8_t *) &pdu->bhs, + ISCSI_BHS_LEN, crc32c); + upd_total += ISCSI_BHS_LEN; + crc32c = istgt_update_crc32c((uint8_t *) pdu->ahs, + (4 * total_ahs_len), crc32c); + upd_total += (4 * total_ahs_len); + crc32c = istgt_fixup_crc32c(upd_total, crc32c); + crc32c = crc32c ^ ISTGT_CRC32C_XOR; + } + MAKE_DIGEST_WORD(pdu->header_digest, crc32c); + + iovec[2].iov_len = ISCSI_DIGEST_LEN; + total += ISCSI_DIGEST_LEN; + } else { + iovec[2].iov_len = 0; + } + + /* Data Segment */ + iovec[3].iov_base = pdu->data; + iovec[3].iov_len = ISCSI_ALIGN(data_len); + total += ISCSI_ALIGN(data_len); + + /* Data Digest */ + iovec[4].iov_base = pdu->data_digest; + if (enable_digest && conn->data_digest && data_len != 0) { + crc32c = istgt_crc32c(pdu->data, ISCSI_ALIGN(data_len)); + MAKE_DIGEST_WORD(pdu->data_digest, crc32c); + + iovec[4].iov_len = ISCSI_DIGEST_LEN; + total += ISCSI_DIGEST_LEN; + } else { + iovec[4].iov_len = 0; + } + + /* write all bytes from iovec */ + nbytes = total; + ISTGT_TRACELOG(ISTGT_TRACE_NET, "PDU write %d\n", nbytes); + errno = 0; + start = time(NULL); + while (nbytes > 0) { + rc = writev(conn->sock, &iovec[0], 5); + if (rc < 0) { + now = time(NULL); + ISTGT_ERRLOG("writev() failed (errno=%d,%s,time=%d)\n", + errno, conn->initiator_name, (int)difftime(now, start)); + return -1; + } + nbytes -= rc; + if (nbytes == 0) + break; + /* adjust iovec length */ + for (i = 0; i < 5; i++) { + if (iovec[i].iov_len != 0 && iovec[i].iov_len > (size_t)rc) { + iovec[i].iov_base + = (void *) (((uintptr_t)iovec[i].iov_base) + rc); + iovec[i].iov_len -= rc; + break; + } else { + rc -= iovec[i].iov_len; + iovec[i].iov_len = 0; + } + } + } + + return total; +} +#endif /* defined (ISTGT_USE_IOVEC) */ + int istgt_iscsi_copy_pdu(ISCSI_PDU_Ptr dst_pdu, ISCSI_PDU_Ptr src_pdu) { @@ -3239,10 +3558,10 @@ istgt_iscsi_op_scsi(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) if (SN32_GT(CmdSN, conn->sess->ExpCmdSN)) { if (conn->sess->connections > 1) { struct timespec abstime; - time_t now; + time_t start, now; SESS_MTX_UNLOCK(conn); - now = time(NULL); + start = now = time(NULL); memset(&abstime, 0, sizeof abstime); abstime.tv_sec = now + (MAX_MCSREVWAIT / 1000); abstime.tv_nsec = (MAX_MCSREVWAIT % 1000) * 1000000; @@ -3269,8 +3588,11 @@ istgt_iscsi_op_scsi(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) } } if (rc < 0) { - ISTGT_ERRLOG("MCS: CmdSN(%u) error ExpCmdSN=%u\n", - CmdSN, conn->sess->ExpCmdSN); + now = time(NULL); + ISTGT_ERRLOG("MCS: CmdSN(%u) error ExpCmdSN=%u " + "(time=%d)\n", + CmdSN, conn->sess->ExpCmdSN, + (int)difftime(now, start)); SESS_MTX_UNLOCK(conn); return -1; } @@ -5162,7 +5484,11 @@ worker(void *arg) return NULL; } conn->kq = kq; +#if defined (ISTGT_USE_IOVEC) && defined (NOTE_LOWAT) + ISTGT_EV_SET(&kev, conn->sock, EVFILT_READ, EV_ADD, NOTE_LOWAT, ISCSI_BHS_LEN, NULL); +#else ISTGT_EV_SET(&kev, conn->sock, EVFILT_READ, EV_ADD, 0, 0, NULL); +#endif rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); @@ -5351,7 +5677,9 @@ worker(void *arg) conn->pdu.copy_pdu = 0; rc = istgt_iscsi_read_pdu(conn, &conn->pdu); if (rc < 0) { - ISTGT_ERRLOG("conn->state = %d\n", conn->state); + if (conn->state != CONN_STATE_EXITING) { + ISTGT_ERRLOG("conn->state = %d\n", conn->state); + } if (conn->state != CONN_STATE_RUNNING) { if (errno == EINPROGRESS) { sleep(1); @@ -5788,6 +6116,14 @@ istgt_create_conn(ISTGT_Ptr istgt, PORTAL_Ptr portal, ISTGT_ERRLOG("istgt_set_sendtimeo() failed\n"); goto error_return; } +#if defined (ISTGT_USE_IOVEC) + /* set low water mark */ + rc = istgt_set_recvlowat(conn->sock, ISCSI_BHS_LEN); + if (rc != 0) { + ISTGT_ERRLOG("istgt_set_recvlowat() failed\n"); + goto error_return; + } +#endif rc = pipe(conn->task_pipe); if (rc != 0) {