📄 iscsi_target.c
字号:
TRACE(TRACE_ERROR_RECOVERY, "Start payload digest error recovery\n"); err_rec.curr_conn = cmd->conn; err_rec.pdu_hdr = (struct generic_pdu *)buffer; err_rec.cmd = cmd; err_rec.err_type = PAYLOAD_DIGERR; err = targ_do_error_recovery(&err_rec); } my_kfree((void**)&data_buf, "data_buf"); } else { *result = data_buf; } } else { err = -1; } return err;}/* * executed only by the rx thread. * read data segment for command into list of buffers at given offset * this can be either immediate data in a write, or the payload of a DataIn * returns > 0 if ok, < 0 if error, = 0 if error recovery completed ok */static intread_list_data_seg(struct generic_pdu *hdr, struct iscsi_cmnd *cmd, struct scatterlist *st_list, int offset){ struct targ_error_rec err_rec; struct iovec *iov; int size, niov, padding, err, sgindex; __u32 digest, pad_bytes; size = hdr->length; niov = find_iovec_needed(size, st_list, offset); if (niov <= 0) { TRACE_ERROR("%s Trouble in find_iovec_needed\n", current->comm); return -1; } /* allocate 2 extra iov slots for possible padding and crc */ iov = (struct iovec *)my_kmalloc((niov + 2) * sizeof(struct iovec), "iovec"); if (!iov) { return -1; } /* gives back number of st_list elements used */ sgindex = fill_iovec(iov, 0, niov, st_list, &offset, size); padding = (-size) & 3; if (padding) { iov[niov].iov_base = &pad_bytes; iov[niov].iov_len = padding; niov++; size += padding; } if (cmd->conn->data_crc) { iov[niov].iov_base = &digest; iov[niov].iov_len = CRC_LEN; niov++; size += CRC_LEN; } err = iscsi_rx_data(cmd->conn, iov, niov, size); if (err == size) { /* we received everything we expected to receive * Store scatter list count and offset for recovery purposes - SAI */ cmd->scatter_list_count += sgindex; cmd->scatter_list_offset = offset; update_after_read(hdr, cmd); } else { /* Payload Digest Error Recovery - SAI */ if (err == PAYLOAD_DIGERR) { TRACE(TRACE_ERROR_RECOVERY, "Start payload digest error recovery\n"); err_rec.curr_conn = cmd->conn; err_rec.pdu_hdr = hdr; err_rec.cmd = cmd; err_rec.err_type = PAYLOAD_DIGERR; err = targ_do_error_recovery(&err_rec); } } my_kfree((void**)&iov, "iovec"); return err;}/* * executed only by the tx thread. * iscsi_tx_data: will transmit a fixed-size PDU of any type. * INPUT: struct iscsi_conn (what connection), struct iovec, number of iovecs, * total amount of data in bytes, * iovec[0] must be the 48-byte PDU header. * iovec[1] (if header digests are in use, calculate it in this function) * iovec[...] the data * iovec[niov-1] (if data digests are in use, calculate it in this function) * OUTPUT: int total bytes written if everything is okay * < 0 if trouble */intiscsi_tx_data(struct iscsi_conn *conn, struct iovec *iov, int niov, int data){# ifdef DEBUG_DATA int i, j; struct iovec *debug_iov; __u8 *to_print;# endif struct msghdr msg; int msg_iovlen; int total_tx, tx_loop; __u32 hdr_crc, data_crc; int data_len, k; mm_segment_t oldfs; struct iovec *iov_copy, *iov_ptr; struct generic_pdu *pdu; if (!conn->conn_socket) { TRACE_ERROR("%s NULL conn_socket\n", current->comm); return -1; }# ifdef DEBUG_DATA TRACE(TRACE_DEBUG, "iscsi_tx_data: iovlen %d\n", niov); debug_iov = iov; for (i = 0; i < niov; i++) { to_print = (__u8 *) debug_iov->iov_base; for (j = 0; ((j < debug_iov->iov_len) && (j < 64)); j++) { TRACE(TRACE_DEBUG, "%.2x ", to_print[j]); if (((j + 1) % 16) == 0) TRACE(TRACE_DEBUG, "\n"); else if (((j + 1) % 4) == 0) TRACE(TRACE_DEBUG, " "); } TRACE(TRACE_DEBUG, "\n"); debug_iov++; }# endif /* compute optional header digest */ if (conn->hdr_crc) { hdr_crc = 0; do_crc(iov[0].iov_base, ISCSI_HDR_LEN, &hdr_crc); iov[1].iov_base = &hdr_crc; iov[1].iov_len = CRC_LEN; TRACE(TRACE_ISCSI_FULL, "Send header crc 0x%08x\n", ntohl(hdr_crc)); } /* compute optional data digest */ if (conn->data_crc && niov > conn->hdr_crc + 2) { data_len = 0; data_crc = 0; for (k = conn->hdr_crc + 1; k < niov - 1; k++) { do_crc(iov[k].iov_base, iov[k].iov_len, &data_crc); data_len += iov[k].iov_len; } iov[niov - 1].iov_base = &data_crc; iov[niov - 1].iov_len = CRC_LEN; TRACE(TRACE_ISCSI_FULL, "Send data len %d, data crc 0x%08x\n", data_len, ntohl(data_crc)); } iov_copy = (struct iovec *)my_kmalloc(niov * sizeof(struct iovec), "iovec"); if (iov_copy == NULL) return -1; total_tx = 0; while (total_tx < data) { /* get a clean copy of the original io vector to work with */ memcpy(iov_copy, iov, niov * sizeof(struct iovec)); msg_iovlen = niov; iov_ptr = iov_copy; if ((tx_loop = total_tx)) { TRACE(TRACE_ISCSI, "iscsi_tx_data: data %d not completed, recompute iov\n",data); do { if (iov_ptr->iov_len <= tx_loop) { tx_loop -= iov_ptr->iov_len; iov_ptr++; msg_iovlen--; } else { iov_ptr->iov_base += tx_loop; iov_ptr->iov_len -= tx_loop; tx_loop = 0; } } while (tx_loop); TRACE(TRACE_ISCSI, "sock_sendmsg total_tx %d, data %d, niov %d, " "msg_iovlen %d\n", total_tx, data, niov, msg_iovlen); } memset(&msg, 0, sizeof(struct msghdr)); msg.msg_iov = iov_ptr; msg.msg_iovlen = msg_iovlen; msg.msg_flags = MSG_NOSIGNAL; TRACE(TRACE_DEBUG, "iscsi_tx_data: niov %d, data %d, total_tx %d\n", niov, data, total_tx); oldfs = get_fs(); set_fs(get_ds()); tx_loop = sock_sendmsg(conn->conn_socket, &msg, (data - total_tx)); set_fs(oldfs); if (tx_loop <= 0) { pdu = (struct generic_pdu *)iov[0].iov_base; TRACE_ERROR("%s sock_sendmsg error %d, total_tx %d, data %d, niov " "%d, msg_iovlen %d, op 0x%02x, flags 0x%02x, ITT %u\n", current->comm, tx_loop, total_tx, data, niov, msg_iovlen, pdu->opcode, pdu->flags, ntohl(pdu->init_task_tag)); my_kfree((void**)&iov_copy, "iovec"); return tx_loop; } total_tx += tx_loop; TRACE(TRACE_DEBUG, "iscsi_tx_data: tx_loop %d total_tx %d\n", tx_loop, total_tx); } my_kfree((void**)&iov_copy, "iovec"); return total_tx;}/* * executed only by the tx thread. * RDR */static intsend_hdr_plus_1_data(struct iscsi_conn *conn, void *iscsi_hdr, void *data_buf, int data_len){ struct iovec iov[5]; int niov, total_size, padding, retval; int pad_bytes = 0; /* set up the header in the first iov slot */ iov[0].iov_base = iscsi_hdr; iov[0].iov_len = ISCSI_HDR_LEN; total_size = ISCSI_HDR_LEN; niov = 1; /* one for the header */ if (conn->hdr_crc) { /* set up the header digest in the second iov slot */ iov[niov].iov_len = CRC_LEN; total_size += CRC_LEN; niov++; /* one for header digest */ } if (data_len) { /* set up the data in the next iov slot */ iov[niov].iov_base = data_buf; iov[niov].iov_len = data_len; total_size += data_len; niov++; /* one for data */ padding = (-data_len) & 3; if (padding) { /* set up the data padding in the next iov slot */ iov[niov].iov_base = &pad_bytes; iov[niov].iov_len = padding; total_size += padding; niov++; /* one for padding */ TRACE(TRACE_DEBUG, "padding attached: %d bytes\n", padding); } if (conn->data_crc) { /* set up the data digest in the next iov slot */ iov[niov].iov_len = CRC_LEN; total_size += CRC_LEN; niov++; /* one for data digest */ } } retval = iscsi_tx_data(conn, iov, niov, total_size); if (retval != total_size) { TRACE_ERROR("%s Trouble in iscsi_tx_data, expected %d bytes, got %d\n", current->comm, total_size, retval); retval = -1; } return retval;}/* * executed only by the tx thread. * RDR */static inline intsend_hdr_only(struct iscsi_conn *conn, void *iscsi_hdr){ return send_hdr_plus_1_data(conn, iscsi_hdr, NULL, 0);}/* Draft 20, Section 3.2.2.1 Command Numbering and Acknowledging * ... * For non-immediate commands, the CmdSN field can take any value from * ExpCmdSN to MaxCmdSN inclusive. The target MUST silently ignore * any non-immediate command outside of this range or non-immediate * duplicates within the range. The CmdSN carried by immediate commands * may lie outside the ExpCmdSN to MaxCmdSN range. *//* RDR: check that cmd_sn is within the range [ExpCmdSN .. MaxCmdSN] * returns 0 if equal to exp_cmd_sn (and exp_cmd_sn is updated) * 1 if greater than exp_cmd_sn (exp_cmd_sn not updated) * -1 if outside the range (and hence should be ignored) * 0 for all immediate commands, regardless of cmd_sn value * when called, session->cmnd_sem is locked so access to session-wide * max_cmd_sn and exp_cmd_sn fields is atomic. */static intcheck_cmd_sn(struct iscsi_cmnd *cmnd, void *ptr, struct iscsi_session *session, __u32 increment){ struct generic_pdu *pdu = ptr; int delta; if (!(pdu->opcode & I_BIT)) { /* this is an non-immediate command */ delta = session->max_cmd_sn - pdu->cmd_sn; if (delta < 0) { /* cmd_sn is greater than MaxCmdSN */ return -1; } delta = pdu->cmd_sn - session->exp_cmd_sn; if (delta < 0) { /* cmd_sn is less than ExpCmdSN */ return -1; } if (delta > 0) { /* cmd_sn is in range, but out of order */ return 1; } /* non-immediate command is in expected order */ if (increment) { session->exp_cmd_sn++; } else { /* exp_cmd_sn is not incremented. * This only happens on in-order, non-immediate SCSI command pdus, * which don't bump exp_cmd_sn until after midlevel confirms * command was delivered. Set cmd_sn_increment to tell * check_queued_cmnd() not to process this command once it has been * queued! */ cmnd->cmd_sn_increment = 1; /* increment exp_cmd_sn later */ } } return 0;}/* * executed only by the rx thread. * find commands for this connection already in the queue that have already * sent everything and are being ACKed by this command in order to mark them * for dequeueing. * Then adds this command to end of queue if flag is set. * Finally, if any command's state was changed, or if this command was added * to the queue, wake up the tx thread. * NOTE: cmnd MAY be NULL, and if it is, add_cmnd_to_queue MUST be 0! * returns 0 on success, -1 on error. */static intack_sent_cmnds(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd, __u32 exp_stat_sn, int add_cmnd_to_queue){ struct iscsi_cmnd *temp; int delta, changed_something, count; changed_something = add_cmnd_to_queue; if (down_interruptible(&conn->session->cmnd_sem)) return -1; count = 0; for (temp = conn->session->cmnd_list; temp != NULL; temp = temp->next) { if (temp->conn == conn) { count++; if (temp->state == ISCSI_SENT) { delta = temp->stat_sn - exp_stat_sn; if (delta < 0) { TRACE(TRACE_DEBUG, "set dequeue command statsn %d, " "received exp_stat_sn %d, command state %d\n", temp->stat_sn, exp_stat_sn, temp->state); temp->state = ISCSI_DEQUEUE; changed_something = 1; } } } if (temp->next == NULL) break; } if (add_cmnd_to_queue) { /* Add this command to the queue */ TRACE(TRACE_DEBUG, "%s add command %p to queue, ITT %u, CmdSN %u, " "state %d, count %d\n", current->comm, cmnd, cmnd->init_task_tag, cmnd->cmd_sn, cmnd->state, count); count++; if (temp) temp->next = cmnd; else conn->session->cmnd_list = cmnd; } up(&conn->session->cmnd_sem); /* tell the tx thread it has something to do */ if (changed_something) { if (atomic_read(&conn->tx_sem.count) <= 0) { TRACE(TRACE_DEBUG, "%s up(tx_sem), %d cmnds in queue\n", current->comm, count); up(&conn->tx_sem); } } return 0;}/* * RDR * allocate and zero out a new struct iscsi_cmnd. * Returns pointer to the new struct if all ok, else NULL after error message */struct iscsi_cmnd *get_new_cmnd( void ){ struct iscsi_cmnd *cmnd; cmnd = (struct iscsi_cmnd *)my_kmalloc(sizeof(struct iscsi_cmnd), "cmnd"); if (cmnd) { memset(cmnd, 0, sizeof(struct iscsi_cmnd)); cmnd->state = ISCSI_NEW_CMND; } return cmnd;}/**************************************************************************//* handle income pdu *//**************************************************************************//* * called only from rx thread. * handle_login: This function is responsible for interpreting the login * message and then making sure the necessary details get filled in, a * new session is opened if required by the login and the response is returned * * INPUT: Details of the connection, buffer containing the login * OUTPUT: 0 if everything is okay, < 0 if there is a problem * Note a 0 value returned may still mean that a connection is rejected */static inthandle_login(struct iscsi_conn *conn, __u8 *buffer){ struct iscsi_init_login_cmnd *pdu = (struct iscsi_init_login_cmnd *) buffer; struct i
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -