From 070b7216e0f98eb3ac9240505497233f5ba40f8e Mon Sep 17 00:00:00 2001 From: Claudio Jeker Date: Wed, 27 Apr 2011 07:25:27 +0000 Subject: 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@ --- usr.sbin/iscsid/session.c | 155 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 7 deletions(-) (limited to 'usr.sbin/iscsid/session.c') 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 @@ -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; } -- cgit v1.2.3