osmo-bsc/src/osmo-bsc/bts_setup_ramp.c

250 lines
6.9 KiB
C

/* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
*
* Author: Alexander Couzens <acouzens@sysmocom.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <stdbool.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/utils.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/bts_sm.h>
#include <osmocom/bsc/bts_setup_ramp.h>
#include <osmocom/bsc/nm_common_fsm.h>
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);
}