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 + -
显示快捷键?