From 700e518a6b2f073a6874df112653e6e64d7d0a66 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Wed, 19 Dec 2018 17:19:02 +0100 Subject: make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc --- src/osmo-bsc/assignment_fsm.c | 2 +- src/osmo-bsc/bsc_subscr_conn_fsm.c | 2 +- src/osmo-bsc/handover_fsm.c | 6 +- src/osmo-bsc/lchan_fsm.c | 142 +++++++++++++++++++------------------ src/osmo-bsc/lchan_rtp_fsm.c | 26 +++---- src/osmo-bsc/osmo_bsc_bssap.c | 2 +- 6 files changed, 91 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c index 93362f85b..a24f7f904 100644 --- a/src/osmo-bsc/assignment_fsm.c +++ b/src/osmo-bsc/assignment_fsm.c @@ -385,7 +385,7 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts .s15_s0 = req->s15_s0, .requires_voice_stream = conn->assignment.requires_voice_stream, .msc_assigned_cic = req->msc_assigned_cic, - .old_lchan = conn->lchan, + .re_use_mgw_endpoint_from_lchan = conn->lchan, }; lchan_activate(conn->assignment.new_lchan, &info); } diff --git a/src/osmo-bsc/bsc_subscr_conn_fsm.c b/src/osmo-bsc/bsc_subscr_conn_fsm.c index 85e754fa4..c1ac6ebf4 100644 --- a/src/osmo-bsc/bsc_subscr_conn_fsm.c +++ b/src/osmo-bsc/bsc_subscr_conn_fsm.c @@ -550,7 +550,7 @@ bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn, } else verb = MGCP_VERB_CRCX; - gscon_ensure_mgw_endpoint(conn, for_lchan->activate.msc_assigned_cic); + gscon_ensure_mgw_endpoint(conn, for_lchan->activate.info.msc_assigned_cic); if (!conn->user_plane.mgw_endpoint) { LOGPFSML(conn->fi, LOGL_ERROR, "Unable to allocate endpoint info\n"); diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c index aae50b96a..35f2e5553 100644 --- a/src/osmo-bsc/handover_fsm.c +++ b/src/osmo-bsc/handover_fsm.c @@ -360,7 +360,7 @@ static void handover_start_intra_bsc(struct gsm_subscriber_connection *conn) .chan_mode = conn->lchan->tch_mode, .requires_voice_stream = conn->lchan->mgw_endpoint_ci_bts ? true : false, .msc_assigned_cic = conn->ho.inter_bsc_in.msc_assigned_cic, - .old_lchan = conn->lchan, + .re_use_mgw_endpoint_from_lchan = conn->lchan, .wait_before_switching_rtp = true, }; @@ -706,7 +706,7 @@ static void send_handover_performed(struct gsm_subscriber_connection *conn) ho_perf_params.chosen_encr_alg = lchan->encr.alg_id; ho_perf_params.chosen_encr_alg_present = true; - if (ho->new_lchan->activate.requires_voice_stream) { + if (ho->new_lchan->activate.info.requires_voice_stream) { /* Speech Version (chosen) 3.2.2.51 */ ho_perf_params.speech_version_chosen = gsm0808_permitted_speech(lchan->type, lchan->tch_mode); ho_perf_params.speech_version_chosen_present = true; @@ -1010,7 +1010,7 @@ static void ho_fsm_post_lchan_established(struct osmo_fsm_inst *fi) struct gsm_subscriber_connection *conn = ho_fi_conn(fi); struct handover *ho = &conn->ho; - if (ho->new_lchan->activate.requires_voice_stream + if (ho->new_lchan->activate.info.requires_voice_stream && (ho->scope & HO_INTER_BSC_IN)) ho_fsm_state_chg(HO_ST_WAIT_MGW_ENDPOINT_TO_MSC); else diff --git a/src/osmo-bsc/lchan_fsm.c b/src/osmo-bsc/lchan_fsm.c index c87302587..fc1bcbf0c 100644 --- a/src/osmo-bsc/lchan_fsm.c +++ b/src/osmo-bsc/lchan_fsm.c @@ -151,7 +151,7 @@ static void lchan_on_fully_established(struct gsm_lchan *lchan) return; lchan->activate.concluded = true; - switch (lchan->activate.activ_for) { + switch (lchan->activate.info.activ_for) { case FOR_MS_CHANNEL_REQUEST: /* No signalling to do here, MS is free to use the channel, and should go on to connect * to the MSC and establish a subscriber connection. */ @@ -201,7 +201,7 @@ static void lchan_on_fully_established(struct gsm_lchan *lchan) default: LOG_LCHAN(lchan, LOGL_NOTICE, "lchan %s fully established\n", - lchan_activate_mode_name(lchan->activate.activ_for)); + lchan_activate_mode_name(lchan->activate.info.activ_for)); break; } } @@ -238,7 +238,7 @@ struct state_timeout lchan_fsm_timeouts[32] = { lchan_set_last_error(_lchan, "lchan %s in state %s: " fmt, \ _lchan->activate.concluded ? "failure" : "allocation failed", \ osmo_fsm_state_name(fsm, state_was), ## args); \ - lchan_on_activation_failure(_lchan, _lchan->activate.activ_for, _lchan->conn); \ + lchan_on_activation_failure(_lchan, _lchan->activate.info.activ_for, _lchan->conn); \ if (fi->state != state_chg) \ lchan_fsm_state_chg(state_chg); \ else \ @@ -481,8 +481,6 @@ static void lchan_fsm_unused(struct osmo_fsm_inst *fi, uint32_t event, void *dat { struct lchan_activate_info *info = data; struct gsm_lchan *lchan = lchan_fi_lchan(fi); - struct gsm_bts *bts = lchan->ts->trx->bts; - struct gsm48_multi_rate_conf mr_conf; switch (event) { @@ -493,63 +491,8 @@ static void lchan_fsm_unused(struct osmo_fsm_inst *fi, uint32_t event, void *dat lchan_set_last_error(lchan, NULL); lchan->release.requested = false; - lchan->conn = info->for_conn; - lchan->activate.activ_for = info->activ_for; - lchan->activate.requires_voice_stream = info->requires_voice_stream; - lchan->activate.wait_before_switching_rtp = info->wait_before_switching_rtp; - lchan->activate.msc_assigned_cic = info->msc_assigned_cic; + lchan->activate.info = *info; lchan->activate.concluded = false; - lchan->activate.re_use_mgw_endpoint_from_lchan = info->old_lchan; - - if (info->old_lchan) - lchan->encr = info->old_lchan->encr; - else { - lchan->encr = (struct gsm_encr){ - .alg_id = RSL_ENC_ALG_A5(0), /* no encryption */ - }; - } - - /* If there is a previous lchan, and the new lchan is on the same cell as previous one, - * take over power and TA values. Otherwise, use max power and zero TA. */ - if (info->old_lchan && info->old_lchan->ts->trx->bts == bts) { - lchan->ms_power = info->old_lchan->ms_power; - lchan->bs_power = info->old_lchan->bs_power; - lchan->rqd_ta = info->old_lchan->rqd_ta; - } else { - lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); - /* From lchan_reset(): - * - bs_power is still zero, 0dB reduction, output power = Pn. - * - TA is still zero, to be determined by RACH. */ - } - - if (info->chan_mode == GSM48_CMODE_SPEECH_AMR) { - gsm48_mr_cfg_from_gsm0808_sc_cfg(&mr_conf, info->s15_s0); - if (lchan_mr_config(lchan, &mr_conf) < 0) { - lchan_fail("Can not generate multirate configuration IE\n"); - return; - } - } - - switch (info->chan_mode) { - - case GSM48_CMODE_SIGN: - lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; - lchan->tch_mode = GSM48_CMODE_SIGN; - break; - - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_EFR: - case GSM48_CMODE_SPEECH_AMR: - lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - lchan->tch_mode = info->chan_mode; - break; - - default: - lchan_fail("Not implemented: cannot activate for chan mode %s", - gsm48_chan_mode_name(info->chan_mode)); - return; - } - lchan_fsm_state_chg(LCHAN_ST_WAIT_TS_READY); break; @@ -561,18 +504,75 @@ static void lchan_fsm_unused(struct osmo_fsm_inst *fi, uint32_t event, void *dat static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { struct gsm_lchan *lchan = lchan_fi_lchan(fi); - struct mgwep_ci *use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan); + struct gsm48_multi_rate_conf mr_conf; + struct gsm_bts *bts = lchan->ts->trx->bts; + struct mgwep_ci *use_mgwep_ci; + struct gsm_lchan *old_lchan = lchan->activate.info.re_use_mgw_endpoint_from_lchan; + struct lchan_activate_info *info = &lchan->activate.info; if (lchan->release.requested) { lchan_fail("Release requested while activating"); return; } + lchan->conn = info->for_conn; + + if (old_lchan) + lchan->encr = old_lchan->encr; + else { + lchan->encr = (struct gsm_encr){ + .alg_id = RSL_ENC_ALG_A5(0), /* no encryption */ + }; + } + + /* If there is a previous lchan, and the new lchan is on the same cell as previous one, + * take over power and TA values. Otherwise, use max power and zero TA. */ + if (old_lchan && old_lchan->ts->trx->bts == bts) { + lchan->ms_power = old_lchan->ms_power; + lchan->bs_power = old_lchan->bs_power; + lchan->rqd_ta = old_lchan->rqd_ta; + } else { + lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); + /* From lchan_reset(): + * - bs_power is still zero, 0dB reduction, output power = Pn. + * - TA is still zero, to be determined by RACH. */ + } + + if (info->chan_mode == GSM48_CMODE_SPEECH_AMR) { + gsm48_mr_cfg_from_gsm0808_sc_cfg(&mr_conf, info->s15_s0); + if (lchan_mr_config(lchan, &mr_conf) < 0) { + lchan_fail("Can not generate multirate configuration IE\n"); + return; + } + } + + switch (info->chan_mode) { + + case GSM48_CMODE_SIGN: + lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; + lchan->tch_mode = GSM48_CMODE_SIGN; + break; + + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_EFR: + case GSM48_CMODE_SPEECH_AMR: + lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; + lchan->tch_mode = info->chan_mode; + break; + + default: + lchan_fail("Not implemented: cannot activate for chan mode %s", + gsm48_chan_mode_name(info->chan_mode)); + return; + } + + use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan); + LOG_LCHAN(lchan, LOGL_INFO, "Activation requested: %s voice=%s MGW-ci=%s type=%s tch-mode=%s\n", - lchan_activate_mode_name(lchan->activate.activ_for), - lchan->activate.requires_voice_stream ? "yes" : "no", - lchan->activate.requires_voice_stream ? + lchan_activate_mode_name(lchan->activate.info.activ_for), + lchan->activate.info.requires_voice_stream ? "yes" : "no", + lchan->activate.info.requires_voice_stream ? (use_mgwep_ci ? mgwep_ci_name(use_mgwep_ci) : "new") : "none", gsm_lchant_name(lchan->type), @@ -583,7 +583,7 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p osmo_fsm_inst_dispatch(lchan->ts->fi, TS_EV_LCHAN_REQUESTED, lchan); /* Prepare an MGW endpoint CI if appropriate. */ - if (lchan->activate.requires_voice_stream) + if (lchan->activate.info.requires_voice_stream) lchan_rtp_fsm_start(lchan); } @@ -627,7 +627,7 @@ static void lchan_fsm_wait_activ_ack_onenter(struct osmo_fsm_inst *fi, uint32_t return; } - switch (lchan->activate.activ_for) { + switch (lchan->activate.info.activ_for) { case FOR_MS_CHANNEL_REQUEST: act_type = RSL_ACT_INTRA_IMM_ASS; break; @@ -709,7 +709,7 @@ static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi) return; } - switch (lchan->activate.activ_for) { + switch (lchan->activate.info.activ_for) { case FOR_MS_CHANNEL_REQUEST: rc = rsl_tx_imm_assignment(lchan); @@ -764,7 +764,7 @@ static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi) default: LOG_LCHAN(lchan, LOGL_NOTICE, "lchan %s is now active\n", - lchan_activate_mode_name(lchan->activate.activ_for)); + lchan_activate_mode_name(lchan->activate.info.activ_for)); break; } @@ -784,7 +784,7 @@ static void lchan_fsm_wait_rll_rtp_establish(struct osmo_fsm_inst *fi, uint32_t switch (event) { case LCHAN_EV_RLL_ESTABLISH_IND: - if (!lchan->activate.requires_voice_stream + if (!lchan->activate.info.requires_voice_stream || lchan_rtp_established(lchan)) lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED); return; @@ -1381,6 +1381,8 @@ void lchan_forget_conn(struct gsm_lchan *lchan) if (!lchan) return; + lchan->activate.info.for_conn = NULL; + conn = lchan->conn; if (conn) { /* Log for both lchan FSM and conn FSM to ease reading the log in case of problems */ diff --git a/src/osmo-bsc/lchan_rtp_fsm.c b/src/osmo-bsc/lchan_rtp_fsm.c index 6ab3da4d9..84cc28729 100644 --- a/src/osmo-bsc/lchan_rtp_fsm.c +++ b/src/osmo-bsc/lchan_rtp_fsm.c @@ -111,9 +111,9 @@ void lchan_rtp_fsm_start(struct gsm_lchan *lchan) /* Use old lchan only if there is an MGW endpoint present. Otherwise, on ROLLBACK, we might put * an endpoint "back" to an lchan that never had one to begin with. */ - if (lchan->activate.re_use_mgw_endpoint_from_lchan - && !lchan->activate.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts) - lchan->activate.re_use_mgw_endpoint_from_lchan = NULL; + if (lchan->activate.info.re_use_mgw_endpoint_from_lchan + && !lchan->activate.info.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts) + lchan->activate.info.re_use_mgw_endpoint_from_lchan = NULL; lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE); } @@ -127,8 +127,8 @@ struct mgwep_ci *lchan_use_mgw_endpoint_ci_bts(struct gsm_lchan *lchan) return lchan->mgw_endpoint_ci_bts; if (lchan_state_is(lchan, LCHAN_ST_ESTABLISHED)) return NULL; - if (lchan->activate.re_use_mgw_endpoint_from_lchan) - return lchan->activate.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts; + if (lchan->activate.info.re_use_mgw_endpoint_from_lchan) + return lchan->activate.info.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts; return NULL; } @@ -147,7 +147,7 @@ static void lchan_rtp_fsm_wait_mgw_endpoint_available_onenter(struct osmo_fsm_in return; } - mgwep = gscon_ensure_mgw_endpoint(lchan->conn, lchan->activate.msc_assigned_cic); + mgwep = gscon_ensure_mgw_endpoint(lchan->conn, lchan->activate.info.msc_assigned_cic); if (!mgwep) { lchan_rtp_fail("Internal error: cannot obtain MGW endpoint handle for conn"); return; @@ -232,7 +232,7 @@ static void lchan_rtp_fsm_switch_rtp(struct osmo_fsm_inst *fi) { struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi); - if (lchan->activate.wait_before_switching_rtp) { + if (lchan->activate.info.wait_before_switching_rtp) { LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "Waiting for an event by caller before switching RTP\n"); lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP); @@ -305,7 +305,7 @@ static void lchan_rtp_fsm_wait_ipacc_crcx_ack(struct osmo_fsm_inst *fi, uint32_t return; case LCHAN_RTP_EV_READY_TO_SWITCH_RTP: - lchan->activate.wait_before_switching_rtp = false; + lchan->activate.info.wait_before_switching_rtp = false; return; case LCHAN_RTP_EV_RELEASE: @@ -365,7 +365,7 @@ static void lchan_rtp_fsm_wait_ipacc_mdcx_ack(struct osmo_fsm_inst *fi, uint32_t return; case LCHAN_RTP_EV_READY_TO_SWITCH_RTP: - lchan->activate.wait_before_switching_rtp = false; + lchan->activate.info.wait_before_switching_rtp = false; return; case LCHAN_RTP_EV_RELEASE: @@ -437,7 +437,7 @@ static void lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter(struct osmo_fsm_i uint32_t prev_state) { struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi); - struct gsm_lchan *old_lchan = lchan->activate.re_use_mgw_endpoint_from_lchan; + struct gsm_lchan *old_lchan = lchan->activate.info.re_use_mgw_endpoint_from_lchan; if (lchan->release.requested) { lchan_rtp_fail("Release requested while activating"); @@ -514,7 +514,7 @@ static void lchan_rtp_fsm_ready(struct osmo_fsm_inst *fi, uint32_t event, void * static void lchan_rtp_fsm_rollback_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi); - struct gsm_lchan *old_lchan = lchan->activate.re_use_mgw_endpoint_from_lchan; + struct gsm_lchan *old_lchan = lchan->activate.info.re_use_mgw_endpoint_from_lchan; if (!lchan->mgw_endpoint_ci_bts || !old_lchan) { osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0); @@ -526,7 +526,7 @@ static void lchan_rtp_fsm_rollback_onenter(struct osmo_fsm_inst *fi, uint32_t pr static void lchan_rtp_fsm_rollback(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi); - struct gsm_lchan *old_lchan = lchan->activate.re_use_mgw_endpoint_from_lchan; + struct gsm_lchan *old_lchan = lchan->activate.info.re_use_mgw_endpoint_from_lchan; switch (event) { @@ -560,7 +560,7 @@ static void lchan_rtp_fsm_established_onenter(struct osmo_fsm_inst *fi, uint32_t struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi); /* Make sure that we will not hand back the MGW endpoint to any old lchan from here on. */ - lchan->activate.re_use_mgw_endpoint_from_lchan = NULL; + lchan->activate.info.re_use_mgw_endpoint_from_lchan = NULL; } static void lchan_rtp_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void *data) diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c index 60ec5fbf2..f2ccf2cf1 100644 --- a/src/osmo-bsc/osmo_bsc_bssap.c +++ b/src/osmo-bsc/osmo_bsc_bssap.c @@ -1101,7 +1101,7 @@ enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection }; /* speech_codec_chosen */ - if (ho->new_lchan->activate.requires_voice_stream && gscon_is_aoip(conn)) { + if (ho->new_lchan->activate.info.requires_voice_stream && gscon_is_aoip(conn)) { int perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode); params.speech_codec_chosen_present = true; rc = gsm0808_speech_codec_from_chan_type(¶ms.speech_codec_chosen, perm_spch); -- cgit v1.2.3