laforge has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41035?usp=email )
Change subject: enb_registry: track eNB uptime ...................................................................... enb_registry: track eNB uptime Now that we have the global eNB registry, it makes more sense to perform uptime tracking there instead of spawning a separate enb_uptime process for each eNB connection. Change-Id: I94fd06e559ae52d4d9c8b22e618e48dff718b53c Related: SYS#7594, SYS#7066 --- M src/enb_registry.erl D src/enb_uptime.erl M src/s1ap_proxy.erl 3 files changed, 59 insertions(+), 109 deletions(-) Approvals: Jenkins Builder: Verified laforge: Looks good to me, approved pespin: Looks good to me, but someone else must approve diff --git a/src/enb_registry.erl b/src/enb_registry.erl index 4495d44..f2be559 100644 --- a/src/enb_registry.erl +++ b/src/enb_registry.erl @@ -51,6 +51,11 @@ -include_lib("kernel/include/logger.hrl"). +-include("s1gw_metrics.hrl"). + +%% Heartbeat interval +-define(HEARTBEAT_INTERVAL, 5_000). + -type enb_handle() :: non_neg_integer(). @@ -73,6 +78,7 @@ mon_ref := reference(), %% monitor() reference state := enb_state(), %% connection state reg_time := integer(), %% registration time (monotonic) + uptime := non_neg_integer(), %% seconds since reg_time genb_id_str => string(), %% Global-eNB-ID enb_id => s1ap_proxy:enb_id(), %% eNB-ID plmn_id => s1ap_proxy:plmn_id(), %% PLMN-ID @@ -141,6 +147,7 @@ %% ------------------------------------------------------------------ init([]) -> + spawn_link(fun() -> heartbeat(?HEARTBEAT_INTERVAL) end), {ok, #state{enbs = maps:new(), pids = maps:new(), next_handle = 0}}. @@ -163,7 +170,8 @@ pid => Pid, mon_ref => MonRef, state => connecting, - reg_time => erlang:monotonic_time()}, + reg_time => erlang:monotonic_time(), + uptime => 0}, ?LOG_INFO("eNB (handle=~p, pid ~p) registered", [Handle, Pid]), {reply, {ok, Handle}, S#state{enbs = ENBs#{Handle => EnbInfo}, pids = PIDs#{Pid => Handle}, @@ -178,6 +186,7 @@ {ok, #{pid := Pid, mon_ref := MonRef}} -> erlang:demonitor(MonRef, [flush]), + enb_metrics_reset(maps:get(Handle, ENBs)), ?LOG_INFO("eNB (handle=~p) unregistered", [Handle]), {reply, ok, S#state{enbs = maps:remove(Handle, ENBs), pids = maps:remove(Pid, PIDs)}}; @@ -227,12 +236,19 @@ {ok, EnbInfo0} -> ?LOG_INFO("eNB (handle=~p) event: ~p", [Handle, Event]), EnbInfo1 = enb_handle_event(EnbInfo0, Event), + enb_metrics_register(EnbInfo1), {noreply, S#state{enbs = maps:update(Handle, EnbInfo1, ENBs)}}; error -> ?LOG_ERROR("eNB (handle=~p) is *not* registered", [Handle]), {noreply, S} end; +handle_cast(heartbeat, + #state{enbs = ENBs} = S) -> + T = erlang:monotonic_time(), + Fun = fun(Handle, EnbInfo) -> enb_report_uptime(Handle, EnbInfo, T) end, + {noreply, S#state{enbs = maps:map(Fun, ENBs)}}; + handle_cast(Info, S) -> ?LOG_ERROR("unknown ~p(): ~p", [?FUNCTION_NAME, Info]), {noreply, S}. @@ -245,6 +261,7 @@ case maps:find(Pid, PIDs) of {ok, Pid} -> Handle = maps:get(Pid, PIDs), + enb_metrics_reset(maps:get(Handle, ENBs)), ?LOG_INFO("eNB (handle=~p, pid=~p) has been unregistered", [Handle, Pid]), {noreply, S#state{enbs = maps:remove(Handle, ENBs), pids = maps:remove(Pid, PIDs)}}; @@ -267,6 +284,20 @@ %% private API %% ------------------------------------------------------------------ +-spec enb_metrics_register(enb_info()) -> term(). +enb_metrics_register(#{genb_id_str := GlobalENBId}) -> + catch exometer:new(?S1GW_CTR_ENB_UPTIME(GlobalENBId), counter); + +enb_metrics_register(_) -> ok. + + +-spec enb_metrics_reset(enb_info()) -> term(). +enb_metrics_reset(#{genb_id_str := GlobalENBId}) -> + s1gw_metrics:ctr_reset(?S1GW_CTR_ENB_UPTIME(GlobalENBId)); + +enb_metrics_reset(_) -> ok. + + -spec enb_handle_event(enb_info(), enb_event()) -> enb_info(). enb_handle_event(EnbInfo, {connecting, ConnInfo}) -> EnbInfo#{state => connecting, @@ -316,4 +347,28 @@ maps:get(Field, M, undefined) =:= Value. +-spec enb_report_uptime(Handle, EnbInfo, T1) -> enb_info() + when Handle :: enb_handle(), + EnbInfo :: enb_info(), + T1 :: integer(). +enb_report_uptime(Handle, #{reg_time := T0} = EnbInfo, T1) -> + Uptime = erlang:convert_time_unit(T1 - T0, native, second), + ?LOG_DEBUG("eNB (handle=~p) uptime ~p s", [Handle, Uptime]), + %% update ?S1GW_CTR_ENB_UPTIME + case EnbInfo of + #{genb_id_str := GlobalENBId} -> + Current = s1gw_metrics:get_current_value(?S1GW_CTR_ENB_UPTIME(GlobalENBId)), + s1gw_metrics:ctr_inc(?S1GW_CTR_ENB_UPTIME(GlobalENBId), Uptime - Current); + _ -> nop + end, + EnbInfo#{uptime => Uptime}. + + +-spec heartbeat(timeout()) -> no_return(). +heartbeat(Tval) -> + timer:sleep(Tval), + gen_server:cast(?MODULE, ?FUNCTION_NAME), + heartbeat(Tval). %% keep going + + %% vim:set ts=4 sw=4 et: diff --git a/src/enb_uptime.erl b/src/enb_uptime.erl deleted file mode 100644 index ac71ebd..0000000 --- a/src/enb_uptime.erl +++ /dev/null @@ -1,100 +0,0 @@ -%% Copyright (C) 2025 by sysmocom - s.f.m.c. GmbH <i...@sysmocom.de> -%% Author: Vadim Yanitskiy <vyanits...@sysmocom.de> -%% -%% All Rights Reserved -%% -%% SPDX-License-Identifier: AGPL-3.0-or-later -%% -%% 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 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 <https://www.gnu.org/licenses/>. -%% -%% Additional Permission under GNU AGPL version 3 section 7: -%% -%% If you modify this Program, or any covered work, by linking or -%% combining it with runtime libraries of Erlang/OTP as released by -%% Ericsson on https://www.erlang.org (or a modified version of these -%% libraries), containing parts covered by the terms of the Erlang Public -%% License (https://www.erlang.org/EPLICENSE), the licensors of this -%% Program grant you additional permission to convey the resulting work -%% without the need to license the runtime libraries of Erlang/OTP under -%% the GNU Affero General Public License. Corresponding Source for a -%% non-source form of such a combination shall include the source code -%% for the parts of the runtime libraries of Erlang/OTP used as well as -%% that of the covered work. - --module(enb_uptime). - --export([start_link/0, - genb_id_ind/2, - shutdown/1]). - --include_lib("kernel/include/logger.hrl"). - --include("s1gw_metrics.hrl"). - --define(INTERVAL, 5_000). - - -%% ------------------------------------------------------------------ -%% public API -%% ------------------------------------------------------------------ - --spec start_link() -> pid(). -start_link() -> - T0 = erlang:monotonic_time(), - spawn_link(fun() -> loop(#{time => T0}) end). - - --spec genb_id_ind(pid(), string()) -> ok. -genb_id_ind(Pid, GlobalENBId) -> - Pid ! {?FUNCTION_NAME, GlobalENBId}, - ok. - - --spec shutdown(pid()) -> ok. -shutdown(Pid) -> - Pid ! stop, - ok. - - -%% ------------------------------------------------------------------ -%% private API -%% ------------------------------------------------------------------ - -loop(#{genb_id := GlobalENBId, time := T0} = S) -> - receive - stop -> - s1gw_metrics:ctr_reset(?S1GW_CTR_ENB_UPTIME(GlobalENBId)), - ok; - Event -> - ?LOG_ERROR("Rx unexpected event: ~p", [Event]) - after ?INTERVAL -> - T1 = erlang:monotonic_time(), - Diff = erlang:convert_time_unit(T1 - T0, native, second), - s1gw_metrics:ctr_inc(?S1GW_CTR_ENB_UPTIME(GlobalENBId), Diff), - loop(S#{time => T1}) - end; - -loop(#{} = S) -> - receive - {genb_id_ind, GlobalENBId} -> - catch exometer:new(?S1GW_CTR_ENB_UPTIME(GlobalENBId), counter), - loop(S#{genb_id => GlobalENBId}); - stop -> - ok; - Event -> - ?LOG_ERROR("Rx unexpected event: ~p", [Event]) - end. - - -%% vim:set ts=4 sw=4 et: diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl index 252e237..bcba952 100644 --- a/src/s1ap_proxy.erl +++ b/src/s1ap_proxy.erl @@ -82,8 +82,7 @@ mme_ue_id :: undefined | mme_ue_id(), enb_ue_id :: undefined | enb_ue_id(), erab_id :: undefined | erab_id(), - path :: [s1ap_ie_id()], - enb_uptime :: pid() + path :: [s1ap_ie_id()] }). -type proxy_state() :: #proxy_state{}. @@ -147,8 +146,7 @@ init([EnbHandle, ConnInfo]) -> process_flag(trap_exit, true), - {ok, #proxy_state{enb_uptime = enb_uptime:start_link(), - enb_handle = EnbHandle, + {ok, #proxy_state{enb_handle = EnbHandle, conn_info = ConnInfo, erabs = dict:new(), path = []}}. @@ -193,8 +191,7 @@ {noreply, S}. -terminate(Reason, #proxy_state{} = S) -> - enb_uptime:shutdown(S#proxy_state.enb_uptime), +terminate(Reason, #proxy_state{}) -> ?LOG_NOTICE("Terminating, reason ~p", [Reason]), ok. @@ -407,8 +404,6 @@ enb_registry:enb_event(S#proxy_state.enb_handle, {s1setup, enb_info(S)}), gtpu_kpi_enb_register(S), - enb_uptime:genb_id_ind(S#proxy_state.enb_uptime, - S#proxy_state.genb_id_str), %% there's nothing to patch in this PDU, so we forward it as-is {forward, S}; -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41035?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: merged Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I94fd06e559ae52d4d9c8b22e618e48dff718b53c Gerrit-Change-Number: 41035 Gerrit-PatchSet: 5 Gerrit-Owner: fixeria <vyanits...@sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: jolly <andr...@eversberg.eu> Gerrit-Reviewer: laforge <lafo...@osmocom.org> Gerrit-Reviewer: pespin <pes...@sysmocom.de>