[PATCH 4/10][RFC] linux-iscsi driver

Mike Christie
2005-01-10T22:57:32Z

[-- 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