public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 4/10][RFC] linux-iscsi driver
@ 2005-01-10 22:57 Mike Christie
  2005-01-10 23:24 ` Christoph Hellwig
  0 siblings, 1 reply; 8+ messages in thread
From: Mike Christie @ 2005-01-10 22:57 UTC (permalink / raw)
  To: linux-scsi

[-- Attachment #1: Type: text/plain, Size: 25 bytes --]

Core iSCSI session code.

[-- Attachment #2: 04-iscsi-session.patch --]
[-- Type: text/x-patch, Size: 50293 bytes --]

diff -Naurp scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-session.c scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-session.c
--- scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-session.c	1969-12-31 16:00:00.000000000 -0800
+++ scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-session.c	2005-01-10 12:22:24.875910647 -0800
@@ -0,0 +1,1533 @@
+/*
+ * 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.
+ *
+ * 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 <scsi/scsi_device.h>
+
+#include "iscsi-session.h"
+#include "iscsi-ioctl.h"
+#include "iscsi-task.h"
+#include "iscsi-login.h"
+#include "iscsi-sfnet.h"
+
+/*
+ * 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 void
+signal_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 */
+void
+iscsi_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;
+	signal_iscsi_threads(session);
+}
+
+static void
+handle_logout_timeouts(unsigned long data)
+{
+	struct iscsi_session *session = (struct iscsi_session *)data;
+
+	if (test_bit(SESSION_TERMINATED, &session->control_bits) ||
+	    !test_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits))
+		return;
+	/*
+	 * we're waiting for tasks to complete before logging out. No need to
+	 * check the CmdSN window, since we won't be starting any more tasks.
+	 */
+	if (test_and_set_bit(SESSION_IN_LOGOUT, &session->control_bits)) {
+		/*
+		 * passed the deadline for a logout response, just drop the
+		 * session
+		 */
+		iscsi_host_err(session, "Logout response timed out, dropping "
+			       "session\n");
+		iscsi_drop_session(session);
+	} else {
+		iscsi_wake_tx_thread(TX_LOGOUT, session);
+		mod_timer(&session->logout_timer,
+			  jiffies + (session->logout_response_timeout * HZ));
+	}
+
+}
+
+/* caller must hold session->task_lock */
+void
+iscsi_request_logout(struct iscsi_session *session, int logout_timeout,
+		     int logout_response_timeout)
+{
+	int timeout;
+
+	if (test_and_set_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits))
+		return;
+	/*
+	 * we should not be sending any new requests, so we do not want
+	 * the net timer to send pings. If we have active tasks then
+	 * we delay logout, but one way or another this session is going
+	 * so we do not need the net timer even if transport is bad.
+	 */
+	del_timer(&session->transport_timer);
+
+	session->logout_response_timeout = logout_response_timeout;
+	if (session->num_active_tasks == 0) {
+		timeout = session->logout_response_timeout;
+		set_bit(SESSION_IN_LOGOUT, &session->control_bits);
+		iscsi_wake_tx_thread(TX_LOGOUT, session);
+	} else
+		timeout = logout_timeout;
+	mod_timer(&session->logout_timer, jiffies + (timeout * HZ));
+}
+
+/*
+ * return value:
+ * 	1: login successfully.
+ *	0: Failed to login. No need to retry. Give up.
+ * 	-1: Failed to login. Retry.
+ */
+static int
+login_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 int
+check_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");
+			if (session->username || session->password_length ||
+			    session->bidirectional_auth)
+				/*
+				 * retry, and hope we can allocate the auth
+				 * structures next time.
+				 */
+				return -1;
+			else 
+				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 void
+login_timed_out(unsigned long data)
+{
+	struct iscsi_session *session = (struct iscsi_session *)data;
+
+	iscsi_host_err(session, "Login phase timed out, timeout was set for "
+		       "%d secs\n", session->login_timeout);
+	kill_proc(session->rx_task->pid, SIGHUP, 1);
+}
+
+static int
+__establish_session(struct iscsi_session *session)
+{
+	int ret = -1;
+	u8 status_class;
+	u8 status_detail;
+	enum iscsi_login_status login_status;
+	struct timer_list login_timer;
+
+	/*
+	 * del FFP timers if running
+	 */
+	del_timer_sync(&session->transport_timer);
+	del_timer_sync(&session->logout_timer);
+	if (signal_pending(current))
+		flush_signals(current);
+
+	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);
+
+	iscsi_disconnect(session);
+
+	init_timer(&login_timer);
+	if (session->login_timeout) {
+		login_timer.data = (unsigned long)session;
+		login_timer.function = login_timed_out;
+		login_timer.expires = jiffies + (session->login_timeout * HZ);
+		add_timer(&login_timer);
+	}
+
+	if (iscsi_connect(session)) {
+		iscsi_host_err(session, "establish_session failed. Could not "
+			       "connect to target\n");
+		goto done;
+	}
+
+	/*
+	 * Grab the config mutex a little early incase update_session
+	 * is running and something went wacko, the connect/login timer
+	 * above will break us out.
+	 */
+	if (down_interruptible(&session->config_mutex)) {
+		iscsi_host_err(session, "Failed to acquire mutex before "
+			       "login\n");
+		goto done;
+	}
+
+	/*
+	 * initialize session fields for the iscsi-login code
+	 */
+	session->type = ISCSI_SESSION_TYPE_NORMAL;
+	/*
+	 * use 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;
+	/*
+	 * we do not want to allocate memory here since this might be a
+	 * relogin with IO in progress, so we reuse the rx_buffer. Note
+	 * that extra care must be taken when using this buffer for both
+	 * send and recv here , becuase the net subsys does not copy data
+	 * in sendpage.
+	 */
+	login_status = iscsi_login(session, session->rx_buffer,
+				   sizeof(session->rx_buffer), &status_class,
+				   &status_detail);
+	up(&session->config_mutex);
+
+	ret = login_response_status(session, login_status);
+	if (ret < 1)
+		goto done;
+
+	ret = check_iscsi_status_class(session, status_class, status_detail);
+	if (ret < 2)
+		goto done;
+	/*
+	 * logged in ok, get the new session ready
+	 */
+	session->ever_established = 1;
+	session->window_closed = 0;
+	session->session_established_time = jiffies;
+	session->session_drop_time = 0;
+
+	session->nop_reply.ttt = ISCSI_RSVD_TASK_TAG;
+	INIT_LIST_HEAD(&session->nop_reply_list);
+
+	clear_bit(SESSION_WINDOW_CLOSED, &session->control_bits);
+	clear_bit(SESSION_REPLACEMENT_TIMEDOUT, &session->control_bits);
+	set_bit(SESSION_ESTABLISHED, &session->control_bits);
+
+	spin_lock_bh(&session->task_lock);
+	iscsi_mod_session_timer(session, session->idle_timeout);
+	spin_unlock_bh(&session->task_lock);
+	/*
+	 * ready to go, so wake up everyone waiting for the session
+	 * to be established
+	 */
+	wake_up(&session->login_wait_q);
+ done:
+	del_timer_sync(&login_timer);
+	/* 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 int
+update_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;
+
+	/*
+	 * update all the values or none of them
+	 */
+	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;
+		}
+	}
+
+	if (ioctld->username_in[0] && (!session->username_in ||
+	    strcmp(ioctld->username_in, session->username_in))) {
+		uname_in = strdup(ioctld->username_in, &rc);
+		if (!uname_in) {
+			iscsi_host_err(session, "Failed to change incoming "
+				       "username\n");
+				goto failed;
+		}
+	}
+
+	if (ioctld->password_length && (!session->password ||
+	    session->password_length != ioctld->password_length ||
+	    memcmp(ioctld->password, session->password,
+		   session->password_length))) {
+		pw = kmalloc(ioctld->password_length + 1, GFP_KERNEL);
+		if (!pw) {
+			iscsi_host_err(session, "Failed to change outgoing "
+				       "password\n");
+			rc = -ENOMEM;
+			goto failed;
+		}
+		memcpy(pw, ioctld->password, ioctld->password_length);
+	}
+
+	if (ioctld->password_length_in && (!session->password_in ||
+	    session->password_length_in != ioctld->password_length_in ||
+	    memcmp(ioctld->password_in, session->password_in,
+		   session->password_length_in))) {
+		pw_in = kmalloc(ioctld->password_length_in + 1, GFP_KERNEL);
+		if (!pw_in) {
+			iscsi_host_err(session, "Failed to change incoming "
+				       "password\n");
+			rc = -ENOMEM;
+			goto failed;
+		}
+		memcpy(pw_in, ioctld->password_in, ioctld->password_length_in);
+	}
+
+	if (iname) {
+		kfree(session->initiator_name);	
+		session->initiator_name = iname;
+		rc = 1;
+	}
+	if (alias || (!ioctld->initiator_alias[0] &&
+		      session->initiator_alias[0])) {
+		kfree(session->initiator_alias);
+		session->initiator_alias = alias;
+		rc = 1;
+	}
+	if (uname || (!ioctld->username[0] && session->username)) {
+		kfree(session->username);
+		session->username = uname;
+		rc = 1;
+	}
+	if (uname_in || (!ioctld->username_in[0] && session->username_in)) {
+		kfree(session->username_in);
+		session->username_in = uname_in;
+		rc = 1;
+	}
+	if (pw || (!ioctld->password_length && session->password)) {
+		kfree(session->password);
+		session->password = pw;
+		session->password_length = ioctld->password_length;
+		rc = 1;
+	}
+	if (pw_in || (!ioctld->password_length_in && session->password_in)) {
+		kfree(session->password_in);
+		session->password_in = pw_in;
+		session->password_length_in = ioctld->password_length_in;
+		rc = 1;
+	}
+	return rc;
+ failed:
+	kfree(iname);
+	kfree(alias);
+	kfree(uname);
+	kfree(uname_in);
+	kfree(pw);
+	kfree(pw_in);
+	return rc;
+}
+
+static int
+alloc_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;
+}
+
+int
+iscsi_update_session(struct iscsi_session *session,
+		     struct iscsi_session_ioctl *ioctld)
+{
+	int rc = 0;
+	int relogin = 0;
+
+	if (down_interruptible(&session->config_mutex)) {
+		iscsi_host_err(session, "Session configuration update aborted "
+			       "by signal\n");
+		return -EINTR;
+	}
+	if (test_bit(SESSION_TERMINATED, &session->control_bits))
+		return -EINVAL;
+
+	if (ioctld->update && (ioctld->config_number < session->config_number))
+		/* this update is obsolete, ignore it */
+		goto err_exit;
+
+	if (ioctld->username_in[0] || ioctld->password_length_in)
+		session->bidirectional_auth = 1;
+	else
+		session->bidirectional_auth = 0;
+	rc = alloc_auth_buffers(session);
+	if (rc < 0)
+		goto err_exit;
+
+	rc = update_iscsi_strings(session, ioctld);
+	if (rc > 0)
+		relogin = 1;
+	else if (rc < 0) 
+		goto err_exit;
+
+	session->config_number = ioctld->config_number;
+
+	if (memcmp(session->isid, ioctld->isid, sizeof(session->isid))) {
+		memcpy(session->isid, ioctld->isid, sizeof(session->isid));
+		relogin = 1;
+	}
+
+	/*
+	 * after we release the mutex we cannot touch any field that
+	 * may be freed by a shutdown that is running at the same time
+	 */
+	up(&session->config_mutex);
+
+	/*
+	 * the portals are guarded by a spinlock instead of the config
+	 * mutex, so that we can request portal changes while a login is
+	 * occuring.
+	 */
+	spin_lock(&session->portal_lock);
+	if (iscsi_update_portal_info(&session->portal, &ioctld->portal))
+		relogin = 1;
+	spin_unlock(&session->portal_lock);
+
+	if (relogin)
+		if (test_bit(SESSION_ESTABLISHED, &session->control_bits)) {
+			spin_lock_bh(&session->task_lock);
+			iscsi_request_logout(session, 3,
+					     session->active_timeout);
+			spin_unlock_bh(&session->task_lock);
+		}
+	return 0;
+
+ err_exit:
+	up(&session->config_mutex);
+	return rc;
+}
+
+static int
+copy_iscsi_strings(struct iscsi_session *session,
+		   struct iscsi_session_ioctl *ioctld)
+{
+	int rc;
+
+	session->initiator_name = strdup(ioctld->initiator_name, &rc);
+	if (rc == -EINVAL) {
+		iscsi_host_err(session, "No InitiatorName\n");
+		return rc;
+	}
+	if (rc == -ENOMEM) {
+		iscsi_host_err(session, "Cannot allocate InitiatorName\n");
+		return rc;
+	}
+
+	session->initiator_alias = strdup(ioctld->initiator_alias, &rc);
+	/* Alias is not ciritical so just print an error */
+	if (!session->initiator_alias)
+		iscsi_host_err(session, "Cannot create InitiatorAlias\n");
+
+	session->target_name = strdup(ioctld->target_name, &rc);
+	if (rc == -EINVAL) {
+		iscsi_err("No TargetName\n");
+		return rc;
+	}
+	if (rc == -ENOMEM) {
+		iscsi_host_err(session, "Cannot allocate TargetName\n");
+		return rc;
+	}
+
+	session->username = strdup(ioctld->username, &rc);
+	if (rc == -ENOMEM) {
+		iscsi_host_err(session, "Failed to allocate outgoing "
+			       "username\n");
+		return rc;
+	}
+
+	session->username_in = strdup(ioctld->username_in, &rc);
+	if (rc == -ENOMEM) {
+		iscsi_host_err(session, "Failed to allocate incoming "
+			       "username\n");
+		return rc;
+	}
+
+	if (ioctld->password_length) {
+		session->password = kmalloc(ioctld->password_length + 1,
+					    GFP_KERNEL);
+		if (!session->password) {
+			iscsi_host_err(session, "Failed to allocate outgoing "
+				       "password\n");
+			return -ENOMEM;
+		}
+		memcpy(session->password, ioctld->password,
+		       ioctld->password_length);
+		session->password_length = ioctld->password_length;
+	}
+
+	if (ioctld->password_length_in) {
+		session->password_in = kmalloc(ioctld->password_length_in + 1,
+					       GFP_KERNEL);
+		if (!session->password_in) {
+			iscsi_host_err(session, "Failed to allocate incoming "
+				       "password\n");
+			return -ENOMEM;
+		}
+		memcpy(session->password_in, ioctld->password_in,
+		       ioctld->password_length_in);
+		session->password_length_in = ioctld->password_length_in;
+	}
+
+	return 0;
+}
+
+/**
+ * clear_session - clear session fields before attempting a re-login.
+ * @session: session to initialize.
+ **/
+static void
+clear_session(struct iscsi_session *session)
+{
+	struct iscsi_nop_info *nop_info, *tmp;
+
+	session->last_mgmt_itt = ISCSI_RSVD_TASK_TAG;
+
+	session->nop_reply.ttt = ISCSI_RSVD_TASK_TAG;
+	list_for_each_entry_safe(nop_info, tmp, &session->nop_reply_list,
+				 reply_list) {
+		list_del(&nop_info->reply_list);
+		kfree(nop_info);
+	}
+
+	clear_bit(SESSION_IN_LOGOUT, &session->control_bits);
+	clear_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits);
+	session->logout_response_timeout = 0;
+}
+
+static void
+init_session_structure(struct iscsi_session *session,
+		       struct iscsi_session_ioctl *ioctld)
+{
+	INIT_LIST_HEAD(&session->list);
+	session->config_number = ioctld->config_number;
+	spin_lock_init(&session->portal_lock);
+	session->portal_group_tag = -1;
+	/* the first down should block */
+	sema_init(&session->config_mutex, 0);
+	INIT_LIST_HEAD(&session->pending_queue);
+	INIT_LIST_HEAD(&session->active_queue);
+	INIT_LIST_HEAD(&session->done_queue);
+	spin_lock_init(&session->task_lock);
+	INIT_LIST_HEAD(&(session->tx_task_head));
+	init_waitqueue_head(&session->tx_wait_q);
+	init_waitqueue_head(&session->login_wait_q);
+	sema_init(&session->tx_blocked, 0);
+	session->next_itt = 1;
+	session->time2wait = -1;
+	session->last_mgmt_itt = ISCSI_RSVD_TASK_TAG;
+	session->mgmt_task_complete = NULL;
+}
+
+/*
+ * Timer processing for a session in Full Feature Phase (minus logout).
+ * This timer may rearm itself.
+ */
+static void
+check_transport_timeouts(unsigned long data)
+{
+	struct iscsi_session *session = (struct iscsi_session *)data;
+	unsigned long timeout, next_timeout = 0, last_rx;
+
+	spin_lock(&session->task_lock);
+
+	if (test_bit(SESSION_TERMINATED, &session->control_bits) ||
+	    !test_bit(SESSION_ESTABLISHED, &session->control_bits))
+		goto done;
+
+	if (session->num_active_tasks)
+		timeout = session->active_timeout;
+	else
+		timeout = session->idle_timeout;
+	if (!timeout)
+		goto check_window;
+
+	timeout *= HZ;
+	last_rx = session->last_rx;
+
+	if (session->ping_timeout &&
+	    time_before_eq(last_rx + timeout + (session->ping_timeout * HZ),
+			   jiffies)) {
+		iscsi_host_err(session, "ping timeout of %d secs expired, "
+			       "last rx %lu, last ping %lu, now %lu\n",
+			       session->ping_timeout, last_rx,
+			       session->last_ping, jiffies);
+		iscsi_drop_session(session);
+		goto done;
+	}
+
+	if (time_before_eq(last_rx + timeout, jiffies)) {
+		if (time_before_eq(session->last_ping, last_rx)) {
+			/*
+			 * send a ping to try to provoke some
+			 * traffic
+			 */
+			session->last_ping = jiffies;
+			iscsi_wake_tx_thread(TX_PING, session);
+		}
+		next_timeout = last_rx + timeout + (session->ping_timeout * HZ);
+	} else
+		next_timeout = last_rx + timeout;
+
+ check_window:
+	/*
+	 * Do we still want to do this, or was it for an older
+	 * bad target that has been fixed?
+	 */
+	if (test_bit(SESSION_WINDOW_CLOSED, &session->control_bits)) {
+		/*
+		 * command window closed, ping once every 5 secs to ensure
+		 * we find out when it re-opens.  Target should send
+		 * us an update when it does, but we're not very
+		 * trusting of target correctness.
+		 */
+		if (time_before(session->last_ping + (5 * HZ), jiffies))
+			iscsi_wake_tx_thread(TX_PING, session);
+		if (next_timeout)
+			next_timeout = min(jiffies + (5 * HZ), next_timeout);
+		else
+			next_timeout = jiffies + (5 * HZ);
+	}
+
+	if (next_timeout)
+		mod_timer(&session->transport_timer, next_timeout);
+ done:
+	spin_unlock(&session->task_lock);
+}
+
+/**
+ * iscsi_mod_session_timer - modify the session's transport timer
+ * @session: iscsi session
+ * @timeout: timeout in seconds
+ *
+ * Note:
+ *    Must hold the task lock. And, if the new timeout was shorter
+ *    than the window_closed_timeout we will end up delaying the
+ *    new timeout. This should be rare and not really hurt anything
+ *    so we ignore it for now.
+ **/
+void
+iscsi_mod_session_timer(struct iscsi_session *session, int timeout)
+{
+	/*
+	 * reset last_rx and last_ping, so that it does not look like
+	 * we timed out when we are just switching states
+	 */
+	session->last_rx = jiffies;
+	session->last_ping = jiffies;
+
+	if (test_bit(SESSION_WINDOW_CLOSED, &session->control_bits))
+		return;
+
+	if (timeout)
+		mod_timer(&session->transport_timer, jiffies + (timeout * HZ));
+	else
+		del_timer(&session->transport_timer);
+}
+
+void
+iscsi_wake_tx_thread(int control_bit, struct iscsi_session *session)
+{
+	set_bit(control_bit, &session->control_bits);
+	set_bit(TX_WAKE, &session->control_bits);
+	wake_up(&session->tx_wait_q);
+}
+
+/**
+ * iscsi_wait_for_session - Wait for a session event to be established.
+ * @session: session to wait on.
+ * @ignore_timeout: If zero this will return when the replacement timeout fires.
+ *
+ * Description:
+ *    Returns 1 to indicate sesssion was established, or 0 to indicate
+ *    we timed out (if ignore_timeout == 0) or are terminating.
+ **/
+int
+iscsi_wait_for_session(struct iscsi_session *session, int ignore_timeout)
+{
+	int rc = 0;
+
+	while (1) {
+		wait_event_interruptible(session->login_wait_q,
+			test_bit(SESSION_ESTABLISHED, &session->control_bits) ||
+			test_bit(SESSION_TERMINATING, &session->control_bits) ||
+			(!ignore_timeout &&
+			 test_bit(SESSION_REPLACEMENT_TIMEDOUT,
+				  &session->control_bits)));
+
+		if (signal_pending(current))
+			flush_signals(current);
+
+		/*
+		 * need to test for termnination first to avoid falling
+		 * in the tx request loop for ever
+		 */
+		if (test_bit(SESSION_TERMINATING, &session->control_bits))
+			break;
+
+		if (test_bit(SESSION_ESTABLISHED, &session->control_bits)) {
+			rc = 1;
+			break;
+		}
+
+		if (!ignore_timeout && test_bit(SESSION_REPLACEMENT_TIMEDOUT,
+						 &session->control_bits))
+			break;
+	}
+
+	return rc;
+}
+
+/*
+ * Note the ordering matches the TX_* bit ordering.
+ * See iscsi_tx_thread comment, this basically a
+ * workqueue_struct.
+ */
+static struct {
+	void (* request_fn)(struct iscsi_session *);
+} tx_request_fns[] = {
+	{ iscsi_send_nop_out },
+	{ iscsi_send_task_mgmt },
+	{ iscsi_run_pending_queue },
+	{ iscsi_send_nop_replys },
+	{ iscsi_send_r2t_data },
+	{ iscsi_send_logout },
+};
+
+static void
+wait_for_tx_requests(struct iscsi_session *session)
+{
+	int req;
+
+	wait_event_interruptible(session->tx_wait_q,
+		test_and_clear_bit(TX_WAKE, &session->control_bits));
+
+	for (req = 0; req < TX_WAKE; req++) {
+		if (signal_pending(current))
+			return;
+		/*
+		 * when a logout is in progress or about to be sent
+		 * we do not start new requests, but we continue to
+		 * respond to R2Ts and Nops.
+		 */
+		if (test_and_clear_bit(req, &session->control_bits)) {
+			if (test_bit(SESSION_LOGOUT_REQUESTED,
+				     &session->control_bits) &&
+			    req <= TX_SCSI_COMMAND)
+				continue;
+
+			tx_request_fns[req].request_fn(session);
+		}
+	}
+}
+
+/**
+ * session_kthread_sleep - put a thread to sleep while waiting for shutdown.
+ * @session: session. 
+ *
+ * Description:
+ *    If for some reason we could not relogin into a session we sleep here
+ *    and and wait for someone to remove the session. Returns -EPERM to
+ *    indicate the thread should exit, or zero to indicate that the thread
+ *    can proceed with its normal action.
+ **/
+static inline int
+session_kthread_sleep(struct iscsi_session *session)
+{
+ retest:
+	set_current_state(TASK_INTERRUPTIBLE);
+	if (kthread_should_stop()) {
+		__set_current_state(TASK_RUNNING);
+		return -EPERM;
+	}
+
+	/*
+	 * We fall into this sleep, when someone has broken us
+	 * out of the lower loops that process requests or log us in,
+	 * terminate the session (session drops will not sleep here),
+	 * but have not (yet) cleaned up the host and called kthread_stop()).
+	 */
+	if (test_bit(SESSION_TERMINATING, &session->control_bits)) {
+		schedule();
+		if (signal_pending(current))
+			flush_signals(current);
+		goto retest;
+	}
+	__set_current_state(TASK_RUNNING);
+	return 0;
+}
+
+static void
+replacement_timed_out(unsigned long data)
+{
+	struct iscsi_session *session = (struct iscsi_session *)data;
+
+	iscsi_host_err(session, "replacement session time out after %d "
+		       "seconds, drop %lu, now %lu, failing all commands\n",
+		       session->replacement_timeout,
+		       session->session_drop_time, jiffies);
+	/*
+	 * need a lock or some real state code.
+	 */
+	if (test_bit(SESSION_ESTABLISHED, &session->control_bits) ||
+	    test_and_set_bit(SESSION_REPLACEMENT_TIMEDOUT,
+			     &session->control_bits))
+		return;
+
+	wake_up_all(&session->login_wait_q);
+}
+
+/*
+ * the writer thread
+ * TODO? - this could be nicely replaced with a work queue
+ * having a work struct replacing each TX_* req, but will
+ * using a singlethreaded_workqueue hurt perf when all
+ * targets use the same cpu_workqueue_struct?
+ * Or to reduce the number of threads, should we use one
+ * per cpu workqueue for the entire driver for all sends?
+ */
+static int
+iscsi_tx_thread(void *data)
+{
+	struct iscsi_session *session = data;
+	int rc;
+	unsigned long tmo;
+	struct timer_list replacement_timer;
+
+	init_timer(&replacement_timer);
+	replacement_timer.data = (unsigned long)session;
+	replacement_timer.function = replacement_timed_out;
+
+	current->flags |= PF_MEMALLOC;
+	allow_signal(SIGHUP);
+
+	/*
+	 * tell the rx thread that we're about to block, and that
+	 * it can safely call iscsi_sendmsg now as part of
+	 * the Login phase.
+	 */
+	up(&session->tx_blocked);
+
+	while (!session_kthread_sleep(session)) {
+		tmo = session->replacement_timeout * HZ;
+		if (tmo && session->session_drop_time)
+			mod_timer(&replacement_timer, jiffies + tmo);
+		rc = iscsi_wait_for_session(session, 1);
+		del_timer_sync(&replacement_timer);
+		if (!rc)
+			continue;
+
+		down(&session->tx_blocked);
+
+		/*
+		 * make sure we start sending commands again,
+		 * and clear any stale requests
+		 */
+		clear_bit(TX_TMF, &session->control_bits);
+		clear_bit(TX_LOGOUT, &session->control_bits);
+		clear_bit(TX_DATA, &session->control_bits);
+		set_bit(TX_PING, &session->control_bits);
+		set_bit(TX_SCSI_COMMAND, &session->control_bits);
+		set_bit(TX_WAKE, &session->control_bits);
+
+		while (!signal_pending(current))
+			wait_for_tx_requests(session);
+		flush_signals(current);
+
+		up(&session->tx_blocked);
+	}
+
+	return 0;
+}
+
+static int
+establish_session(struct iscsi_session *session, unsigned int login_delay)
+{
+	int rc;
+	unsigned long login_failures = 0;
+
+	while (!test_bit(SESSION_ESTABLISHED, &session->control_bits)) {
+		if (login_delay) {
+			iscsi_host_notice(session, "Waiting %u seconds before "
+					  "next login attempt\n", login_delay);
+			msleep_interruptible(login_delay * 1000);
+		}
+
+		if (test_bit(SESSION_TERMINATING, &session->control_bits))
+			return 0;
+		/*
+		 * wait for the tx thread to block or exit, ignoring signals.
+		 * the rx thread needs to know that the tx thread is not
+		 * running before it can safely close the socket and start
+		 * a new login phase on a new socket.
+		 */
+		down(&session->tx_blocked);
+		up(&session->tx_blocked);
+
+		rc = __establish_session(session);
+		if (rc > 0)
+			/* established or redirected */
+			login_failures = 0;
+		else if (rc < 0)
+			/* failed, retry */
+			login_failures++;
+		 else {
+			/* failed, give up */
+			iscsi_host_err(session, "Session giving up\n");
+			set_bit(SESSION_TERMINATING, &session->control_bits);
+			return 0;
+		}
+
+		/* slowly back off the frequency of login attempts */
+		if (login_failures == 0)
+			login_delay = 0;
+		else if (login_failures < 30)
+			login_delay = 1;
+		else if (login_failures < 48)
+			login_delay = 5;
+		else if (!test_bit(SESSION_REPLACEMENT_TIMEDOUT,
+				   &session->control_bits))
+			login_delay = 10;
+		 else
+			login_delay = 60;
+	}
+
+	return 1;
+}
+
+/**
+ * get_time2wait - return iSCSI DefaultTime2Wait
+ * @session: iscsi session
+ * @short_sessions: number of short sessions
+ *
+ * Description:
+ *   Return DefaultTime2Wait. However, if the session dies really
+ *   quicky after we reach FFP, we'll not be interoperable due to bugs
+ *   in the target (or this driver) that send illegal opcodes,
+ *   or disagreements about how to do CRC  calculations. To
+ *   avoid spinning, we track sessions with really short
+ *   lifetimes, and decrease the login frequency if we keep
+ *   getting session failures, like we do for login failures.
+ **/
+static unsigned int
+get_time2wait(struct iscsi_session *session, unsigned long *short_sessions)
+{
+	unsigned int login_delay = 0;
+
+	if (session->time2wait >= 0) {
+		login_delay = session->time2wait;
+		session->time2wait = -1;
+	} else
+		login_delay = session->def_time2wait;
+
+	if (time_before_eq(session->session_drop_time,
+		     	   session->session_established_time + (2 * HZ))) {
+		(*short_sessions)++;
+
+		if (*short_sessions < 30)
+			login_delay = max_t(unsigned int, login_delay, 1);
+		else if (*short_sessions < 48)
+			login_delay = max_t(unsigned int, login_delay, 5);
+		else if (!test_bit(SESSION_REPLACEMENT_TIMEDOUT,
+				   &session->control_bits))
+			login_delay = max_t(unsigned int, login_delay, 10);
+		else
+			login_delay = max_t(unsigned int, login_delay, 60);
+
+		iscsi_host_warn(session, "Session has ended quickly %lu times, "
+				"login delay %u seconds\n", *short_sessions,
+				login_delay);
+	} else
+		/* session lived long enough that the target is probably ok */
+		*short_sessions = 0;
+
+	return login_delay;
+}
+
+static int
+iscsi_rx_thread(void *data)
+{
+	struct iscsi_session *session = data;
+	struct iscsi_hdr hdr;
+	unsigned int login_delay = 0;
+	unsigned long short_sessions = 0;
+
+	current->flags |= PF_MEMALLOC;
+	allow_signal(SIGHUP);
+
+        while (!session_kthread_sleep(session)) {
+		if (!establish_session(session, login_delay))
+			continue;
+
+		while (!signal_pending(current))
+			iscsi_recv_pdu(session, &hdr, session->header_digest,
+				       session->rx_buffer, ISCSI_RXCTRL_SIZE,
+				       session->data_digest);
+		flush_signals(current);
+
+		login_delay = get_time2wait(session, &short_sessions);
+		/*
+		 * if this is a session drop we need to wait for
+		 * the tx thread to stop queueing and processing requests
+		 */
+		down(&session->tx_blocked);
+		up(&session->tx_blocked);
+
+		/*
+		 * session dropped unexpectedly, often due to
+		 * network problems
+		 */
+		iscsi_host_err(session, "Session dropped\n");
+		spin_lock_bh(&session->task_lock);
+		iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_BUS_BUSY);
+		clear_session(session);
+		spin_unlock_bh(&session->task_lock);
+	}
+	/*
+	 * If there are any commands left this will remove them.
+	 */
+	spin_lock_bh(&session->task_lock);
+	iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_NO_CONNECT);
+	spin_unlock_bh(&session->task_lock);
+
+	return 0;
+}
+
+static int
+start_session_threads(struct iscsi_session *session)
+{
+	/*
+	 * transport timer reused for replacement timer and
+	 * testing transport connection
+	 */
+	init_timer(&session->transport_timer);
+	session->transport_timer.data = (unsigned long)session;
+	session->transport_timer.function = check_transport_timeouts;
+
+	init_timer(&session->logout_timer);
+	session->logout_timer.data = (unsigned long)session;
+	session->logout_timer.function = handle_logout_timeouts;
+
+	session->tx_task = kthread_run(iscsi_tx_thread, session, "iscsi-tx");
+	if (IS_ERR(session->tx_task)) {
+		iscsi_host_err(session, "Failed to start tx thread, terminating"
+			       " session\n");
+		goto fail;
+	}
+
+	session->rx_task = kthread_run(iscsi_rx_thread, session, "iscsi-rx");
+	if (IS_ERR(session->rx_task)) {
+		iscsi_host_err(session, "Failed to start rx thread, terminating"
+			       " session\n");
+		goto shutdown_tx_thread;
+	}
+
+	return 0;
+
+ shutdown_tx_thread:
+	set_bit(SESSION_TERMINATING, &session->control_bits);
+	kthread_stop(session->tx_task);
+ fail:
+	return -EAGAIN;
+}
+
+static void
+free_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);
+}
+
+void
+iscsi_destroy_session(struct iscsi_session *session)
+{
+	set_bit(SESSION_TERMINATING, &session->control_bits);
+	clear_bit(SESSION_ESTABLISHED, &session->control_bits);
+
+	down(&iscsi_session_sem);
+	list_del(&session->list);
+	up(&iscsi_session_sem);
+
+	session->session_drop_time = jiffies ? jiffies : 1;
+	signal_iscsi_threads(session);
+
+	kthread_stop(session->tx_task);
+	kthread_stop(session->rx_task);
+
+	iscsi_disconnect(session);
+
+	set_bit(SESSION_TERMINATED, &session->control_bits);
+	del_timer_sync(&session->transport_timer);
+	del_timer_sync(&session->logout_timer);
+	/*
+	 * grab the config mutex to make sure update_session is not
+	 * accessing the session fields we are going to free
+	 */
+	down(&session->config_mutex);
+	free_session(session);
+	up(&session->config_mutex);
+}
+
+int
+iscsi_create_session(struct iscsi_session *session,
+		     struct iscsi_session_ioctl *ioctld)
+{
+	int rc;
+
+	init_session_structure(session, ioctld);
+
+	session->preallocated_task = kmem_cache_alloc(iscsi_task_cache,
+						      GFP_KERNEL);
+	if (!session->preallocated_task) {
+		iscsi_host_err(session, "Couldn't preallocate task\n");
+		rc = -ENOMEM;
+		goto free_session;
+	}
+
+	session->mgmt_task = kmem_cache_alloc(iscsi_task_cache, GFP_KERNEL);
+	if (!session->mgmt_task) {
+		iscsi_host_err(session, "Couldn't preallocate mgmt task\n");
+		rc = -ENOMEM;
+		goto free_session;
+	}
+	memset(session->mgmt_task, 0, sizeof(*session->mgmt_task));
+	iscsi_init_task(session->mgmt_task);
+
+	rc = copy_iscsi_strings(session, ioctld);
+	if (rc)
+		goto free_session;
+
+	memcpy(session->isid, ioctld->isid, sizeof(session->isid));
+
+	/*
+	 * FIXME: Do we have to check on both the username_in and
+	 * password_length_in. Same with iscsi_update_session as well? Smitha
+	 */
+	if (ioctld->username_in[0] || ioctld->password_length_in)
+		session->bidirectional_auth = 1;
+	else
+		session->bidirectional_auth = 0;
+	rc = alloc_auth_buffers(session);
+	if (rc)
+		goto free_session;
+
+	memcpy(&session->portal, &ioctld->portal, sizeof(ioctld->portal));
+	iscsi_set_portal(session);
+
+	/*
+	 * preallocate rx/tx_tfm, so that we do not have to possibly
+	 * call crypto_alloc_tfm (it uses GFP_KERNEL) while IO is queued.
+	 */
+	session->rx_tfm = crypto_alloc_tfm("crc32c", 0);
+	if (!session->rx_tfm) {
+		rc = -ENOMEM;
+		goto free_session;
+	}
+
+	session->tx_tfm = crypto_alloc_tfm("crc32c", 0);
+	if (!session->tx_tfm) {
+		rc = -ENOMEM;
+		goto free_session;
+	}
+
+	rc = start_session_threads(session);
+	up(&session->config_mutex);
+	if (rc)
+		goto free_session;
+
+	down(&iscsi_session_sem);
+	list_add_tail(&session->list, &iscsi_sessions);
+	up(&iscsi_session_sem);
+
+	wait_event_interruptible(session->login_wait_q,
+		test_bit(SESSION_ESTABLISHED, &session->control_bits));
+	if (!test_bit(SESSION_ESTABLISHED, &session->control_bits)) {
+		iscsi_destroy_session(session);
+		return -ENOTCONN;
+	}
+
+	return 0;
+	
+ free_session:
+	free_session(session);
+	return rc;
+}
+
+struct iscsi_session *
+iscsi_find_session(const char *target_name, u8 isid[6], int tpgt)
+{
+	struct iscsi_session *session;
+
+	down(&iscsi_session_sem);
+
+	list_for_each_entry(session, &iscsi_sessions, list) {
+		 if (!strcmp(session->target_name, target_name) &&
+		     !memcmp(session->isid, isid, sizeof(isid)) &&
+		     session->portal_group_tag == tpgt) {
+			if (scsi_host_get(session->shost)) {
+				up(&iscsi_session_sem);
+				return session;
+			}
+			break;
+		}
+	}
+
+	up(&iscsi_session_sem);
+	return NULL;
+}
+
+int
+iscsi_update_address(struct iscsi_session *session, char *address)
+{
+	struct sockaddr_in *addr;
+	char *tag;
+	char *port;
+	int err = 1;
+
+	tag = strrchr(address, ',');
+	if (tag) {
+		*tag = '\0';
+		tag++;
+	}
+
+	port = strrchr(address, ':');
+	if (port) {
+		*port = '\0';
+		port++;
+	}
+
+	/*
+	 * Still only ipv4 is supported. No access to ipv6
+	 * to test so feel free to implement it later...
+	 */
+	if (address[0] == '[') {
+		iscsi_host_err(session, "Driver does not support ipv6 "
+			       "addresses\n");
+		err = 0;
+		goto done;
+	}
+
+	addr = (struct sockaddr_in *)&session->addr;
+	addr->sin_addr.s_addr = in_aton(address);
+	if (port)
+		addr->sin_port = htons(simple_strtoul(port, NULL, 0));
+	else
+		addr->sin_port = htons(ISCSI_TCP_PORT);
+
+ done:
+	/* restore the original strings */
+	if (tag) {
+		--tag;
+		*tag = ',';
+	}
+
+	if (port) {
+		--port;
+		*port = ':';
+	}
+
+	return err;
+}
diff -Naurp scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-session.h scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-session.h
--- scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-session.h	1969-12-31 16:00:00.000000000 -0800
+++ scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-session.h	2005-01-10 12:22:24.875910647 -0800
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ *
+ * define the iSCSI session structure needed by the login library
+ */
+#ifndef ISCSI_SESSION_H_
+#define ISCSI_SESSION_H_
+
+#include <linux/crypto.h>
+#include <linux/socket.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include "iscsi-auth-client.h"
+#include "iscsi-portal.h"
+
+struct iscsi_session_ioctl;
+struct iscsi_task;
+
+/* used for replying to NOPs */
+struct iscsi_nop_info {
+	struct list_head reply_list;
+	u32 ttt;
+	unsigned char lun[8];
+};
+
+#define ISCSI_RXCTRL_SIZE PAGE_SIZE
+
+struct iscsi_session {
+	struct Scsi_Host	*shost;
+	struct list_head	list;
+	/*
+	 * the config mutex along with the portal lock protect
+	 * and serialize the creation and update of session info
+	 */
+	struct semaphore	config_mutex;
+	u32			config_number;
+	/*
+	 * iSCSI settings
+	 */
+	unsigned char		*initiator_name;
+	unsigned char		*initiator_alias;
+	unsigned char		*target_name;
+	unsigned char		*target_alias;
+	u8			isid[6];
+	u16			tsih;
+	u32			cmd_sn;
+	u32			exp_cmd_sn;
+	u32			max_cmd_sn;
+	u32			exp_stat_sn;
+	int			immediate_data;
+	int			initial_r2t;
+	/* the value we declare */
+	int			max_recv_data_segment_len; 
+	/* the value declared by the target */
+	int			max_xmit_data_segment_len;
+	int			first_burst_len;
+	int			max_burst_len;
+	int			data_pdu_in_order;
+	int			data_seq_in_order;
+	int			def_time2wait;
+	int			def_time2retain;
+	int			header_digest;
+	int			data_digest;
+	int			type;
+	int			current_stage;
+	int			next_stage;
+	int			partial_response;
+	int			portal_group_tag;
+	int			vendor_specific_keys;
+	int			send_async_text;
+	unsigned int		irrelevant_keys_bitmap;
+	u32			next_itt;
+	long			time2wait;
+	/*
+	 * Authentication settings
+	 */
+	char			*username;
+	unsigned char		*password;
+	int			password_length;
+	char			*username_in;
+	unsigned char		*password_in;
+	int			password_length_in;
+	struct crypto_tfm	*md5_tfm;
+	int			bidirectional_auth;
+	struct iscsi_acl	*auth_client_block;
+	struct auth_str_block	*auth_recv_string_block;
+	struct auth_str_block	*auth_send_string_block;
+	struct auth_large_binary *auth_recv_binary_block;
+	struct auth_large_binary *auth_send_binary_block;
+	/*
+	 * Portal/Network settings
+	 * support ipv4 when we finish the interface
+	 */
+	struct socket		*socket;
+	/* we only support ipv4 until we can find a setup to test */
+	struct sockaddr 	addr;
+	int			tcp_window_size;
+	spinlock_t		portal_lock;
+	struct iscsi_portal_info portal;
+	/*
+	 * various accounting sutff
+	 */
+	/*
+	 * *_time fields used to detect sessions that die as soon
+	 * as we hit FFP
+	 */
+	unsigned long		session_drop_time;
+	unsigned long		session_established_time;
+	int			ever_established;
+	int			commands_queued;
+	/*
+	 * timer fields
+	 */
+	struct timer_list	transport_timer;
+	struct timer_list	logout_timer;
+	unsigned long		last_rx;
+	unsigned long		last_ping;
+	unsigned long		window_closed;
+	int			login_timeout;
+	int			active_timeout;
+	int			idle_timeout;
+	int			ping_timeout;
+	int			abort_timeout;
+	int			reset_timeout;
+	int			replacement_timeout;
+	int			logout_response_timeout;
+	/*
+	 * iSCSI task/request
+	 * - Requests originating from SCSI-ml like scsi cmnds and
+	 * management functions are task backed.
+	 * - iSCSI requests like Nop, Logout or Login do not
+	 * have a struct iscsi_task to avoid allocating memory
+	 * when not needed.
+	 *
+	 * The task lock protects the task/cmnd queues and the
+	 * access to the task when the tx and rx thread could
+	 * be accessing it at the same time.
+	 */
+	spinlock_t		task_lock;
+	struct iscsi_task	*preallocated_task;
+	struct list_head	pending_queue;
+	struct list_head	active_queue;
+	struct list_head	done_queue;
+	struct list_head	tx_task_head;
+	int			num_active_tasks;
+	struct iscsi_nop_info	nop_reply;
+	struct list_head	nop_reply_list;
+	/* itt of the last mgmt task we sent */
+	u32			last_mgmt_itt;
+	/* preallocated task for TMFs */
+	struct iscsi_task	*mgmt_task;
+	struct completion	*mgmt_task_complete;
+	/*
+	 * thread control stuff
+	 */
+	unsigned long		control_bits;
+	wait_queue_head_t	tx_wait_q;
+	wait_queue_head_t	login_wait_q;
+	struct semaphore	tx_blocked;
+	struct task_struct	*rx_task;
+	struct task_struct	*tx_task;
+	struct crypto_tfm	*rx_tfm;
+	struct crypto_tfm	*tx_tfm;
+	/*
+	 * preallocated buffer for iSCSI requests that have
+	 * data, and do not originate from scsi-ml
+	 */
+	unsigned char		rx_buffer[ISCSI_RXCTRL_SIZE];
+};
+
+/* session control bits */
+enum {
+	/*
+	 * the tx bits match the tx_request array in
+	 * iscsi-initiator.c, so if you modify this don't forget
+	 */
+	TX_PING,		/* NopOut, reply requested */
+	TX_TMF,
+	TX_SCSI_COMMAND,
+	TX_NOP_REPLY,		/* reply to a Nop-in from the target */
+	TX_DATA,
+	TX_LOGOUT,
+	TX_WAKE,
+
+	SESSION_CREATED,
+	SESSION_RELEASING,
+	SESSION_REPLACEMENT_TIMEDOUT,
+	SESSION_ESTABLISHED,
+	SESSION_LOGOUT_REQUESTED,
+	SESSION_IN_LOGOUT,
+	SESSION_WINDOW_CLOSED,
+	SESSION_TERMINATING,
+	SESSION_TERMINATED,
+};
+
+extern void iscsi_wake_tx_thread(int control_bit,
+				 struct iscsi_session *session);
+extern void iscsi_request_logout(struct iscsi_session *session, int logout,
+				 int logout_response);
+extern void iscsi_drop_session(struct iscsi_session *session);
+extern int iscsi_update_session(struct iscsi_session *session,
+				struct iscsi_session_ioctl *ioctld);
+extern int iscsi_create_session(struct iscsi_session *session,
+				struct iscsi_session_ioctl *ioctld);
+extern void iscsi_destroy_session(struct iscsi_session *session);
+extern struct iscsi_session *iscsi_find_session(const char *target_name,
+						u8 isid[6], int tpgt);
+extern int iscsi_update_address(struct iscsi_session *session, char *address);
+extern int iscsi_wait_for_session(struct iscsi_session *session,
+				  int ignore_timeout);
+extern void iscsi_mod_session_timer(struct iscsi_session *session, int timeout);
+
+extern struct list_head iscsi_sessions;
+
+#endif

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2005-01-11 15:48 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-01-10 22:57 [PATCH 4/10][RFC] linux-iscsi driver Mike Christie
2005-01-10 23:24 ` Christoph Hellwig
2005-01-11  0:45   ` James Bottomley
2005-01-11  4:35     ` Dave Wysochanski
2005-01-11 15:44       ` James Bottomley
2005-01-11  9:59     ` Christoph Hellwig
2005-01-11 15:47       ` James Bottomley
2005-01-11  1:09   ` Mike Christie

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox