iscsi-session.c
来自「iSCSI协议在LINUX下的源码.源代码是IBM公布的.主要是结合其OSD设备」· C语言 代码 · 共 1,649 行 · 第 1/3 页
C
1,649 行
/* * iSCSI driver for Linux * Copyright (C) 2001 Cisco Systems, Inc. * Copyright (C) 2004 Mike Christie * Copyright (C) 2004 IBM Corporation * maintained by linux-iscsi-devel@lists.sourceforge.net * * 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 of the License, 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. * * See the file COPYING included with this distribution for more details. * * $Id: iscsi-session.c,v 1.90 2005/02/03 11:49:18 smithan Exp $ * * This File implements the funtions related to establishing and * managing the session. */#include <linux/blkdev.h>#include <linux/kthread.h>#include <linux/delay.h>#include <linux/inet.h>#include <linux/interrupt.h>#include <scsi/scsi_device.h>#include "iscsi-session.h"#include "iscsi-ioctl.h"#include "iscsi-task.h"#include "iscsi-login.h"#include "iscsi-sfnet.h"#define DEF_SESSION_TIMEOUT (jiffies + (5 * HZ))/* * list of initialized iscsi sessions - this should be replaced * with a driver model equivalent if possible. */LIST_HEAD(iscsi_sessions);static DECLARE_MUTEX(iscsi_session_sem);static voidsignal_iscsi_threads(struct iscsi_session *session){ if (session->tx_task) kill_proc(session->tx_task->pid, SIGHUP, 1); if (session->rx_task) kill_proc(session->rx_task->pid, SIGHUP, 1);}/* drop an iscsi session */voidiscsi_drop_session(struct iscsi_session *session){ if (!test_and_clear_bit(SESSION_ESTABLISHED, &session->control_bits)) return; /* so we know whether to abort the connection */ session->session_drop_time = jiffies ? jiffies : 1; session->session_alive = 0; signal_iscsi_threads(session);}voidiscsi_update_replacement_timeout(struct iscsi_session *session, int timeout) { spin_lock(&session->portal_lock); session->portal.replacement_timeout = session->replacement_timeout = timeout; del_timer_sync(&session->replacement_timer); spin_lock_bh(&session->task_lock); if ((test_bit(SESSION_ESTABLISHED, &session->control_bits)) || (test_bit(SESSION_REPLACEMENT_TIMEDOUT, &session->control_bits)) || !timeout) { spin_unlock_bh(&session->task_lock); spin_unlock(&session->portal_lock); return; } spin_unlock_bh(&session->task_lock); timeout *= HZ; session->replacement_timer.expires = jiffies + timeout; add_timer(&session->replacement_timer); spin_unlock(&session->portal_lock);}/* caller must hold session->task_lock */voidiscsi_request_logout(struct iscsi_session *session, int logout, int logout_response){ if (atomic_read(&session->num_active_tasks) == 0) { session->logout_response_deadline = jiffies + (logout_response * HZ); if (session->logout_response_deadline == 0) session->logout_response_deadline = 1; set_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits); iscsi_wake_tx_thread(TX_LOGOUT, session); } else { session->logout_deadline = jiffies + (logout * HZ); if (session->logout_deadline == 0) session->logout_deadline = 1; session->logout_response_deadline = session->logout_deadline + (logout_response * HZ); if (session->logout_response_deadline == 0) session->logout_response_deadline = 1; set_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits); }}static voidfree_session(struct iscsi_session *session){ if (session->preallocated_task) kmem_cache_free(iscsi_task_cache, session->preallocated_task); if (session->mgmt_task) kmem_cache_free(iscsi_task_cache, session->mgmt_task); if (session->rx_tfm) crypto_free_tfm(session->rx_tfm); if (session->tx_tfm) crypto_free_tfm(session->tx_tfm); if (session->md5_tfm) crypto_free_tfm(session->md5_tfm); kfree(session->auth_client_block); kfree(session->auth_recv_string_block); kfree(session->auth_send_string_block); kfree(session->auth_recv_binary_block); kfree(session->auth_send_binary_block); kfree(session->username); kfree(session->password); kfree(session->username_in); kfree(session->password_in); kfree(session->initiator_name); kfree(session->initiator_alias); kfree(session->target_name); kfree(session->target_alias);}static intalloc_auth_buffers(struct iscsi_session *session){ if (!(session->bidirectional_auth || session->username || session->password)) return 0; if (session->auth_client_block) return 0; session->md5_tfm = crypto_alloc_tfm("md5", 0); if (!session->md5_tfm) return -ENOMEM; session->auth_client_block = kmalloc(sizeof(*session->auth_client_block), GFP_KERNEL); if (!session->auth_client_block) goto error; session->auth_recv_string_block = kmalloc(sizeof(*session->auth_recv_string_block), GFP_KERNEL); if (!session->auth_recv_string_block) goto error; session->auth_send_string_block = kmalloc(sizeof(*session->auth_send_string_block), GFP_KERNEL); if (!session->auth_send_string_block) goto error; session->auth_recv_binary_block = kmalloc(sizeof(*session->auth_recv_binary_block), GFP_KERNEL); if (!session->auth_recv_binary_block) goto error; session->auth_send_binary_block = kmalloc(sizeof(*session->auth_send_binary_block), GFP_KERNEL); if (!session->auth_send_binary_block) goto error; return 0; error: crypto_free_tfm(session->md5_tfm); kfree(session->auth_client_block); kfree(session->auth_recv_string_block); kfree(session->auth_send_string_block); kfree(session->auth_recv_binary_block); iscsi_host_err(session, "Session requires authentication but couldn't " "allocate authentication stuctures\n"); return -ENOMEM;}/* * return value: * 1: login successfully. * 0: Failed to login. No need to retry. Give up. * -1: Failed to login. Retry. */static intlogin_response_status(struct iscsi_session *session, enum iscsi_login_status login_status){ int ret; switch (login_status) { case LOGIN_OK: /* check the status class and detail */ ret = 1; break; case LOGIN_IO_ERROR: case LOGIN_WRONG_PORTAL_GROUP: case LOGIN_REDIRECTION_FAILED: iscsi_disconnect(session); ret = -1; break; default: iscsi_disconnect(session); /* * these are problems that will probably occur with any portal * of this target. */ if (session->commands_queued) /* * the session has found LUNs and been used before, so * applications or the buffer cache may be expecting * it to continue working. Keep trying to login even * though clearing the error may require * reconfiguration on the target. */ ret = -1; else { iscsi_host_err(session, "Session giving up on login " "attempts\n"); ret = 0; } } return ret;}/* * return value: * 2: login successfully. * 1: Redirected. Retry login. * 0: Failed to login. No need to retry. Give up. * -1: Failed to login. Retry. */static intcheck_iscsi_status_class(struct iscsi_session *session, u8 status_class, u8 status_detail){ switch (status_class) { case ISCSI_STATUS_CLS_SUCCESS: return 2; case ISCSI_STATUS_CLS_REDIRECT: switch (status_detail) { case ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP: return 1; /* not really success, but we want to * retry immediately, with no delay */ case ISCSI_LOGIN_STATUS_TGT_MOVED_PERM: /* * for a permanent redirect, we need to update the * portal address, and then try again. */ spin_lock(&session->portal_lock); /* reset the address in the current portal info */ memcpy(&session->portal.addr, &session->addr, sizeof(struct sockaddr)); spin_unlock(&session->portal_lock); return 1; /* not really success, but we want to * retry immediately, with no delay */ default: iscsi_host_err(session, "Login rejected: redirection " "type 0x%x not supported\n", status_detail); iscsi_disconnect(session); return -1; } case ISCSI_STATUS_CLS_INITIATOR_ERR: iscsi_disconnect(session); switch (status_detail) { case ISCSI_LOGIN_STATUS_AUTH_FAILED: iscsi_host_err(session, "Login rejected: Initiator " "failed authentication with target\n"); return 0; case ISCSI_LOGIN_STATUS_TGT_FORBIDDEN: iscsi_host_err(session, "Login rejected: initiator " "failed authorization with target\n"); return 0; case ISCSI_LOGIN_STATUS_TGT_NOT_FOUND: iscsi_host_err(session, "Login rejected: initiator " "error - target not found (%02x/%02x)\n", status_class, status_detail); return 0; case ISCSI_LOGIN_STATUS_NO_VERSION: /* * FIXME: if we handle multiple protocol versions, * before we log an error, try the other supported * versions. */ iscsi_host_err(session, "Login rejected: incompatible " "version (%02x/%02x), non-retryable, " "giving up\n", status_class, status_detail); return 0; default: iscsi_host_err(session, "Login rejected: initiator " "error (%02x/%02x), non-retryable, " "giving up\n", status_class, status_detail); return 0; } case ISCSI_STATUS_CLS_TARGET_ERR: iscsi_host_err(session, "Login rejected: target error " "(%02x/%02x)\n", status_class, status_detail); iscsi_disconnect(session); /* * We have no idea what the problem is. But spec says initiator * may retry later. */ return -1; default: iscsi_host_err(session, "Login response with unknown status " "class 0x%x, detail 0x%x\n", status_class, status_detail); iscsi_disconnect(session); return 0; }}static voidinit_session_parameters(struct iscsi_session *session){ /* logged in, get the new session ready */ session->last_rx = jiffies; session->last_ping = jiffies - 1; session->last_window_check = jiffies; session->last_kill = 0; session->window_closed = 0; session->window_full = 0; session->nop_reply.ttt = ISCSI_RSVD_TASK_TAG; INIT_LIST_HEAD(&session->nop_reply_list); /* used to detect sessions that die as soon as we hit FFP */ session->session_established_time = jiffies; session->session_alive = 1; /* used to detect sessions that aren't coming back up */ session->session_drop_time = 0; session->login_phase_timer = 0;}static intestablish_session(struct iscsi_session *session){ int ret = -1; u8 status_class; u8 status_detail; enum iscsi_login_status login_status; spin_lock(&session->portal_lock); /* * Set almost everything based on the portal's settings. * Don't change the address, since a temporary redirect * may have already changed the address, * and we want to use the redirected address rather than * the portal's address. */ iscsi_set_portal_info(session); spin_unlock(&session->portal_lock); /* set a timer on the connect */ if (session->login_timeout) session->login_phase_timer = jiffies + (session->login_timeout * HZ); if (iscsi_connect(session)) { if (signal_pending(current)) iscsi_host_err(session, "Connect timed out\n"); else iscsi_host_err(session, "Connect failed\n"); goto done; } /* * We need to grab the config_mutex before we start trying to * login, to ensure update_session doesn't try to change the * per-session settings while the login code is using them. Any * config updates will be deferred until after the login * completes. We grab the mutex now, so that the connect timeout * will break us out if we can't get the mutex for some reason. */ if (down_interruptible(&session->config_mutex)) { iscsi_host_err(session, "Failed to acquire mutex before " "login\n"); goto done; } /* clear the connect timer */ session->login_phase_timer = 0; if (signal_pending(current)) flush_signals(current); /* * try to make sure other timeouts don't go off as soon as the * session is established */ session->last_rx = jiffies; session->last_ping = jiffies - 1; /* initialize session fields for the iscsi-login code */ session->type = ISCSI_SESSION_TYPE_NORMAL; /* * iSCSI default, unless declared otherwise by the * target during login */ session->max_xmit_data_segment_len = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; session->vendor_specific_keys = 1; /* * use the session's rx_buffer for a login PDU buffer, since it is * currently unused. We can't afford to dynamically allocate * memory right now, since it's possible we're reconnecting, and * the VM system is already blocked trying to write dirty pages to * the iSCSI device we're trying to reconnect. The session's * rx_buffer was sized to have enough space for us to handle the login * phase. */ login_status = iscsi_login(session, session->rx_buffer, sizeof(session->rx_buffer), &status_class, &status_detail); /* * release the lock on the per-session settings used by the login code */ up(&session->config_mutex); ret = login_response_status(session, login_status); if (ret < 1) goto done; /* check the login status */ ret = check_iscsi_status_class(session, status_class, status_detail); if (ret < 2) goto done; iscsi_host_notice(session, "Session established\n"); init_session_parameters(session); spin_lock(&session->portal_lock); del_timer_sync(&session->replacement_timer); /* mark the session as up and accepting commands again */ spin_lock_bh(&session->task_lock); clear_bit(SESSION_REPLACEMENT_TIMEDOUT, &session->control_bits); set_bit(SESSION_ESTABLISHED, &session->control_bits); spin_unlock_bh(&session->task_lock); spin_unlock(&session->portal_lock); /* wake up everyone waiting for the session to be established */ wake_up(&session->login_wait_q); /* make sure we start sending commands again */ iscsi_wake_tx_thread(TX_SCSI_COMMAND, session); done: /* clear any timer that may have been left running */ session->login_phase_timer = 0; /* cleanup after a possible timeout expiration */ if (signal_pending(current)) { flush_signals(current); if (test_bit(SESSION_TERMINATING, &session->control_bits)) return 0; else return -1; } return ret;}static char*strdup(char *str, int *err){ int len; char *s; *err = 0; len = strlen(str) + 1; if (len == 1) { *err = -EINVAL; return NULL; } s = kmalloc(len, GFP_KERNEL); if (!s) { *err = -ENOMEM; return NULL; } return strcpy(s, str);}/* * return value: * 1: name/alias updated. Relogin required. * 0: No updated needed. * -Exxx: Failed to update. */static intupdate_iscsi_strings(struct iscsi_session *session, struct iscsi_session_ioctl *ioctld){ char *iname = NULL; char *alias = NULL; char *uname = NULL; char *uname_in = NULL; char *pw = NULL; char *pw_in = NULL; int rc = 0; if (!ioctld->initiator_name[0]) { iscsi_host_err(session, "No InitiatorName\n"); return -EINVAL; } if (strcmp(ioctld->initiator_name, session->initiator_name)) { iname = strdup(ioctld->initiator_name, &rc); if (!iname) { iscsi_host_err(session, "Failed to change " "InitiatorName from %s to %s\n", session->initiator_name, ioctld->initiator_name); return rc; } } if (ioctld->initiator_alias[0] && (!session->initiator_alias || strcmp(ioctld->initiator_alias, session->initiator_alias))) { alias = strdup(ioctld->initiator_alias, &rc); if (!alias) /* Alias is not ciritical so just print an error */ iscsi_host_err(session, "Failed to change " "InitiatorAlias\n"); } if (ioctld->username[0] && (!session->username || strcmp(ioctld->username, session->username))) { uname = strdup(ioctld->username, &rc); if (!uname) { iscsi_host_err(session, "Failed to change outgoing " "username\n"); goto failed; }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?