/* (C) 2022 by sysmocom s.f.m.c. GmbH * * Author: Alexander Couzens * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include static void _bts_setup_ramp_unblock_bts(struct gsm_bts *bts) { llist_del_init(&bts->bts_setup_ramp.list); bts->bts_setup_ramp.state = BTS_SETUP_RAMP_READY; nm_fsm_dispatch_all_configuring(bts, NM_EV_SETUP_RAMP_READY, NULL); } /*! * Unblock a BTS from BTS setup ramping to continue setup and configure. * * \param bts pointer to the bts * \return 0 on success, -EINVAL when the BTS is not waiting. */ int bts_setup_ramp_unblock_bts(struct gsm_bts *bts) { if (bts->bts_setup_ramp.state != BTS_SETUP_RAMP_WAIT) return -EINVAL; if (llist_empty(&bts->bts_setup_ramp.list)) return -EINVAL; _bts_setup_ramp_unblock_bts(bts); return 0; } /*! * Timer callback and called by bts_setup_ramp_deactivate * \param _net pointer to struct gsm_network */ static void bts_setup_ramp_timer_cb(void *_net) { struct gsm_network *net = (struct gsm_network *) _net; struct gsm_bts *bts, *n; net->bts_setup_ramp.count = 0; llist_for_each_entry_safe(bts, n, &net->bts_setup_ramp.head, bts_setup_ramp.list) { net->bts_setup_ramp.count++; _bts_setup_ramp_unblock_bts(bts); LOG_BTS(bts, DNM, LOGL_INFO, "Unblock BTS %d from BTS ramping.\n", bts->nr); if (bts_setup_ramp_active(net) && net->bts_setup_ramp.count >= net->bts_setup_ramp.step_size) break; } if (bts_setup_ramp_active(net)) osmo_timer_schedule(&net->bts_setup_ramp.timer, net->bts_setup_ramp.step_interval, 0); } const struct value_string bts_setup_ramp_state_values[] = { { BTS_SETUP_RAMP_INIT, "Initial" }, { BTS_SETUP_RAMP_WAIT, "Waiting" }, { BTS_SETUP_RAMP_READY, "Ready" }, { 0, NULL }, }; const char *bts_setup_ramp_get_state_str(struct gsm_bts *bts) { return get_value_string_or_null(bts_setup_ramp_state_values, bts->bts_setup_ramp.state); } /* return true when state has been changed. */ static bool check_config(struct gsm_network *net) { bool new_state = (net->bts_setup_ramp.enabled && net->bts_setup_ramp.step_size > 0 && net->bts_setup_ramp.step_interval > 0); if (!new_state && bts_setup_ramp_active(net)) { net->bts_setup_ramp.active = false; osmo_timer_del(&net->bts_setup_ramp.timer); /* clear bts list */ bts_setup_ramp_timer_cb(net); return true; } else if (new_state && !bts_setup_ramp_active(net)) { net->bts_setup_ramp.active = true; osmo_timer_schedule(&net->bts_setup_ramp.timer, net->bts_setup_ramp.step_interval, 0); return true; } return false; } /*! * Enable the bts setup ramping feature * * The BTS setup ramping prevents BSC overload when too many BTS tries to setup and * configure at the same time. E.g. this might happen if there is a major network outage * between all BTS and the BSC. * * \param[in] net a pointer to the gsm network */ void bts_setup_ramp_enable(struct gsm_network *net) { net->bts_setup_ramp.enabled = true; check_config(net); } /*! * Disable the bts setup ramping feature * * \param[in] net a pointer to the gsm network */ void bts_setup_ramp_disable(struct gsm_network *net) { net->bts_setup_ramp.enabled = false; check_config(net); } /*! Checks if the bts setup ramp correct configured and active * * \param[in] net a pointer to the gsm network * \return true if the bts setup ramp is active */ bool bts_setup_ramp_active(struct gsm_network *net) { return net->bts_setup_ramp.active; } /*! * Check if the BTS should wait to setup. * * Can be called multiple times by the same BTS. * * \param bts pointer to the bts * \return true if the bts should wait */ bool bts_setup_ramp_wait(struct gsm_bts *bts) { struct gsm_network *net = bts->network; if (!bts_setup_ramp_active(net)) { bts->bts_setup_ramp.state = BTS_SETUP_RAMP_READY; return false; } switch (bts->bts_setup_ramp.state) { case BTS_SETUP_RAMP_INIT: break; case BTS_SETUP_RAMP_WAIT: return true; case BTS_SETUP_RAMP_READY: return false; } if (net->bts_setup_ramp.count < net->bts_setup_ramp.step_size) { LOG_BTS(bts, DNM, LOGL_INFO, "BTS %d can configure without waiting for BTS ramping.\n", bts->nr); net->bts_setup_ramp.count++; bts->bts_setup_ramp.state = BTS_SETUP_RAMP_READY; return false; } bts->bts_setup_ramp.state = BTS_SETUP_RAMP_WAIT; llist_add_tail(&bts->bts_setup_ramp.list, &net->bts_setup_ramp.head); LOGP(DNM, LOGL_INFO, "BTS %d will wait for BTS ramping.\n", bts->nr); return true; } void bts_setup_ramp_init_network(struct gsm_network *net) { INIT_LLIST_HEAD(&net->bts_setup_ramp.head); osmo_timer_setup(&net->bts_setup_ramp.timer, bts_setup_ramp_timer_cb, net); } void bts_setup_ramp_init_bts(struct gsm_bts *bts) { /* Initialize bts_setup_ramp.list (llist_entry) to have llist_empty() available */ INIT_LLIST_HEAD(&bts->bts_setup_ramp.list); bts->bts_setup_ramp.state = BTS_SETUP_RAMP_INIT; } /*! * Remove the bts from the bts setup ramp waiting list and resets the BTS setup ramping state. * Should be called when removing the BTS * * \param bts pointer to the bts */ void bts_setup_ramp_remove(struct gsm_bts *bts) { if (!llist_empty(&bts->bts_setup_ramp.list)) llist_del_init(&bts->bts_setup_ramp.list); bts->bts_setup_ramp.state = BTS_SETUP_RAMP_INIT; } /*! * Set the BTS setup ramping step interval. * * Within the time window of \param step_interval only a limited amount (see step_size) * of BTS will be configured. * * \param[in] net a pointer to the gsm network * \param step_interval in seconds */ void bts_setup_ramp_set_step_interval(struct gsm_network *net, unsigned int step_interval) { net->bts_setup_ramp.step_interval = step_interval; check_config(net); } /*! * Set the BTS setup ramping step_size * * Within the time window of step_interval only a limited amount of BTS (\param step_size) * will be configured. * * \param[in] net a pointer to the gsm network * \param step_size the step size */ void bts_setup_ramp_set_step_size(struct gsm_network *net, unsigned int step_size) { net->bts_setup_ramp.step_size = step_size; check_config(net); }