Skip to content

Commit

Permalink
[SCSI] iscsi class, libiscsi: add iscsi sysfs session state file
Browse files Browse the repository at this point in the history
This adds a iscsi session state file which exports the session
state for both software and hardware iscsi. It also hooks libiscsi
in.

Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
  • Loading branch information
Mike Christie authored and James Bottomley committed Feb 8, 2008
1 parent 84ac86c commit 6eabafb
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 33 deletions.
41 changes: 26 additions & 15 deletions drivers/scsi/libiscsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,7 @@ enum {
FAILURE_SESSION_IN_RECOVERY,
FAILURE_SESSION_RECOVERY_TIMEOUT,
FAILURE_SESSION_LOGGING_OUT,
FAILURE_SESSION_NOT_READY,
};

int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
Expand All @@ -1016,6 +1017,12 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
session = iscsi_hostdata(host->hostdata);
spin_lock(&session->lock);

reason = iscsi_session_chkready(session_to_cls(session));
if (reason) {
sc->result = reason;
goto fault;
}

/*
* ISCSI_STATE_FAILED is a temp. state. The recovery
* code will decide what is best to do with command queued
Expand All @@ -1032,25 +1039,31 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
switch (session->state) {
case ISCSI_STATE_IN_RECOVERY:
reason = FAILURE_SESSION_IN_RECOVERY;
goto reject;
sc->result = DID_IMM_RETRY << 16;
break;
case ISCSI_STATE_LOGGING_OUT:
reason = FAILURE_SESSION_LOGGING_OUT;
goto reject;
sc->result = DID_IMM_RETRY << 16;
break;
case ISCSI_STATE_RECOVERY_FAILED:
reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
sc->result = DID_NO_CONNECT << 16;
break;
case ISCSI_STATE_TERMINATE:
reason = FAILURE_SESSION_TERMINATE;
sc->result = DID_NO_CONNECT << 16;
break;
default:
reason = FAILURE_SESSION_FREED;
sc->result = DID_NO_CONNECT << 16;
}
goto fault;
}

conn = session->leadconn;
if (!conn) {
reason = FAILURE_SESSION_FREED;
sc->result = DID_NO_CONNECT << 16;
goto fault;
}

Expand Down Expand Up @@ -1090,9 +1103,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))

fault:
spin_unlock(&session->lock);
printk(KERN_ERR "iscsi: cmd 0x%x is not queued (%d)\n",
sc->cmnd[0], reason);
sc->result = (DID_NO_CONNECT << 16);
debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
scsi_set_resid(sc, scsi_bufflen(sc));
sc->scsi_done(sc);
spin_lock(host->host_lock);
Expand Down Expand Up @@ -1238,7 +1249,8 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
* Fail commands. session lock held and recv side suspended and xmit
* thread flushed
*/
static void fail_all_commands(struct iscsi_conn *conn, unsigned lun)
static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
int error)
{
struct iscsi_cmd_task *ctask, *tmp;

Expand All @@ -1250,15 +1262,15 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun)
if (lun == ctask->sc->device->lun || lun == -1) {
debug_scsi("failing pending sc %p itt 0x%x\n",
ctask->sc, ctask->itt);
fail_command(conn, ctask, DID_BUS_BUSY << 16);
fail_command(conn, ctask, error << 16);
}
}

list_for_each_entry_safe(ctask, tmp, &conn->requeue, running) {
if (lun == ctask->sc->device->lun || lun == -1) {
debug_scsi("failing requeued sc %p itt 0x%x\n",
ctask->sc, ctask->itt);
fail_command(conn, ctask, DID_BUS_BUSY << 16);
fail_command(conn, ctask, error << 16);
}
}

Expand Down Expand Up @@ -1572,7 +1584,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
/* need to grab the recv lock then session lock */
write_lock_bh(conn->recv_lock);
spin_lock(&session->lock);
fail_all_commands(conn, sc->device->lun);
fail_all_commands(conn, sc->device->lun, DID_ERROR);
conn->tmf_state = TMF_INITIAL;
spin_unlock(&session->lock);
write_unlock_bh(conn->recv_lock);
Expand Down Expand Up @@ -2018,11 +2030,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
conn->stop_stage = 0;
conn->tmf_state = TMF_INITIAL;
session->age++;
spin_unlock_bh(&session->lock);

