summaryrefslogtreecommitdiff
path: root/usr.sbin/iscsid/session.c
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2011-04-27 07:25:27 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2011-04-27 07:25:27 +0000
commit070b7216e0f98eb3ac9240505497233f5ba40f8e (patch)
treeddf733e28572928620a50284416ef8280a683d62 /usr.sbin/iscsid/session.c
parent157ba4c6c2044e8df0dbd0428dae409dc8995a58 (diff)
Start implementing the FSM. Introduce a session FSM that is run via a
callback and implement some of the connection FSM actions. Implement logouts so that discovery sessions do a nice login -> query -> logout. Fix the task scheduling especially for immediate and connection specific tasks. The session will now only schedule tasks to a session that is in LOGGED_IN state. looks good dlg@
Diffstat (limited to 'usr.sbin/iscsid/session.c')
-rw-r--r--usr.sbin/iscsid/session.c155
1 files changed, 148 insertions, 7 deletions
diff --git a/usr.sbin/iscsid/session.c b/usr.sbin/iscsid/session.c
index 5527f9a2668..ad570f47503 100644
--- a/usr.sbin/iscsid/session.c
+++ b/usr.sbin/iscsid/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.1 2011/04/05 18:26:19 claudio Exp $ */
+/* $OpenBSD: session.c,v 1.2 2011/04/27 07:25:26 claudio Exp $ */
/*
* Copyright (c) 2011 Claudio Jeker <claudio@openbsd.org>
@@ -33,6 +33,13 @@
#include "iscsid.h"
#include "log.h"
+void session_fsm_callback(int, short, void *);
+int sess_do_start(struct session *, struct sessev *);
+int sess_do_fail(struct session *, struct sessev *);
+
+const char *sess_state(int);
+const char *sess_event(enum s_event);
+
struct session *
session_find(struct initiator *i, char *name)
{
@@ -59,7 +66,7 @@ session_new(struct initiator *i, u_int8_t st)
s->cmdseqnum = arc4random();
s->itt = arc4random();
s->initiator = i;
- s->state = SESS_FREE;
+ s->state = SESS_INIT;
if (st == SESSION_TYPE_DISCOVERY)
s->target = 0;
@@ -69,12 +76,14 @@ session_new(struct initiator *i, u_int8_t st)
TAILQ_INSERT_HEAD(&i->sessions, s, entry);
TAILQ_INIT(&s->connections);
TAILQ_INIT(&s->tasks);
+ SIMPLEQ_INIT(&s->fsmq);
+ evtimer_set(&s->fsm_ev, session_fsm_callback, s);
return s;
}
void
-session_close(struct session *s)
+session_cleanup(struct session *s)
{
struct connection *c;
@@ -131,10 +140,142 @@ session_schedule(struct session *s)
/* wake up a idle connection or a not busy one */
/* XXX this needs more work as it makes the daemon go wrooOOOMM */
- TAILQ_REMOVE(&s->tasks, t, entry);
TAILQ_FOREACH(c, &s->connections, entry)
- if (conn_task_issue(c, t))
+ if (conn_task_ready(c)) {
+ TAILQ_REMOVE(&s->tasks, t, entry);
+ conn_task_issue(c, t);
return;
- /* all connections are busy readd task to the head */
- TAILQ_INSERT_HEAD(&s->tasks, t, entry);
+ }
+}
+
+/*
+ * The session FSM runs from a callback so that the connection FSM can finish.
+ */
+void
+session_fsm(struct session *s, enum s_event ev, struct connection *c)
+{
+ struct timeval tv;
+ struct sessev *sev;
+
+ if ((sev = malloc(sizeof(*sev))) == NULL)
+ fatal("session_fsm");
+ sev->conn = c;
+ sev->event = ev;
+ SIMPLEQ_INSERT_TAIL(&s->fsmq, sev, entry);
+
+ timerclear(&tv);
+ if (evtimer_add(&s->fsm_ev, &tv) == -1)
+ fatal("session_fsm");
+}
+
+struct {
+ int state;
+ enum s_event event;
+ int (*action)(struct session *, struct sessev *);
+} s_fsm[] = {
+ { SESS_INIT, SESS_EV_START, sess_do_start },
+ { SESS_FREE, SESS_EV_CONN_FAIL, sess_do_fail },
+ { SESS_FREE, SESS_EV_CONN_CLOSED, sess_do_fail },
+ { 0, 0, NULL }
+};
+
+/* ARGSUSED */
+void
+session_fsm_callback(int fd, short event, void *arg)
+{
+ struct session *s = arg;
+ struct sessev *sev;
+ int i, ns;
+
+ while ((sev = SIMPLEQ_FIRST(&s->fsmq))) {
+ SIMPLEQ_REMOVE_HEAD(&s->fsmq, entry);
+ for (i = 0; s_fsm[i].action != NULL; i++) {
+ if (s->state & s_fsm[i].state &&
+ sev->event == s_fsm[i].event) {
+ log_debug("sess_fsm[%s]: %s ev %s",
+ s->config.SessionName, sess_state(s->state),
+ sess_event(sev->event));
+ ns = s_fsm[i].action(s, sev);
+ if (ns == -1)
+ /* XXX better please */
+ fatalx("sess_fsm: action failed");
+ log_debug("sess_fsm[%s]: new state %s",
+ s->config.SessionName,
+ sess_state(ns));
+ s->state = ns;
+ break;
+ }
+ }
+ if (s_fsm[i].action == NULL) {
+ log_warnx("sess_fsm[%s]: unhandled state transition "
+ "[%s, %s]", s->config.SessionName,
+ sess_state(s->state), sess_event(sev->event));
+ fatalx("bjork bjork bjork");
+ }
+ free(sev);
+ }
+}
+
+int
+sess_do_start(struct session *s, struct sessev *sev)
+{
+ log_debug("new connection to %s",
+ log_sockaddr(&s->config.connection.TargetAddr));
+ conn_new(s, &s->config.connection);
+
+ return (SESS_FREE);
+}
+
+int
+sess_do_fail(struct session *s, struct sessev *sev)
+{
+ /*
+ * cleanup connections:
+ * Connections in state FREE can be removed.
+ * Connections in any error state will cause the session to enter
+ * the FAILED state. If no sessions are left and the session was
+ * not already FREE then explicit recovery needs to be done.
+ */
+ return (SESS_FREE);
+}
+
+const char *
+sess_state(int s)
+{
+ static char buf[15];
+
+ switch (s) {
+ case SESS_INIT:
+ return "INIT";
+ case SESS_FREE:
+ return "FREE";
+ case SESS_LOGGED_IN:
+ return "LOGGED_IN";
+ case SESS_FAILED:
+ return "FAILED";
+ default:
+ snprintf(buf, sizeof(buf), "UKNWN %x", s);
+ return buf;
+ }
+ /* NOTREACHED */
+}
+
+const char *
+sess_event(enum s_event e)
+{
+ static char buf[15];
+
+ switch (e) {
+ case SESS_EV_START:
+ return "start";
+ case SESS_EV_CONN_FAIL:
+ return "connection fail";
+ case SESS_EV_CONN_CLOSED:
+ return "connection closed";
+ case SESS_EV_FAIL:
+ return "fail";
+ }
+
+ snprintf(buf, sizeof(buf), "UKNWN %d", e);
+ return buf;
}