aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmsc/subscr_conn.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmsc/subscr_conn.c')
-rw-r--r--src/libmsc/subscr_conn.c191
1 files changed, 127 insertions, 64 deletions
diff --git a/src/libmsc/subscr_conn.c b/src/libmsc/subscr_conn.c
index 91ffe406..b28a5112 100644
--- a/src/libmsc/subscr_conn.c
+++ b/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,68 @@ 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)
{
- struct gsm_subscriber_connection *conn = fi->priv;
- if (!conn)
+ switch (event) {
+ case SUBSCR_CONN_E_COMMUNICATING:
+ /* no-op */
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));
+ 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;
+
+ conn->conn_fsm = NULL;
+ msc_subscr_conn_close(conn, cause);
+ 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 +236,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 +271,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 +286,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 +301,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 +309,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);