📄 initiator_error_rec.c
字号:
/* initiator/initiator_error_rec.c * * vi: set autoindent tabstop=8 shiftwidth=8 : * * This file contains auxilliary functions for iscsi initiator * code that are responsible for dealing with error recovery. * * Copyright (C) 2001-2004 InterOperability Lab (IOL) * University of New Hampshire (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 IOL and/or UNH may not be used to endorse or promote * products derived from this software without specific prior * written permission.*/#include "iscsi_initiator.h"#include "initiator_utilities.h"#include "initiator_error_rec.h"static DECLARE_MUTEX_LOCKED(init_sess_recovery_sem);static struct task_struct *init_rec_thread = NULL;/************************************************************************* The Main Error Recovery Handler Called from iscsi_initiator when it* finds any error.* When called, session lock must be locked* Returns >0 on success* =0 on failure, session lock is locked* <0 on failure, session lock is NOT locked************************************************************************/intinit_do_error_recovery(struct init_error_rec *err_rec){ int retval = 0; /* assume this will fail with lock held */ struct connection *curr_conn; TRACE(TRACE_ENTER_LEAVE, "Enter init_do_error_recovery \n"); if (err_rec == NULL) { TRACE_ERROR("Error Recovery structure is NULL\n"); goto out; } if (err_rec->curr_conn == NULL) { TRACE_ERROR ("NULL Connection Pointer in Error Recovery struct\n"); goto out; } curr_conn = err_rec->curr_conn; switch (curr_conn->my_session->oper_param->ErrorRecoveryLevel) { case SESSION_RECOVERY: retval = init_session_recovery(err_rec); break; case DIGEST_RECOVERY: case CONNECTION_RECOVERY: retval = init_digest_recovery(err_rec); break; }out: TRACE(TRACE_ENTER_LEAVE, "Leave init_do_error_recovery, retval %d\n", retval); return retval;}/************************************************************************ * executed only by rx thread. * the current_session->sess_lock MUST be held by the calling process/thread. * This function handles the session recovery. It creates a recovery * thread that will do session recovery. * While the recovery thread is running the rx thread is blocked waiting for it. * Returns <0 forced failure, session lock is NOT locked * In all cases, current thread should not continue after the return! ************************************************************************/intinit_session_recovery(struct init_error_rec *err_rec){ struct connection *current_connection; struct session *current_session; TRACE(TRACE_ENTER_LEAVE, "Enter init_session_recovery \n"); if (!global_hostdata->init_sessrec_flg) { /* globally mark that session recovery is in progress, so * attempts by SCSI to queue new commands are caught */ global_hostdata->init_sessrec_flg = 1; current_connection = err_rec->curr_conn; current_session = current_connection->my_session; init_MUTEX_LOCKED(&init_sess_recovery_sem); /* Release any resources held by this thread */ UNH_UNLOCK(¤t_session->sess_lock, current_connection->rx_lock_flags); TRACE(TRACE_ISCSI, "Starting initiator session recovery\n"); /* Create a recovery thread */ kernel_thread((int (*)(void *)) init_recovery_thread, (void *) err_rec, 0); /* Wait till the recovery thread completes processing */ down_interruptible(&init_sess_recovery_sem); } TRACE(TRACE_ENTER_LEAVE, "Leave init_session_recovery\n"); return -ESRCH; /* any error will do, so give "No such process" */}/* Create a new recovery session for the failing session before cleaning-up * the failing session */struct sess_rec_group *set_rec_group(struct scsi_cmnd * Cmnd){ struct sess_rec_group *sess_grp; struct sess_rec_group *prev_sess_grp; struct sess_rec_cmnd *cmnd; TRACE(TRACE_ENTER_LEAVE, "Enter set_rec_group\n"); prev_sess_grp = sess_grp = global_hostdata->sess_rec_head; for (; sess_grp != NULL; sess_grp = sess_grp->next) {/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 if (sess_grp->cmnd_list_head->Cmnd->device->id == Cmnd->device->id)#else if (sess_grp->cmnd_list_head->Cmnd->target == Cmnd->target)#endif break; prev_sess_grp = sess_grp; } if (sess_grp == NULL) { sess_grp = (struct sess_rec_group *) my_kmalloc(sizeof(struct sess_rec_group), "sess_rec_group"); if (!sess_grp) { goto out; } memset(sess_grp, 0, sizeof(struct sess_rec_group)); if (global_hostdata->sess_rec_head == NULL) { TRACE(TRACE_DEBUG, "Previous Recovery list empty - " "creating new list\n"); global_hostdata->sess_rec_head = sess_grp; } else { prev_sess_grp->next = sess_grp; } } cmnd = (struct sess_rec_cmnd *)my_kmalloc(sizeof(struct sess_rec_cmnd), "sess_rec_cmnd"); if (unlikely(cmnd == NULL)) { my_kfree((void **)&sess_grp, "sess_rec_group"); goto out; } memset(cmnd, 0, sizeof(struct sess_rec_cmnd)); cmnd->Cmnd = Cmnd; if (sess_grp->cmnd_list_head == NULL) { sess_grp->cmnd_list_head = cmnd; } else { sess_grp->cmnd_list_tail->next = cmnd; } sess_grp->cmnd_list_tail = cmnd;out: TRACE(TRACE_ENTER_LEAVE, "Leave set_rec_group sess_grp %p\n", sess_grp); return sess_grp;}voidclean_rec_group(struct sess_rec_group *sess){ struct sess_rec_group *sess_grp = global_hostdata->sess_rec_head; struct sess_rec_group *prev_sess_grp = NULL; struct sess_rec_cmnd *cmnd = NULL, *tmp_cmnd = NULL; TRACE(TRACE_ENTER_LEAVE, "Enter clean_rec_group \n"); for (; sess_grp != NULL; sess_grp = sess_grp->next) { if (sess_grp == sess) break; prev_sess_grp = sess; } cmnd = sess_grp->cmnd_list_head; while (cmnd != NULL) { tmp_cmnd = cmnd; cmnd = cmnd->next; my_kfree((void **) &tmp_cmnd, "sess_rec_cmnd"); } if (sess_grp == global_hostdata->sess_rec_head) { global_hostdata->sess_rec_head = sess_grp->next; } else prev_sess_grp->next = sess_grp->next; if (sess_grp) my_kfree((void **) &sess_grp, "sess_rec_group"); TRACE(TRACE_ENTER_LEAVE, "Leave clean_rec_group \n");}/************************************************************************* This thread handles the session recovery. Session Recovery will ** abort all pending tasks and close all transport connections ** Section (6.12.4) *************************************************************************/voidinit_recovery_thread(void *arg){ unsigned long flags; int target, lun, cid; int received = 1; __u32 size; struct response_pdu *header = NULL; struct init_error_rec *err_rec; struct connection *conn; struct session *session; struct sess_rec_cmnd *cmnd = NULL; struct sess_rec_group *sess_grp = NULL; struct sockaddr *old_ip_address, *new_ip_address; int new_ip_length;#if defined(CONFIG_ISCSI_DEBUG) __u32 save_trace;#endif /* Lock kernel to daemonize thread and update init_rec_thread pointer */ lock_kernel();/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 daemonize(#else daemonize(); /* NOTE: in struct task_struct have declaration char comm[16] */ strcpy(current->comm,#endif "ini_recovery");#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 18) /* * Arne Redlich, agr1@users.sourceforge.net: * This prevents the recovery thread from becoming a zombie after exit. */ reparent_to_init();#endif siginitsetinv(¤t->blocked, ISCSI_SHUTDOWN_SIGBITS); /* mark that the recovery thread is alive */ init_rec_thread = current; unlock_kernel(); err_rec = (struct init_error_rec *)arg; conn = err_rec->curr_conn; session = conn->my_session; printk("%s Starting pid %d conn %p\n", current->comm, current->pid, conn); /* turn on some tracing */ TRACE_GET(save_trace); TRACE_SET(save_trace |(TRACE_ISCSI_FULL|TRACE_ISCSI)); /* Like the rx thread it is replacing, * this thread normally holds exclusive access to the iscsi structures. */ UNH_LOCK(&session->sess_lock, conn->rx_lock_flags); target = session->scsi_target_id; old_ip_address = conn->ip_address; cid = conn->connection_id; /* Abort all queued scsi commands with an appropriate * scsi service response Section - 6.12.4 */ if (!(conn->connection_flags & CONN_HIT_EOF)) { /* the connection is still open, try for a graceful shutdown */ drive_logout(session, conn, LOGOUT_CLOSE_SESSION); if (conn->rx_buf == NULL) { TRACE_ERROR("scsi Response Header is NULL\n"); /* release our exclusive access to iscsi structures */ UNH_UNLOCK(&session->sess_lock, conn->rx_lock_flags); goto rec_thread_out; } header = (struct response_pdu *)conn->rx_buf; size = ntohl(header->length); if (size > 0) { /* target response has data attached, toss it */ received = recv_ignore_data(conn, size); } while (received > 0) { if ((received = recv_pdu_header(conn)) <= 0) { /* connection died or we got a kill signal, * stop trying to read more input */ break; } /* ignore header digests while waiting for logout * during recovery */ header = (struct response_pdu *)conn->rx_buf; size = ntohl(header->length); if (size > MASK_24_BITS) { /* bigger than 2^24 - 1 */ TRACE_ERROR("TotalAHSLength 0x%02x, " "expected 0x00\n", size >> 24); break; } /* mark that we got some activity from the target */ conn->connection_flags |= GOT_ACTIVITY; /* Print the received iSCSI header */ TRACE_BUFFER(TRACE_BUF, conn->rx_buf, ISCSI_HDR_LEN, "%s got message\n", current->comm); if (conn->connection_flags & NEED_TX_WAKEUP) { /* get the tx thread started again */ TRACE(TRACE_ISCSI_FULL, "%s wake up tx_thread\n", current->comm); up(&conn->tx_sem); } if (size > 0) { /* target response has data attached, toss it */ received = recv_ignore_data(conn, size); } if (header->opcode == ISCSI_TARG_LOGOUT_RSP) { TRACE(TRACE_DEBUG, "Got LogOut Response\n"); break; } } } /* release exclusive access to iscsi structures if we already have it */ if (received >= 0) { UNH_UNLOCK(&session->sess_lock, conn->rx_lock_flags); } /* remove all the luns in this session, thereby removing the session */ if ((lun = remove_luns(session, global_host, global_hostdata)) < 0) { lun = 0; } /* at this point all structures and threads for original session * and all its connections should be gone! */ dup_inet_struct(old_ip_address, &new_ip_address, &new_ip_length); if (create_session(target, new_ip_address, new_ip_length, lun, cid, global_host) != 1) { TRACE_ERROR("Unable to create recovery session for target %d " "lun %d cid %d\n", target, lun, cid); goto rec_thread_out; } UNH_LOCK(&host_data_lock, flags); session = find_session_by_id(target, global_hostdata); if (session == NULL || (conn = session->connection_head) == NULL) { TRACE_ERROR("Unable to find recovery session for target %d " "lun %d cid %d\n", target, lun, cid); goto rec_thread_out_locked; } sess_grp = global_hostdata->sess_rec_head; for (; sess_grp != NULL; sess_grp = sess_grp->next) { if (sess_grp->cmnd_list_head) {/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 if (sess_grp->cmnd_list_head->Cmnd->device->id#else if (sess_grp->cmnd_list_head->Cmnd->target#endif == session->scsi_target_id) break; } } if (sess_grp) { cmnd = sess_grp->cmnd_list_head; for (; cmnd != NULL; cmnd = cmnd->next) { UNH_LOCK(&session->sess_lock, conn->rx_lock_flags); scsi_to_iscsi(cmnd->Cmnd, session); UNH_UNLOCK(&session->sess_lock, conn->rx_lock_flags); } clean_rec_group(sess_grp); }rec_thread_out_locked: UNH_UNLOCK(&host_data_lock, flags);rec_thread_out: global_hostdata->init_sessrec_flg = 0; init_rec_thread = NULL; up(&init_sess_recovery_sem); TRACE_SET(save_trace); printk("%s Exiting pid %d\n", current->comm, current->pid);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -