📄 target_error_rec.c
字号:
/**************************************************************************** create an R2T cookie and store the R2T details in the cookie. This cookie* is esssential for R2T re-transmssions in error conditions.***************************************************************************/struct iscsi_cookie *create_r2t_cookie(struct iscsi_cmnd *cmnd){ struct iscsi_cookie *cookie = NULL; TRACE(TRACE_ENTER_LEAVE, "Enter create_r2t_cookie\n"); cookie = (struct iscsi_cookie *)kmalloc(sizeof(struct iscsi_cookie), GFP_KERNEL); if (cookie == NULL) { TRACE_ERROR("%s Malloc problem in creating R2T cookie\n", current->comm); goto end_iscsi_cookie; } cookie->next = NULL; if (cmnd->first_r2t_cookie == NULL) { cmnd->first_r2t_cookie = cmnd->last_r2t_cookie = cookie; } else { cmnd->last_r2t_cookie->next = cookie; cmnd->last_r2t_cookie = cookie; }end_iscsi_cookie: TRACE(TRACE_ENTER_LEAVE, "Leave create_r2t_cookie\n"); return cookie;}/* free the long pending R2T cookie list after the command is completed - SAI */voidfree_r2t_cookie(struct iscsi_cmnd *cmnd){ struct iscsi_cookie *cookie = cmnd->first_r2t_cookie; struct iscsi_cookie *tmp_cookie = NULL; TRACE(TRACE_ENTER_LEAVE, "Enter free_r2t_cookie\n"); while (cookie) { tmp_cookie = cookie; cookie = cookie->next; kfree(tmp_cookie); } TRACE(TRACE_ENTER_LEAVE, "Leave free_r2t_cookie\n"); return;}/******************************************************************************** This function uses the offset and data length parameter or cookie information* to build a recovery R2T to be sent to the intiator for missing Dataouts. This* function is called by SNACK mechanism and also by other places in the code* that find a discrepency in Dataouts using data sequence numbering or offsets.* with regard to R2TSN updating for recovery R2Ts:** RFC 3720 Section 2.1. Definitions* "- Recovery R2T: An R2T generated by a target upon detecting the loss* of one or more Data-Out PDUs through one of the following means: a* digest error, a sequence error, or a sequence reception timeout. A* recovery R2T carries the next unused R2TSN, but requests all or* part of the data burst that an earlier R2T (with a lower R2TSN) had* already requested."********************************************************************************/intsend_recovery_r2t(struct iscsi_cmnd *cmnd, int data_offset, struct iscsi_cookie *cookie, struct generic_pdu *hdr){ int data_length; int max_burst_len; int err = 0; TRACE(TRACE_ENTER_LEAVE, "Enter send_recovery_r2t, recovery_r2t %u\n", cmnd->recovery_r2t); if (cmnd->recovery_r2t) { /* don't send recovery_r2t for another error */ if (hdr->flags & F_BIT) { /* last DataOut in sequence to be recovered */ cmnd->recovery_r2t = 2; cmnd->next_burst_len = 0; cmnd->data_sn = 0; goto out_wakeup; } goto out; } max_burst_len = cmnd->conn->session->oper_param->MaxBurstLength; data_length = cmnd->data_length - data_offset; if (!cookie) { cmnd->r2t_data = data_length; cookie = create_r2t_cookie(cmnd); if (!cookie) { err = -1; goto out; } cmnd->startsn = cmnd->endsn = cmnd->r2t_sn; cookie->seq = cmnd->r2t_sn++; /* recovery R2T stays in R2T sequence */ cookie->offset = data_offset; cookie->xfer_len = (data_length <= max_burst_len) ? data_length : max_burst_len; } else { data_offset = cookie->offset; cmnd->r2t_data = cmnd->data_length - data_offset; cmnd->r2t_sn = cookie->seq; cmnd->startsn = cmnd->endsn = cmnd->r2t_sn; cmnd->outstanding_r2t--; } TRACE_WARNING("%s Send recovery R2T, ITT %u R2TSN %u Buffer Offset %d\n", current->comm, cmnd->init_task_tag, cmnd->r2t_sn, data_offset); if (hdr->flags & F_BIT) { cmnd->recovery_r2t = 2; cmnd->next_burst_len = 0; cmnd->data_sn = 0; } else { cmnd->recovery_r2t = 1; } cmnd->state = ISCSI_BUFFER_RDY;out_wakeup: if (atomic_read(&cmnd->conn->tx_sem.count) <= 0) { up(&cmnd->conn->tx_sem); }out: TRACE(TRACE_ENTER_LEAVE, "Leave send_recovery_r2t, recovery_r2t %u, " "err = %d\n", cmnd->recovery_r2t, err); return err;}/************************************************************************ This thread remains blocked on a semaphore for most of the time.* When a r2t timeout happens this thread is awakened for re-transmitting* any r2t requests that have not generated any activity in a long time.***********************************************************************/intiscsi_retran_thread(void *param){ struct iscsi_session *session = (struct iscsi_session *) param; struct iscsi_cmnd *cmnd; lock_kernel();/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 daemonize(#else daemonize(); snprintf(current->comm, sizeof(current->comm),#endif "iscsi_tran_%d", session->devdata->ntsih);#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 18) /* * Arne Redlich, agr1@users.sourceforge.net: * This prevents the retran thread from becoming a zombie after it exits */ reparent_to_init();#endif siginitsetinv(¤t->blocked, ISCSI_SHUTDOWN_SIGBITS); /* mark that this retransmit thread is alive */ session->retran_thread = current; unlock_kernel(); printk("%s Starting pid %d\n", current->comm, current->pid); while (1) { /* wait for periodic r2t timer to interrupt us */ if (down_interruptible(&session->retran_sem)) break; /* lock the session-wide list of commands */ if (down_interruptible(&session->cmnd_sem)) continue; for (cmnd = session->cmnd_list; cmnd; cmnd = cmnd->next) { if (cmnd->outstanding_r2t > 0 && jiffies > (cmnd->timestamp+session->r2t_period) && cmnd->state == ISCSI_BUFFER_RDY && !cmnd->retransmit_flg) { /* this is a WRITE command with outstanding * R2Ts and no activity for a while. Try * retransmitting the last R2T. */ TRACE(TRACE_ERROR_RECOVERY, "%s activity timeout, ITT %u, " "recovery_r2t %u\n", current->comm, cmnd->init_task_tag, cmnd->recovery_r2t); if (cmnd->recovery_r2t) { if (cmnd->recovery_r2t == 1) cmnd->recovery_r2t = 2; } else { cmnd->retransmit_flg = 1; cmnd->endsn = cmnd->startsn = cmnd->r2t_sn - 1; } /* signal the tx thread to do the rexmit */ if (atomic_read(&cmnd->conn->tx_sem.count) <= 0) { up(&cmnd->conn->tx_sem); } } } up(&session->cmnd_sem); } session->retran_thread = NULL; up(&session->thr_kill_sem); printk("%s Exiting pid %d\n", current->comm, current->pid); return 0;}voidadd_data_to_queue(struct iscsi_cmnd *cmd, struct iscsi_cookie *dataq){ TRACE(TRACE_ENTER_LEAVE, "Entering add_data_to_queue\n"); if (!cmd->first_data_q) { cmd->first_data_q = dataq; cmd->last_data_q = dataq; } else { cmd->last_data_q->next = dataq; cmd->last_data_q = cmd->last_data_q->next; } TRACE(TRACE_ENTER_LEAVE, "Leaving add_data_to_queue\n"); return;}/********************************************************************* This function drives the out-of-order dataouts received into the * appropriate scatter gather buffer location maintained by scsi. The* information about the out-of-order dataout and also the corresponding* scatter offsets are queued for future use.**********************************************************************/intqueue_data(struct targ_error_rec *err_rec){ int offset = 0, size = 0, list_offset = 0; int count = 0; int niov, padding, err, retval = 0, data_crc_len = 0; __u32 data_sn, digest, pad_bytes; struct iscsi_conn *conn = NULL; struct iscsi_cmnd *cmd; struct iscsi_init_scsi_data_out *hdr; struct iscsi_cookie *dataq = NULL; struct scatterlist *st_list; struct iovec *iov = NULL; TRACE(TRACE_ENTER_LEAVE, "Entering queue_data\n"); cmd = err_rec->cmd; hdr = (struct iscsi_init_scsi_data_out *) err_rec->pdu_hdr; list_offset = offset = hdr->offset; data_sn = hdr->data_sn; size = hdr->length; conn = err_rec->curr_conn; st_list = (struct scatterlist *) cmd->cmnd->req->sr_buffer; while (st_list[count].length <= list_offset) { list_offset -= st_list[count].length; count++; } st_list += count; niov = find_iovec_needed(size, st_list, list_offset); if (niov < 0) { TRACE_ERROR("%s queue data: find_iovec_needed returned an error\n", current->comm); return -1; } padding = -(size) & 3; if (padding) niov++; if (conn->data_crc == 1) niov++; iov = (struct iovec *) kmalloc(niov * sizeof(struct iovec), GFP_KERNEL); if (!iov) { TRACE_ERROR("%s queue_data: Could not malloc iov\n", current->comm); return -1; } TRACE(TRACE_DEBUG, "queue_data: no. of iovec needed %d\n", niov); retval = fill_iovec(iov, 0, niov, st_list, &list_offset, size); if (padding) { iov[niov - 1 - conn->data_crc].iov_base = &pad_bytes; iov[niov - 1 - conn->data_crc].iov_len = padding; } if (conn->data_crc == 1) { data_crc_len = CRC_LEN; iov[niov - 1].iov_base = &digest; iov[niov - 1].iov_len = CRC_LEN; } err = iscsi_rx_data(cmd->conn, iov, niov, size + padding + data_crc_len); if (err != (size + padding + data_crc_len)) { TRACE_ERROR("%s queue_data: Trouble in iscsi_rx_data\n", current->comm); if (err == PAYLOAD_DIGERR) return err; } dataq = kmalloc(sizeof(struct iscsi_cookie), GFP_KERNEL); if (!dataq) { TRACE_ERROR("%s add_data_to_queue: Malloc returned an error\n", current->comm); return -1; } dataq->offset = offset; dataq->xfer_len = size; dataq->list_offset = list_offset; dataq->list_count = retval; dataq->next = NULL; add_data_to_queue(cmd, dataq); TRACE(TRACE_ENTER_LEAVE, "Leaving queue_data\n"); return 0;}/************************************************************************ This function is called everytime a in-sequence dataout pdu is* received. The function verifies if there was any previous* out-of-order dataouts for the command and updates the state variables* accordingly.***********************************************************************/voidsearch_data_q(struct iscsi_cmnd *cmd){ int unhook_flg = 0; struct iscsi_cookie *dataq, *prev, *tmp_q; TRACE(TRACE_ENTER_LEAVE, "Entering search_data_q\n"); if (!cmd->first_data_q) return; dataq = prev = cmd->first_data_q; while (dataq) { if (cmd->data_done > dataq->offset) unhook_flg = 1; else if (cmd->data_done == dataq->offset) { cmd->scatter_list_count += dataq->list_count; cmd->scatter_list_offset = dataq->list_offset; cmd->data_done += dataq->xfer_len; unhook_flg = 1; } if (unhook_flg) { /* unhook data node */ if (prev == cmd->first_data_q) { cmd->first_data_q = dataq->next; prev = dataq->next; } else if (dataq == cmd->last_data_q) cmd->last_data_q = prev; else prev->next = dataq->next; tmp_q = dataq; dataq = dataq->next; kfree(tmp_q); unhook_flg = 0; } else { prev = dataq; dataq = dataq->next; } if (cmd->data_done >= cmd->data_length) break; } TRACE(TRACE_ENTER_LEAVE, "Leaving search_data_q\n");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -