Blame usr/initiator.c

Packit eace71
/*
Packit eace71
 * iSCSI Session Management and Slow-path Control
Packit eace71
 *
Packit eace71
 * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
Packit eace71
 * Copyright (C) 2006 Mike Christie
Packit eace71
 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
Packit eace71
 * maintained by open-iscsi@googlegroups.com
Packit eace71
 *
Packit eace71
 * This program is free software; you can redistribute it and/or modify
Packit eace71
 * it under the terms of the GNU General Public License as published
Packit eace71
 * by the Free Software Foundation; either version 2 of the License, or
Packit eace71
 * (at your option) any later version.
Packit eace71
 *
Packit eace71
 * This program is distributed in the hope that it will be useful, but
Packit eace71
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit eace71
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit eace71
 * General Public License for more details.
Packit eace71
 *
Packit eace71
 * See the file COPYING included with this distribution for more details.
Packit eace71
 */
Packit eace71
Packit eace71
#include <sys/time.h>
Packit eace71
#include <sys/types.h>
Packit eace71
#include <sys/stat.h>
Packit eace71
#include <sys/resource.h>
Packit eace71
#include <unistd.h>
Packit eace71
#include <string.h>
Packit eace71
#include <stdio.h>
Packit eace71
#include <stdlib.h>
Packit eace71
#include <errno.h>
Packit eace71
#include <dirent.h>
Packit eace71
#include <fcntl.h>
Packit eace71
Packit eace71
#include "initiator.h"
Packit eace71
#include "transport.h"
Packit eace71
#include "iscsid.h"
Packit eace71
#include "iscsi_if.h"
Packit eace71
#include "mgmt_ipc.h"
Packit eace71
#include "event_poll.h"
Packit eace71
#include "iscsi_ipc.h"
Packit eace71
#include "idbm.h"
Packit eace71
#include "log.h"
Packit eace71
#include "iscsi_util.h"
Packit eace71
#include "scsi.h"
Packit eace71
#include "iscsi_sysfs.h"
Packit eace71
#include "iscsi_settings.h"
Packit eace71
#include "iface.h"
Packit eace71
#include "host.h"
Packit eace71
#include "sysdeps.h"
Packit eace71
#include "iscsi_err.h"
Packit eace71
#include "kern_err_table.h"
Packit eace71
Packit eace71
#define ISCSI_CONN_ERR_REOPEN_DELAY	3
Packit eace71
#define ISCSI_INTERNAL_ERR_REOPEN_DELAY	5
Packit eace71
Packit eace71
#define PROC_DIR "/proc"
Packit eace71
Packit eace71
struct login_task_retry_info {
Packit eace71
	actor_t retry_actor;
Packit eace71
	queue_task_t *qtask;
Packit eace71
	node_rec_t *rec;
Packit eace71
	int retry_count;
Packit eace71
};
Packit eace71
Packit eace71
static void iscsi_login_timedout(void *data);
Packit eace71
static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context,
Packit eace71
				  struct iscsi_conn *conn, unsigned long tmo,
Packit eace71
				  int event);
Packit eace71
static int queue_session_login_task_retry(struct login_task_retry_info *info,
Packit eace71
					  node_rec_t *rec, queue_task_t *qtask);
