aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libmsc/subscr_conn.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2016-06-19 18:06:02 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2017-06-18 17:46:32 +0200
commit5fd3a2cd21d90027391f14466f737837dd6e6674 (patch)
tree64df9f678640465b060470600c9f2ad7b51e4eab /openbsc/src/libmsc/subscr_conn.c
parent9e1721f332d283cc8832d6584cff8297b41101c7 (diff)
Use libvlr in libmsc (large refactoring)
Original libvlr code is by Harald Welte <laforge@gnumonks.org>, polished and tweaked by Neels Hofmeyr <nhofmeyr@sysmocom.de>. This is a long series of development collapsed in one patch. The original history may still be available as branch neels/vlr_orig. TODO: This commit may be split in several smaller changes before merging to master. SMS: The SQL based lookup of SMS for attached subscribers no longer works since the SQL database no longer has the subscriber data. Replace with a round-robin on the SMS recipient MSISDNs paired with a VLR subscriber RAM lookup whether the subscriber is currently attached. If there are many SMS for not-attached subscribers in the SMS database, this will become inefficient: a DB hit returns a pending SMS, the RAM lookup will reveal that the subscriber is not attached, after which the DB is hit for the next SMS. It would become more efficient e.g. by having an MSISDN based hash list for the VLR subscribers and by marking non-attached SMS recipients in the SMS database so that they can be excluded with the SQL query already. There is a sanity limit to do at most 100 db hits per attempt to find a pending SMS. So if there are more than 100 stored SMS waiting for their recipients to actually attach to the MSC, it may take more than one SMS queue trigger to deliver SMS for subscribers that are actually attached. This is not very beautiful, but is merely intended to carry us over to a time when we have a proper separate SMSC entity. Introduce gsm_subscriber_connection ref-counting in libmsc. Related: OS#1592 Change-Id: I702ba504ce2de93507312c28eca8d11f09f4ee8b
Diffstat (limited to 'openbsc/src/libmsc/subscr_conn.c')
-rw-r--r--openbsc/src/libmsc/subscr_conn.c195
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);