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