Packit eace71
Packit eace71
static int iscsi_ev_context_alloc(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	int i;
Packit eace71
Packit eace71
	for (i = 0; i < CONTEXT_POOL_MAX; i++) {
Packit eace71
		conn->context_pool[i] = calloc(1,
Packit eace71
					   sizeof(struct iscsi_ev_context) +
Packit eace71
					   ipc->ctldev_bufmax);
Packit eace71
		if (!conn->context_pool[i]) {
Packit eace71
			int j;
Packit eace71
			for (j = 0; j < i; j++)
Packit eace71
				free(conn->context_pool[j]);
Packit eace71
			return ENOMEM;
Packit eace71
		}
Packit eace71
		conn->context_pool[i]->conn = conn;
Packit eace71
	}
Packit eace71
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_ev_context_free(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	int i;
Packit eace71
Packit eace71
	for (i = 0; i < CONTEXT_POOL_MAX; i++) {
Packit eace71
		if (!conn->context_pool[i])
Packit eace71
			continue;
Packit eace71
Packit eace71
		if (conn->context_pool[i]->allocated)
Packit eace71
			/* missing flush on shutdown */
Packit eace71
			log_error("BUG: context_pool leak %p",
Packit eace71
				  conn->context_pool[i]);
Packit eace71
		free(conn->context_pool[i]);
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static struct iscsi_ev_context *
Packit eace71
iscsi_ev_context_get(iscsi_conn_t *conn, int ev_size)
Packit eace71
{
Packit eace71
	struct iscsi_ev_context *ev_context;
Packit eace71
	int i;
Packit eace71
Packit eace71
	if (ev_size > ipc->ctldev_bufmax)
Packit eace71
		return NULL;
Packit eace71
Packit eace71
	for (i = 0; i < CONTEXT_POOL_MAX; i++) {
Packit eace71
		if (!conn->context_pool[i])
Packit eace71
			continue;
Packit eace71
Packit eace71
		if (!conn->context_pool[i]->allocated) {
Packit eace71
			ev_context = conn->context_pool[i];
Packit eace71
Packit eace71
			memset(&ev_context->actor, 0,
Packit eace71
				sizeof(struct actor));
Packit eace71
			ev_context->allocated = 1;
Packit eace71
			/* some callers abuse this pointer */
Packit eace71
			ev_context->data = (void *)ev_context +
Packit eace71
					sizeof(struct iscsi_ev_context);
Packit eace71
			log_debug(7, "get ev context %p",
Packit eace71
				  &ev_context->actor);
Packit eace71
			return ev_context;
Packit eace71
		}
Packit eace71
	}
Packit eace71
	return NULL;
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_ev_context_put(struct iscsi_ev_context *ev_context)
Packit eace71
{
Packit eace71
	log_debug(7, "put ev context %p", &ev_context->actor);
Packit eace71
	ev_context->allocated = 0;
Packit eace71
}
Packit eace71
Packit eace71
static void session_online_devs(int host_no, int sid)
Packit eace71
{
Packit eace71
	iscsi_sysfs_for_each_device(NULL, host_no, sid,
Packit eace71
				    iscsi_sysfs_set_device_online);
Packit eace71
}
Packit eace71
Packit eace71
static conn_login_status_e
Packit eace71
__login_response_status(iscsi_conn_t *conn,
Packit eace71
		      enum iscsi_login_status login_status)
Packit eace71
{
Packit eace71
	switch (login_status) {
Packit eace71
	case LOGIN_OK:
Packit eace71
		/* check the status class and detail */
Packit eace71
		return CONN_LOGIN_SUCCESS;
Packit eace71
	case LOGIN_REDIRECT:
Packit eace71
		return CONN_LOGIN_IMM_REDIRECT_RETRY;
Packit eace71
	case LOGIN_IO_ERROR:
Packit eace71
	case LOGIN_REDIRECTION_FAILED:
Packit eace71
		return CONN_LOGIN_RETRY;
Packit eace71
	default:
Packit eace71
		log_error("Login error (Login status %d) on conn %d", conn->id,
Packit eace71
			  login_status);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
Packit eace71
	return CONN_LOGIN_FAILED;
Packit eace71
}
Packit eace71
Packit eace71
static conn_login_status_e
Packit eace71
__check_iscsi_status_class(iscsi_session_t *session, int cid,
Packit eace71
			uint8_t status_class, uint8_t status_detail)
Packit eace71
{
Packit eace71
	iscsi_conn_t *conn = &session->conn[cid];
Packit eace71
Packit eace71
	switch (status_class) {
Packit eace71
	case ISCSI_STATUS_CLS_SUCCESS:
Packit eace71
		return CONN_LOGIN_SUCCESS;
Packit eace71
	case ISCSI_STATUS_CLS_REDIRECT:
Packit eace71
		switch (status_detail) {
Packit eace71
		case ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP:
Packit eace71
			return CONN_LOGIN_IMM_RETRY;
Packit eace71
		case ISCSI_LOGIN_STATUS_TGT_MOVED_PERM:
Packit eace71
			/*
Packit eace71
			 * for a permanent redirect, we need to update the
Packit eace71
			 * failback address
Packit eace71
			 */
Packit eace71
			memset(&conn->failback_saddr, 0,
Packit eace71
				sizeof(struct sockaddr_storage));
Packit eace71
			conn->failback_saddr = conn->saddr;
Packit eace71
                        return CONN_LOGIN_IMM_REDIRECT_RETRY;
Packit eace71
		default:
Packit eace71
			log_error("conn %d login rejected: redirection "
Packit eace71
			        "type 0x%x not supported",
Packit eace71
				conn->id, status_detail);
Packit eace71
			return CONN_LOGIN_RETRY;
Packit eace71
		}
Packit eace71
	case ISCSI_STATUS_CLS_INITIATOR_ERR:
Packit eace71
		switch (status_detail) {
Packit eace71
		case ISCSI_LOGIN_STATUS_AUTH_FAILED:
Packit eace71
			log_error("session %d login rejected: Initiator "
Packit eace71
			       "failed authentication with target",
Packit eace71
				session->id);
Packit eace71
			return CONN_LOGIN_AUTH_FAILED;
Packit eace71
		case ISCSI_LOGIN_STATUS_TGT_FORBIDDEN:
Packit eace71
			log_error("conn %d login rejected: initiator "
Packit eace71
			       "failed authorization with target", conn->id);
Packit eace71
			return CONN_LOGIN_AUTH_FAILED;
Packit eace71
		case ISCSI_LOGIN_STATUS_TGT_NOT_FOUND:
Packit eace71
			log_error("conn %d login rejected: initiator "
Packit eace71
			       "error - target not found (%02x/%02x)",
Packit eace71
			       conn->id, status_class, status_detail);
Packit eace71
			return CONN_LOGIN_FAILED;
Packit eace71
		case ISCSI_LOGIN_STATUS_NO_VERSION:
Packit eace71
			/*
Packit eace71
			 * FIXME: if we handle multiple protocol versions,
Packit eace71
			 * before we log an error, try the other supported
Packit eace71
			 * versions.
Packit eace71
			 */
Packit eace71
			log_error("conn %d login rejected: incompatible "
Packit eace71
			       "version (%02x/%02x), non-retryable, "
Packit eace71
			       "giving up", conn->id, status_class,
Packit eace71
			       status_detail);
Packit eace71
			return CONN_LOGIN_FAILED;
Packit eace71
		default:
Packit eace71
			log_error("conn %d login rejected: initiator "
Packit eace71
			       "error (%02x/%02x)", conn->id, status_class,
Packit eace71
			       status_detail);
Packit eace71
			return CONN_LOGIN_FAILED;
Packit eace71
		}
Packit eace71
	case ISCSI_STATUS_CLS_TARGET_ERR:
Packit eace71
		log_error("conn %d login rejected: target error "
Packit eace71
		       "(%02x/%02x)", conn->id, status_class, status_detail);
Packit eace71
		/*
Packit eace71
		 * We have no idea what the problem is. But spec says initiator
Packit eace71
		 * may retry later.
Packit eace71
		 */
Packit eace71
		 return CONN_LOGIN_RETRY;
Packit eace71
	default:
Packit eace71
		log_error("conn %d login response with unknown status "
Packit eace71
		       "class 0x%x, detail 0x%x", conn->id, status_class,
Packit eace71
		       status_detail);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
Packit eace71
	return CONN_LOGIN_FAILED;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
__session_conn_create(iscsi_session_t *session, int cid)
Packit eace71
{
Packit eace71
	iscsi_conn_t *conn = &session->conn[cid];
Packit eace71
	conn_rec_t *conn_rec = &session->nrec.conn[cid];
Packit eace71
	node_rec_t *rec = &session->nrec;
Packit eace71
	int err;
Packit eace71
Packit eace71
	if (iscsi_ev_context_alloc(conn)) {
Packit eace71
		log_error("cannot allocate context_pool for conn cid %d", cid);
Packit eace71
		return ISCSI_ERR_NOMEM;
Packit eace71
	}
Packit eace71
Packit eace71
	/* set session reconnection retry max */
Packit eace71
	session->reopen_max = rec->session.reopen_max;
Packit eace71
Packit eace71
	conn->state = ISCSI_CONN_STATE_FREE;
Packit eace71
	conn->session = session;
Packit eace71
	actor_init(&conn->login_timer, iscsi_login_timedout, NULL);
Packit eace71
	/*
Packit eace71
	 * TODO: we must export the socket_fd/transport_eph from sysfs
Packit eace71
	 * so if iscsid is resyncing up we can pick that up and cleanup up
Packit eace71
	 * the old connection. Right now we leak a connection.
Packit eace71
	 * We can also probably merge these two fields.
Packit eace71
	 */
Packit eace71
	conn->socket_fd = -1;
Packit eace71
	conn->transport_ep_handle = -1;
Packit eace71
	/* connection's timeouts */
Packit eace71
	conn->id = cid;
Packit eace71
	conn->logout_timeout = conn_rec->timeo.logout_timeout;
Packit eace71
	if (!conn->logout_timeout) {
Packit eace71
		log_error("Invalid timeo.logout_timeout. Must be greater "
Packit eace71
			  "than zero. Using default %d.",
Packit eace71
			  DEF_LOGOUT_TIMEO);
Packit eace71
		conn->logout_timeout = DEF_LOGOUT_TIMEO;
Packit eace71
	}
Packit eace71
Packit eace71
	conn->login_timeout = conn_rec->timeo.login_timeout;
Packit eace71
	if (!conn->login_timeout) {
Packit eace71
		log_error("Invalid timeo.login_timeout. Must be greater "
Packit eace71
			  "than zero. Using default %d.",
Packit eace71
			  DEF_LOGIN_TIMEO);
Packit eace71
		conn->login_timeout = DEF_LOGIN_TIMEO;
Packit eace71
	}
Packit eace71
Packit eace71
	conn->auth_timeout = conn_rec->timeo.auth_timeout;
Packit eace71
Packit eace71
	/* noop-out setting */
Packit eace71
	conn->noop_out_interval = conn_rec->timeo.noop_out_interval;
Packit eace71
	conn->noop_out_timeout = conn_rec->timeo.noop_out_timeout;
Packit eace71
	if (conn->noop_out_interval && !conn->noop_out_timeout) {
Packit eace71
		log_error("Invalid timeo.noop_out_timeout. Must be greater "
Packit eace71
			  "than zero. Using default %d.",
Packit eace71
			  DEF_NOOP_OUT_TIMEO);
Packit eace71
		conn->noop_out_timeout = DEF_NOOP_OUT_TIMEO;
Packit eace71
	}
Packit eace71
Packit eace71
	if (conn->noop_out_timeout && !conn->noop_out_interval) {
Packit eace71
		log_error("Invalid timeo.noop_out_interval. Must be greater "
Packit eace71
			  "than zero. Using default %d.",
Packit eace71
			  DEF_NOOP_OUT_INTERVAL);
Packit eace71
		conn->noop_out_interval = DEF_NOOP_OUT_INTERVAL;
Packit eace71
	}
Packit eace71
Packit eace71
	iscsi_copy_operational_params(conn, &session->nrec.session.iscsi,
Packit eace71
				      &conn_rec->iscsi);
Packit eace71
Packit eace71
	/* TCP options */
Packit eace71
	conn->tcp_window_size = conn_rec->tcp.window_size;
Packit eace71
	/* FIXME: type_of_service */
Packit eace71
Packit eace71
	/* resolve the string address to an IP address */
Packit eace71
	err = iscsi_setup_portal(conn, conn_rec->address, conn_rec->port);
Packit eace71
	if (err)
Packit eace71
		return err;
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
session_release(iscsi_session_t *session)
Packit eace71
{
Packit eace71
	log_debug(2, "Releasing session %p", session);
Packit eace71
Packit eace71
	if (session->target_alias)
Packit eace71
		free(session->target_alias);
Packit eace71
	iscsi_ev_context_free(&session->conn[0]);
Packit eace71
	free(session);
Packit eace71
}
Packit eace71
Packit eace71
static iscsi_session_t*
Packit eace71
__session_create(node_rec_t *rec, struct iscsi_transport *t, int *rc)
Packit eace71
{
Packit eace71
	iscsi_session_t *session;
Packit eace71
	int hostno;
Packit eace71
Packit eace71
	*rc = 0;
Packit eace71
Packit eace71
	session = calloc(1, sizeof (*session));
Packit eace71
	if (session == NULL) {
Packit eace71
		log_debug(1, "can not allocate memory for session");
Packit eace71
		*rc = ISCSI_ERR_NOMEM;
Packit eace71
		return NULL;
Packit eace71
	}
Packit eace71
	log_debug(2, "Allocted session %p", session);
Packit eace71
Packit eace71
	INIT_LIST_HEAD(&session->list);
Packit eace71
	session->t = t;
Packit eace71
	session->reopen_qtask.mgmt_ipc_fd = -1;
Packit eace71
	session->id = -1;
Packit eace71
	session->use_ipc = 1;
Packit eace71
Packit eace71
	/* save node record. we might need it for redirection */
Packit eace71
	memcpy(&session->nrec, rec, sizeof(node_rec_t));
Packit eace71
Packit eace71
	session->portal_group_tag = rec->tpgt;
Packit eace71
	session->type = ISCSI_SESSION_TYPE_NORMAL;
Packit eace71
	session->r_stage = R_STAGE_NO_CHANGE;
Packit eace71
	strlcpy(session->target_name, rec->name, TARGET_NAME_MAXLEN);
Packit eace71
Packit eace71
	if (strlen(session->nrec.iface.iname))
Packit eace71
		session->initiator_name = session->nrec.iface.iname;
Packit eace71
	else if (dconfig->initiator_name)
Packit eace71
		session->initiator_name = dconfig->initiator_name;
Packit eace71
	else {
Packit eace71
		log_error("No initiator name set. Cannot create session.");
Packit eace71
		*rc = ISCSI_ERR_INVAL;
Packit eace71
		goto free_session;
Packit eace71
	}
Packit eace71
Packit eace71
	if (strlen(session->nrec.iface.alias))
Packit eace71
		session->initiator_alias = session->nrec.iface.alias;
Packit eace71
	else
Packit eace71
		session->initiator_alias = dconfig->initiator_alias;
Packit eace71
Packit eace71
	/* session's eh parameters */
Packit eace71
	session->replacement_timeout = rec->session.timeo.replacement_timeout;
Packit eace71
	session->fast_abort = rec->session.iscsi.FastAbort;
Packit eace71
	session->abort_timeout = rec->session.err_timeo.abort_timeout;
Packit eace71
	session->lu_reset_timeout = rec->session.err_timeo.lu_reset_timeout;
Packit eace71
	session->tgt_reset_timeout = rec->session.err_timeo.tgt_reset_timeout;
Packit eace71
	session->host_reset_timeout = rec->session.err_timeo.host_reset_timeout;
Packit eace71
Packit eace71
	/* OUI and uniqifying number */
Packit eace71
	session->isid[0] = DRIVER_ISID_0;
Packit eace71
	session->isid[1] = DRIVER_ISID_1;
Packit eace71
	session->isid[2] = DRIVER_ISID_2;
Packit eace71
	session->isid[3] = 0;
Packit eace71
	session->isid[4] = 0;
Packit eace71
	session->isid[5] = 0;
Packit eace71
Packit eace71
	/* setup authentication variables for the session*/
Packit eace71
	iscsi_setup_authentication(session, &rec->session.auth);
Packit eace71
Packit eace71
	iscsi_session_init_params(session);
Packit eace71
Packit eace71
        if (t->template->bind_ep_required) {
Packit eace71
                hostno = iscsi_sysfs_get_host_no_from_hwinfo(&rec->iface, rc);
Packit eace71
                if (!*rc) {
Packit eace71
                        /*
Packit eace71
                         * if the netdev or mac was set, then we are going to want
Packit eace71
                         * to want to bind the all the conns/eps to a specific host
Packit eace71
                         * if offload is used.
Packit eace71
                         */
Packit eace71
                        session->conn[0].bind_ep = 1;
Packit eace71
                        session->hostno = hostno;
Packit eace71
                } else if (*rc == ISCSI_ERR_HOST_NOT_FOUND) {
Packit eace71
                        goto free_session;	
Packit eace71
                } else {
Packit eace71
                         *rc = 0;
Packit eace71
                }
Packit eace71
        }
Packit eace71
Packit eace71
	/* reset session reopen count */
Packit eace71
	session->reopen_cnt = 0;
Packit eace71
Packit eace71
	list_add_tail(&session->list, &t->sessions);
Packit eace71
	return session;
Packit eace71
Packit eace71
free_session:
Packit eace71
	free(session);
Packit eace71
	return NULL;
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_flush_context_pool(struct iscsi_session *session)
Packit eace71
{
Packit eace71
	struct iscsi_ev_context *ev_context;
Packit eace71
	struct iscsi_conn *conn = &session->conn[0];
Packit eace71
	int i;
Packit eace71
Packit eace71
	for (i = 0; i < CONTEXT_POOL_MAX; i++) {
Packit eace71
		ev_context = conn->context_pool[i];
Packit eace71
		if (!ev_context)
Packit eace71
			continue;
Packit eace71
Packit eace71
		if (ev_context->allocated) {
Packit eace71
			actor_delete(&(conn->context_pool[i]->actor));
Packit eace71
			iscsi_ev_context_put(ev_context);
Packit eace71
		}
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
__session_destroy(iscsi_session_t *session)
Packit eace71
{
Packit eace71
	log_debug(1, "destroying session");
Packit eace71
	list_del(&session->list);
Packit eace71
	iscsi_flush_context_pool(session);
Packit eace71
	session_release(session);
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
conn_delete_timers(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	actor_delete(&conn->login_timer);
Packit eace71
	actor_delete(&conn->nop_out_timer);
Packit eace71
}
Packit eace71
Packit eace71
static int 
Packit eace71
session_conn_shutdown(iscsi_conn_t *conn, queue_task_t *qtask,
Packit eace71
		      int err)
Packit eace71
{
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
Packit eace71
	log_debug(2, "disconnect conn");
Packit eace71
	/* this will check for a valid interconnect connection */
Packit eace71
	if (session->t->template->ep_disconnect)
Packit eace71
		session->t->template->ep_disconnect(conn);
Packit eace71
Packit eace71
	if (session->id == -1)
Packit eace71
		goto cleanup;
Packit eace71
Packit eace71
	if (!iscsi_sysfs_session_has_leadconn(session->id))
Packit eace71
		goto cleanup;
Packit eace71
Packit eace71
	if (conn->state == ISCSI_CONN_STATE_IN_LOGIN ||
Packit eace71
	    conn->state == ISCSI_CONN_STATE_IN_LOGOUT ||
Packit eace71
	    conn->state == ISCSI_CONN_STATE_LOGGED_IN) {
Packit eace71
		log_debug(2, "stop conn (conn state %d)", conn->state);
Packit eace71
		if (ipc->stop_conn(session->t->handle, session->id,
Packit eace71
				   conn->id, STOP_CONN_TERM)) {
Packit eace71
			log_error("can't stop connection %d:%d (%d)",
Packit eace71
				  session->id, conn->id, errno);
Packit eace71
			return ISCSI_ERR_INTERNAL;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	log_debug(2, "kdestroy conn");
Packit eace71
	if (ipc->destroy_conn(session->t->handle, session->id,
Packit eace71
		conn->id)) {
Packit eace71
		log_error("can not safely destroy connection %d", conn->id);
Packit eace71
		return ISCSI_ERR_INTERNAL;
Packit eace71
	}
Packit eace71
Packit eace71
cleanup:
Packit eace71
	if (session->id != -1) {
Packit eace71
		log_debug(2, "kdestroy session %u", session->id);
Packit eace71
		session->r_stage = R_STAGE_SESSION_DESTOYED;
Packit eace71
		if (ipc->destroy_session(session->t->handle, session->id)) {
Packit eace71
			log_error("can not safely destroy session %d",
Packit eace71
				  session->id);
Packit eace71
			return ISCSI_ERR_INTERNAL;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	log_warning("Connection%d:%d to [target: %s, portal: %s,%d] "
Packit eace71
		    "through [iface: %s] is shutdown.",
Packit eace71
		    session->id, conn->id, session->nrec.name,
Packit eace71
		    session->nrec.conn[conn->id].address,
Packit eace71
		    session->nrec.conn[conn->id].port,
Packit eace71
		    session->nrec.iface.name);
Packit eace71
Packit eace71
	mgmt_ipc_write_rsp(qtask, err);
Packit eace71
	conn_delete_timers(conn);
Packit eace71
	__session_destroy(session);
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
queue_delayed_reopen(queue_task_t *qtask, int delay)
Packit eace71
{
Packit eace71
	iscsi_conn_t *conn = qtask->conn;
Packit eace71
Packit eace71
	log_debug(4, "Requeue reopen attempt in %d secs", delay);
Packit eace71
Packit eace71
	/*
Packit eace71
 	 * iscsi_login_eh can handle the login resched as
Packit eace71
 	 * if it were login time out
Packit eace71
 	 */
Packit eace71
	actor_timer_mod(&conn->login_timer, delay, qtask);
Packit eace71
}
Packit eace71
Packit eace71
static int iscsi_conn_connect(struct iscsi_conn *conn, queue_task_t *qtask)
Packit eace71
{
Packit eace71
	struct iscsi_ev_context *ev_context;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	ev_context = iscsi_ev_context_get(conn, 0);
Packit eace71
	if (!ev_context) {
Packit eace71
		/* while reopening the recv pool should be full */
Packit eace71
		log_error("BUG: __session_conn_reopen could not get conn "
Packit eace71
			  "context for recv.");
Packit eace71
		return ENOMEM;
Packit eace71
	}
Packit eace71
	ev_context->data = qtask;
Packit eace71
Packit eace71
	rc = conn->session->t->template->ep_connect(conn, 1);
Packit eace71
	if (rc < 0 && errno != EINPROGRESS) {
Packit eace71
		char serv[NI_MAXSERV];
Packit eace71
Packit eace71
		getnameinfo((struct sockaddr *) &conn->saddr,
Packit eace71
			    sizeof(conn->saddr),
Packit eace71
			    conn->host, sizeof(conn->host), serv, sizeof(serv),
Packit eace71
			    NI_NUMERICHOST|NI_NUMERICSERV);
Packit eace71
Packit eace71
		log_error("cannot make a connection to %s:%s (%d,%d)",
Packit eace71
			  conn->host, serv, rc, errno);
Packit eace71
		iscsi_ev_context_put(ev_context);
Packit eace71
		return ENOTCONN;
Packit eace71
	}
Packit eace71
Packit eace71
	iscsi_sched_ev_context(ev_context, conn, 0, EV_CONN_POLL);
Packit eace71
	log_debug(3, "Setting login timer %p timeout %d", &conn->login_timer,
Packit eace71
		  conn->login_timeout);
Packit eace71
	actor_timer_mod(&conn->login_timer, conn->login_timeout, qtask);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
__session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop,
Packit eace71
		      int redirected)
Packit eace71
{
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
	uint32_t delay;
Packit eace71
Packit eace71
	log_debug(1, "re-opening session %d (reopen_cnt %d)", session->id,
Packit eace71
			session->reopen_cnt);
Packit eace71
Packit eace71
	qtask->conn = conn;
Packit eace71
Packit eace71
	/* flush stale polls or errors queued */
Packit eace71
	iscsi_flush_context_pool(session);
Packit eace71
	conn_delete_timers(conn);
Packit eace71
	conn->state = ISCSI_CONN_STATE_XPT_WAIT;
Packit eace71
Packit eace71
	conn->session->t->template->ep_disconnect(conn);
Packit eace71
	if (do_stop) {
Packit eace71
		/* state: ISCSI_CONN_STATE_CLEANUP_WAIT */
Packit eace71
		if (ipc->stop_conn(session->t->handle, session->id,
Packit eace71
				   conn->id, do_stop)) {
Packit eace71
			log_error("can't stop connection %d:%d (%d)",
Packit eace71
				  session->id, conn->id, errno);
Packit eace71
			delay = ISCSI_INTERNAL_ERR_REOPEN_DELAY;
Packit eace71
			goto queue_reopen;
Packit eace71
		}
Packit eace71
		log_debug(3, "connection %d:%d is stopped for recovery",
Packit eace71
			  session->id, conn->id);
Packit eace71
	}
Packit eace71
Packit eace71
	if (!redirected) {
Packit eace71
		delay = session->def_time2wait;
Packit eace71
		session->def_time2wait = 0;
Packit eace71
		if (delay)
Packit eace71
			goto queue_reopen;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!redirected)
Packit eace71
		session->reopen_cnt++;
Packit eace71
Packit eace71
	/* uIP will needs to be re-triggered on the connection re-open */
Packit eace71
	if (iscsi_set_net_config(conn->session->t, conn->session,
Packit eace71
				 &conn->session->nrec.iface) != 0)
Packit eace71
		goto queue_reopen;
Packit eace71
Packit eace71
	if (iscsi_conn_connect(conn, qtask)) {
Packit eace71
		delay = ISCSI_CONN_ERR_REOPEN_DELAY;
Packit eace71
		goto queue_reopen;
Packit eace71
	}
Packit eace71
	return;
Packit eace71
Packit eace71
queue_reopen:
Packit eace71
	log_debug(4, "Waiting %u seconds before trying to reconnect.", delay);
Packit eace71
	queue_delayed_reopen(qtask, delay);
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop)
Packit eace71
{
Packit eace71
	/*
Packit eace71
	 * If we were temporarily redirected, we need to fall back to
Packit eace71
	 * the original address to see where the target will send us
Packit eace71
	 * for the retry
Packit eace71
	 */
Packit eace71
	memset(&conn->saddr, 0, sizeof(struct sockaddr_storage));
Packit eace71
	conn->saddr = conn->failback_saddr;
Packit eace71
Packit eace71
	__session_conn_reopen(conn, qtask, do_stop, 0);
Packit eace71
}
Packit eace71
Packit eace71
static int iscsi_retry_initial_login(struct iscsi_conn *conn)
Packit eace71
{
Packit eace71
	int initial_login_retry_max;
Packit eace71
	struct timeval now, timeout, fail_time;
Packit eace71
Packit eace71
	initial_login_retry_max =
Packit eace71
			conn->session->nrec.session.initial_login_retry_max;
Packit eace71
Packit eace71
	memset(&now, 0, sizeof(now));
Packit eace71
	memset(&timeout, 0, sizeof(timeout));
Packit eace71
	memset(&fail_time, 0, sizeof(fail_time));
Packit eace71
Packit eace71
	timeout.tv_sec = initial_login_retry_max * conn->login_timeout;
Packit eace71
	if (gettimeofday(&now, NULL)) {
Packit eace71
		log_error("Could not get time of day. Dropping down to "
Packit eace71
			  "max retry check.");
Packit eace71
		return initial_login_retry_max > conn->session->reopen_cnt;
Packit eace71
	}
Packit eace71
	timeradd(&conn->initial_connect_time, &timeout, &fail_time);
Packit eace71
Packit eace71
	/*
Packit eace71
	 * if we have been trying for login_retry_max * login_timeout
Packit eace71
	 * then it is time to give up
Packit eace71
	 */
Packit eace71
	if (timercmp(&now, &fail_time, >)) {
Packit eace71
		log_debug(1, "Giving up on initial login attempt after "
Packit eace71
			  "%u seconds.",
Packit eace71
			  initial_login_retry_max * conn->login_timeout);
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
static int iscsi_login_is_fatal_err(int err)
Packit eace71
{
Packit eace71
	if (err == ISCSI_ERR_LOGIN_AUTH_FAILED ||
Packit eace71
	    err == ISCSI_ERR_FATAL_LOGIN)
Packit eace71
		return 1;
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_login_eh(struct iscsi_conn *conn, struct queue_task *qtask,
Packit eace71
			   int err)
Packit eace71
{
Packit eace71
	struct iscsi_session *session = conn->session;
Packit eace71
Packit eace71
	log_debug(3, "iscsi_login_eh");
Packit eace71
	/*
Packit eace71
	 * Flush polls and other events
Packit eace71
	 */
Packit eace71
	iscsi_flush_context_pool(conn->session);
Packit eace71
Packit eace71
	switch (conn->state) {
Packit eace71
	case ISCSI_CONN_STATE_XPT_WAIT:
Packit eace71
		switch (session->r_stage) {
Packit eace71
		case R_STAGE_NO_CHANGE:
Packit eace71
			log_debug(6, "login failed ISCSI_CONN_STATE_XPT_WAIT/"
Packit eace71
				  "R_STAGE_NO_CHANGE");
Packit eace71
			/* timeout during initial connect.
Packit eace71
			 * clean connection. write ipc rsp or retry */
Packit eace71
			if (iscsi_login_is_fatal_err(err) ||
Packit eace71
			    !iscsi_retry_initial_login(conn))
Packit eace71
				session_conn_shutdown(conn, qtask, err);
Packit eace71
			else {
Packit eace71
				session->reopen_cnt++;
Packit eace71
				session->t->template->ep_disconnect(conn);
Packit eace71
				if (iscsi_conn_connect(conn, qtask))
Packit eace71
					queue_delayed_reopen(qtask,
Packit eace71
						ISCSI_CONN_ERR_REOPEN_DELAY);
Packit eace71
			}
Packit eace71
			break;
Packit eace71
		case R_STAGE_SESSION_REDIRECT:
Packit eace71
			log_debug(6, "login failed ISCSI_CONN_STATE_XPT_WAIT/"
Packit eace71
				  "R_STAGE_SESSION_REDIRECT");
Packit eace71
			/* timeout during initial redirect connect
Packit eace71
			 * clean connection. write ipc rsp or retry */
Packit eace71
			if (iscsi_login_is_fatal_err(err) ||
Packit eace71
			    !iscsi_retry_initial_login(conn))
Packit eace71
				session_conn_shutdown(conn, qtask, err);
Packit eace71
			else
Packit eace71
				session_conn_reopen(conn, qtask, 0);
Packit eace71
			break;
Packit eace71
		case R_STAGE_SESSION_REOPEN:
Packit eace71
			log_debug(6, "login failed ISCSI_CONN_STATE_XPT_WAIT/"
Packit eace71
				  "R_STAGE_SESSION_REOPEN (reopen_cnt=%d, reopen_max=%d)",
Packit eace71
				  session->reopen_cnt, session->reopen_max);
Packit eace71
			if (session->reopen_max &&
Packit eace71
			    (session->reopen_cnt > session->reopen_max)) {
Packit eace71
				log_info("Giving up on session %d after %d retries", 
Packit eace71
						session->id, session->reopen_max);
Packit eace71
				session_conn_shutdown(conn, qtask, err);
Packit eace71
				break;
Packit eace71
			}
Packit eace71
			/* timeout during reopen connect. try again */
Packit eace71
			session_conn_reopen(conn, qtask, 0);
Packit eace71
			break;
Packit eace71
		case R_STAGE_SESSION_CLEANUP:
Packit eace71
			session_conn_shutdown(conn, qtask, err);
Packit eace71
			break;
Packit eace71
		default:
Packit eace71
			break;
Packit eace71
		}
Packit eace71
Packit eace71
		break;
Packit eace71
	case ISCSI_CONN_STATE_IN_LOGIN:
Packit eace71
		switch (session->r_stage) {
Packit eace71
		case R_STAGE_NO_CHANGE:
Packit eace71
		case R_STAGE_SESSION_REDIRECT:
Packit eace71
			log_debug(6, "login failed ISCSI_CONN_STATE_IN_LOGIN/"
Packit eace71
				  "R_STAGE_NO_CHANGE %d",
Packit eace71
				  session->reopen_cnt);
Packit eace71
			/*
Packit eace71
			 * send pdu timeout during initial connect or
Packit eace71
			 * initial redirected connect. Clean connection
Packit eace71
			 * and write rsp or retry.
Packit eace71
			 */
Packit eace71
			if (iscsi_login_is_fatal_err(err) ||
Packit eace71
			    !iscsi_retry_initial_login(conn))
Packit eace71
				session_conn_shutdown(conn, qtask, err);
Packit eace71
			else
Packit eace71
				session_conn_reopen(conn, qtask,
Packit eace71
						    STOP_CONN_RECOVER);
Packit eace71
			break;
Packit eace71
		case R_STAGE_SESSION_REOPEN:
Packit eace71
			log_debug(6, "login failed ISCSI_CONN_STATE_IN_LOGIN/"
Packit eace71
				  "R_STAGE_SESSION_REOPEN %d",
Packit eace71
				  session->reopen_cnt);
Packit eace71
			session_conn_reopen(conn, qtask, STOP_CONN_RECOVER);
Packit eace71
			break;
Packit eace71
		case R_STAGE_SESSION_CLEANUP:
Packit eace71
			session_conn_shutdown(conn, qtask,
Packit eace71
					      ISCSI_ERR_PDU_TIMEOUT);
Packit eace71
			break;
Packit eace71
		default:
Packit eace71
			break;
Packit eace71
		}
Packit eace71
Packit eace71
		break;
Packit eace71
	default:
Packit eace71
		log_error("Ignoring login error %d in conn state %d.",
Packit eace71
			  err, conn->state);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
__conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	int i;
Packit eace71
Packit eace71
	/*
Packit eace71
	 * if we got an error while trying to logout for the user then
Packit eace71
	 * just cleanup and return to the user.
Packit eace71
	 */
Packit eace71
	if (conn->logout_qtask) {
Packit eace71
		session_conn_shutdown(conn, conn->logout_qtask, ISCSI_SUCCESS);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	switch (conn->state) {
Packit eace71
	case ISCSI_CONN_STATE_IN_LOGOUT:
Packit eace71
		/* logout was from eh - fall down to cleanup */
Packit eace71
	case ISCSI_CONN_STATE_LOGGED_IN:
Packit eace71
		/* mark failed connection */
Packit eace71
		conn->state = ISCSI_CONN_STATE_CLEANUP_WAIT;
Packit eace71
Packit eace71
		if (session->erl > 0) {
Packit eace71
			/* check if we still have some logged in connections */
Packit eace71
			for (i=0; i
Packit eace71
				if (session->conn[i].state ==
Packit eace71
				    ISCSI_CONN_STATE_LOGGED_IN)
Packit eace71
					break;
Packit eace71
			}
Packit eace71
			if (i != ISCSI_CONN_MAX) {
Packit eace71
				/* FIXME: re-assign leading connection
Packit eace71
				 *        for ERL>0 */
Packit eace71
			}
Packit eace71
Packit eace71
			break;
Packit eace71
		}
Packit eace71
Packit eace71
		/* mark all connections as failed */
Packit eace71
		for (i=0; i
Packit eace71
			if (session->conn[i].state ==
Packit eace71
			    ISCSI_CONN_STATE_LOGGED_IN)
Packit eace71
				session->conn[i].state =
Packit eace71
						ISCSI_CONN_STATE_CLEANUP_WAIT;
Packit eace71
		}
Packit eace71
		session->r_stage = R_STAGE_SESSION_REOPEN;
Packit eace71
		break;
Packit eace71
	case ISCSI_CONN_STATE_IN_LOGIN:
Packit eace71
		if (session->r_stage == R_STAGE_SESSION_REOPEN) {
Packit eace71
			queue_task_t *qtask;
Packit eace71
Packit eace71
			if (session->notify_qtask)
Packit eace71
				qtask = session->notify_qtask;
Packit eace71
			else
Packit eace71
				qtask = &session->reopen_qtask;
Packit eace71
			iscsi_login_eh(conn, qtask, ISCSI_ERR_TRANS);
Packit eace71
			return;
Packit eace71
		}
Packit eace71
		log_debug(1, "ignoring conn error in login. "
Packit eace71
			  "let it timeout");
Packit eace71
		return;
Packit eace71
	case ISCSI_CONN_STATE_XPT_WAIT:
Packit eace71
		log_debug(1, "ignoring conn error in XPT_WAIT. "
Packit eace71
			  "let connection fail on its own");
Packit eace71
		return;
Packit eace71
	case ISCSI_CONN_STATE_CLEANUP_WAIT:
Packit eace71
		log_debug(1, "ignoring conn error in CLEANUP_WAIT. "
Packit eace71
			  "let connection stop");
Packit eace71
		return;
Packit eace71
	default:
Packit eace71
		log_debug(8, "invalid state %d", conn->state);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	if (session->r_stage == R_STAGE_SESSION_REOPEN) {
Packit eace71
		session_conn_reopen(conn, &session->reopen_qtask,
Packit eace71
				    STOP_CONN_RECOVER);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static void session_conn_error(void *data)
Packit eace71
{
Packit eace71
	struct iscsi_ev_context *ev_context = data;
Packit eace71
	enum iscsi_err error = *(enum iscsi_err *)ev_context->data;
Packit eace71
	iscsi_conn_t *conn = ev_context->conn;
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
Packit eace71
	log_warning("Kernel reported iSCSI connection %d:%d error (%d - %s) "
Packit eace71
		    "state (%d)", session->id, conn->id, error,
Packit eace71
		    kern_err_code_to_string(error), conn->state);
Packit eace71
Packit eace71
	iscsi_ev_context_put(ev_context);
Packit eace71
Packit eace71
	switch (error) {
Packit eace71
	case ISCSI_ERR_INVALID_HOST:
Packit eace71
		if (session_conn_shutdown(conn, NULL, ISCSI_SUCCESS))
Packit eace71
			log_error("BUG: Could not shutdown session.");
Packit eace71
		break;
Packit eace71
	default:
Packit eace71
		__conn_error_handle(session, conn);
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_login_timedout(void *data)
Packit eace71
{
Packit eace71
	struct queue_task *qtask = data;
Packit eace71
	struct iscsi_conn *conn = qtask->conn;
Packit eace71
Packit eace71
	switch (conn->state) {
Packit eace71
	case ISCSI_CONN_STATE_XPT_WAIT:
Packit eace71
		iscsi_login_eh(conn, qtask, ISCSI_ERR_TRANS_TIMEOUT);
Packit eace71
		break;
Packit eace71
	case ISCSI_CONN_STATE_IN_LOGIN:
Packit eace71
		iscsi_login_eh(conn, qtask, ISCSI_ERR_PDU_TIMEOUT);
Packit eace71
		break;
Packit eace71
	default:
Packit eace71
		iscsi_login_eh(conn, qtask, ISCSI_ERR_INTERNAL);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_login_redirect(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
	iscsi_login_context_t *c = &conn->login_context;
Packit eace71
Packit eace71
	log_debug(3, "login redirect ...");
Packit eace71
Packit eace71
	if (session->r_stage == R_STAGE_NO_CHANGE)
Packit eace71
		session->r_stage = R_STAGE_SESSION_REDIRECT;
Packit eace71
Packit eace71
	__session_conn_reopen(conn, c->qtask, STOP_CONN_RECOVER, 1);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
__send_nopin_rsp(iscsi_conn_t *conn, struct iscsi_nopin *rhdr, char *data)
Packit eace71
{
Packit eace71
	struct iscsi_nopout hdr;
Packit eace71
Packit eace71
	memset(&hdr, 0, sizeof(struct iscsi_nopout));
Packit eace71
	hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
Packit eace71
	hdr.flags = ISCSI_FLAG_CMD_FINAL;
Packit eace71
	hdr.dlength[0] = rhdr->dlength[0];
Packit eace71
	hdr.dlength[1] = rhdr->dlength[1];
Packit eace71
	hdr.dlength[2] = rhdr->dlength[2];
Packit eace71
	memcpy(hdr.lun, rhdr->lun, 8);
Packit eace71
	hdr.ttt = rhdr->ttt;
Packit eace71
	hdr.itt = ISCSI_RESERVED_TAG;
Packit eace71
Packit eace71
	return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr,
Packit eace71
	       ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, 0);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
__send_nopout(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	struct iscsi_nopout hdr;
Packit eace71
Packit eace71
	memset(&hdr, 0, sizeof(struct iscsi_nopout));
Packit eace71
	hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
Packit eace71
	hdr.flags = ISCSI_FLAG_CMD_FINAL;
Packit eace71
	hdr.itt = 0;  /* XXX: let kernel send_pdu set for us*/
Packit eace71
	hdr.ttt = ISCSI_RESERVED_TAG;
Packit eace71
	/* we have hdr.lun reserved, and no data */
Packit eace71
	return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr,
Packit eace71
		ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 0);
Packit eace71
}
Packit eace71
Packit eace71
static void conn_nop_out_timeout(void *data)
Packit eace71
{
Packit eace71
	iscsi_conn_t *conn = (iscsi_conn_t*)data;
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
Packit eace71
	log_warning("Nop-out timedout after %d seconds on connection %d:%d "
Packit eace71
		    "state (%d). Dropping session.", conn->noop_out_timeout,
Packit eace71
		    session->id, conn->id, conn->state);
Packit eace71
	/* XXX: error handle */
Packit eace71
	__conn_error_handle(session, conn);
Packit eace71
}
Packit eace71
Packit eace71
static void conn_send_nop_out(void *data)
Packit eace71
{
Packit eace71
	iscsi_conn_t *conn = data;
Packit eace71
Packit eace71
	/*
Packit eace71
	 * we cannot start new request during logout and the logout timer
Packit eace71
	 * will figure things out.
Packit eace71
	 */
Packit eace71
	if (conn->state == ISCSI_CONN_STATE_IN_LOGOUT)
Packit eace71
		return;
Packit eace71
Packit eace71
	__send_nopout(conn);
Packit eace71
Packit eace71
	actor_timer(&conn->nop_out_timer, conn->noop_out_timeout,
Packit eace71
		    conn_nop_out_timeout, conn);
Packit eace71
	log_debug(3, "noop out timeout timer %p start, timeout %d",
Packit eace71
		 &conn->nop_out_timer, conn->noop_out_timeout);
Packit eace71
}
Packit eace71
Packit eace71
void free_initiator(void)
Packit eace71
{
Packit eace71
	struct iscsi_transport *t;
Packit eace71
	iscsi_session_t *session, *tmp;
Packit eace71
Packit eace71
	list_for_each_entry(t, &transports, list) {
Packit eace71
		list_for_each_entry_safe(session, tmp, &t->sessions, list) {
Packit eace71
			list_del(&session->list);
Packit eace71
			iscsi_flush_context_pool(session);
Packit eace71
			session_release(session);
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	free_transports();
Packit eace71
}
Packit eace71
Packit eace71
static void session_scan_host(struct iscsi_session *session, int hostno,
Packit eace71
			      queue_task_t *qtask)
Packit eace71
{
Packit eace71
	pid_t pid;
Packit eace71
Packit eace71
	pid = iscsi_sysfs_scan_host(hostno, 1, idbm_session_autoscan(session));
Packit eace71
	if (pid == 0) {
Packit eace71
		mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
Packit eace71
Packit eace71
		if (session)
Packit eace71
			iscsi_sysfs_for_each_device(
Packit eace71
					&session->nrec.session.queue_depth,
Packit eace71
					hostno, session->id,
Packit eace71
					iscsi_sysfs_set_queue_depth);
Packit eace71
		exit(0);
Packit eace71
	} else if (pid > 0) {
Packit eace71
		reap_inc();
Packit eace71
		if (qtask && qtask->mgmt_ipc_fd >= 0) {
Packit eace71
			close(qtask->mgmt_ipc_fd);
Packit eace71
			free(qtask);
Packit eace71
		}
Packit eace71
	} else
Packit eace71
		mgmt_ipc_write_rsp(qtask, ISCSI_ERR_INTERNAL);
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
setup_full_feature_phase(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
	iscsi_login_context_t *c = &conn->login_context;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	actor_delete(&conn->login_timer);
Packit eace71
Packit eace71
	if (iscsi_session_set_neg_params(conn)) {
Packit eace71
		iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	if (ipc->start_conn(session->t->handle, session->id, conn->id,
Packit eace71
			    &rc) || rc) {
Packit eace71
		log_error("can't start connection %d:%d retcode %d (%d)",
Packit eace71
			  session->id, conn->id, rc, errno);
Packit eace71
		iscsi_login_eh(conn, c->qtask, ISCSI_ERR_INTERNAL);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	conn->state = ISCSI_CONN_STATE_LOGGED_IN;
Packit eace71
	if (session->r_stage == R_STAGE_NO_CHANGE ||
Packit eace71
	    session->r_stage == R_STAGE_SESSION_REDIRECT) {
Packit eace71
		/*
Packit eace71
		 * scan host is one-time deal. We
Packit eace71
		 * don't want to re-scan it on recovery.
Packit eace71
		 */
Packit eace71
		if (conn->id == 0)
Packit eace71
			session_scan_host(session, session->hostno, c->qtask);
Packit eace71
Packit eace71
		log_warning("Connection%d:%d to [target: %s, portal: %s,%d] "
Packit eace71
			    "through [iface: %s] is operational now",
Packit eace71
			    session->id, conn->id, session->nrec.name,
Packit eace71
			    session->nrec.conn[conn->id].address,
Packit eace71
			    session->nrec.conn[conn->id].port,
Packit eace71
			    session->nrec.iface.name);
Packit eace71
	} else {
Packit eace71
		session->notify_qtask = NULL;
Packit eace71
Packit eace71
		session_online_devs(session->hostno, session->id);
Packit eace71
		mgmt_ipc_write_rsp(c->qtask, ISCSI_SUCCESS);
Packit eace71
		log_warning("connection%d:%d is operational after recovery "
Packit eace71
			    "(%d attempts)", session->id, conn->id,
Packit eace71
			     session->reopen_cnt);
Packit eace71
	}
Packit eace71
Packit eace71
	/*
Packit eace71
	 * reset ERL=0 reopen counter
Packit eace71
	 */
Packit eace71
	session->reopen_cnt = 0;
Packit eace71
	session->r_stage = R_STAGE_NO_CHANGE;
Packit eace71
Packit eace71
	/* noop_out */
Packit eace71
	if (conn->userspace_nop && conn->noop_out_interval) {
Packit eace71
		actor_timer(&conn->nop_out_timer, conn->noop_out_interval,
Packit eace71
			   conn_send_nop_out, conn);
Packit eace71
		log_debug(3, "noop out timer %p start",
Packit eace71
			  &conn->nop_out_timer);
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_logout_timedout(void *data)
Packit eace71
{
Packit eace71
	struct iscsi_ev_context *ev_context = data;
Packit eace71
	struct iscsi_conn *conn = ev_context->conn;
Packit eace71
Packit eace71
	iscsi_ev_context_put(ev_context);
Packit eace71
	/*
Packit eace71
	 * assume we were in ISCSI_CONN_STATE_IN_LOGOUT or there
Packit eace71
	 * was some nasty error
Packit eace71
	 */
Packit eace71
	log_debug(3, "logout timeout, dropping conn...");
Packit eace71
	__conn_error_handle(conn->session, conn);
Packit eace71
}
Packit eace71
Packit eace71
static int iscsi_send_logout(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	struct iscsi_logout hdr;
Packit eace71
	struct iscsi_ev_context *ev_context;
Packit eace71
Packit eace71
	if (conn->state != ISCSI_CONN_STATE_LOGGED_IN)
Packit eace71
		return EINVAL;
Packit eace71
Packit eace71
	memset(&hdr, 0, sizeof(struct iscsi_logout));
Packit eace71
	hdr.opcode = ISCSI_OP_LOGOUT | ISCSI_OP_IMMEDIATE;
Packit eace71
	hdr.flags = ISCSI_FLAG_CMD_FINAL |
Packit eace71
	   (ISCSI_LOGOUT_REASON_CLOSE_SESSION & ISCSI_FLAG_LOGOUT_REASON_MASK);
Packit eace71
	/* kernel will set the rest */
Packit eace71
Packit eace71
	if (!iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr,
Packit eace71
			       ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 0))
Packit eace71
		return EIO;
Packit eace71
	conn->state = ISCSI_CONN_STATE_IN_LOGOUT;
Packit eace71
Packit eace71
	ev_context = iscsi_ev_context_get(conn, 0);
Packit eace71
	if (!ev_context)
Packit eace71
		/* unbounded logout */
Packit eace71
		log_warning("Could not allocate conn context for logout.");
Packit eace71
	else {
Packit eace71
		iscsi_sched_ev_context(ev_context, conn,
Packit eace71
					 conn->logout_timeout,
Packit eace71
					 EV_CONN_LOGOUT_TIMER);
Packit eace71
		log_debug(3, "logout timeout timer %u",
Packit eace71
			  conn->logout_timeout * 1000);
Packit eace71
	}
Packit eace71
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_stop(void *data)
Packit eace71
{
Packit eace71
	struct iscsi_ev_context *ev_context = data;
Packit eace71
	struct iscsi_conn *conn = ev_context->conn;
Packit eace71
	int rc = 0;
Packit eace71
Packit eace71
	iscsi_ev_context_put(ev_context);
Packit eace71
Packit eace71
	if (!(conn->session->t->caps & CAP_LOGIN_OFFLOAD)) {
Packit eace71
		if (!iscsi_send_logout(conn))
Packit eace71
			return;
Packit eace71
	}
Packit eace71
Packit eace71
	rc = session_conn_shutdown(conn, conn->logout_qtask, ISCSI_SUCCESS);
Packit eace71
	if (rc)
Packit eace71
		log_error("BUG: Could not shutdown session.");
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_recv_nop_in(iscsi_conn_t *conn, struct iscsi_hdr *hdr)
Packit eace71
{
Packit eace71
	if (!conn->userspace_nop) {
Packit eace71
		log_error("Got nop in, but kernel supports nop handling.");
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	if (hdr->ttt == ISCSI_RESERVED_TAG) {
Packit eace71
		/* noop out rsp */
Packit eace71
		actor_delete(&conn->nop_out_timer);
Packit eace71
		/* schedule a new ping */
Packit eace71
		actor_timer(&conn->nop_out_timer, conn->noop_out_interval,
Packit eace71
			    conn_send_nop_out, conn);
Packit eace71
	} else /*  noop in req */
Packit eace71
		if (!__send_nopin_rsp(conn, (struct iscsi_nopin*)hdr,
Packit eace71
				      conn->data)) {
Packit eace71
			log_error("can not send nopin response");
Packit eace71
		}
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_recv_logout_rsp(iscsi_conn_t *conn, struct iscsi_hdr *hdr)
Packit eace71
{
Packit eace71
	struct iscsi_logout_rsp *logout_rsp = (struct iscsi_logout_rsp *)hdr;
Packit eace71
Packit eace71
	log_debug(3, "Recv: logout response %d", logout_rsp->response);
Packit eace71
	if (logout_rsp->response == 2 || logout_rsp->response == 3) {
Packit eace71
		conn->session->def_time2wait = ntohs(logout_rsp->t2wait);
Packit eace71
		log_debug(4, "logout rsp returned time2wait %u",
Packit eace71
			  conn->session->def_time2wait);
Packit eace71
	}
Packit eace71
	/* TODO process the hdr */
Packit eace71
	__conn_error_handle(conn->session, conn);
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_recv_async_msg(iscsi_conn_t *conn, struct iscsi_hdr *hdr)
Packit eace71
{
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
	struct iscsi_async *async_hdr = (struct iscsi_async *)hdr;
Packit eace71
	char *buf = conn->data;
Packit eace71
	unsigned int senselen;
Packit eace71
	struct scsi_sense_hdr sshdr;
Packit eace71
Packit eace71
	log_debug(3, "Read AEN %d", async_hdr->async_event);
Packit eace71
Packit eace71
	switch (async_hdr->async_event) {
Packit eace71
	case ISCSI_ASYNC_MSG_SCSI_EVENT:
Packit eace71
		senselen = (buf[0] << 8) | buf[1];
Packit eace71
		buf += 2;
Packit eace71
Packit eace71
		if (!scsi_normalize_sense((uint8_t *)buf, senselen, &sshdr)) {
Packit eace71
			log_error("Could not handle AEN %d. Invalid sense.",
Packit eace71
				  async_hdr->async_event);
Packit eace71
			break;
Packit eace71
		}
Packit eace71
Packit eace71
		if (sshdr.asc == 0x3f && sshdr.ascq == 0x0e
Packit eace71
		    && idbm_session_autoscan(session))
Packit eace71
			session_scan_host(session, session->hostno, NULL);
Packit eace71
		break;
Packit eace71
	case ISCSI_ASYNC_MSG_REQUEST_LOGOUT:
Packit eace71
		log_warning("Target requests logout within %u seconds for "
Packit eace71
			   "connection", ntohs(async_hdr->param3));
Packit eace71
		if (iscsi_send_logout(conn))
Packit eace71
			log_error("Could not send logout in response to"
Packit eace71
				 "logout request aen");
Packit eace71
		break;
Packit eace71
	case ISCSI_ASYNC_MSG_DROPPING_CONNECTION:
Packit eace71
		log_warning("Target dropping connection %u, reconnect min %u "
Packit eace71
			    "max %u", ntohs(async_hdr->param1),
Packit eace71
			    ntohs(async_hdr->param2), ntohs(async_hdr->param3));
Packit eace71
		session->def_time2wait =
Packit eace71
			(uint32_t)ntohs(async_hdr->param2) & 0x0000FFFFFL;
Packit eace71
		break;
Packit eace71
	case ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS:
Packit eace71
		log_warning("Target dropping all connections, reconnect min %u "
Packit eace71
			    "max %u", ntohs(async_hdr->param2),
Packit eace71
			     ntohs(async_hdr->param3));
Packit eace71
		session->def_time2wait =
Packit eace71
			(uint32_t)ntohs(async_hdr->param2) & 0x0000FFFFFL;
Packit eace71
		break;
Packit eace71
	case ISCSI_ASYNC_MSG_PARAM_NEGOTIATION:
Packit eace71
		log_warning("Received async event param negotiation, "
Packit eace71
			    "dropping session");
Packit eace71
		__conn_error_handle(session, conn);
Packit eace71
		break;
Packit eace71
	case ISCSI_ASYNC_MSG_VENDOR_SPECIFIC:
Packit eace71
	default:
Packit eace71
		log_warning("AEN not supported");
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_recv_login_rsp(struct iscsi_conn *conn)
Packit eace71
{ 
Packit eace71
	struct iscsi_session *session = conn->session;
Packit eace71
	iscsi_login_context_t *c = &conn->login_context;
Packit eace71
	int err = ISCSI_ERR_FATAL_LOGIN;
Packit eace71
Packit eace71
	if (iscsi_login_rsp(session, c)) {
Packit eace71
		log_debug(1, "login_rsp ret (%d)", c->ret);
Packit eace71
Packit eace71
		switch (__login_response_status(conn, c->ret)) {
Packit eace71
		case CONN_LOGIN_FAILED:
Packit eace71
			goto failed;
Packit eace71
		case CONN_LOGIN_RETRY:
Packit eace71
			goto retry;
Packit eace71
		case CONN_LOGIN_IMM_REDIRECT_RETRY:
Packit eace71
			iscsi_login_redirect(conn);
Packit eace71
			return;
Packit eace71
		default:
Packit eace71
			; /* success - fall through */
Packit eace71
		}
Packit eace71
Packit eace71
		/* check the login status */
Packit eace71
		switch (__check_iscsi_status_class(session, conn->id,
Packit eace71
						   c->status_class,
Packit eace71
						   c->status_detail)) {
Packit eace71
		case CONN_LOGIN_AUTH_FAILED:
Packit eace71
			err = ISCSI_ERR_LOGIN_AUTH_FAILED;
Packit eace71
			goto failed;
Packit eace71
		case CONN_LOGIN_FAILED:
Packit eace71
			goto failed;
Packit eace71
		case CONN_LOGIN_IMM_REDIRECT_RETRY:
Packit eace71
			iscsi_login_redirect(conn);
Packit eace71
			return;
Packit eace71
		case CONN_LOGIN_IMM_RETRY:
Packit eace71
		case CONN_LOGIN_RETRY:
Packit eace71
			goto retry;
Packit eace71
		default:
Packit eace71
			; /* success - fall through */
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	if (conn->current_stage != ISCSI_FULL_FEATURE_PHASE) {
Packit eace71
		/* more nego. needed! */
Packit eace71
		conn->state = ISCSI_CONN_STATE_IN_LOGIN;
Packit eace71
		if (iscsi_login_req(session, c)) {
Packit eace71
			iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN);
Packit eace71
			return;
Packit eace71
		}
Packit eace71
	} else
Packit eace71
		setup_full_feature_phase(conn);
Packit eace71
Packit eace71
	return;
Packit eace71
retry:
Packit eace71
	/* retry if not initial login or initial login has not timed out */
Packit eace71
	iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN);
Packit eace71
	return;
Packit eace71
failed:
Packit eace71
	/* force failure if initial login */
Packit eace71
	session->reopen_cnt = session->nrec.session.initial_login_retry_max;
Packit eace71
	iscsi_login_eh(conn, c->qtask, err);
Packit eace71
	return;
Packit eace71
}
Packit eace71
Packit eace71
static void session_conn_recv_pdu(void *data)
Packit eace71
{
Packit eace71
	struct iscsi_ev_context *ev_context = data;
Packit eace71
	iscsi_conn_t *conn = ev_context->conn;
Packit eace71
	struct iscsi_hdr hdr;
Packit eace71
Packit eace71
	conn->recv_context = ev_context;
Packit eace71
Packit eace71
	switch (conn->state) {
Packit eace71
	case ISCSI_CONN_STATE_IN_LOGIN:
Packit eace71
		iscsi_recv_login_rsp(conn);
Packit eace71
		break;
Packit eace71
	case ISCSI_CONN_STATE_LOGGED_IN:
Packit eace71
	case ISCSI_CONN_STATE_IN_LOGOUT:
Packit eace71
	case ISCSI_CONN_STATE_LOGOUT_REQUESTED:
Packit eace71
		/* read incoming PDU */
Packit eace71
		if (iscsi_io_recv_pdu(conn, &hdr, ISCSI_DIGEST_NONE,
Packit eace71
				      conn->data, ISCSI_DEF_MAX_RECV_SEG_LEN,
Packit eace71
				      ISCSI_DIGEST_NONE, 0) < 0)
Packit eace71
			return;
Packit eace71
Packit eace71
		switch (hdr.opcode & ISCSI_OPCODE_MASK) {
Packit eace71
		case ISCSI_OP_NOOP_IN:
Packit eace71
			iscsi_recv_nop_in(conn, &hdr);
Packit eace71
			break;
Packit eace71
		case ISCSI_OP_LOGOUT_RSP:
Packit eace71
			iscsi_recv_logout_rsp(conn, &hdr);
Packit eace71
			break;
Packit eace71
		case ISCSI_OP_ASYNC_EVENT:
Packit eace71
			iscsi_recv_async_msg(conn, &hdr);
Packit eace71
			break;
Packit eace71
		default:
Packit eace71
			log_error("unsupported opcode 0x%x", hdr.opcode);
Packit eace71
			break;
Packit eace71
		}
Packit eace71
		break;
Packit eace71
	case ISCSI_CONN_STATE_XPT_WAIT:
Packit eace71
		iscsi_ev_context_put(ev_context);
Packit eace71
		log_debug(1, "ignoring incoming PDU in XPT_WAIT. "
Packit eace71
			  "let connection re-establish or fail");
Packit eace71
		break;
Packit eace71
	case ISCSI_CONN_STATE_CLEANUP_WAIT:
Packit eace71
		iscsi_ev_context_put(ev_context);
Packit eace71
		log_debug(1, "ignoring incoming PDU in XPT_WAIT. "
Packit eace71
			  "let connection cleanup");
Packit eace71
		break;
Packit eace71
	default:
Packit eace71
		iscsi_ev_context_put(ev_context);
Packit eace71
		log_error("Invalid state. Dropping PDU.");
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static void session_increase_wq_priority(struct iscsi_session *session)
Packit eace71
{
Packit eace71
	DIR *proc_dir;
Packit eace71
	struct dirent *proc_dent;
Packit eace71
	struct stat statb;
Packit eace71
	char stat_file[PATH_SIZE];
Packit eace71
	char sbuf[1024];	/* got this from ps */
Packit eace71
	int pid, stat_fd, num_read;
Packit eace71
	char *proc_name, *proc_name_end;
Packit eace71
	uint32_t host_no;
Packit eace71
Packit eace71
	/* drivers like bnx2i and qla4xxx do not have a write wq */
Packit eace71
	if (session->t->caps & CAP_DATA_PATH_OFFLOAD)
Packit eace71
		return;
Packit eace71
Packit eace71
	proc_dir = opendir(PROC_DIR);
Packit eace71
	if (!proc_dir)
Packit eace71
		goto fail;
Packit eace71
Packit eace71
	while ((proc_dent = readdir(proc_dir))) {
Packit eace71
		if (!strcmp(proc_dent->d_name, ".") ||
Packit eace71
		    !strcmp(proc_dent->d_name, ".."))
Packit eace71
			continue;
Packit eace71
		if (sscanf(proc_dent->d_name, "%d", &pid) != 1)
Packit eace71
			continue;
Packit eace71
Packit eace71
		memset(stat_file, 0, sizeof(stat_file));
Packit eace71
		sprintf(stat_file, PROC_DIR"/%d/stat", pid);
Packit eace71
		if (stat(stat_file, &statb))
Packit eace71
			continue;
Packit eace71
Packit eace71
		if (!S_ISREG( statb.st_mode))
Packit eace71
			continue;
Packit eace71
Packit eace71
		stat_fd = open(stat_file, O_RDONLY);
Packit eace71
		if (stat_fd == -1)
Packit eace71
			continue;
Packit eace71
Packit eace71
		memset(sbuf, 0, sizeof(sbuf));
Packit eace71
		num_read = read(stat_fd, sbuf, sizeof(sbuf));
Packit eace71
		close(stat_fd);
Packit eace71
		if (num_read == -1)
Packit eace71
			continue;
Packit eace71
		if (num_read == sizeof(sbuf))
Packit eace71
			sbuf[num_read - 1] = '\0';
Packit eace71
		else
Packit eace71
			sbuf[num_read] = '\0';
Packit eace71
Packit eace71
		/*
Packit eace71
		 * Finally match proc name to iscsi thread name.
Packit eace71
		 * In newer kernels the name is iscsi_wq_%HOST_NO.
Packit eace71
		 * In older kernels before 2.6.30, it was scsi_wq_%HOST_NO.
Packit eace71
		 *
Packit eace71
		 * We only support newer kernels.
Packit eace71
		 */
Packit eace71
		proc_name = strchr(sbuf, '(') + 1;
Packit eace71
		if (!proc_name)
Packit eace71
			continue;
Packit eace71
Packit eace71
		proc_name_end = strchr(proc_name, ')');
Packit eace71
		if (!proc_name_end)
Packit eace71
			continue;
Packit eace71
Packit eace71
		*proc_name_end = '\0';
Packit eace71
Packit eace71
		if (sscanf(proc_name, "iscsi_q_%u\n", &host_no) == 1) {
Packit eace71
			if (host_no == session->hostno) {
Packit eace71
				if (!setpriority(PRIO_PROCESS, pid,
Packit eace71
					session->nrec.session.xmit_thread_priority)) {
Packit eace71
					closedir(proc_dir);
Packit eace71
					return;
Packit eace71
				} else
Packit eace71
					break;
Packit eace71
			}
Packit eace71
		}
Packit eace71
	}
Packit eace71
	closedir(proc_dir);
Packit eace71
fail:
Packit eace71
	log_error("Could not set session%d priority. "
Packit eace71
		  "READ/WRITE throughout and latency could be "
Packit eace71
		  "affected.", session->id);
Packit eace71
}
Packit eace71
Packit eace71
static int session_ipc_create(struct iscsi_session *session)
Packit eace71
{
Packit eace71
	struct iscsi_conn *conn = &session->conn[0];
Packit eace71
	int err = 0, pass_ep = 1;
Packit eace71
	uint32_t host_no = -1;
Packit eace71
Packit eace71
	if (session->t->template->ep_connect != ktransport_ep_connect)
Packit eace71
		pass_ep = 0;
Packit eace71
retry_create:
Packit eace71
	err = ipc->create_session(session->t->handle,
Packit eace71
				  pass_ep ? conn->transport_ep_handle : 0,
Packit eace71
				  session->nrec.session.initial_cmdsn,
Packit eace71
				  session->nrec.session.cmds_max,
Packit eace71
				  session->nrec.session.queue_depth,
Packit eace71
				  &session->id, &host_no);
Packit eace71
	/*
Packit eace71
	 * Older kernels were not passed the sessions's leading conn ep,
Packit eace71
	 * so we will get -EINVAL || -ENOSYS for iser.
Packit eace71
	 *
Packit eace71
	 * 2.6.22 and earlier would send -EINVAL instead of -ENOSYS.
Packit eace71
	 */
Packit eace71
	if (pass_ep && (err == -ENOSYS || err == -EINVAL)) {
Packit eace71
		pass_ep = 0;
Packit eace71
		goto retry_create;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!err) {
Packit eace71
		session->hostno = host_no;
Packit eace71
		session_increase_wq_priority(session);
Packit eace71
	}
Packit eace71
	return err;
Packit eace71
}
Packit eace71
Packit eace71
static void setup_offload_login_phase(iscsi_conn_t *conn)
Packit eace71
{
Packit eace71
	iscsi_session_t *session = conn->session;
Packit eace71
	iscsi_login_context_t *c = &conn->login_context;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	actor_delete(&conn->login_timer);
Packit eace71
Packit eace71
	if (iscsi_session_set_params(conn)) {
Packit eace71
		iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	if (iscsi_session_set_neg_params(conn)) {
Packit eace71
		iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	if (iscsi_host_set_params(session)) {
Packit eace71
		iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	conn->state = ISCSI_CONN_STATE_IN_LOGIN;
Packit eace71
	if (ipc->start_conn(session->t->handle, session->id, conn->id,
Packit eace71
			    &rc) || rc) {
Packit eace71
		if (rc == -EEXIST) {
Packit eace71
			log_error("Session already exists.");
Packit eace71
			session_conn_shutdown(conn, c->qtask,
Packit eace71
					      ISCSI_ERR_SESS_EXISTS);
Packit eace71
		} else {
Packit eace71
			log_error("can't start connection %d:%d retcode (%d)",
Packit eace71
				  session->id, conn->id, rc);
Packit eace71
			iscsi_login_eh(conn, c->qtask, ISCSI_ERR_INTERNAL);
Packit eace71
		}
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	session->notify_qtask = c->qtask;
Packit eace71
}
Packit eace71
Packit eace71
Packit eace71
static void session_conn_poll(void *data)
Packit eace71
{
Packit eace71
	struct iscsi_ev_context *ev_context = data;
Packit eace71
	iscsi_conn_t *conn = ev_context->conn;
Packit eace71
	struct iscsi_session *session = conn->session;
Packit eace71
	int err = ISCSI_SUCCESS;
Packit eace71
	queue_task_t *qtask = ev_context->data;
Packit eace71
	iscsi_login_context_t *c = &conn->login_context;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	iscsi_ev_context_put(ev_context);
Packit eace71
Packit eace71
	if (conn->state != ISCSI_CONN_STATE_XPT_WAIT)
Packit eace71
		return;
Packit eace71
Packit eace71
	rc = session->t->template->ep_poll(conn, 1);
Packit eace71
	if (rc == 0) {
Packit eace71
		log_debug(4, "poll not connected %d", rc);
Packit eace71
		ev_context = iscsi_ev_context_get(conn, 0);
Packit eace71
		if (!ev_context) {
Packit eace71
			/* while polling the recv pool should be full */
Packit eace71
			log_error("BUG: session_conn_poll could not get conn "
Packit eace71
				  "context.");
Packit eace71
			iscsi_login_eh(conn, qtask, ISCSI_ERR_INTERNAL);
Packit eace71
			return;
Packit eace71
		}
Packit eace71
		ev_context->data = qtask;
Packit eace71
		/* not connected yet, check later */
Packit eace71
		iscsi_sched_ev_context(ev_context, conn, 1, EV_CONN_POLL);
Packit eace71
	} else if (rc > 0) {
Packit eace71
		/* connected! */
Packit eace71
		memset(c, 0, sizeof(iscsi_login_context_t));
Packit eace71
Packit eace71
		/* do not allocate new connection in case of reopen */
Packit eace71
		if (session->id == -1) {
Packit eace71
			if (conn->id == 0 && session_ipc_create(session)) {
Packit eace71
				log_error("Can't create session.");
Packit eace71
				err = ISCSI_ERR_INTERNAL;
Packit eace71
				goto cleanup;
Packit eace71
			}
Packit eace71
			log_debug(3, "created new iSCSI session sid %d host "
Packit eace71
				  "no %u", session->id, session->hostno);
Packit eace71
Packit eace71
			err = ipc->create_conn(session->t->handle,
Packit eace71
					session->id, conn->id, &conn->id);
Packit eace71
			if (err) {
Packit eace71
				log_error("Can't create connection.");
Packit eace71
				err = ISCSI_ERR_INTERNAL;
Packit eace71
				goto cleanup;
Packit eace71
			}
Packit eace71
			log_debug(3, "created new iSCSI connection "
Packit eace71
				  "%d:%d", session->id, conn->id);
Packit eace71
		}
Packit eace71
Packit eace71
		iscsi_copy_operational_params(conn,
Packit eace71
					&session->nrec.session.iscsi,
Packit eace71
					&session->nrec.conn[conn->id].iscsi);
Packit eace71
		/*
Packit eace71
		 * TODO: use the iface number or some other value
Packit eace71
		 * so this will be persistent
Packit eace71
		 */
Packit eace71
		session->isid[3] = (session->id >> 16) & 0xff;
Packit eace71
		session->isid[4] = (session->id >>  8) & 0xff;
Packit eace71
		session->isid[5] = session->id & 0xff;
Packit eace71
Packit eace71
		if (ipc->bind_conn(session->t->handle, session->id,
Packit eace71
				   conn->id, conn->transport_ep_handle,
Packit eace71
				   (conn->id == 0), &rc) || rc) {
Packit eace71
			log_error("can't bind conn %d:%d to session %d, "
Packit eace71
				  "retcode %d (%d)", session->id, conn->id,
Packit eace71
				   session->id, rc, errno);
Packit eace71
			iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN);
Packit eace71
			return;
Packit eace71
		}
Packit eace71
		log_debug(3, "bound iSCSI connection %d:%d to session %d",
Packit eace71
			  session->id, conn->id, session->id);
Packit eace71
Packit eace71
		c->qtask = qtask;
Packit eace71
		c->cid = conn->id;
Packit eace71
		c->buffer = conn->data;
Packit eace71
		c->bufsize = sizeof(conn->data);
Packit eace71
Packit eace71
		conn->exp_statsn = iscsi_sysfs_get_exp_statsn(session->id);
Packit eace71
Packit eace71
		if (session->t->caps & CAP_LOGIN_OFFLOAD) {
Packit eace71
			setup_offload_login_phase(conn);
Packit eace71
			return;
Packit eace71
		}
Packit eace71
Packit eace71
		if (iscsi_session_set_params(conn)) {
Packit eace71
			iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN);
Packit eace71
			return;
Packit eace71
		}
Packit eace71
Packit eace71
		if (iscsi_host_set_params(session)) {
Packit eace71
			iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN);
Packit eace71
			return;
Packit eace71
		}
Packit eace71
Packit eace71
		if (iscsi_login_begin(session, c)) {
Packit eace71
			iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN);
Packit eace71
			return;
Packit eace71
		}
Packit eace71
Packit eace71
		conn->state = ISCSI_CONN_STATE_IN_LOGIN;
Packit eace71
		if (iscsi_login_req(session, c)) {
Packit eace71
			iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN);
Packit eace71
			return;
Packit eace71
		}
Packit eace71
	} else {
Packit eace71
		log_debug(4, "poll error %d", rc);
Packit eace71
		queue_delayed_reopen(qtask, ISCSI_CONN_ERR_REOPEN_DELAY);
Packit eace71
	}
Packit eace71
Packit eace71
	return;
Packit eace71
Packit eace71
cleanup:
Packit eace71
	session_conn_shutdown(conn, qtask, err);
Packit eace71
}
Packit eace71
Packit eace71
static void session_conn_process_login(void *data)
Packit eace71
{
Packit eace71
	struct iscsi_ev_context *ev_context = data;
Packit eace71
	enum iscsi_conn_state state = *(enum iscsi_conn_state *)
Packit eace71
							ev_context->data;
Packit eace71
	struct iscsi_conn *conn = ev_context->conn;
Packit eace71
	struct iscsi_session *session = conn->session;
Packit eace71
	iscsi_login_context_t *c = &conn->login_context;
Packit eace71
	queue_task_t *qtask;
Packit eace71
Packit eace71
	iscsi_ev_context_put(ev_context);
Packit eace71
	if (!(session->t->caps & CAP_LOGIN_OFFLOAD))
Packit eace71
		return;
Packit eace71
Packit eace71
	if (state == ISCSI_CONN_STATE_FREE)
Packit eace71
		goto failed_login;
Packit eace71
Packit eace71
	if (conn->state == ISCSI_CONN_STATE_LOGGED_IN)
Packit eace71
		return;
Packit eace71
Packit eace71
	conn->state = ISCSI_CONN_STATE_LOGGED_IN;
Packit eace71
	/*
Packit eace71
	 * ok we were in_login and now we got the notification that we are
Packit eace71
	 * logged in
Packit eace71
	 */
Packit eace71
	log_debug(3, "session created sid %u host no %d", session->id,
Packit eace71
		  session->hostno);
Packit eace71
Packit eace71
	if (session->r_stage == R_STAGE_NO_CHANGE ||
Packit eace71
	    session->r_stage == R_STAGE_SESSION_REDIRECT) {
Packit eace71
		/*
Packit eace71
		 * scan host is one-time deal. We
Packit eace71
		 * don't want to re-scan it on recovery.
Packit eace71
		 */
Packit eace71
		session_scan_host(session, session->hostno,
Packit eace71
				 c->qtask);
Packit eace71
		session->notify_qtask = NULL;
Packit eace71
Packit eace71
		log_warning("Connection%d:%d to [target: %s, portal: %s,%d] "
Packit eace71
			    "through [iface: %s] is operational now",
Packit eace71
			    session->id, conn->id, session->nrec.name,
Packit eace71
			    session->nrec.conn[conn->id].address,
Packit eace71
			    session->nrec.conn[conn->id].port,
Packit eace71
			    session->nrec.iface.name);
Packit eace71
	} else {
Packit eace71
		session->notify_qtask = NULL;
Packit eace71
		mgmt_ipc_write_rsp(c->qtask, ISCSI_SUCCESS);
Packit eace71
	}
Packit eace71
Packit eace71
	/*
Packit eace71
	 * reset ERL=0 reopen counter
Packit eace71
	 */
Packit eace71
	session->reopen_cnt = 0;
Packit eace71
	session->r_stage = R_STAGE_NO_CHANGE;
Packit eace71
Packit eace71
	return;
Packit eace71
Packit eace71
failed_login:
Packit eace71
	qtask = session->notify_qtask;
Packit eace71
	session->notify_qtask = NULL;
Packit eace71
	mgmt_ipc_write_rsp(qtask, ISCSI_ERR_LOGIN);
Packit eace71
	if (ipc->destroy_conn(session->t->handle, session->id, conn->id))
Packit eace71
		log_error("can not safely destroy connection %d", conn->id);
Packit eace71
	if (ipc->destroy_session(session->t->handle, session->id))
Packit eace71
		log_error("can not safely destroy session %d", session->id);
Packit eace71
	__session_destroy(session);
Packit eace71
Packit eace71
}
Packit eace71
Packit eace71
static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context,
Packit eace71
				  struct iscsi_conn *conn, unsigned long tmo,
Packit eace71
				  int event)
Packit eace71
{
Packit eace71
	enum iscsi_err error;
Packit eace71
Packit eace71
	log_debug(7, "sched conn context %p event %d, tmo %lu",
Packit eace71
		  &ev_context->actor, event, tmo);
Packit eace71
Packit eace71
	ev_context->conn = conn;
Packit eace71
	switch (event) {
Packit eace71
	case EV_CONN_RECV_PDU:
Packit eace71
		actor_init(&ev_context->actor, session_conn_recv_pdu,
Packit eace71
			  ev_context);
Packit eace71
		actor_schedule(&ev_context->actor);
Packit eace71
		break;
Packit eace71
	case EV_CONN_ERROR:
Packit eace71
		error = *(enum iscsi_err *)ev_context->data;
Packit eace71
Packit eace71
		actor_init(&ev_context->actor, session_conn_error,
Packit eace71
			  ev_context);
Packit eace71
		/*
Packit eace71
		 * We handle invalid host, by killing the session.
Packit eace71
		 * It must go at the head of the queue, so we do not
Packit eace71
		 * initiate error handling or logout or some other op.
Packit eace71
		 */
Packit eace71
		if (error == ISCSI_ERR_INVALID_HOST)
Packit eace71
			actor_schedule_head(&ev_context->actor);
Packit eace71
		else
Packit eace71
			actor_schedule(&ev_context->actor);
Packit eace71
		break;
Packit eace71
	case EV_CONN_LOGIN:
Packit eace71
		actor_init(&ev_context->actor, session_conn_process_login,
Packit eace71
			  ev_context);
Packit eace71
		actor_schedule(&ev_context->actor);
Packit eace71
		break;
Packit eace71
	case EV_CONN_POLL:
Packit eace71
		actor_timer(&ev_context->actor, tmo,
Packit eace71
			    session_conn_poll, ev_context);
Packit eace71
		break;
Packit eace71
	case EV_CONN_LOGOUT_TIMER:
Packit eace71
		actor_timer(&ev_context->actor, tmo,
Packit eace71
			    iscsi_logout_timedout, ev_context);
Packit eace71
		break;
Packit eace71
	case EV_CONN_STOP:
Packit eace71
		actor_init(&ev_context->actor, iscsi_stop,
Packit eace71
			  ev_context);
Packit eace71
		actor_schedule(&ev_context->actor);
Packit eace71
		break;
Packit eace71
	default:
Packit eace71
		log_error("Invalid event type %d.", event);
Packit eace71
	}
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static iscsi_session_t* session_find_by_rec(node_rec_t *rec)
Packit eace71
{
Packit eace71
	struct iscsi_transport *t;
Packit eace71
	iscsi_session_t *session;
Packit eace71
Packit eace71
	list_for_each_entry(t, &transports, list) {
Packit eace71
		list_for_each_entry(session, &t->sessions, list) {
Packit eace71
			if (__iscsi_match_session(rec, session->nrec.name,
Packit eace71
					 session->nrec.conn[0].address,
Packit eace71
					 session->nrec.conn[0].port,
Packit eace71
					 &session->nrec.iface,
Packit eace71
					 MATCH_ANY_SID))
Packit eace71
				return session;
Packit eace71
		}
Packit eace71
	}
Packit eace71
	return NULL;
Packit eace71
}
Packit eace71
Packit eace71
/*
Packit eace71
 * a session could be running in the kernel but not in iscsid
Packit eace71
 * due to a resync or because some other app started the session
Packit eace71
 */
Packit eace71
static int session_is_running(node_rec_t *rec)
Packit eace71
{
Packit eace71
	int nr_found = 0;
Packit eace71
Packit eace71
	if (session_find_by_rec(rec))
Packit eace71
		return 1;
Packit eace71
Packit eace71
	if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_session,
Packit eace71
					 0))
Packit eace71
		return 1;
Packit eace71
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static int __session_login_task(node_rec_t *rec, queue_task_t *qtask)
Packit eace71
{
Packit eace71
	iscsi_session_t *session;
Packit eace71
	iscsi_conn_t *conn;
Packit eace71
	struct iscsi_transport *t;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	if (session_is_running(rec)) {
Packit eace71
		if (rec->session.multiple)
Packit eace71
			log_debug(2, "Adding a copy of an existing session");
Packit eace71
		else
Packit eace71
			return ISCSI_ERR_SESS_EXISTS;
Packit eace71
	}
Packit eace71
Packit eace71
	t = iscsi_sysfs_get_transport_by_name(rec->iface.transport_name);
Packit eace71
	if (!t)
Packit eace71
		return ISCSI_ERR_TRANS_NOT_FOUND;
Packit eace71
Packit eace71
	if ((!(t->caps & CAP_RECOVERY_L0) &&
Packit eace71
	     rec->session.iscsi.ERL != 0) ||
Packit eace71
	    (!(t->caps & CAP_RECOVERY_L1) &&
Packit eace71
	     rec->session.iscsi.ERL > 1)) {
Packit eace71
		log_error("Transport '%s' does not support ERL %d."
Packit eace71
			  "Setting ERL to ERL0.",
Packit eace71
			  t->name, rec->session.iscsi.ERL);
Packit eace71
		rec->session.iscsi.ERL = 0;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!(t->caps & CAP_MULTI_R2T) &&
Packit eace71
	    rec->session.iscsi.MaxOutstandingR2T) {
Packit eace71
		log_error("Transport '%s' does not support "
Packit eace71
			  "MaxOutstandingR2T %d. Setting "
Packit eace71
			  "MaxOutstandingR2T to 1.", t->name,
Packit eace71
			  rec->session.iscsi.MaxOutstandingR2T);
Packit eace71
		rec->session.iscsi.MaxOutstandingR2T = 1;		
Packit eace71
	}
Packit eace71
Packit eace71
	if (!(t->caps & CAP_HDRDGST) &&
Packit eace71
	    rec->conn[0].iscsi.HeaderDigest) {
Packit eace71
		log_error("Transport '%s' does not support "
Packit eace71
			  "HeaderDigest != None. Setting HeaderDigest "
Packit eace71
			  "to None.", t->name);
Packit eace71
		rec->conn[0].iscsi.HeaderDigest = CONFIG_DIGEST_NEVER;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!(t->caps & CAP_DATADGST) &&
Packit eace71
	    rec->conn[0].iscsi.DataDigest) {
Packit eace71
		log_error("Transport '%s' does not support "
Packit eace71
			  "DataDigest != None. Setting DataDigest "
Packit eace71
			  "to None", t->name);
Packit eace71
		rec->conn[0].iscsi.DataDigest = CONFIG_DIGEST_NEVER;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!(t->caps & CAP_MARKERS) &&
Packit eace71
	    rec->conn[0].iscsi.IFMarker) {
Packit eace71
		log_error("Transport '%s' does not support IFMarker. "
Packit eace71
			  "Disabling IFMarkers.", t->name);
Packit eace71
		rec->conn[0].iscsi.IFMarker = 0;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!(t->caps & CAP_MARKERS) &&
Packit eace71
	    rec->conn[0].iscsi.OFMarker) {
Packit eace71
		log_error("Transport '%s' does not support OFMarker."
Packit eace71
			  "Disabling OFMarkers.", t->name);
Packit eace71
		rec->conn[0].iscsi.OFMarker = 0;
Packit eace71
	}
Packit eace71
Packit eace71
	session = __session_create(rec, t, &rc);
Packit eace71
	if (rc == ISCSI_ERR_HOST_NOT_FOUND)
Packit eace71
		return rc;
Packit eace71
	else if (!session)
Packit eace71
		return ISCSI_ERR_LOGIN;
Packit eace71
Packit eace71
	/* FIXME: login all connections! marked as "automatic" */
Packit eace71
Packit eace71
	/* create leading connection */
Packit eace71
	rc = __session_conn_create(session, 0);
Packit eace71
	if (rc) {
Packit eace71
		__session_destroy(session);
Packit eace71
		return rc;
Packit eace71
	}
Packit eace71
	conn = &session->conn[0];
Packit eace71
	qtask->conn = conn;
Packit eace71
Packit eace71
	rc = iscsi_host_set_net_params(&rec->iface, session);
Packit eace71
	if (rc == ISCSI_ERR_AGAIN) {
Packit eace71
		/*
Packit eace71
		 * host/iscsiuio not ready. Cannot block iscsid, so caller is
Packit eace71
		 * going to internally retry the operation.
Packit eace71
		 */
Packit eace71
		__session_destroy(session);
Packit eace71
		return ISCSI_ERR_HOST_NOT_FOUND;
Packit eace71
	} else if (rc) {
Packit eace71
		__session_destroy(session);
Packit eace71
		return ISCSI_ERR_LOGIN;
Packit eace71
	}
Packit eace71
Packit eace71
	if (gettimeofday(&conn->initial_connect_time, NULL))
Packit eace71
		log_error("Could not get initial connect time. If "
Packit eace71
			  "login errors iscsid may give up the initial "
Packit eace71
			  "login early. You should manually login.");
Packit eace71
Packit eace71
	conn->state = ISCSI_CONN_STATE_XPT_WAIT;
Packit eace71
	qtask->rsp.command = MGMT_IPC_SESSION_LOGIN;
Packit eace71
	qtask->rsp.err = ISCSI_SUCCESS;
Packit eace71
Packit eace71
	if (iscsi_conn_connect(conn, qtask)) {
Packit eace71
		log_debug(4, "Initial connect failed. Waiting %u seconds "
Packit eace71
			  "before trying to reconnect.",
Packit eace71
			  ISCSI_CONN_ERR_REOPEN_DELAY);
Packit eace71
		queue_delayed_reopen(qtask, ISCSI_CONN_ERR_REOPEN_DELAY);
Packit eace71
	}
Packit eace71
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
int
Packit eace71
session_login_task(node_rec_t *rec, queue_task_t *qtask)
Packit eace71
{
Packit eace71
	int rc;
Packit eace71
Packit eace71
	rc = __session_login_task(rec, qtask);
Packit eace71
	if (rc == ISCSI_ERR_HOST_NOT_FOUND) {
Packit eace71
		rc = queue_session_login_task_retry(NULL, rec, qtask);
Packit eace71
		if (rc)
Packit eace71
			return rc;
Packit eace71
		/*
Packit eace71
		 * we are going to internally retry. Will return final rc
Packit eace71
		 * when completed
Packit eace71
		 */
Packit eace71
		return ISCSI_SUCCESS;
Packit eace71
	}
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
static void session_login_task_retry(void *data)
Packit eace71
{
Packit eace71
	struct login_task_retry_info *info = data;
Packit eace71
	struct node_rec *rec = info->rec;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	rc = __session_login_task(rec, info->qtask);
Packit eace71
	if (rc == ISCSI_ERR_HOST_NOT_FOUND) {
Packit eace71
		if (info->retry_count == rec->conn[0].timeo.login_timeout) {
Packit eace71
			/* give up */
Packit eace71
			goto write_rsp;
Packit eace71
		}
Packit eace71
Packit eace71
		rc = queue_session_login_task_retry(info, rec, info->qtask);
Packit eace71
		if (rc)
Packit eace71
			goto write_rsp;
Packit eace71
		/* we are going to internally retry */
Packit eace71
		return;
Packit eace71
	} else if (rc) {
Packit eace71
		/* hard error - no retry */
Packit eace71
		goto write_rsp;
Packit eace71
	} else
Packit eace71
		/* successfully started login operation */
Packit eace71
		goto free;
Packit eace71
write_rsp:
Packit eace71
	mgmt_ipc_write_rsp(info->qtask, rc);
Packit eace71
free:
Packit eace71
	free(info);
Packit eace71
}
Packit eace71
Packit eace71
static int queue_session_login_task_retry(struct login_task_retry_info *info,
Packit eace71
					  node_rec_t *rec, queue_task_t *qtask)
Packit eace71
{
Packit eace71
	if (!info) {
Packit eace71
		info = malloc(sizeof(*info));
Packit eace71
		if (!info)
Packit eace71
			return ISCSI_ERR_NOMEM;
Packit eace71
		memset(info, 0, sizeof(*info));
Packit eace71
		info->qtask = qtask;
Packit eace71
		info->rec = rec;
Packit eace71
	}
Packit eace71
Packit eace71
	info->retry_count++;
Packit eace71
	log_debug(4, "queue session setup attempt in %d secs, retries %d",
Packit eace71
		  1, info->retry_count);
Packit eace71
	actor_timer(&info->retry_actor, 1, session_login_task_retry, info);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
sync_conn(iscsi_session_t *session, uint32_t cid)
Packit eace71
{
Packit eace71
	iscsi_conn_t *conn;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	rc = __session_conn_create(session, cid);
Packit eace71
	if (rc)
Packit eace71
		return rc;
Packit eace71
	conn = &session->conn[cid];
Packit eace71
Packit eace71
	/* TODO: must export via sysfs so we can pick this up */
Packit eace71
	conn->state = ISCSI_CONN_STATE_CLEANUP_WAIT;
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
int
Packit eace71
iscsi_sync_session(node_rec_t *rec, queue_task_t *qtask, uint32_t sid)
Packit eace71
{
Packit eace71
	iscsi_session_t *session;
Packit eace71
	struct iscsi_transport *t;
Packit eace71
	int err;
Packit eace71
Packit eace71
	t = iscsi_sysfs_get_transport_by_name(rec->iface.transport_name);
Packit eace71
	if (!t)
Packit eace71
		return ISCSI_ERR_TRANS_NOT_FOUND;
Packit eace71
Packit eace71
	session = __session_create(rec, t, &err;;
Packit eace71
	if (!session)
Packit eace71
		return ISCSI_ERR_LOGIN;
Packit eace71
Packit eace71
	session->id = sid;
Packit eace71
	session->hostno = iscsi_sysfs_get_host_no_from_sid(sid, &err;;
Packit eace71
	if (err) {
Packit eace71
		log_error("Could not get hostno for session %d", sid);
Packit eace71
		goto destroy_session;
Packit eace71
	}
Packit eace71
Packit eace71
	session->r_stage = R_STAGE_SESSION_REOPEN;
Packit eace71
Packit eace71
	err = sync_conn(session, 0);
Packit eace71
	if (err)
Packit eace71
		goto destroy_session;
Packit eace71
Packit eace71
	qtask->rsp.command = MGMT_IPC_SESSION_SYNC;
Packit eace71
Packit eace71
	log_debug(3, "Started sync iSCSI session %d", session->id);
Packit eace71
	session->notify_qtask = qtask;
Packit eace71
	session_conn_reopen(&session->conn[0], qtask,
Packit eace71
			    STOP_CONN_RECOVER);
Packit eace71
Packit eace71
	return 0;
Packit eace71
Packit eace71
destroy_session:
Packit eace71
	__session_destroy(session);
Packit eace71
	log_error("Could not sync session%d err %d", sid, err);
Packit eace71
	return err;
Packit eace71
}
Packit eace71
Packit eace71
static int session_unbind(struct iscsi_session *session)
Packit eace71
{
Packit eace71
	int err;
Packit eace71
Packit eace71
	err = ipc->unbind_session(session->t->handle, session->id);
Packit eace71
	if (err)
Packit eace71
		/* older kernels did not support unbind */
Packit eace71
		log_debug(2, "Could not unbind session %d.", err);
Packit eace71
	return err;
Packit eace71
}
Packit eace71
Packit eace71
int session_logout_task(int sid, queue_task_t *qtask)
Packit eace71
{
Packit eace71
	iscsi_session_t *session;
Packit eace71
	iscsi_conn_t *conn;
Packit eace71
	int rc = ISCSI_SUCCESS;
Packit eace71
Packit eace71
	session = session_find_by_sid(sid);
Packit eace71
	if (!session) {
Packit eace71
                log_debug(1, "session sid %d not found.", sid);
Packit eace71
		return ISCSI_ERR_SESS_NOT_FOUND;
Packit eace71
	}
Packit eace71
	conn = &session->conn[0];
Packit eace71
Packit eace71
	/*
Packit eace71
	 * If syncing up, in XPT_WAIT, and REOPENing, then return
Packit eace71
	 * an informative error, since the target for this session
Packit eace71
	 * is likely not connected
Packit eace71
	 */
Packit eace71
	if (session->notify_qtask &&
Packit eace71
	    (conn->state == ISCSI_CONN_STATE_XPT_WAIT) &&
Packit eace71
	    (session->r_stage == R_STAGE_SESSION_REOPEN)) {
Packit eace71
		log_warning("session cannot be terminted because it's trying to reconnect: try again later");
Packit eace71
		return ISCSI_ERR_SESSION_NOT_CONNECTED;
Packit eace71
	}
Packit eace71
Packit eace71
	/*
Packit eace71
	 * If syncing up and not reconnecting,
Packit eace71
	 * or if this is the initial login and mgmt_ipc
Packit eace71
	 * has not been notified of that result fail the logout request
Packit eace71
	 */
Packit eace71
	if (session->notify_qtask ||
Packit eace71
	    ((conn->state == ISCSI_CONN_STATE_XPT_WAIT ||
Packit eace71
	      conn->state == ISCSI_CONN_STATE_IN_LOGIN) &&
Packit eace71
	    (session->r_stage == R_STAGE_NO_CHANGE ||
Packit eace71
	     session->r_stage == R_STAGE_SESSION_REDIRECT))) {
Packit eace71
invalid_state:
Packit eace71
		log_error("session in invalid state for logout. "
Packit eace71
			   "Try again later");
Packit eace71
		return ISCSI_ERR_INTERNAL;
Packit eace71
	}
Packit eace71
Packit eace71
	if (dconfig->safe_logout && session_in_use(sid)) {
Packit eace71
		log_error("Session is actively in use for mounted storage, "
Packit eace71
			  "and iscsid.safe_logout is configured.");
Packit eace71
		return ISCSI_ERR_BUSY;
Packit eace71
	}
Packit eace71
Packit eace71
	/* FIXME: logout all active connections */
Packit eace71
	conn = &session->conn[0];
Packit eace71
	if (conn->logout_qtask)
Packit eace71
		goto invalid_state;
Packit eace71
Packit eace71
	qtask->conn = conn;
Packit eace71
	qtask->rsp.command = MGMT_IPC_SESSION_LOGOUT;
Packit eace71
	conn->logout_qtask = qtask;
Packit eace71
Packit eace71
	switch (conn->state) {
Packit eace71
	case ISCSI_CONN_STATE_LOGGED_IN:
Packit eace71
		if (!session_unbind(session))
Packit eace71
			return ISCSI_SUCCESS;
Packit eace71
Packit eace71
		/* LLDs that offload login also offload logout */
Packit eace71
		if (!(session->t->caps & CAP_LOGIN_OFFLOAD)) {
Packit eace71
			/* unbind is not supported so just do old logout */
Packit eace71
			if (!iscsi_send_logout(conn))
Packit eace71
				return ISCSI_SUCCESS;
Packit eace71
		}
Packit eace71
Packit eace71
		log_error("Could not send logout pdu. Dropping session");
Packit eace71
		/* fallthrough */
Packit eace71
	default:
Packit eace71
		rc = session_conn_shutdown(conn, qtask, ISCSI_SUCCESS);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
int
Packit eace71
iscsi_host_send_targets(queue_task_t *qtask, int host_no, int do_login,
Packit eace71
			struct sockaddr_storage *ss)
Packit eace71
{
Packit eace71
	struct iscsi_transport *t;
Packit eace71
Packit eace71
	t = iscsi_sysfs_get_transport_by_hba(host_no);
Packit eace71
	if (!t) {
Packit eace71
		log_error("Invalid host no %d for sendtargets", host_no);
Packit eace71
		return ISCSI_ERR_TRANS_NOT_FOUND;
Packit eace71
	}
Packit eace71
	if (!(t->caps & CAP_SENDTARGETS_OFFLOAD))
Packit eace71
		return ISCSI_ERR_TRANS_CAPS;
Packit eace71
Packit eace71
	if (ipc->sendtargets(t->handle, host_no, (struct sockaddr *)ss))
Packit eace71
		return ISCSI_ERR;
Packit eace71
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
/*
Packit eace71
 * HW drivers like qla4xxx present an interface that hides most of the iscsi
Packit eace71
 * details. Userspace sends down a discovery event then it gets notified
Packit eace71
 * if the sessions that were logged in as a result asynchronously, or
Packit eace71
 * the card will have sessions preset in the FLASH and will log into them
Packit eace71
 * automaotically then send us notification that a session is setup.
Packit eace71
 */
Packit eace71
static void iscsi_async_session_creation(uint32_t host_no, uint32_t sid)
Packit eace71
{
Packit eace71
	struct iscsi_transport *transport;
Packit eace71
Packit eace71
	transport = iscsi_sysfs_get_transport_by_hba(host_no);
Packit eace71
	if (!transport)
Packit eace71
		return;
Packit eace71
Packit eace71
	if (!(transport->caps & CAP_FW_DB))
Packit eace71
		return;
Packit eace71
Packit eace71
	log_debug(3, "session created sid %u host no %d", sid, host_no);
Packit eace71
	session_online_devs(host_no, sid);
Packit eace71
	session_scan_host(NULL, host_no, NULL);
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_async_session_destruction(uint32_t host_no, uint32_t sid)
Packit eace71
{
Packit eace71
	log_debug(3, "session destroyed sid %u host no %d", sid, host_no);
Packit eace71
}
Packit eace71
Packit eace71
static struct iscsi_ipc_ev_clbk ipc_clbk = {
Packit eace71
	.create_session		= iscsi_async_session_creation,
Packit eace71
	.destroy_session	= iscsi_async_session_destruction,
Packit eace71
	.get_ev_context		= iscsi_ev_context_get,
Packit eace71
	.put_ev_context		= iscsi_ev_context_put,
Packit eace71
	.sched_ev_context	= iscsi_sched_ev_context,
Packit eace71
};
Packit eace71
Packit eace71
void iscsi_initiator_init(void)
Packit eace71
{
Packit eace71
	ipc_register_ev_callback(&ipc_clbk);
Packit eace71
}