Blame usr/initiator.c

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