diff options
Diffstat (limited to 'src/osmo-bsc/chan_alloc.c')
-rw-r--r-- | src/osmo-bsc/chan_alloc.c | 582 |
1 files changed, 7 insertions, 575 deletions
diff --git a/src/osmo-bsc/chan_alloc.c b/src/osmo-bsc/chan_alloc.c index c73ecb74b..9c434145f 100644 --- a/src/osmo-bsc/chan_alloc.c +++ b/src/osmo-bsc/chan_alloc.c @@ -31,576 +31,12 @@ #include <osmocom/bsc/abis_rsl.h> #include <osmocom/bsc/debug.h> #include <osmocom/bsc/signal.h> +#include <osmocom/bsc/timeslot_fsm.h> +#include <osmocom/bsc/lchan_fsm.h> #include <osmocom/bsc/gsm_04_08_rr.h> #include <osmocom/core/talloc.h> -bool ts_is_usable(const struct gsm_bts_trx_ts *ts) -{ - if (!trx_is_usable(ts->trx)) { - LOGP(DRLL, LOGL_DEBUG, "%s not usable\n", gsm_trx_name(ts->trx)); - return false; - } - - /* If a TCH/F_PDCH TS is busy changing, it is already taken or not - * yet available. */ - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) { - if (ts->flags & TS_F_PDCH_PENDING_MASK) { - LOGP(DRLL, LOGL_DEBUG, "%s in switchover, not available\n", - gsm_ts_and_pchan_name(ts)); - return false; - } - } - - /* If a dynamic channel is busy changing, it is already taken or not - * yet available. */ - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { - if (ts->dyn.pchan_is != ts->dyn.pchan_want) { - LOGP(DRLL, LOGL_DEBUG, "%s in switchover, not available\n", - gsm_ts_and_pchan_name(ts)); - return false; - } - } - - return true; -} - -static int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx_ts *ts; - int j, ss; - int count = 0; - - if (!trx_is_usable(trx)) - return 0; - - for (j = 0; j < ARRAY_SIZE(trx->ts); j++) { - enum gsm_phys_chan_config ts_pchan_is; - ts = &trx->ts[j]; - if (!ts_is_usable(ts)) - continue; - - ts_pchan_is = ts_pchan(ts); - - if (ts_pchan_is == GSM_PCHAN_PDCH) { - /* Dynamic timeslots in PDCH mode will become TCH if needed. */ - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_PDCH: - if (pchan == GSM_PCHAN_TCH_F) - count++; - continue; - - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - if (pchan == GSM_PCHAN_TCH_F) - count++; - else if (pchan == GSM_PCHAN_TCH_H) - count += 2; - continue; - - default: - /* Not dynamic, not applicable. */ - continue; - } - } - - if (ts_pchan_is != pchan) - continue; - /* check if all sub-slots are allocated yet */ - for (ss = 0; ss < ts_subslots(ts); ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->type == GSM_LCHAN_NONE && - lc->state == LCHAN_S_NONE) - count++; - } - } - - return count; -} - -/* Count number of free TS of given pchan type */ -int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx *trx; - int count = 0; - - llist_for_each_entry(trx, &bts->trx_list, list) - count += trx_count_free_ts(trx, pchan); - - return count; -} - -static bool ts_usable_as_pchan(struct gsm_bts_trx_ts *ts, - enum gsm_phys_chan_config as_pchan) -{ - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_PDCH: - if (ts->flags & TS_F_PDCH_PENDING_MASK) { - /* currently being switched over. Not usable. */ - return false; - } - switch (as_pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_PDCH: - /* continue to check below. */ - break; - default: - return false; - } - break; - - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - if (ts->dyn.pchan_is != ts->dyn.pchan_want) { - /* currently being switched over. Not usable. */ - return false; - } - switch (as_pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - case GSM_PCHAN_PDCH: - /* continue to check below. */ - break; - default: - return false; - } - break; - - default: - /* static timeslots never switch. */ - return ts->pchan == as_pchan; - } - - /* Dynamic timeslots -- Checks depending on the current actual pchan mode: */ - switch (ts_pchan(ts)) { - case GSM_PCHAN_NONE: - /* Not initialized, possibly because GPRS was disabled. We may switch. */ - return true; - - case GSM_PCHAN_PDCH: - /* This slot is in PDCH mode and available to switch pchan mode. But check for - * error states: */ - if (ts->lchan->state != LCHAN_S_NONE && ts->lchan->state != LCHAN_S_ACTIVE) - return false; - return true; - - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - /* No need to switch at all? */ - if (ts_pchan(ts) == as_pchan) - return true; - - /* If any lchan is in use, we can't change the pchan kind */ - { - int ss; - int subslots = ts_subslots(ts); - for (ss = 0; ss < subslots; ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->type != GSM_LCHAN_NONE || lc->state != LCHAN_S_NONE) - return false; - } - } - return true; - - default: - /* Not implemented. */ - return false; - } -} - -static struct gsm_lchan * -_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan, - enum gsm_phys_chan_config as_pchan) -{ - struct gsm_bts_trx_ts *ts; - int j, start, stop, dir, ss; - int check_subslots; - -#define LOGPLCHANALLOC(fmt, args...) \ - LOGP(DRLL, LOGL_DEBUG, "looking for lchan %s as %s: " fmt, \ - gsm_pchan_name(pchan), gsm_pchan_name(as_pchan), ## args) - - if (!trx_is_usable(trx)) { - LOGPLCHANALLOC("%s trx not usable\n", gsm_trx_name(trx)); - return NULL; - } - - if (trx->bts->chan_alloc_reverse) { - /* check TS 7..0 */ - start = 7; - stop = -1; - dir = -1; - } else { - /* check TS 0..7 */ - start = 0; - stop = 8; - dir = 1; - } - - for (j = start; j != stop; j += dir) { - ts = &trx->ts[j]; - if (!ts_is_usable(ts)) - continue; - /* The caller first selects what kind of TS to search in, e.g. looking for exact - * GSM_PCHAN_TCH_F, or maybe among dynamic GSM_PCHAN_TCH_F_TCH_H_PDCH... */ - if (ts->pchan != pchan) { - LOGPLCHANALLOC("%s is != %s\n", gsm_ts_and_pchan_name(ts), - gsm_pchan_name(pchan)); - continue; - } - /* Next, is this timeslot in or can it be switched to the pchan we want to use it for? */ - if (!ts_usable_as_pchan(ts, as_pchan)) { - LOGPLCHANALLOC("%s is not usable as %s\n", gsm_ts_and_pchan_name(ts), - gsm_pchan_name(as_pchan)); - continue; - } - /* If we need to switch it, after above check we are also allowed to switch it, and we - * will always use the first lchan after the switch. Return that lchan and rely on the - * caller to perform the pchan switchover. */ - if (ts_pchan(ts) != as_pchan) { - LOGPLCHANALLOC("%s is a match, will switch to %s\n", gsm_ts_and_pchan_name(ts), - gsm_pchan_name(as_pchan)); - return ts->lchan; - } - - /* TS is in desired pchan mode. Go ahead and check for an available lchan. */ - check_subslots = ts_subslots(ts); - for (ss = 0; ss < check_subslots; ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->type == GSM_LCHAN_NONE && - lc->state == LCHAN_S_NONE) { - LOGPLCHANALLOC("%s ss=%d is available\n", gsm_ts_and_pchan_name(ts), - lc->nr); - return lc; - } - LOGPLCHANALLOC("%s ss=%d in type=%s,state=%s not suitable\n", - gsm_ts_and_pchan_name(ts), lc->nr, gsm_lchant_name(lc->type), - gsm_lchans_name(lc->state)); - } - } - - return NULL; -#undef LOGPLCHANALLOC -} - -static struct gsm_lchan * -_lc_dyn_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan, - enum gsm_phys_chan_config dyn_as_pchan) -{ - struct gsm_bts_trx *trx; - struct gsm_lchan *lc; - - if (bts->chan_alloc_reverse) { - llist_for_each_entry_reverse(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan, dyn_as_pchan); - if (lc) - return lc; - } - } else { - llist_for_each_entry(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan, dyn_as_pchan); - if (lc) - return lc; - } - } - - return NULL; -} - -static struct gsm_lchan * -_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) -{ - return _lc_dyn_find_bts(bts, pchan, pchan); -} - -/* Allocate a logical channel. - * - * Dynamic channel types: we always prefer a dedicated TS, and only pick + - * switch a dynamic TS if no pure TS of the requested PCHAN is available. - * - * TCH_F/PDCH: if we pick a PDCH ACT style dynamic TS as TCH/F channel, PDCH - * will be disabled in rsl_chan_activate_lchan(); there is no need to check - * whether PDCH mode is currently active, here. - */ -struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, - int allow_bigger) -{ - struct gsm_lchan *lchan = NULL; - enum gsm_phys_chan_config first, first_cbch, second, second_cbch; - - LOGP(DRLL, LOGL_DEBUG, "(bts=%d) lchan_alloc(%s)\n", bts->nr, gsm_lchant_name(type)); - - switch (type) { - case GSM_LCHAN_SDCCH: - if (bts->chan_alloc_reverse) { - first = GSM_PCHAN_SDCCH8_SACCH8C; - first_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; - second = GSM_PCHAN_CCCH_SDCCH4; - second_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; - } else { - first = GSM_PCHAN_CCCH_SDCCH4; - first_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; - second = GSM_PCHAN_SDCCH8_SACCH8C; - second_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; - } - - lchan = _lc_find_bts(bts, first); - if (lchan == NULL) - lchan = _lc_find_bts(bts, first_cbch); - if (lchan == NULL) - lchan = _lc_find_bts(bts, second); - if (lchan == NULL) - lchan = _lc_find_bts(bts, second_cbch); - - /* allow to assign bigger channels */ - if (allow_bigger) { - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - - /* try dynamic TCH/F_PDCH */ - if (lchan == NULL) { - lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_PDCH, - GSM_PCHAN_TCH_F); - /* TCH/F_PDCH will be used as TCH/F */ - if (lchan) - type = GSM_LCHAN_TCH_F; - } - - /* try fully dynamic TCH/F_TCH/H_PDCH */ - if (lchan == NULL) { - lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - /* - * No need to check fully dynamic channels for TCH/F: - * if no TCH/H was available, neither will be TCH/F. - */ - } - break; - case GSM_LCHAN_TCH_F: - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - /* If we don't have TCH/F available, fall-back to TCH/H */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - /* If we don't have TCH/H either, try dynamic TCH/F_PDCH */ - if (!lchan) { - lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_PDCH, - GSM_PCHAN_TCH_F); - /* TCH/F_PDCH used as TCH/F -- here, type is already - * set to GSM_LCHAN_TCH_F, but for clarity's sake... */ - if (lchan) - type = GSM_LCHAN_TCH_F; - } - - /* Try fully dynamic TCH/F_TCH/H_PDCH as TCH/F... */ - if (!lchan && bts->network->dyn_ts_allow_tch_f) { - lchan = _lc_dyn_find_bts(bts, - GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - /* ...and as TCH/H. */ - if (!lchan) { - lchan = _lc_dyn_find_bts(bts, - GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - break; - case GSM_LCHAN_TCH_H: - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - /* If we don't have TCH/H available, fall-back to TCH/F */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - /* No dedicated TCH/x available -- try fully dynamic - * TCH/F_TCH/H_PDCH */ - if (!lchan) { - lchan = _lc_dyn_find_bts(bts, - GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - /* - * No need to check TCH/F_TCH/H_PDCH channels for TCH/F: - * if no TCH/H was available, neither will be TCH/F. - */ - /* If we don't have TCH/F either, try dynamic TCH/F_PDCH */ - if (!lchan) { - lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_PDCH, - GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - break; - default: - LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); - } - - if (lchan) { - lchan->type = type; - - LOGP(DRLL, LOGL_INFO, "%s Allocating lchan=%u as %s\n", - gsm_ts_and_pchan_name(lchan->ts), - lchan->nr, gsm_lchant_name(lchan->type)); - - /* reset measurement report counter and index */ - lchan->meas_rep_count = 0; - lchan->meas_rep_idx = 0; - lchan->meas_rep_last_seen_nr = 255; - - /* clear sapis */ - memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); - - /* clear multi rate config */ - memset(&lchan->mr_ms_lv, 0, sizeof(lchan->mr_ms_lv)); - memset(&lchan->mr_bts_lv, 0, sizeof(lchan->mr_bts_lv)); - lchan->broken_reason = ""; - } else { - struct challoc_signal_data sig; - - LOGP(DRLL, LOGL_ERROR, "(bts=%d) Failed to allocate %s channel\n", - bts->nr, gsm_lchant_name(type)); - - sig.bts = bts; - sig.type = type; - osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig); - } - - return lchan; -} - -/* Free a logical channel */ -void lchan_free(struct gsm_lchan *lchan) -{ - struct challoc_signal_data sig; - int i; - - sig.type = lchan->type; - lchan->type = GSM_LCHAN_NONE; - - - if (lchan->conn - && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && lchan->ts->dyn.pchan_is != lchan->ts->dyn.pchan_want)) { - struct lchan_signal_data sig; - - /* We might kill an active channel... */ - sig.lchan = lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig); - } - - /* stop the timer */ - osmo_timer_del(&lchan->T3101); - - /* clear cached measuement reports */ - lchan->meas_rep_idx = 0; - for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) { - lchan->meas_rep[i].flags = 0; - lchan->meas_rep[i].nr = 0; - } - for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) - lchan->neigh_meas[i].arfcn = 0; - - if (lchan->rqd_ref) { - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - } - - sig.lchan = lchan; - sig.bts = lchan->ts->trx->bts; - osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_FREED, &sig); - - if (lchan->conn - && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && lchan->ts->dyn.pchan_is != lchan->ts->dyn.pchan_want)) { - LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n"); - lchan->conn = NULL; - } - - /* FIXME: ts_free() the timeslot, if we're the last logical - * channel using it */ - - /* reset RTP voice connection related data */ - memset(&lchan->abis_ip, 0, sizeof(lchan->abis_ip)); -} - -/* - * There was an error with the TRX and we need to forget - * any state so that a lchan can be allocated again after - * the trx is fully usable. - * - * This should be called after lchan_free to force a channel - * be available for allocation again. This means that this - * method will stop the "delay after error"-timer and set the - * state to LCHAN_S_NONE. - */ -void lchan_reset(struct gsm_lchan *lchan) -{ - osmo_timer_del(&lchan->T3101); - osmo_timer_del(&lchan->T3109); - osmo_timer_del(&lchan->T3111); - osmo_timer_del(&lchan->error_timer); - - lchan->type = GSM_LCHAN_NONE; - rsl_lchan_set_state(lchan, LCHAN_S_NONE); -} - -/* Drive the release process of the lchan */ -static void _lchan_handle_release(struct gsm_lchan *lchan, - int sacch_deact, int mode) -{ - /* Release all SAPIs on the local end and continue */ - rsl_release_sapis_from(lchan, 1, RSL_REL_LOCAL_END); - - /* - * Shall we send a RR Release, start T3109 and wait for the - * release indication from the BTS or just take it down (e.g. - * on assignment requests) - */ - if (sacch_deact) { - gsm48_send_rr_release(lchan); - - /* Deactivate the SACCH on the BTS side */ - rsl_deact_sacch(lchan); - rsl_start_t3109(lchan); - } else if (lchan->sapis[0] == LCHAN_SAPI_UNUSED) { - rsl_direct_rf_release(lchan); - } else { - rsl_release_request(lchan, 0, mode); - } -} - -/* Consider releasing the channel now */ -int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode mode) -{ - DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan)); - rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); - - lchan->conn = NULL; - _lchan_handle_release(lchan, sacch_deact, mode); - return 1; -} - void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) { struct gsm_bts_trx *trx; @@ -615,22 +51,18 @@ void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; - struct load_counter *pl = &cl->pchan[ts->pchan]; - int j; - int subslots; + struct load_counter *pl = &cl->pchan[ts->pchan_on_init]; + struct gsm_lchan *lchan; /* skip administratively deactivated timeslots */ if (!nm_is_running(&ts->mo.nm_state)) continue; - subslots = ts_subslots(ts); - for (j = 0; j < subslots; j++) { - struct gsm_lchan *lchan = &ts->lchan[j]; - + ts_for_each_lchan(lchan, ts) { pl->total++; - switch (lchan->state) { - case LCHAN_S_NONE: + switch (lchan->fi->state) { + case LCHAN_ST_UNUSED: break; default: pl->used++; |