diff options
Diffstat (limited to 'openbsc/src/libmsc/subscr_conn.c')
-rw-r--r-- | openbsc/src/libmsc/subscr_conn.c | 195 |
1 files changed, 132 insertions, 63 deletions
diff --git a/openbsc/src/libmsc/subscr_conn.c b/openbsc/src/libmsc/subscr_conn.c index 91ffe4069..9be53cf4a 100644 --- a/openbsc/src/libmsc/subscr_conn.c +++ b/openbsc/src/libmsc/subscr_conn.c @@ -23,19 +23,24 @@ #include <osmocom/core/logging.h> #include <osmocom/core/fsm.h> +#include <osmocom/core/signal.h> #include <openbsc/osmo_msc.h> #include <openbsc/vlr.h> #include <openbsc/debug.h> #include <openbsc/transaction.h> +#include <openbsc/signal.h> + +#define SUBSCR_CONN_TIMEOUT 5 /* seconds */ static const struct value_string subscr_conn_fsm_event_names[] = { OSMO_VALUE_STRING(SUBSCR_CONN_E_INVALID), + OSMO_VALUE_STRING(SUBSCR_CONN_E_START), OSMO_VALUE_STRING(SUBSCR_CONN_E_ACCEPTED), + OSMO_VALUE_STRING(SUBSCR_CONN_E_COMMUNICATING), OSMO_VALUE_STRING(SUBSCR_CONN_E_BUMP), OSMO_VALUE_STRING(SUBSCR_CONN_E_MO_CLOSE), OSMO_VALUE_STRING(SUBSCR_CONN_E_CN_CLOSE), - OSMO_VALUE_STRING(SUBSCR_CONN_E_CLOSE_CONF), { 0, NULL } }; @@ -50,14 +55,21 @@ const struct value_string subscr_conn_from_names[] = { static void paging_resp(struct gsm_subscriber_connection *conn, enum gsm_paging_event pe) { - subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->subscr); + subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->vsub); +} + +void subscr_conn_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + OSMO_ASSERT(event == SUBSCR_CONN_E_START); + osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_NEW, + SUBSCR_CONN_TIMEOUT, 0); } void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; enum subscr_conn_from from = SUBSCR_CONN_FROM_INVALID; - enum gsm_paging_event pe; + bool success; if (data) { from = *(enum subscr_conn_from*)data; @@ -67,12 +79,12 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) /* If accepted, transition the state, all other cases mean failure. */ switch (event) { case SUBSCR_CONN_E_ACCEPTED: - osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, 0, 0); + osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, + SUBSCR_CONN_TIMEOUT, 0); break; case SUBSCR_CONN_E_MO_CLOSE: case SUBSCR_CONN_E_CN_CLOSE: - case SUBSCR_CONN_E_CLOSE_CONF: break; default: @@ -81,23 +93,27 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) break; } - /* if appropriate, signal paging success or failure */ - if (from == SUBSCR_CONN_FROM_PAGING_RESP) { - pe = (fi->state == SUBSCR_CONN_S_ACCEPTED)? - GSM_PAGING_SUCCEEDED : GSM_PAGING_EXPIRED; - paging_resp(conn, pe); - } + success = (fi->state == SUBSCR_CONN_S_ACCEPTED); + + if (from == SUBSCR_CONN_FROM_LU) + rate_ctr_inc(&conn->network->msc_ctrs->ctr[ + success ? MSC_CTR_LOC_UPDATE_COMPLETED + : MSC_CTR_LOC_UPDATE_FAILED]); + + /* signal paging success or failure in case this was a paging */ + if (from == SUBSCR_CONN_FROM_PAGING_RESP) + paging_resp(conn, + success ? GSM_PAGING_SUCCEEDED + : GSM_PAGING_EXPIRED); /* On failure, discard the conn */ - if (fi->state != SUBSCR_CONN_S_ACCEPTED) { + if (!success) { /* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and * await BSC confirmation? */ osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); return; } - /* On success, handle pending requests and/or close conn */ - if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) { conn->received_cm_service_request = true; LOGPFSM(fi, "received_cm_service_request = true\n"); @@ -106,33 +122,6 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data); } -#if 0 - case SUBSCR_CONN_E_PARQ_SUCCESS: - osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, 0, 0); - accept_conn = true; - /* fall through */ - case SUBSCR_CONN_E_PARQ_FAILURE: - parq_type = data ? *(enum vlr_parq_type*)data : VLR_PR_ARQ_T_INVALID; - switch (parq_type) { - - case VLR_PR_ARQ_T_CM_SERV_REQ: - accept_conn = handle_cm_serv_result(fi, accept_conn); - break; - - case VLR_PR_ARQ_T_PAGING_RESP: - accept_conn = handle_paging_result(fi, accept_conn); - break; - - default: - LOGPFSML(fi, LOGL_ERROR, - "Invalid VLR Process Access Request type" - " %d\n", parq_type); - accept_conn = false; - break; - } - break; -#endif - static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; @@ -143,8 +132,7 @@ static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void if (conn->received_cm_service_request) return; - /* is this needed? */ - if (conn->subscr && !llist_empty(&conn->subscr->requests)) + if (conn->vsub && !llist_empty(&conn->vsub->cs.requests)) return; if (trans_has_conn(conn)) @@ -153,9 +141,19 @@ static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); } +static void subscr_conn_fsm_accepted_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_subscriber_connection *conn = fi->priv; + osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, conn->vsub); +} + static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch (event) { + case SUBSCR_CONN_E_COMMUNICATING: + osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_COMMUNICATING, 0, 0); + return; + case SUBSCR_CONN_E_BUMP: subscr_conn_fsm_bump(fi, event, data); return; @@ -169,34 +167,74 @@ static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, v osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); } -static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state) +static void subscr_conn_fsm_communicating(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case SUBSCR_CONN_E_COMMUNICATING: + /* no-op */ + return; + + case SUBSCR_CONN_E_BUMP: + subscr_conn_fsm_bump(fi, event, data); + return; + + default: + break; + } + /* Whatever unexpected happens in the accepted state, it means release. + * Even if an unexpected event is passed, the safest thing to do is + * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */ + osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); +} + +static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi, + enum osmo_fsm_term_cause cause) { struct gsm_subscriber_connection *conn = fi->priv; + fi->priv = NULL; + if (!conn) return; - /* temporary hack, see owned_by_msc */ - if (!conn->owned_by_msc) { - DEBUGP(DMM, "%s leaving bsc_subscr_con_free() to bsc_api.c, owned_by_msc = false\n", - subscr_name(conn->subscr)); + if (conn->in_release) return; - } + conn->in_release = true; + conn->conn_fsm = NULL; + + /* If we're closing in a middle of a trans, we need to clean up */ + trans_conn_closed(conn); + + msc_subscr_conn_put(conn); +} + +int subscr_conn_fsm_timeout(struct osmo_fsm_inst *fi) +{ + struct gsm_subscriber_connection *conn = fi->priv; + if (conn) + vlr_subscr_conn_timeout(conn->vsub); + osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_CN_CLOSE, NULL); + return 0; +} - DEBUGP(DMM, "%s calling bsc_subscr_con_free(), owned_by_msc = true\n", - subscr_name(conn->subscr)); - gsm0808_clear(conn); - bsc_subscr_con_free(conn); +static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); } #define S(x) (1 << (x)) static const struct osmo_fsm_state subscr_conn_fsm_states[] = { + [SUBSCR_CONN_S_INIT] = { + .name = OSMO_STRINGIFY(SUBSCR_CONN_S_INIT), + .in_event_mask = S(SUBSCR_CONN_E_START), + .out_state_mask = S(SUBSCR_CONN_S_NEW), + .action = subscr_conn_fsm_init, + }, [SUBSCR_CONN_S_NEW] = { .name = OSMO_STRINGIFY(SUBSCR_CONN_S_NEW), .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) | S(SUBSCR_CONN_E_MO_CLOSE) | - S(SUBSCR_CONN_E_CN_CLOSE) | - S(SUBSCR_CONN_E_CLOSE_CONF), + S(SUBSCR_CONN_E_CN_CLOSE), .out_state_mask = S(SUBSCR_CONN_S_ACCEPTED) | S(SUBSCR_CONN_S_RELEASED), .action = subscr_conn_fsm_new, @@ -204,14 +242,27 @@ static const struct osmo_fsm_state subscr_conn_fsm_states[] = { [SUBSCR_CONN_S_ACCEPTED] = { .name = OSMO_STRINGIFY(SUBSCR_CONN_S_ACCEPTED), /* allow everything to release for any odd behavior */ - .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) | - S(SUBSCR_CONN_E_BUMP) | + .in_event_mask = S(SUBSCR_CONN_E_COMMUNICATING) | + S(SUBSCR_CONN_E_BUMP) | + S(SUBSCR_CONN_E_ACCEPTED) | S(SUBSCR_CONN_E_MO_CLOSE) | - S(SUBSCR_CONN_E_CN_CLOSE) | - S(SUBSCR_CONN_E_CLOSE_CONF), - .out_state_mask = S(SUBSCR_CONN_S_RELEASED), + S(SUBSCR_CONN_E_CN_CLOSE), + .out_state_mask = S(SUBSCR_CONN_S_RELEASED) | + S(SUBSCR_CONN_S_COMMUNICATING), + .onenter = subscr_conn_fsm_accepted_enter, .action = subscr_conn_fsm_accepted, }, + [SUBSCR_CONN_S_COMMUNICATING] = { + .name = OSMO_STRINGIFY(SUBSCR_CONN_S_COMMUNICATING), + /* allow everything to release for any odd behavior */ + .in_event_mask = S(SUBSCR_CONN_E_BUMP) | + S(SUBSCR_CONN_E_ACCEPTED) | + S(SUBSCR_CONN_E_COMMUNICATING) | + S(SUBSCR_CONN_E_MO_CLOSE) | + S(SUBSCR_CONN_E_CN_CLOSE), + .out_state_mask = S(SUBSCR_CONN_S_RELEASED), + .action = subscr_conn_fsm_communicating, + }, [SUBSCR_CONN_S_RELEASED] = { .name = OSMO_STRINGIFY(SUBSCR_CONN_S_RELEASED), .onenter = subscr_conn_fsm_release, @@ -226,6 +277,8 @@ static struct osmo_fsm subscr_conn_fsm = { .allstate_action = NULL, .log_subsys = DVLR, .event_names = subscr_conn_fsm_event_names, + .cleanup = subscr_conn_fsm_cleanup, + .timer_cb = subscr_conn_fsm_timeout, }; int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id) @@ -239,7 +292,14 @@ int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id) return -EINVAL; } - fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn, conn, LOGL_DEBUG, id); + /* Allocate the FSM not with the subscr_conn. Semantically it would + * make sense, but in subscr_conn_fsm_cleanup(), we want to discard the + * subscriber connection. If the FSM is freed along with the subscriber + * connection, then in _osmo_fsm_inst_term() the osmo_fsm_inst_free() + * that follows the cleanup() call would run into a double free. */ + fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn->network, + msc_subscr_conn_get(conn), + LOGL_DEBUG, id); if (!fi) { LOGP(DMM, LOGL_ERROR, @@ -247,6 +307,7 @@ int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id) return -ENOMEM; } conn->conn_fsm = fi; + osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_START, NULL); return 0; } @@ -254,15 +315,23 @@ bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn) { if (!conn) return false; - if (!conn->subscr) + if (!conn->vsub) return false; if (!conn->conn_fsm) return false; - if (conn->conn_fsm->state != SUBSCR_CONN_S_ACCEPTED) + if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED + || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING)) return false; return true; } +void msc_subscr_conn_communicating(struct gsm_subscriber_connection *conn) +{ + OSMO_ASSERT(conn); + osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_COMMUNICATING, + NULL); +} + void msc_subscr_conn_init(void) { osmo_fsm_register(&subscr_conn_fsm); |