iscsi_unblock_session(session_to_cls(session));
wake_up(&conn->ehwait);
return 0;
break;
case STOP_CONN_TERM:
conn->stop_stage = 0;
break;
Expand All @@ -2031,6 +2039,8 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
}
spin_unlock_bh(&session->lock);

iscsi_unblock_session(session_to_cls(session));
wake_up(&conn->ehwait);
return 0;
}
EXPORT_SYMBOL_GPL(iscsi_conn_start);
Expand Down Expand Up @@ -2122,7 +2132,8 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
* flush queues.
*/
spin_lock_bh(&session->lock);
fail_all_commands(conn, -1);
fail_all_commands(conn, -1,
STOP_CONN_RECOVER ? DID_BUS_BUSY : DID_ERROR);
flush_control_queues(session, conn);
spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
Expand Down
107 changes: 104 additions & 3 deletions drivers/scsi/scsi_transport_iscsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/iscsi_if.h>

#define ISCSI_SESSION_ATTRS 18
#define ISCSI_SESSION_ATTRS 19
#define ISCSI_CONN_ATTRS 11
#define ISCSI_HOST_ATTRS 4
#define ISCSI_TRANSPORT_VERSION "2.0-867"
Expand Down Expand Up @@ -221,6 +221,54 @@ static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid)
* The following functions can be used by LLDs that allocate
* their own scsi_hosts or by software iscsi LLDs
*/
static struct {
int value;
char *name;
} iscsi_session_state_names[] = {
{ ISCSI_SESSION_LOGGED_IN, "LOGGED_IN" },
{ ISCSI_SESSION_FAILED, "FAILED" },
{ ISCSI_SESSION_FREE, "FREE" },
};

const char *iscsi_session_state_name(int state)
{
int i;
char *name = NULL;

for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) {
if (iscsi_session_state_names[i].value == state) {
name = iscsi_session_state_names[i].name;
break;
}
}
return name;
}

int iscsi_session_chkready(struct iscsi_cls_session *session)
{
unsigned long flags;
int err;

spin_lock_irqsave(&session->lock, flags);
switch (session->state) {
case ISCSI_SESSION_LOGGED_IN:
err = 0;
break;
case ISCSI_SESSION_FAILED:
err = DID_IMM_RETRY << 16;
break;
case ISCSI_SESSION_FREE:
err = DID_NO_CONNECT << 16;
break;
default:
err = DID_NO_CONNECT << 16;
break;
}
spin_unlock_irqrestore(&session->lock, flags);
return err;
}
EXPORT_SYMBOL_GPL(iscsi_session_chkready);

static void iscsi_session_release(struct device *dev)
{
struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
Expand Down Expand Up @@ -259,26 +307,57 @@ static void session_recovery_timedout(struct work_struct *work)
struct iscsi_cls_session *session =
container_of(work, struct iscsi_cls_session,
recovery_work.work);
unsigned long flags;

dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed "
"out after %d secs\n", session->recovery_tmo);

spin_lock_irqsave(&session->lock, flags);
switch (session->state) {
case ISCSI_SESSION_FAILED:
session->state = ISCSI_SESSION_FREE;
break;
case ISCSI_SESSION_LOGGED_IN:
case ISCSI_SESSION_FREE:
/* we raced with the unblock's flush */
spin_unlock_irqrestore(&session->lock, flags);
return;
}
spin_unlock_irqrestore(&session->lock, flags);

if (session->transport->session_recovery_timedout)
session->transport->session_recovery_timedout(session);

scsi_target_unblock(&session->dev);
}

void iscsi_unblock_session(struct iscsi_cls_session *session)
void __iscsi_unblock_session(struct iscsi_cls_session *session)
{
if (!cancel_delayed_work(&session->recovery_work))
flush_workqueue(iscsi_eh_timer_workq);
scsi_target_unblock(&session->dev);
}

void iscsi_unblock_session(struct iscsi_cls_session *session)
{
unsigned long flags;

spin_lock_irqsave(&session->lock, flags);
session->state = ISCSI_SESSION_LOGGED_IN;
spin_unlock_irqrestore(&session->lock, flags);

__iscsi_unblock_session(session);
}
EXPORT_SYMBOL_GPL(iscsi_unblock_session);

