📄 target_error_rec.c
字号:
/* target/target_error_rec.c vi: set autoindent tabstop=4 shiftwidth=4 :*//* Copyright (C) 2001-2003 InterOperability Lab (IOL) University of New Hampshier (UNH) Durham, NH 03824 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The name of IOL and/or UNH may not be used to endorse or promote products derived from this software without specific prior written permission.*/#include "iscsi_target.h"#include "target_error_rec.h"static struct semaphore targ_sess_recovery_sem;/************************************************************************ * The Main Error Recovery Handler Called from iscsi_initiator when it * * finds any error. * * Returns 0 if recovery completed ok, -1 otherwise * ************************************************************************/inttarg_do_error_recovery(struct targ_error_rec *err_rec){ int retval = 0; struct iscsi_conn *curr_conn; TRACE(TRACE_ENTER_LEAVE, "Entering targ_do_error_recovery\n"); if (err_rec == NULL) { TRACE_ERROR("%s Error Recovery structure is NULL\n", current->comm); retval = -1; goto out; } if (err_rec->curr_conn == NULL) { TRACE_ERROR("%s NULL Connection Pointer in Error Recovery structure\n", current->comm); retval = -1; goto out; } curr_conn = err_rec->curr_conn; switch (curr_conn->session->oper_param->ErrorRecoveryLevel) { case SESSION_RECOVERY: targ_session_recovery(curr_conn); break; case DIGEST_RECOVERY: retval = targ_digest_recovery(err_rec); break; case CONNECTION_RECOVERY: TRACE_ERROR("%s ErrorRecoveryLevel %u Not Implemented yet\n", current->comm, CONNECTION_RECOVERY); retval = -1; break; }out: TRACE(TRACE_ENTER_LEAVE, "Leaving targ_do_error_recovery, retval %d\n", retval); return retval;}/************************************************************************* This function handles the session recovery. It creates a recovery ** thread that will do session recovery. *************************************************************************/voidtarg_session_recovery(struct iscsi_conn *current_connection){ TRACE(TRACE_ENTER_LEAVE, "Enter targ_session_recovery\n"); TRACE(TRACE_ISCSI, "Executing Target Session Recovery - cancelling Receive Thread\n"); init_MUTEX_LOCKED(&targ_sess_recovery_sem); /* Create a recovery thread */ if (kernel_thread(targ_recovery_thread,(void *)current_connection,0) < 0) { TRACE_ERROR("%s Unable to start targ_recovery_thread\n", current->comm); } else { /* Wait till the recovery thread completes processing */ down_interruptible(&targ_sess_recovery_sem); } TRACE(TRACE_ENTER_LEAVE, "Leave targ_session_recovery\n"); return;}/************************************************************************* This thread handles the session recovery. Session Recovery will ** abort all pending tasks and closes all transport connection ** Section (6.12.4) *************************************************************************/inttarg_recovery_thread(void *arg){ struct iscsi_conn *conn; struct iscsi_session *sess; struct iscsi_global *host; conn = (struct iscsi_conn *)arg; sess = conn->session; host = sess->devdata; lock_kernel();/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 daemonize(#else daemonize(); snprintf(current->comm, sizeof(current->comm),#endif "iscsi_reco_%d", host->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); unlock_kernel(); printk("%s Starting pid %d\n", current->comm, current->pid); if (!down_interruptible(&host->session_sem)) { iscsi_release_session(sess); up(&host->session_sem); } up(&targ_sess_recovery_sem); printk("%s Exiting pid %d\n", current->comm, current->pid); return 0;}/************************************************************************ * This function handles the digest errors and sequence errors that may * * have caused because of previous digest errors. * * Returns 0 if recovery is completed correctly, -1 otherwise * ************************************************************************/inttarg_digest_recovery(struct targ_error_rec *err_rec){ int retval = 0, data_length = 0; int size = 0, err_type = 0, data_offset = 0; unsigned int opcode = 0; struct iscsi_conn *curr_conn; struct iscsi_init_scsi_data_out *data_out_hdr; struct iscsi_cmnd *cmd; curr_conn = err_rec->curr_conn; err_type = err_rec->err_type; cmd = err_rec->cmd; TRACE(TRACE_ENTER_LEAVE, "Entering targ_digest_recovery\n"); if (err_rec->pdu_hdr == NULL) { TRACE_ERROR("%s iscsi NULL PDU Header\n", current->comm); retval = -1; goto digest_out; } else { opcode = err_rec->pdu_hdr->opcode; size = err_rec->pdu_hdr->length; if (size > MASK_24_BITS) { TRACE_ERROR("%s TotalAHSLength 0x%02x, expected 0x00\n", current->comm, size >> 24); retval = -1; goto digest_out; } } if (err_type == HEADER_DIGERR) { if (size > 0) { retval = targ_drop_pdu_data(curr_conn, size); } goto digest_out; } if (opcode == ISCSI_INIT_SCSI_DATA_OUT) { if (cmd == NULL) { retval = -1; goto digest_out; } if (err_type == PAYLOAD_DIGERR) { enqueue_reject(curr_conn, REASON_DATA_DIGEST_ERR); retval = send_recovery_r2t(cmd, cmd->data_done, NULL, err_rec->pdu_hdr); } else if (err_type == SEQUENCE_ERR) { /* error recovery ver ref18_04 - SAI */ /* if there is a sequence error and the arrived data * seq. indicates lost packets, queue the received * packets and request re-transmission of lost packets */ queue_data(err_rec); data_length = cmd->data_length - cmd->data_done; data_offset = cmd->data_length - data_length; data_out_hdr = (struct iscsi_init_scsi_data_out *) err_rec->pdu_hdr; size = (data_out_hdr->offset - data_offset); retval = send_recovery_r2t(cmd, data_offset, NULL, err_rec->pdu_hdr); //up(&curr_conn->tx_sem); } else { TRACE_ERROR("%s Unknown err_type %d for DataOut recovery\n", current->comm, err_type); retval = -1; } } else if (opcode == ISCSI_INIT_SCSI_CMND) { if (err_type == PAYLOAD_DIGERR) { /* digest error in immediate data attached to write */ enqueue_reject(curr_conn, REASON_DATA_DIGEST_ERR); } else { TRACE_ERROR("%s Unknown err_type %d for SCSI_CMND recovery\n", current->comm, err_type); retval = -1; } } else { TRACE_ERROR("%s Unknown err_type %d for recovery\n", current->comm, err_type); retval = -1; }digest_out: TRACE(TRACE_ENTER_LEAVE, "Leaving targ_digest_recovery, retval %d\n", retval); return retval;}/******************************************************************* * Reads the data portion of the dropped PDU due to digest errors. * * The data read is also ignored. *********************************************************************/inttarg_drop_pdu_data(struct iscsi_conn *curr_conn, int size){ int padding = 0, retval = 0, data_length = 0; int total_rx = 0, rx_loop = 0; char *buffer = NULL; struct msghdr msg; struct iovec iov; mm_segment_t oldfs; TRACE(TRACE_ENTER_LEAVE, "Entering targ_drop_pdu_data\n"); padding = -(size) & 3; size += padding; if (curr_conn->data_crc == 1) { size += CRC_LEN; } TRACE(TRACE_DEBUG, "targ_drop_pdu: data_read %d total_data %d\n", data_length, size); data_length = size; while (data_length > 0) { if (data_length > MAX_MALLOC_SIZE) data_length = MAX_MALLOC_SIZE; buffer = kmalloc(data_length * sizeof(char), GFP_KERNEL); if (!buffer) { TRACE_ERROR("%s Malloc Error: Unable to drop the data PDU\n", current->comm); retval = -ENOMEM; goto end_drop_pdu; } for (total_rx = 0; total_rx < data_length;) { memset(&msg, 0, sizeof(struct msghdr)); msg.msg_iovlen = 1; msg.msg_iov = &iov; iov.iov_len = data_length - total_rx; iov.iov_base = buffer + total_rx; oldfs = get_fs(); set_fs(get_ds()); rx_loop = sock_recvmsg(curr_conn->conn_socket, &msg, (data_length - total_rx), MSG_WAITALL); set_fs(oldfs); if (rx_loop <= 0) { TRACE_ERROR("%s targ_drop_pdu: rx_loop %d total_rx %d\n", current->comm, rx_loop, total_rx); retval = -ECONNRESET; /* Connection reset by peer */ kfree(buffer); goto end_drop_pdu; } total_rx += rx_loop; TRACE(TRACE_DEBUG, "targ_drop_pdu: rx_loop %d total_rx %d\n", rx_loop, total_rx); } size -= data_length; data_length = size; TRACE(TRACE_DEBUG,"targ_drop_pdu: data_read %d total_data %d\n", data_length, size); kfree(buffer); }end_drop_pdu: TRACE(TRACE_ENTER_LEAVE, "Leaving targ_drop_pdu_data\n"); return retval;}void deal_with_r2t_timer(unsigned long data);/********************************************************************* * (re)start R2T Timer is used for tracking the R2T sent. This timer * helps re-transmit the most recent R2T sent to the initiator. ********************************************************************//* (re)start the R2T retransmit timer */voidrestart_r2t_timer(struct iscsi_session *session){ __u32 period = session->r2t_period; TRACE(TRACE_TIMERS, "Enter restart_r2t_timer period %u\n", period); if (period && session->r2t_timer && !timer_pending(session->r2t_timer)){ TRACE(TRACE_TIMERS, "Start r2t timer for %u ticks\n", period); session->r2t_timer->expires = jiffies + period; session->r2t_timer->data = (unsigned long) session; session->r2t_timer->function = deal_with_r2t_timer; add_timer(session->r2t_timer); } else { TRACE_ERROR("%s R2T timer not started for %u ticks\n", current->comm, period); } TRACE(TRACE_TIMERS, "Leave restart_r2t_timer\n");}/********************************************************************** Called by kernel when R2T periodic session timer expires.*********************************************************************/voiddeal_with_r2t_timer(unsigned long data){ struct iscsi_session *session; struct iscsi_cmnd *cmnd = NULL; TRACE(TRACE_TIMERS, "Enter deal_with_r2t_timer\n"); session = (struct iscsi_session *) data; for (cmnd = session->cmnd_list; cmnd != NULL; cmnd = cmnd->next) { if (cmnd->outstanding_r2t > 0 && (jiffies > (cmnd->timestamp + session->r2t_period))) { up(&session->retran_sem); break; } } restart_r2t_timer(session); TRACE(TRACE_TIMERS, "Leave deal_with_r2t_timer\n");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -