diff options
author | Kirill Zakharenko <earwin@gmail.com> | 2020-05-08 23:55:58 +0300 |
---|---|---|
committer | Kirill Zakharenko <earwin@gmail.com> | 2020-05-08 23:55:58 +0300 |
commit | 5d7b1ef2868024ff43cd9c729c6b5b0fd86fd80e (patch) | |
tree | ea947d8fc4aef3fcdc3f1a315a79c3deeb4a9887 | |
parent | f5bbe0880ec1c6883ab1f9e8727aec348f51abdf (diff) | |
parent | f103cd38665618442196de888b1e4390fb256ddb (diff) |
Merge fairwaves/WIP-dyn-chan-load into fairwaves/production
-rw-r--r-- | include/osmocom/bsc/bsc_msc_data.h | 27 | ||||
-rw-r--r-- | include/osmocom/bsc/gsm_data.h | 20 | ||||
-rw-r--r-- | src/osmo-bsc/a_reset.c | 41 | ||||
-rw-r--r-- | src/osmo-bsc/abis_om2000.c | 289 | ||||
-rw-r--r-- | src/osmo-bsc/abis_om2000_vty.c | 43 | ||||
-rw-r--r-- | src/osmo-bsc/bsc_init.c | 9 | ||||
-rw-r--r-- | src/osmo-bsc/bsc_vty.c | 1 | ||||
-rw-r--r-- | src/osmo-bsc/bts_ericsson_rbs2000.c | 14 | ||||
-rw-r--r-- | src/osmo-bsc/bts_ipaccess_nanobts.c | 12 | ||||
-rw-r--r-- | src/osmo-bsc/bts_nokia_site.c | 55 | ||||
-rw-r--r-- | src/osmo-bsc/chan_alloc.c | 30 | ||||
-rw-r--r-- | src/osmo-bsc/gsm_data.c | 40 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_bssap.c | 27 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_msc.c | 50 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_sigtran.c | 6 |
15 files changed, 533 insertions, 131 deletions
diff --git a/include/osmocom/bsc/bsc_msc_data.h b/include/osmocom/bsc/bsc_msc_data.h index b9df4ba8f..dc9628bd1 100644 --- a/include/osmocom/bsc/bsc_msc_data.h +++ b/include/osmocom/bsc/bsc_msc_data.h @@ -56,6 +56,29 @@ enum { MSC_CON_TYPE_LOCAL, }; +/* Constants for the MSC rate counters */ +enum { + MSC_CTR_BSSMAP_RX_UDT_RESET_ACKNOWLEDGE, + MSC_CTR_BSSMAP_RX_UDT_RESET, + MSC_CTR_BSSMAP_RX_UDT_PAGING, + MSC_CTR_BSSMAP_RX_UDT_UNKNOWN, + MSC_CTR_BSSMAP_RX_DT1_CLEAR_CMD, + MSC_CTR_BSSMAP_RX_DT1_CIPHER_MODE_CMD, + MSC_CTR_BSSMAP_RX_DT1_ASSIGMENT_RQST, + MSC_CTR_BSSMAP_RX_DT1_LCLS_CONNECT_CTRL, + MSC_CTR_BSSMAP_RX_DT1_HANDOVER_CMD, + MSC_CTR_BSSMAP_RX_DT1_CLASSMARK_RQST, + MSC_CTR_BSSMAP_RX_DT1_UNKNOWN, + MSC_CTR_BSSMAP_RX_DTAP_MSG, + MSC_CTR_BSSMAP_RX_DTAP_ERROR, +}; + +/* Constants for the MSC stats */ +enum { + MSC_STAT_MSC_LINKS_ACTIVE, + MSC_STAT_MSC_LINKS_TOTAL, +}; + /*! /brief Information on a remote MSC for libbsc. */ struct bsc_msc_data { @@ -99,6 +122,10 @@ struct bsc_msc_data { char *acc_lst_name; + /* structures for keeping rate counters and gauge stats */ + struct rate_ctr_group *msc_ctrs; + struct osmo_stat_item_group *msc_statg; + /* Sigtran connection data */ struct { uint32_t cs7_instance; diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h index 9a8e002ea..5e4b532c5 100644 --- a/include/osmocom/bsc/gsm_data.h +++ b/include/osmocom/bsc/gsm_data.h @@ -1134,7 +1134,15 @@ struct gsm_bts { struct om2k_mo om2k_mo; struct gsm_abis_mo mo; } tf; + struct { + struct om2k_mo om2k_mo; + struct gsm_abis_mo mo; + } mctr; uint32_t use_superchannel:1; + struct { + uint16_t limit; + uint16_t active; + } om2k_version[16]; } rbs2000; struct { uint8_t bts_type; @@ -1143,7 +1151,7 @@ struct gsm_bts { no_loc_rel_cnf:1, /* don't wait for RSL REL CONF */ bts_reset_timer_cnf, /* timer for BTS RESET */ did_reset:1, /* we received a RESET ACK */ - wait_reset:1; /* we are waiting for reset to complete */ + wait_reset:2; /* we are waiting for reset to complete */ struct osmo_timer_list reset_timer; } nokia; }; @@ -1464,8 +1472,8 @@ enum { BTS_STAT_T3122, BTS_STAT_RACH_BUSY, BTS_STAT_RACH_ACCESS, - BTS_STAT_NUM_OML_CONNECTED, - BTS_STAT_NUM_RSL_CONNECTED, + BTS_STAT_OML_CONNECTED, + BTS_STAT_RSL_CONNECTED, }; enum { @@ -1556,10 +1564,9 @@ static const struct rate_ctr_group_desc bsc_ctrg_desc = { bsc_ctr_description, }; +/* Constants for the BSC stats */ enum { - BSC_STAT_MSC_LINK, - BSC_STAT_NUM_BTS_CONNECTED, - BSC_STAT_NUM_TRX_CONNECTED, + BSC_STAT_NUM_BTS_TOTAL, }; struct gsm_tz { @@ -1588,6 +1595,7 @@ struct gsm_network { struct osmo_timer_list congestion_check_timer; } hodec2; + /* structures for keeping rate counters and gauge stats */ struct rate_ctr_group *bsc_ctrs; struct osmo_stat_item_group *bsc_statg; diff --git a/src/osmo-bsc/a_reset.c b/src/osmo-bsc/a_reset.c index 9d7be8585..63273e9f2 100644 --- a/src/osmo-bsc/a_reset.c +++ b/src/osmo-bsc/a_reset.c @@ -66,20 +66,28 @@ static const struct value_string fsm_event_names[] = { {0, NULL} }; -/* Disconnected state */ +/* Disconnected state event handler */ static void fsm_disc_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv; OSMO_ASSERT(reset_ctx); - struct bsc_msc_data *msc = reset_ctx->priv; - LOGPFSML(fi, LOGL_NOTICE, "SIGTRAN connection succeeded.\n"); - osmo_stat_item_set(msc->network->bsc_statg->items[BSC_STAT_MSC_LINK], 1); reset_ctx->conn_loss_counter = 0; osmo_fsm_inst_state_chg(fi, ST_CONN, 0, 0); } -/* Connected state */ +/* Called when entering Disconnected state */ +static void fsm_disc_onenter_cb(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv; + struct bsc_msc_data *msc = reset_ctx->priv; + + LOGPFSML(fi, LOGL_NOTICE, "SIGTRAN connection down, reconnecting...\n"); + if (prev_state != ST_DISC) + osmo_stat_item_dec(msc->msc_statg->items[MSC_STAT_MSC_LINKS_ACTIVE], 1); +} + +/* Connected state event handler */ static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv; @@ -87,12 +95,9 @@ static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) switch (event) { case EV_N_DISCONNECT: - if (reset_ctx->conn_loss_counter >= BAD_CONNECTION_THRESOLD) { - struct bsc_msc_data *msc = reset_ctx->priv; - LOGPFSML(fi, LOGL_NOTICE, "SIGTRAN connection down, reconnecting...\n"); - osmo_stat_item_set(msc->network->bsc_statg->items[BSC_STAT_MSC_LINK], 0); + if (reset_ctx->conn_loss_counter >= BAD_CONNECTION_THRESOLD) osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); - } else + else reset_ctx->conn_loss_counter++; break; case EV_N_CONNECT: @@ -101,6 +106,17 @@ static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) } } +/* Called when entering Connected state */ +static void fsm_conn_onenter_cb(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv; + struct bsc_msc_data *msc = reset_ctx->priv; + + LOGPFSML(fi, LOGL_NOTICE, "SIGTRAN connection succeeded.\n"); + if (prev_state != ST_CONN) + osmo_stat_item_inc(msc->msc_statg->items[MSC_STAT_MSC_LINKS_ACTIVE], 1); +} + /* Timer callback to retransmit the reset signal */ static int fsm_reset_ack_timeout_cb(struct osmo_fsm_inst *fi) { @@ -121,12 +137,14 @@ static struct osmo_fsm_state reset_fsm_states[] = { .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), .name = "DISC", .action = fsm_disc_cb, + .onenter = fsm_disc_onenter_cb, }, [ST_CONN] = { .in_event_mask = (1 << EV_N_DISCONNECT) | (1 << EV_N_CONNECT), .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), .name = "CONN", .action = fsm_conn_cb, + .onenter = fsm_conn_onenter_cb, }, }; @@ -165,6 +183,9 @@ void a_reset_alloc(struct bsc_msc_data *msc, const char *name, void *cb) /* Immediately (1ms) kick off reset sending mechanism */ osmo_fsm_inst_state_chg_ms(reset_fsm, ST_DISC, 1, RESET_RESEND_TIMER_NO); + + /* Count the new MSC link */ + osmo_stat_item_inc(msc->msc_statg->items[MSC_STAT_MSC_LINKS_TOTAL], 1); } /* Confirm that we successfully received a reset acknowledge message */ diff --git a/src/osmo-bsc/abis_om2000.c b/src/osmo-bsc/abis_om2000.c index c32a5fa9a..c69655732 100644 --- a/src/osmo-bsc/abis_om2000.c +++ b/src/osmo-bsc/abis_om2000.c @@ -30,6 +30,7 @@ #include <arpa/inet.h> +#include <osmocom/core/byteswap.h> #include <osmocom/core/msgb.h> #include <osmocom/gsm/tlv.h> #include <osmocom/core/talloc.h> @@ -75,6 +76,7 @@ struct osmo_fsm_inst *osmo_fsm_inst_alloc_child_id(struct osmo_fsm *fsm, #define OM_HEADROOM_SIZE 128 #define OM2K_TIMEOUT 10 +#define TRX_LAPD_TIMEOUT 5 #define TRX_FSM_TIMEOUT 60 #define BTS_FSM_TIMEOUT 60 @@ -219,13 +221,17 @@ enum abis_om2k_msgtype { OM2K_MSGT_FEATURE_CTRL_COMPL = 0x011a, OM2K_MSGT_FEATURE_CTRL_REJ = 0x011b, - OM2K_MSGT_MCTR_CONFIG_REQ = 0x012c, - OM2K_MSGT_MCTR_CONFIG_REQ_ACK = 0x012e, - OM2K_MSGT_MCTR_CONFIG_REQ_REJ = 0x012f, + OM2K_MSGT_MCTR_CONF_REQ = 0x012c, + OM2K_MSGT_MCTR_CONF_REQ_ACK = 0x012e, + OM2K_MSGT_MCTR_CONF_REQ_REJ = 0x012f, - OM2K_MSGT_MCTR_CONFIG_RES_ACK = 0x0130, - OM2K_MSGT_MCTR_CONFIG_RES_NACK = 0x0131, - OM2K_MSGT_MCTR_CONFIG_RES = 0x0132, + OM2K_MSGT_MCTR_CONF_RES_ACK = 0x0130, + OM2K_MSGT_MCTR_CONF_RES_NACK = 0x0131, + OM2K_MSGT_MCTR_CONF_RES = 0x0132, + + OM2K_MSGT_MCTR_STATS_REP_ACK = 0x0134, + OM2K_MSGT_MCTR_STATS_REP_NACK = 0x0135, + OM2K_MSGT_MCTR_STATS_REP = 0x0136, }; enum abis_om2k_dei { @@ -305,6 +311,13 @@ enum abis_om2k_dei { OM2K_DEI_MCTR_FEAT_STATUS_BMAP = 0xab, }; +enum abis_om2k_mostate { + OM2K_MOSTATE_RESET = 0x00, + OM2K_MOSTATE_STARTED = 0x01, + OM2K_MOSTATE_ENABLED = 0x02, + OM2K_MOSTATE_DISABLED = 0x03, +}; + const struct tlv_definition om2k_att_tlvdef = { .def = { [OM2K_DEI_ACCORDANCE_IND] = { TLV_TYPE_TV }, @@ -558,6 +571,9 @@ static const struct value_string om2k_msgcode_vals[] = { { 0x0130, "MCTR Configuration Result ACK" }, { 0x0131, "MCTR Configuration Result NACK" }, { 0x0132, "MCTR Configuration Result" }, + { 0x0134, "MCTR Statistics report ACK" }, + { 0x0135, "MCTR Statistics report NACK" }, + { 0x0136, "MCTR Statistics report" }, { 0, NULL } }; @@ -771,7 +787,9 @@ get_om2k_mo(struct gsm_bts *bts, const struct abis_om2k_mo *abis_mo) case OM2K_MO_CLS_TF: mo = &bts->rbs2000.tf.om2k_mo; break; - + case OM2K_MO_CLS_MCTR: + mo = &bts->rbs2000.mctr.om2k_mo; + break; case OM2K_MO_CLS_TRXC: trx = gsm_bts_trx_num(bts, abis_mo->inst); if (!trx) @@ -863,6 +881,9 @@ mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) return NULL; nm_state = &trx->ts[mo->inst].mo.nm_state; break; + case OM2K_MO_CLS_MCTR: + nm_state = &bts->rbs2000.mctr.mo.nm_state; + break; case OM2K_MO_CLS_TF: nm_state = &bts->rbs2000.tf.mo.nm_state; break; @@ -911,6 +932,7 @@ static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo) if (mo->inst >= ARRAY_SIZE(trx->ts)) return NULL; return &trx->ts[mo->inst]; + case OM2K_MO_CLS_MCTR: case OM2K_MO_CLS_TF: case OM2K_MO_CLS_IS: case OM2K_MO_CLS_CON: @@ -928,13 +950,39 @@ static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); struct gsm_nm_state new_state; struct nm_statechg_signal_data nsd; + bool has_enabled_state; if (!nm_state) return; + switch (mo->class) { + case OM2K_MO_CLS_CF: + case OM2K_MO_CLS_TRXC: + has_enabled_state = false; + break; + default: + has_enabled_state = true; + break; + } + new_state = *nm_state; - /* NOTICE: 12.21 Availability state values != OM2000 */ - new_state.availability = mo_state; + switch (mo_state) { + case OM2K_MOSTATE_RESET: + new_state.availability = NM_AVSTATE_POWER_OFF; + break; + case OM2K_MOSTATE_STARTED: + new_state.availability = has_enabled_state ? NM_AVSTATE_OFF_LINE : NM_AVSTATE_OK; + break; + case OM2K_MOSTATE_ENABLED: + new_state.availability = NM_AVSTATE_OK; + break; + case OM2K_MOSTATE_DISABLED: + new_state.availability = NM_AVSTATE_POWER_OFF; + break; + default: + new_state.availability = NM_AVSTATE_DEGRADED; + break; + } memset(&nsd, 0, sizeof(nsd)); @@ -1247,6 +1295,35 @@ int abis_om2k_tx_con_conf_req(struct gsm_bts *bts) return abis_om2k_sendmsg(bts, msg); } +int abis_om2k_tx_mctr_conf_req(struct gsm_bts *bts) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + struct gsm_bts_trx *trx; + uint8_t trxc_list = 0; + const uint8_t features[] = { 0x00 }; + + /* build trxc list */ + llist_for_each_entry(trx, &bts->trx_list, list) + trxc_list |= (1 << trx->nr); + + /* fill message */ + msgb_tv16_put(msg, OM2K_DEI_TRXC_LIST, osmo_swab16(trxc_list)); /* Read as LE by the BTS ... */ + msgb_tv_put (msg, OM2K_DEI_MAX_ALLOWED_POWER, 0x31); + msgb_tv_put (msg, OM2K_DEI_MAX_ALLOWED_NUM_TRXCS, 0x08); + msgb_tlv_put (msg, OM2K_DEI_MCTR_FEAT_STATUS_BMAP, 1, features); + + /* pre-pend the OM2K header */ + o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &bts->rbs2000.mctr.om2k_mo.addr, + OM2K_MSGT_MCTR_CONF_REQ); + DEBUGP(DNM, "Tx MO=%s %s\n", + om2k_mo_name(&bts->rbs2000.mctr.om2k_mo.addr), + get_value_string(om2k_msgcode_vals, OM2K_MSGT_MCTR_CONF_REQ)); + + return abis_om2k_sendmsg(bts, msg); +} + static void om2k_trx_to_mo(struct abis_om2k_mo *mo, const struct gsm_bts_trx *trx, enum abis_om2k_mo_cls cls) @@ -1278,7 +1355,8 @@ int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx) o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &mo, OM2K_MSGT_RX_CONF_REQ); - msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, trx->arfcn); + /* OM2K_DEI_FREQ_SPEC_RX: Using trx_nr as "RX address" only works for single MCTR case */ + msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, 0x8000 | ((uint16_t)trx->nr << 10)); msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ return abis_om2k_sendmsg(trx->bts, msg); @@ -1296,9 +1374,10 @@ int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx) o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TX_CONF_REQ); - msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_TX, trx->arfcn); + /* OM2K_DEI_FREQ_SPEC_TX: Using trx_nr as "TX address" only works for single MCTR case */ + msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_TX, trx->arfcn | ((uint16_t)trx->nr << 10)); msgb_tv_put(msg, OM2K_DEI_POWER, trx->nominal_power-trx->max_power_red); - msgb_tv_put(msg, OM2K_DEI_FILLING_MARKER, 0); /* Filling enabled */ + msgb_tv_put(msg, OM2K_DEI_FILLING_MARKER, trx != trx->bts->c0); /* Filling enabled for C0 only */ msgb_tv_put(msg, OM2K_DEI_BCC, trx->bts->bsic & 0x7); /* Dedication Information is optional */ @@ -1368,15 +1447,44 @@ static uint8_t ts2comb(struct gsm_bts_trx_ts *ts) * message for it. Rather fail completely right now: */ return 0; } - return pchan2comb(ts->pchan_is); + return pchan2comb(ts->pchan_from_config); } -static int put_freq_list(uint8_t *buf, uint16_t arfcn) +static int put_freq_list(uint8_t *buf, struct gsm_bts_trx_ts *ts, uint16_t arfcn) { - buf[0] = 0x00; /* TX/RX address */ + struct gsm_bts_trx *trx; + + /* Find the TRX that's configured for that ARFCN */ + llist_for_each_entry(trx, &ts->trx->bts->trx_list, list) + if (trx->arfcn == arfcn) + break; + + if (!trx || (trx->arfcn != arfcn)) { + LOGP(DNM, LOGL_ERROR, "Trying to use ARFCN %d for hopping with no TRX configured for it", arfcn); + return 0; + } + + /* + * [7:4] - TX address + * This must be the same number that was used when configuring the TX + * MO object with that target arfcn + * + * [3:0] - RX address + * The logical TRX number we're configuring the hopping sequence for + * This must basically match the MO object instance number + * + * ATM since we only support 1 MCTR, we use trx->nr + */ + buf[0] = (trx->nr << 4) | ts->trx->nr; + + /* ARFCN Number */ buf[1] = (arfcn >> 8); buf[2] = (arfcn & 0xff); + /* C0 marker */ + if (trx == trx->bts->c0) + buf[1] |= 0x04; + return 3; } @@ -1390,10 +1498,10 @@ static int om2k_gen_freq_list(uint8_t *list, struct gsm_bts_trx_ts *ts) unsigned int i; for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) - cur += put_freq_list(cur, i); + cur += put_freq_list(cur, ts, i); } } else - cur += put_freq_list(cur, ts->trx->arfcn); + cur += put_freq_list(cur, ts, ts->trx->arfcn); len = cur - list; @@ -1431,7 +1539,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */ /* Optional: Interference Rejection Combining */ msgb_tv_put(msg, OM2K_DEI_INTERF_REJ_COMB, 0x00); - switch (ts->pchan_is) { + switch (ts->pchan_from_config) { case GSM_PCHAN_CCCH: msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); @@ -1475,7 +1583,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, sizeof(icm_bound_params), icm_bound_params); msgb_tv_put(msg, OM2K_DEI_TTA, 10); /* Timer for Time Alignment */ - if (ts->pchan_is == GSM_PCHAN_TCH_H) + if (ts->pchan_from_config == GSM_PCHAN_TCH_H) msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 1); /* TCH/H */ else msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 0); /* TCH/F */ @@ -1670,6 +1778,9 @@ static void om2k_mo_st_wait_start_res(struct osmo_fsm_inst *fi, uint32_t event, case OM2K_MO_CLS_CON: abis_om2k_tx_con_conf_req(omfp->trx->bts); break; + case OM2K_MO_CLS_MCTR: + abis_om2k_tx_mctr_conf_req(omfp->trx->bts); + break; case OM2K_MO_CLS_TX: abis_om2k_tx_tx_conf_req(omfp->trx); break; @@ -1937,6 +2048,7 @@ int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo, case OM2K_MSGT_CON_CONF_REQ_ACK: case OM2K_MSGT_IS_CONF_REQ_ACK: + case OM2K_MSGT_MCTR_CONF_REQ_ACK: case OM2K_MSGT_RX_CONF_REQ_ACK: case OM2K_MSGT_TF_CONF_REQ_ACK: case OM2K_MSGT_TS_CONF_REQ_ACK: @@ -1947,6 +2059,7 @@ int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo, case OM2K_MSGT_CON_CONF_RES: case OM2K_MSGT_IS_CONF_RES: + case OM2K_MSGT_MCTR_CONF_RES: case OM2K_MSGT_RX_CONF_RES: case OM2K_MSGT_TF_CONF_RES: case OM2K_MSGT_TS_CONF_RES: @@ -2012,7 +2125,7 @@ enum om2k_trx_state { struct om2k_trx_fsm_priv { struct gsm_bts_trx *trx; - uint8_t next_ts_nr; + uint8_t cur_ts_nr; }; static void om2k_trx_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2056,8 +2169,8 @@ static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *d /* Initialize Timeslots after TX */ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TS, TRX_FSM_TIMEOUT, 0); - otfp->next_ts_nr = 0; - ts = &otfp->trx->ts[otfp->next_ts_nr++]; + otfp->cur_ts_nr = 0; + ts = &otfp->trx->ts[otfp->cur_ts_nr]; om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, &ts->rbs2000.om2k_mo); } @@ -2067,9 +2180,14 @@ static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *d struct om2k_trx_fsm_priv *otfp = fi->priv; struct gsm_bts_trx_ts *ts; - if (otfp->next_ts_nr < 8) { + /* notify TS is ready */ + ts = &otfp->trx->ts[otfp->cur_ts_nr]; + osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_READY, NULL); + + /* next ? */ + if (++otfp->cur_ts_nr < 8) { /* iterate to the next timeslot */ - ts = &otfp->trx->ts[otfp->next_ts_nr++]; + ts = &otfp->trx->ts[otfp->cur_ts_nr]; om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, &ts->rbs2000.om2k_mo); } else { @@ -2179,6 +2297,8 @@ enum om2k_bts_event { OM2K_BTS_EVT_IS_DONE, OM2K_BTS_EVT_CON_DONE, OM2K_BTS_EVT_TF_DONE, + OM2K_BTS_EVT_MCTR_DONE, + OM2K_BTS_EVT_TRX_LAPD_UP, OM2K_BTS_EVT_TRX_DONE, OM2K_BTS_EVT_STOP, }; @@ -2189,6 +2309,8 @@ static const struct value_string om2k_bts_events[] = { { OM2K_BTS_EVT_IS_DONE, "IS-DONE" }, { OM2K_BTS_EVT_CON_DONE, "CON-DONE" }, { OM2K_BTS_EVT_TF_DONE, "TF-DONE" }, + { OM2K_BTS_EVT_MCTR_DONE, "MCTR-DONE" }, + { OM2K_BTS_EVT_TRX_LAPD_UP, "TRX-LAPD-UP" }, { OM2K_BTS_EVT_TRX_DONE, "TRX-DONE" }, { OM2K_BTS_EVT_STOP, "STOP" }, { 0, NULL } @@ -2200,6 +2322,8 @@ enum om2k_bts_state { OM2K_BTS_S_WAIT_IS, OM2K_BTS_S_WAIT_CON, OM2K_BTS_S_WAIT_TF, + OM2K_BTS_S_WAIT_MCTR, + OM2K_BTS_S_WAIT_TRX_LAPD, OM2K_BTS_S_WAIT_TRX, OM2K_BTS_S_DONE, OM2K_BTS_S_ERROR, @@ -2263,10 +2387,37 @@ static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void * static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts_trx *trx; + struct gsm_bts *bts = obfp->bts; OSMO_ASSERT(event == OM2K_BTS_EVT_IS_DONE); + /* If we're running OML >= G12R13, start MCTR, else skip directly to TRX */ + if (bts->rbs2000.om2k_version[0].active >= 0x0c0d) { + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_MCTR, + BTS_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(fi, OM2K_BTS_EVT_MCTR_DONE, bts->c0, + &bts->rbs2000.mctr.om2k_mo); + } else { + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD, + TRX_LAPD_TIMEOUT, 0); + } +} + +static void om2k_bts_s_wait_mctr(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + OSMO_ASSERT(event == OM2K_BTS_EVT_MCTR_DONE); + + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD, + TRX_LAPD_TIMEOUT, 0); +} + +static void om2k_bts_s_wait_trx_lapd(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_bts_fsm_priv *obfp = fi->priv; + struct gsm_bts_trx *trx; + + OSMO_ASSERT(event == OM2K_BTS_EVT_TRX_LAPD_UP); + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX, BTS_FSM_TIMEOUT, 0); obfp->next_trx_nr = 0; @@ -2325,10 +2476,24 @@ static const struct osmo_fsm_state om2k_bts_states[] = { [OM2K_BTS_S_WAIT_IS] = { .in_event_mask = S(OM2K_BTS_EVT_IS_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_TRX), + S(OM2K_BTS_S_WAIT_MCTR) | + S(OM2K_BTS_S_WAIT_TRX_LAPD), .name = "WAIT-IS", .action = om2k_bts_s_wait_is, }, + [OM2K_BTS_S_WAIT_MCTR] = { + .in_event_mask = S(OM2K_BTS_EVT_MCTR_DONE), + .out_state_mask = S(OM2K_BTS_S_ERROR) | + S(OM2K_BTS_S_WAIT_TRX_LAPD), + .name = "WAIT-MCTR", + .action = om2k_bts_s_wait_mctr, + }, + [OM2K_BTS_S_WAIT_TRX_LAPD] = { + .in_event_mask = S(OM2K_BTS_EVT_TRX_LAPD_UP), + .out_state_mask = S(OM2K_BTS_S_WAIT_TRX), + .name = "WAIT-TRX-LAPD", + .action = om2k_bts_s_wait_trx_lapd, + }, [OM2K_BTS_S_WAIT_TRX] = { .in_event_mask = S(OM2K_BTS_EVT_TRX_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | @@ -2347,7 +2512,14 @@ static const struct osmo_fsm_state om2k_bts_states[] = { static int om2k_bts_timer_cb(struct osmo_fsm_inst *fi) { - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_ERROR, 0, 0); + switch (fi->state) { + case OM2K_BTS_S_WAIT_TRX_LAPD: + osmo_fsm_inst_dispatch(fi, OM2K_BTS_EVT_TRX_LAPD_UP, NULL); + break; + default: + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_ERROR, 0, 0); + break; + } return 0; } @@ -2404,8 +2576,8 @@ static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2 } struct iwd_version { - uint8_t gen_char[3+1]; - uint8_t rev_char[3+1]; + char gen_char[3+1]; + char rev_char[3+1]; }; struct iwd_type { @@ -2416,11 +2588,13 @@ struct iwd_type { static int om2k_rx_negot_req(struct msgb *msg) { struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; + struct gsm_bts *bts = sign_link->trx->bts; struct abis_om2k_hdr *o2h = msgb_l2(msg); struct iwd_type iwd_types[16]; uint8_t num_iwd_types = o2h->data[2]; uint8_t *cur = o2h->data+3; - unsigned int i, v; + unsigned int i; + int v; uint8_t out_buf[1024]; uint8_t *out_cur = out_buf+1; @@ -2451,25 +2625,59 @@ static int om2k_rx_negot_req(struct msgb *msg) /* Select the last version for each IWD type */ for (i = 0; i < ARRAY_SIZE(iwd_types); i++) { struct iwd_type *type = &iwd_types[i]; - struct iwd_version *last_v; + struct iwd_version *sel_v = NULL, *alt_v = NULL; + uint16_t sel_ver, alt_ver = 0; + int gen, rev; if (type->num_vers == 0) continue; out_num_types++; - last_v = &type->v[type->num_vers-1]; + for (v = type->num_vers-1; v >= 0; v--) { + if ((sscanf(type->v[v].gen_char, "G%2d", &gen) != 1) || + (sscanf(type->v[v].rev_char, "R%2d", &rev) != 1)) + continue; + sel_ver = (gen << 8) | rev; + + if (!alt_v) { + alt_ver = sel_ver; + alt_v = &type->v[v]; + } + + if ((bts->rbs2000.om2k_version[i].limit != 0) && + (bts->rbs2000.om2k_version[i].limit < sel_ver)) + continue; + + sel_v = &type->v[v]; + break; + } + if (!sel_v) { + if (!alt_v) { + LOGP(DNM, LOGL_ERROR, "Couldn't find valid version for IWD Type %u." + "Skipping IWD ... this will most likely fail\n", i); + continue; + } else { + sel_v = alt_v; + sel_ver = alt_ver; + LOGP(DNM, LOGL_ERROR, "Couldn't find suitable version for IWD Type %u." + "Fallback to Gen %s Rev %s\n", i, + sel_v->gen_char, sel_v->rev_char); + } + } + + bts->rbs2000.om2k_version[i].active = sel_ver; *out_cur++ = i; - memcpy(out_cur, last_v->gen_char, 3); + memcpy(out_cur, sel_v->gen_char, 3); out_cur += 3; - memcpy(out_cur, last_v->rev_char, 3); + memcpy(out_cur, sel_v->rev_char, 3); out_cur += 3; } out_buf[0] = out_num_types; - return abis_om2k_tx_negot_req_ack(sign_link->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); + return abis_om2k_tx_negot_req_ack(bts, &o2h->mo, out_buf, out_cur - out_buf); } @@ -2713,6 +2921,9 @@ int abis_om2k_rcvmsg(struct msgb *msg) case OM2K_MSGT_CON_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CON_CONF_RES_ACK); break; + case OM2K_MSGT_MCTR_CONF_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_MCTR_CONF_RES_ACK); + break; case OM2K_MSGT_TX_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TX_CONF_RES_ACK); break; @@ -2740,8 +2951,8 @@ int abis_om2k_rcvmsg(struct msgb *msg) case OM2K_MSGT_CAPA_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CAPA_RES_ACK); break; - case 0x0136: /* Unknown ... something for MCTR */ - rc = abis_om2k_tx_simple(bts, &o2h->mo, 0x0134); + case OM2K_MSGT_MCTR_STATS_REP: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_MCTR_STATS_REP_ACK); break; /* ERrors */ case OM2K_MSGT_START_REQ_REJ: @@ -2751,6 +2962,7 @@ int abis_om2k_rcvmsg(struct msgb *msg) case OM2K_MSGT_TEST_REQ_REJ: case OM2K_MSGT_CON_CONF_REQ_REJ: case OM2K_MSGT_IS_CONF_REQ_REJ: + case OM2K_MSGT_MCTR_CONF_REQ_REJ: case OM2K_MSGT_TX_CONF_REQ_REJ: case OM2K_MSGT_RX_CONF_REQ_REJ: case OM2K_MSGT_TS_CONF_REQ_REJ: @@ -2813,7 +3025,6 @@ void abis_om2k_trx_init(struct gsm_bts_trx *trx) om2k_mo_init(&ts->rbs2000.om2k_mo, OM2K_MO_CLS_TS, bts->nr, trx->nr, i); OSMO_ASSERT(ts->fi); - osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_READY, NULL); } } @@ -2832,6 +3043,8 @@ void abis_om2k_bts_init(struct gsm_bts *bts) bts->nr, 0xFF, 0); om2k_mo_init(&bts->rbs2000.tf.om2k_mo, OM2K_MO_CLS_TF, bts->nr, 0xFF, 0); + om2k_mo_init(&bts->rbs2000.mctr.om2k_mo, OM2K_MO_CLS_MCTR, + bts->nr, 0xFF, 0); // FIXME: There can be multiple MCTRs ... } static __attribute__((constructor)) void abis_om2k_init(void) diff --git a/src/osmo-bsc/abis_om2000_vty.c b/src/osmo-bsc/abis_om2000_vty.c index 972fff04a..222546f87 100644 --- a/src/osmo-bsc/abis_om2000_vty.c +++ b/src/osmo-bsc/abis_om2000_vty.c @@ -467,6 +467,40 @@ DEFUN(cfg_bts_alt_mode, cfg_bts_alt_mode_cmd, return CMD_SUCCESS; } +DEFUN(cfg_bts_om2k_version_limit, cfg_bts_om2k_version_limit_cmd, + "om2000 version-limit (oml|rsl) gen <0-99> rev <0-99>", + "Configure OM2K specific parameters\n" + "Configure optional maximum protocol version to negotiate\n" + "Limit OML IWD version\n" "Limit RSL IWD version\n" + "Generation limit\n" + "Generation number to limit to (inclusive)\n" + "Revision limit\n" + "Revision number to limit to (inclusive)\n") +{ + struct gsm_bts *bts = vty->index; + int iwd; + + if (bts->type != GSM_BTS_TYPE_RBS2000) { + vty_out(vty, "%% Command only works for RBS2000%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[0], "oml")) + iwd = 0; + else if (!strcmp(argv[0], "rsl")) + iwd = 1; + else { + vty_out(vty, "%% Invalid IWD%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + bts->rbs2000.om2k_version[iwd].limit = (atoi(argv[1]) << 8) | atoi(argv[2]); + + return CMD_SUCCESS; +} + DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", "Interface Switch Connection List\n" @@ -591,6 +625,7 @@ void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) { struct is_conn_group *igrp; struct con_group *cgrp; + unsigned int i; llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) vty_out(vty, " is-connection-list add %u %u %u%s", @@ -604,6 +639,13 @@ void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) if (bts->rbs2000.use_superchannel) vty_out(vty, " abis-lower-transport super-channel%s", VTY_NEWLINE); + for (i = 0; i < 2; i++) + if (bts->rbs2000.om2k_version[i].limit) + vty_out(vty, " om2000 version-limit %s gen %02d rev %02d%s", + i ? "rsl" : "oml", + (bts->rbs2000.om2k_version[i].limit >> 8), + (bts->rbs2000.om2k_version[i].limit & 0xff), + VTY_NEWLINE); } int abis_om2k_vty_init(void) @@ -631,6 +673,7 @@ int abis_om2k_vty_init(void) install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); install_element(BTS_NODE, &cfg_bts_alt_mode_cmd); + install_element(BTS_NODE, &cfg_bts_om2k_version_limit_cmd); install_element(BTS_NODE, &cfg_om2k_con_group_cmd); install_element(BTS_NODE, &del_om2k_con_group_cmd); diff --git a/src/osmo-bsc/bsc_init.c b/src/osmo-bsc/bsc_init.c index 0d5829622..d08e9c861 100644 --- a/src/osmo-bsc/bsc_init.c +++ b/src/osmo-bsc/bsc_init.c @@ -45,9 +45,7 @@ #include <stdbool.h> static const struct osmo_stat_item_desc bsc_stat_desc[] = { - { "msc_link", "MSC link status.", "", 16, 0 }, - { "num_bts_connected", "Number of currently connected BTS (OML links).", "", 16, 0 }, - { "num_trx_connected", "Number of currently connected TRX (RSL links).", "", 16, 0 }, + { "num_bts:total", "Number of configured BTS for this BSC", "", 16, 0 }, }; static const struct osmo_stat_item_group_desc bsc_statg_desc = { @@ -283,6 +281,11 @@ static struct gsm_network *bsc_network_init(void *ctx) return NULL; } net->bsc_statg = osmo_stat_item_group_alloc(net, &bsc_statg_desc, 0); + if (!net->bsc_statg) { + rate_ctr_group_free(net->bsc_ctrs); + talloc_free(net); + return NULL; + } INIT_LLIST_HEAD(&net->bts_rejected); gsm_net_update_ctype(net); diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c index f8bcee9a7..0fd462f33 100644 --- a/src/osmo-bsc/bsc_vty.c +++ b/src/osmo-bsc/bsc_vty.c @@ -2077,6 +2077,7 @@ DEFUN(cfg_bts, /* allocate a new one */ bts = bsc_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN, HARDCODED_BSIC); + osmo_stat_item_inc(gsmnet->bsc_statg->items[BSC_STAT_NUM_BTS_TOTAL], 1); /* * Initialize bts->acc_ramp here. Else we could segfault while * processing a configuration file with ACC ramping settings. diff --git a/src/osmo-bsc/bts_ericsson_rbs2000.c b/src/osmo-bsc/bts_ericsson_rbs2000.c index 4d1e91b0f..02ef46387 100644 --- a/src/osmo-bsc/bts_ericsson_rbs2000.c +++ b/src/osmo-bsc/bts_ericsson_rbs2000.c @@ -34,11 +34,15 @@ static void bootstrap_om_bts(struct gsm_bts *bts) { + struct gsm_bts_trx *trx; + LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - /* FIXME: this is global init, not bootstrapping */ + /* Global init (not bootstrapping) */ abis_om2k_bts_init(bts); - abis_om2k_trx_init(bts->c0); + + llist_for_each_entry(trx, &bts->trx_list, list) + abis_om2k_trx_init(trx); /* TODO: Should we wait for a Failure report? */ om2k_bts_fsm_start(bts); @@ -149,14 +153,16 @@ static int inp_sig_cb(unsigned int subsys, unsigned int signal, case S_L_INP_LINE_NOALARM: if (strcasecmp(isd->line->driver->name, "DAHDI") && strcasecmp(isd->line->driver->name, "MISDN_LAPD") - && strcasecmp(isd->line->driver->name, "UNIXSOCKET")) + && strcasecmp(isd->line->driver->name, "UNIXSOCKET") + && strcasecmp(isd->line->driver->name, "E1D")) break; start_sabm_in_line(isd->line, 1); break; case S_L_INP_LINE_ALARM: if (strcasecmp(isd->line->driver->name, "DAHDI") && strcasecmp(isd->line->driver->name, "MISDN_LAPD") - && strcasecmp(isd->line->driver->name, "UNIXSOCKET")) + && strcasecmp(isd->line->driver->name, "UNIXSOCKET") + && strcasecmp(isd->line->driver->name, "E1D")) break; start_sabm_in_line(isd->line, 0); break; diff --git a/src/osmo-bsc/bts_ipaccess_nanobts.c b/src/osmo-bsc/bts_ipaccess_nanobts.c index 8873ad4cb..f004c1598 100644 --- a/src/osmo-bsc/bts_ipaccess_nanobts.c +++ b/src/osmo-bsc/bts_ipaccess_nanobts.c @@ -397,8 +397,7 @@ void ipaccess_drop_rsl(struct gsm_bts_trx *trx, const char *reason) LOG_TRX(trx, DLINP, LOGL_NOTICE, "Dropping RSL link: %s\n", reason); e1inp_sign_link_destroy(trx->rsl_link); trx->rsl_link = NULL; - osmo_stat_item_set(trx->bts->bts_statg->items[BTS_STAT_NUM_RSL_CONNECTED], 0); - osmo_stat_item_dec(trx->bts->network->bsc_statg->items[BSC_STAT_NUM_TRX_CONNECTED], 1); + osmo_stat_item_dec(trx->bts->bts_statg->items[BTS_STAT_RSL_CONNECTED], 1); if (trx->bts->c0 == trx) paging_flush_bts(trx->bts, NULL); @@ -419,8 +418,7 @@ void ipaccess_drop_oml(struct gsm_bts *bts, const char *reason) e1inp_sign_link_destroy(bts->oml_link); bts->oml_link = NULL; bts->uptime = 0; - osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_NUM_OML_CONNECTED], 0); - osmo_stat_item_dec(bts->network->bsc_statg->items[BSC_STAT_NUM_BTS_CONNECTED], 1); + osmo_stat_item_dec(bts->bts_statg->items[BTS_STAT_OML_CONNECTED], 1); /* we have issues reconnecting RSL, drop everything. */ llist_for_each_entry(trx, &bts->trx_list, list) @@ -562,8 +560,7 @@ ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line, sign_link->tei, sign_link->sapi); sign_link->trx->bts->ip_access.flags |= OML_UP; } - osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_NUM_OML_CONNECTED], 1); - osmo_stat_item_inc(bts->network->bsc_statg->items[BSC_STAT_NUM_BTS_CONNECTED], 1); + osmo_stat_item_inc(bts->bts_statg->items[BTS_STAT_OML_CONNECTED], 1); break; case E1INP_SIGN_RSL: { struct e1inp_ts *ts; @@ -591,9 +588,8 @@ ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line, sign_link->trx->bts->ip_access.flags |= (RSL_UP << sign_link->trx->nr); } + osmo_stat_item_inc(bts->bts_statg->items[BTS_STAT_RSL_CONNECTED], 1); break; - osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_NUM_RSL_CONNECTED], 1); - osmo_stat_item_inc(bts->network->bsc_statg->items[BSC_STAT_NUM_TRX_CONNECTED], 1); } default: break; diff --git a/src/osmo-bsc/bts_nokia_site.c b/src/osmo-bsc/bts_nokia_site.c index 66972c2b5..cde0afa97 100644 --- a/src/osmo-bsc/bts_nokia_site.c +++ b/src/osmo-bsc/bts_nokia_site.c @@ -41,6 +41,12 @@ #include <osmocom/abis/lapd.h> +enum reset_timer_state { + RESET_T_NONE = 0, + RESET_T_STOP_LAPD = 1, /* first timer expiration: stop LAPD SAP */ + RESET_T_RESTART_LAPD = 2, /* second timer expiration: restart LAPD SAP */ +}; + /* TODO: put in a separate file ? */ extern int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg); @@ -1461,18 +1467,28 @@ static void reset_timer_cb(void *_bts) struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; struct e1inp_line *line; - bts->nokia.wait_reset = 0; - /* OML link */ line = e1inp_line_find(e1_link->e1_nr); if (!line) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); + LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to non-existing E1 line %u\n", + bts->nr, e1_link->e1_nr); return; } - start_sabm_in_line(line, 0, -1); /* stop all first */ - start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */ + switch (bts->nokia.wait_reset) { + case RESET_T_NONE: /* shouldn't happen */ + break; + case RESET_T_STOP_LAPD: + start_sabm_in_line(line, 0, -1); /* stop all first */ + bts->nokia.wait_reset = RESET_T_RESTART_LAPD; + osmo_timer_schedule(&bts->nokia.reset_timer, bts->nokia.bts_reset_timer_cnf, 0); + break; + case RESET_T_RESTART_LAPD: + bts->nokia.wait_reset = 0; + start_sabm_in_line(line, 0, -1); /* stop all first */ + start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */ + break; + } } /* TODO: put in a separate file ? */ @@ -1574,25 +1590,16 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb) (function handle_ts1_read()) and ignoring the received data. It seems to be necessary for the MetroSite too. */ - bts->nokia.wait_reset = 1; - - osmo_timer_setup(&bts->nokia.reset_timer, - reset_timer_cb, bts); - osmo_timer_schedule(&bts->nokia.reset_timer, bts->nokia.bts_reset_timer_cnf, 0); - - struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; - struct e1inp_line *line; - /* OML link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, - "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, - e1_link->e1_nr); - return -ENOMEM; - } - start_sabm_in_line(line, 0, -1); /* stop all first */ + /* we cannot delete / stop the OML LAPD SAP right here, as we are in + * the middle of processing an LAPD I frame and are subsequently returning + * back to the LAPD I frame processing code that assumes the SAP is still + * active. So we first schedule the timer at 0ms in the future, where we + * kill all LAPD SAP and re-arm the timer for the reset duration, after which + * we re-create them */ + bts->nokia.wait_reset = RESET_T_STOP_LAPD; + osmo_timer_setup(&bts->nokia.reset_timer, reset_timer_cb, bts); + osmo_timer_schedule(&bts->nokia.reset_timer, 0, 0); } /* ACK for CONF DATA message ? */ diff --git a/src/osmo-bsc/chan_alloc.c b/src/osmo-bsc/chan_alloc.c index bea16201c..75cea7162 100644 --- a/src/osmo-bsc/chan_alloc.c +++ b/src/osmo-bsc/chan_alloc.c @@ -37,6 +37,7 @@ #include <osmocom/core/talloc.h> +/* Update channel load calculation for the given BTS */ void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) { struct gsm_bts_trx *trx; @@ -58,20 +59,28 @@ void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) if (!nm_is_running(&ts->mo.nm_state)) continue; - /* dynamic timeslots have to be counted separately + /* Dynamic timeslots have to be counted separately * when not in TCH/F or TCH/H mode because they don't - * have an lchan's allocated to them */ - if ( ( ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH - || ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH) - && ( ts->pchan_is == GSM_PCHAN_NONE - || ts->pchan_is == GSM_PCHAN_PDCH)) { + * have an lchan's allocated to them. At the same time, + * dynamic timeslots in NONE and PDCH modes are same + * as in UNUSED mode from the CS channel load perspective + * beause they can be switched to TCH mode at any moment. + * I.e. they are "available" for TCH. */ + if ((ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH || + ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH) && + (ts->pchan_is == GSM_PCHAN_NONE || + ts->pchan_is == GSM_PCHAN_PDCH)) { pl->total++; } - /* count allocated logical channels. - * Note: When GSM_PCHAN_TCH_F_TCH_H_PDCH is allocation - * in TCH/H mode, this leads to changing the total - * count vs the TCH/F allocation */ + /* Count allocated logical channels. + * Note: A GSM_PCHAN_TCH_F_TCH_H_PDCH can be switched + * to a single TCH/F or to two TCH/H. So when it's in + * the TCH/H mode, total number of available channels + * is 1 more than when it's in the TCH/F mode. + * I.e. "total" count will fluctuate depending on + * whether GSM_PCHAN_TCH_F_TCH_H_PDCH timeslot is + * in TCH/F or TCH/H (or in NONE/PDCH) mode. */ ts_for_each_lchan(lchan, ts) { /* don't even count CBCH slots in total */ if (lchan->type == GSM_LCHAN_CBCH) @@ -91,6 +100,7 @@ void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) } } +/* Update channel load calculation for all BTS in the BSC */ void network_chan_load(struct pchan_load *pl, struct gsm_network *net) { struct gsm_bts *bts; diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c index 497e1b836..8b50805e3 100644 --- a/src/osmo-bsc/gsm_data.c +++ b/src/osmo-bsc/gsm_data.c @@ -370,28 +370,28 @@ void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value) } static const struct osmo_stat_item_desc bts_stat_desc[] = { - { "chanloadavg", "Channel load average.", "%", 16, 0 }, - { "chan_ccch_sdcch4_used", "Number of CCCH+SDCCH4 channels used.", "", 16, 0 }, - { "chan_ccch_sdcch4_total", "Number of CCCH+SDCCH4 channels total.", "", 16, 0 }, - { "chan_tch_f_used", "Number of TCH/F channels used.", "", 16, 0 }, - { "chan_tch_f_total", "Number of TCH/F channels total.", "", 16, 0 }, - { "chan_tch_h_used", "Number of TCH/H channels used.", "", 16, 0 }, - { "chan_tch_h_total", "Number of TCH/H channels total.", "", 16, 0 }, - { "chan_sdcch8_used", "Number of SDCCH8 channels used.", "", 16, 0 }, - { "chan_sdcch8_total", "Number of SDCCH8 channels total.", "", 16, 0 }, - { "chan_tch_f_pdch_used", "Number of TCH/F_PDCH channels used.", "", 16, 0 }, - { "chan_tch_f_pdch_total", "Number of TCH/F_PDCH channels total.", "", 16, 0 }, - { "chan_ccch_sdcch4_cbch_used", "Number of CCCH+SDCCH4+CBCH channels used.", "", 16, 0 }, - { "chan_ccch_sdcch4_cbch_total", "Number of CCCH+SDCCH4+CBCH channels total.", "", 16, 0 }, - { "chan_sdcch8_cbch_used", "Number of SDCCH8+CBCH channels used.", "", 16, 0 }, - { "chan_sdcch8_cbch_total", "Number of SDCCH8+CBCH channels total.", "", 16, 0 }, - { "chan_tch_f_tch_h_pdch_used", "Number of TCH/F_TCH/H_PDCH channels used.", "", 16, 0 }, - { "chan_tch_f_tch_h_pdch_total", "Number of TCH/F_TCH/H_PDCH channels total.", "", 16, 0 }, - { "T3122", "T3122 IMMEDIATE ASSIGNMENT REJECT wait indicator.", "s", 16, GSM_T3122_DEFAULT }, + { "chanloadavg", "Channel load average", "%", 16, 0 }, + { "chan_ccch_sdcch4:used", "Number of CCCH+SDCCH4 channels used", "", 16, 0 }, + { "chan_ccch_sdcch4:total", "Number of CCCH+SDCCH4 channels total", "", 16, 0 }, + { "chan_tch_f:used", "Number of TCH/F channels used", "", 16, 0 }, + { "chan_tch_f:total", "Number of TCH/F channels total", "", 16, 0 }, + { "chan_tch_h:used", "Number of TCH/H channels used", "", 16, 0 }, + { "chan_tch_h:total", "Number of TCH/H channels total", "", 16, 0 }, + { "chan_sdcch8:used", "Number of SDCCH8 channels used", "", 16, 0 }, + { "chan_sdcch8:total", "Number of SDCCH8 channels total", "", 16, 0 }, + { "chan_tch_f_pdch:used", "Number of TCH/F_PDCH channels used", "", 16, 0 }, + { "chan_tch_f_pdch:total", "Number of TCH/F_PDCH channels total", "", 16, 0 }, + { "chan_ccch_sdcch4_cbch:used", "Number of CCCH+SDCCH4+CBCH channels used", "", 16, 0 }, + { "chan_ccch_sdcch4_cbch:total", "Number of CCCH+SDCCH4+CBCH channels total", "", 16, 0 }, + { "chan_sdcch8_cbch:used", "Number of SDCCH8+CBCH channels used", "", 16, 0 }, + { "chan_sdcch8_cbch:total", "Number of SDCCH8+CBCH channels total", "", 16, 0 }, + { "chan_tch_f_tch_h_pdch:used", "Number of TCH/F_TCH/H_PDCH channels used", "", 16, 0 }, + { "chan_tch_f_tch_h_pdch:total", "Number of TCH/F_TCH/H_PDCH channels total", "", 16, 0 }, + { "T3122", "T3122 IMMEDIATE ASSIGNMENT REJECT wait indicator", "s", 16, GSM_T3122_DEFAULT }, { "rach_busy", "RACH slots with signal above threshold", "%", 16, 0 }, { "rach_access", "RACH slots with access bursts in them", "%", 16, 0 }, - { "num_oml_connected", "Number of OML links connected", "", 16, 0 }, - { "num_rsl_connected", "Number of RSL links connected", "", 16, 0 }, + { "oml_connected", "Number of OML links connected", "", 16, 0 }, + { "rsl_connected", "Number of RSL links connected", "", 16, 0 }, }; static const struct osmo_stat_item_group_desc bts_statg_desc = { diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c index 1ba490faa..f1e43be5a 100644 --- a/src/osmo-bsc/osmo_bsc_bssap.c +++ b/src/osmo-bsc/osmo_bsc_bssap.c @@ -1007,6 +1007,7 @@ static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length) { int ret = 0; + struct rate_ctr *ctrs = msc->msc_ctrs->ctr; if (length < 1) { LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); @@ -1018,15 +1019,19 @@ static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc, switch (msg->l4h[0]) { case BSS_MAP_MSG_RESET_ACKNOWLEDGE: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_UDT_RESET_ACKNOWLEDGE]); ret = bssmap_handle_reset_ack(msc, msg, length); break; case BSS_MAP_MSG_RESET: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_UDT_RESET]); ret = bssmap_handle_reset(msc, msg, length); break; case BSS_MAP_MSG_PAGING: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_UDT_PAGING]); ret = bssmap_handle_paging(msc, msg, length); break; default: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_UDT_UNKNOWN]); LOGP(DMSC, LOGL_NOTICE, "Received unimplemented BSSMAP UDT %s\n", gsm0808_bssmap_name(msg->l4h[0])); break; @@ -1039,6 +1044,7 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn, struct msgb *msg, unsigned int length) { int ret = 0; + struct rate_ctr *ctrs = conn->sccp.msc->msc_ctrs->ctr; if (length < 1) { LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); @@ -1050,24 +1056,31 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn, switch (msg->l4h[0]) { case BSS_MAP_MSG_CLEAR_CMD: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_CLEAR_CMD]); ret = bssmap_handle_clear_cmd(conn, msg, length); break; case BSS_MAP_MSG_CIPHER_MODE_CMD: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_CIPHER_MODE_CMD]); ret = bssmap_handle_cipher_mode(conn, msg, length); break; case BSS_MAP_MSG_ASSIGMENT_RQST: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_ASSIGMENT_RQST]); ret = bssmap_handle_assignm_req(conn, msg, length); break; case BSS_MAP_MSG_LCLS_CONNECT_CTRL: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_LCLS_CONNECT_CTRL]); ret = bssmap_handle_lcls_connect_ctrl(conn, msg, length); break; case BSS_MAP_MSG_HANDOVER_CMD: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_HANDOVER_CMD]); ret = bssmap_handle_handover_cmd(conn, msg, length); break; case BSS_MAP_MSG_CLASSMARK_RQST: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_CLASSMARK_RQST]); ret = gsm48_send_rr_classmark_enquiry(conn->lchan); break; default: + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_UNKNOWN]); LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg->l4h[0])); break; @@ -1091,6 +1104,7 @@ static int dtap_rcvmsg(struct gsm_subscriber_connection *conn, struct msgb *gsm48; uint8_t *data; int rc, dtap_rc; + struct rate_ctr *ctrs = conn->sccp.msc->msc_ctrs->ctr; LOGP(DMSC, LOGL_DEBUG, "Rx MSC DTAP: %s\n", osmo_hexdump(msg->l3h, length)); @@ -1102,17 +1116,20 @@ static int dtap_rcvmsg(struct gsm_subscriber_connection *conn, header = (struct dtap_header *) msg->l3h; if (sizeof(*header) >= length) { - LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %zu got: %u\n", sizeof(*header), length); - LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); - return -1; + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DTAP_ERROR]); + LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %zu got: %u, hex: %s\n", + sizeof(*header), length, osmo_hexdump(msg->l3h, length)); + return -1; } if (header->length > length - sizeof(*header)) { - LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length); - LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DTAP_ERROR]); + LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit. Wanted: %u got: %zu, hex: %s\n", + header->length, length - sizeof(*header), osmo_hexdump(msg->l3h, length)); return -1; } + rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DTAP_MSG]); LOGP(DMSC, LOGL_INFO, "Rx MSC DTAP, SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0); /* forward the data */ diff --git a/src/osmo-bsc/osmo_bsc_msc.c b/src/osmo-bsc/osmo_bsc_msc.c index 24c8c6d99..d4d7bec1f 100644 --- a/src/osmo-bsc/osmo_bsc_msc.c +++ b/src/osmo-bsc/osmo_bsc_msc.c @@ -43,6 +43,43 @@ #include <netinet/tcp.h> #include <unistd.h> +static const struct rate_ctr_desc msc_ctr_description[] = { + [MSC_CTR_BSSMAP_RX_UDT_RESET_ACKNOWLEDGE] = {"bssmap:rx_udt_reset_acknowledge", "Number of received BSSMAP UDT RESET ACKNOWLEDGE messages"}, + [MSC_CTR_BSSMAP_RX_UDT_RESET] = {"bssmap:rx_udt_reset", "Number of received BSSMAP UDT RESET messages"}, + [MSC_CTR_BSSMAP_RX_UDT_PAGING] = {"bssmap:rx_udt_paging", "Number of received BSSMAP UDT PAGING messages"}, + [MSC_CTR_BSSMAP_RX_UDT_UNKNOWN] = {"bssmap:rx_udt_unknown", "Number of received BSSMAP unknown UDT messages"}, + [MSC_CTR_BSSMAP_RX_DT1_CLEAR_CMD] = {"bssmap:rx_dt1_clear_cmd", "Number of received BSSMAP DT1 CLEAR CMD messages"}, + [MSC_CTR_BSSMAP_RX_DT1_CIPHER_MODE_CMD] = {"bssmap:rx_dt1_cipher_mode_cmd", "Number of received BSSMAP DT1 CIPHER MODE CMD messages"}, + [MSC_CTR_BSSMAP_RX_DT1_ASSIGMENT_RQST] = {"bssmap:rx_dt1_assignment_rqst", "Number of received BSSMAP DT1 ASSIGMENT RQST messages"}, + [MSC_CTR_BSSMAP_RX_DT1_LCLS_CONNECT_CTRL] = {"bssmap:rx_dt1_lcls_connect_ctrl", "Number of received BSSMAP DT1 LCLS CONNECT CTRL messages"}, + [MSC_CTR_BSSMAP_RX_DT1_HANDOVER_CMD] = {"bssmap:rx_dt1_handover_cmd", "Number of received BSSMAP DT1 HANDOVER CMD messages"}, + [MSC_CTR_BSSMAP_RX_DT1_CLASSMARK_RQST] = {"bssmap:rx_dt1_classmark_rqst", "Number of received BSSMAP DT1 CLASSMARK RQST messages"}, + [MSC_CTR_BSSMAP_RX_DT1_UNKNOWN] = {"bssmap:rx_dt1_unknown", "Number of received BSSMAP unknown DT1 messages"}, + [MSC_CTR_BSSMAP_RX_DTAP_MSG] = {"bssmap:rx_dtap_msg", "Number of received BSSMAP DTAP messages"}, + [MSC_CTR_BSSMAP_RX_DTAP_ERROR] = {"bssmap:rx_dtap_error", "Number of received BSSMAP DATP messages with errors"}, +}; + +static const struct rate_ctr_group_desc msc_ctrg_desc = { + "msc", + "mobile switching center", + OSMO_STATS_CLASS_GLOBAL, + ARRAY_SIZE(msc_ctr_description), + msc_ctr_description, +}; + +static const struct osmo_stat_item_desc msc_stat_desc[] = { + { "msc_links:active", "Number of active MSC links", "", 16, 0 }, + { "msc_links:total", "Number of configured MSC links", "", 16, 0 }, +}; + +static const struct osmo_stat_item_group_desc msc_statg_desc = { + .group_name_prefix = "msc", + .group_description = "mobile switching center", + .class_id = OSMO_STATS_CLASS_GLOBAL, + .num_items = ARRAY_SIZE(msc_stat_desc), + .item_desc = msc_stat_desc, +}; + int osmo_bsc_msc_init(struct bsc_msc_data *msc) { struct gsm_network *net = msc->network; @@ -91,6 +128,19 @@ struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr) if (!msc_data) return NULL; + /* init statistics */ + msc_data->msc_ctrs = rate_ctr_group_alloc(net, &msc_ctrg_desc, nr); + if (!msc_data->msc_ctrs) { + talloc_free(msc_data); + return NULL; + } + msc_data->msc_statg = osmo_stat_item_group_alloc(net, &msc_statg_desc, nr); + if (!msc_data->msc_statg) { + rate_ctr_group_free(msc_data->msc_ctrs); + talloc_free(msc_data); + return NULL; + } + llist_add_tail(&msc_data->entry, &net->bsc_data->mscs); /* Init back pointer */ diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c index 50b03ce47..e4530bfd1 100644 --- a/src/osmo-bsc/osmo_bsc_sigtran.c +++ b/src/osmo-bsc/osmo_bsc_sigtran.c @@ -143,14 +143,14 @@ static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_add return NULL; } -/* Send data to MSC, use the connection id which MSC it is */ +/* Received data from MSC, use the connection id which MSC it is */ static int handle_data_from_msc(struct gsm_subscriber_connection *conn, struct msgb *msg) { msg->l3h = msgb_l2(msg); return bsc_handle_dt(conn, msg, msgb_l2len(msg)); } -/* Sent unitdata to MSC, use the point code to determine which MSC it is */ +/* Received unitdata from MSC, use the point code to determine which MSC it is */ static int handle_unitdata_from_msc(const struct osmo_sccp_addr *msc_addr, struct msgb *msg, const struct osmo_sccp_user *scu) { @@ -215,7 +215,7 @@ refuse: return rc; } -/* Callback function, called by the SSCP stack when data arrives */ +/* Callback function, called by the SCCP stack when data arrives */ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) { struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; |