void iscsi_block_session(struct iscsi_cls_session *session)
{
unsigned long flags;

spin_lock_irqsave(&session->lock, flags);
session->state = ISCSI_SESSION_FAILED;
spin_unlock_irqrestore(&session->lock, flags);

scsi_target_block(&session->dev);
queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
session->recovery_tmo * HZ);
Expand Down Expand Up @@ -327,10 +406,12 @@ iscsi_alloc_session(struct Scsi_Host *shost,

session->transport = transport;
session->recovery_tmo = 120;
session->state = ISCSI_SESSION_FREE;
INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
INIT_LIST_HEAD(&session->host_list);
INIT_LIST_HEAD(&session->sess_list);
INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
spin_lock_init(&session->lock);

/* this is released in the dev's release function */
scsi_host_get(shost);
Expand Down Expand Up @@ -444,7 +525,10 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
* If we are blocked let commands flow again. The lld or iscsi
* layer should set up the queuecommand to fail commands.
*/
iscsi_unblock_session(session);
spin_lock_irqsave(&session->lock, flags);
session->state = ISCSI_SESSION_FREE;
spin_unlock_irqrestore(&session->lock, flags);
__iscsi_unblock_session(session);
iscsi_unbind_session(session);
/*
* If the session dropped while removing devices then we need to make
Expand Down Expand Up @@ -661,16 +745,23 @@ EXPORT_SYMBOL_GPL(iscsi_recv_pdu);

void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error)
{
struct iscsi_cls_session *session = iscsi_conn_to_session(conn);
struct nlmsghdr *nlh;
struct sk_buff *skb;
struct iscsi_uevent *ev;
struct iscsi_internal *priv;
int len = NLMSG_SPACE(sizeof(*ev));
unsigned long flags;

priv = iscsi_if_transport_lookup(conn->transport);
if (!priv)
return;

spin_lock_irqsave(&session->lock, flags);
if (session->state == ISCSI_SESSION_LOGGED_IN)
session->state = ISCSI_SESSION_FAILED;
spin_unlock_irqrestore(&session->lock, flags);

skb = alloc_skb(len, GFP_ATOMIC);
if (!skb) {
dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored "
Expand Down Expand Up @@ -1246,6 +1337,15 @@ iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);

static ssize_t
show_priv_session_state(struct class_device *cdev, char *buf)
{
struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);
return sprintf(buf, "%s\n", iscsi_session_state_name(session->state));
}
static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state,
NULL);

#define iscsi_priv_session_attr_show(field, format) \
static ssize_t \
show_priv_session_##field(struct class_device *cdev, char *buf) \
Expand Down Expand Up @@ -1472,6 +1572,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
SETUP_PRIV_SESSION_RD_ATTR(state);

BUG_ON(count > ISCSI_SESSION_ATTRS);
priv->session_attrs[count] = NULL;
Expand Down
19 changes: 19 additions & 0 deletions include/scsi/libiscsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ static inline void* iscsi_next_hdr(struct iscsi_cmd_task *ctask)
return (void*)ctask->hdr + ctask->hdr_len;
}

/* Connection's states */
enum {
ISCSI_CONN_INITIAL_STAGE,
ISCSI_CONN_STARTED,
ISCSI_CONN_STOPPED,
ISCSI_CONN_CLEANUP_WAIT,
};

struct iscsi_conn {
struct iscsi_cls_conn *cls_conn; /* ptr to class connection */
void *dd_data; /* iscsi_transport data */
Expand Down Expand Up @@ -227,6 +235,17 @@ struct iscsi_pool {
int max; /* Max number of elements */
};

/* Session's states */
enum {
ISCSI_STATE_FREE = 1,
ISCSI_STATE_LOGGED_IN,
ISCSI_STATE_FAILED,
ISCSI_STATE_TERMINATE,
ISCSI_STATE_IN_RECOVERY,
ISCSI_STATE_RECOVERY_FAILED,
ISCSI_STATE_LOGGING_OUT,
};

struct iscsi_session {
/*
* Syncs up the scsi eh thread with the iscsi eh thread when sending
Expand Down
Loading

0 comments on commit 6eabafb

Please sign in to comment.