From 2453a0baa8541dfdbc3b88ac5a858f6fc9e6b2e6 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 27 Oct 2020 20:01:23 +0100 Subject: gprs_ns2: implement a load sharing distribution for E1 Change-Id: Ibfdf4125a3ac9379b7eb78c6e930d41264a48c5f --- include/osmocom/gprs/gprs_ns2.h | 1 + src/gb/gprs_ns2.c | 141 ++++++++++++++++++++++++++++++++++++---- src/gb/gprs_ns2_internal.h | 8 ++- 3 files changed, 138 insertions(+), 12 deletions(-) diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h index 97f9b8a3..4cd16154 100644 --- a/include/osmocom/gprs/gprs_ns2.h +++ b/include/osmocom/gprs/gprs_ns2.h @@ -73,6 +73,7 @@ struct osmo_gprs_ns2_prim { union { struct { enum gprs_ns2_change_ip_endpoint change; + uint32_t link_selector; /* TODO: implement resource distribution * add place holder for the link selector */ long long _resource_distribution_placeholder1; diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c index 763240bd..7a496ff1 100644 --- a/src/gb/gprs_ns2.c +++ b/src/gb/gprs_ns2.c @@ -285,6 +285,118 @@ char *gprs_ns2_ll_str_c(const void *ctx, struct gprs_ns2_vc *nsvc) return gprs_ns2_ll_str_buf(buf, NS2_LL_MAX_STR, nsvc); } +/* select a signalling NSVC and respect sig_counter + * param[out] reset_counter - all counter has to be resetted to their signal weight + * return the chosen nsvc or NULL + */ +static struct gprs_ns2_vc *ns2_load_sharing_signal( + struct gprs_ns2_nse *nse) +{ + struct gprs_ns2_vc *nsvc = NULL, *last = NULL, *tmp; + + llist_for_each_entry(tmp, &nse->nsvc, list) { + if (tmp->sig_weight == 0) + continue; + if (!gprs_ns2_vc_is_unblocked(tmp)) + continue; + if (tmp->sig_counter == 0) { + last = tmp; + continue; + } + + tmp->sig_counter--; + nsvc = tmp; + break; + } + + /* all counter were zero, but there are valid nsvc */ + if (!nsvc && last) { + llist_for_each_entry(tmp, &nse->nsvc, list) { + tmp->sig_counter = tmp->sig_weight; + } + + last->sig_counter--; + return last; + } else { + return nsvc; + } +} + +/* 4.4.1 Load Sharing function for the Frame Relay Sub-Network */ +static struct gprs_ns2_vc *ns2_load_sharing_modulor( + struct gprs_ns2_nse *nse, + uint16_t bvci, + uint32_t load_selector) +{ + struct gprs_ns2_vc *tmp; + uint32_t mod = (bvci + load_selector) % nse->nsvc_data_count; + uint32_t i = 0; + + llist_for_each_entry(tmp, &nse->nsvc, list) { + if (!gprs_ns2_vc_is_unblocked(tmp)) + continue; + if (tmp->data_weight == 0) + continue; + + if (i == mod) + return tmp; + i++; + } + + return NULL; +} + +/* pick the first available data NSVC - no load sharing */ +struct gprs_ns2_vc *ns2_load_sharing_first(struct gprs_ns2_nse *nse) +{ + struct gprs_ns2_vc *nsvc = NULL, *tmp; + + llist_for_each_entry(tmp, &nse->nsvc, list) { + if (!gprs_ns2_vc_is_unblocked(tmp)) + continue; + if (tmp->data_weight == 0) + continue; + + nsvc = tmp; + break; + } + + return nsvc; +} + + +static struct gprs_ns2_vc *ns2_load_sharing( + struct gprs_ns2_nse *nse, + uint16_t bvci, + uint32_t link_selector) +{ + struct gprs_ns2_vc *nsvc = NULL; + + if (bvci == 0) { + /* signalling */ + nsvc = ns2_load_sharing_signal(nse); + } else { + enum gprs_ns_ll ll; + + /* data with load sharing parameter */ + if (llist_empty(&nse->nsvc)) + return NULL; + nsvc = llist_entry(&nse->nsvc, struct gprs_ns2_vc, list); + ll = nsvc->ll; + + switch (ll) { + case GPRS_NS_LL_E1: + nsvc = ns2_load_sharing_modulor(nse, bvci, link_selector); + break; + default: + nsvc = ns2_load_sharing_first(nse); + break; + } + } + + return nsvc; +} + /*! Receive a primitive from the NS User (Gb). * \param[in] nsi NS instance to which the primitive is issued * \param[in] oph The primitive @@ -297,7 +409,7 @@ int gprs_ns2_recv_prim(struct gprs_ns2_inst *nsi, struct osmo_prim_hdr *oph) * the IP endpoint */ struct osmo_gprs_ns2_prim *nsp; struct gprs_ns2_nse *nse = NULL; - struct gprs_ns2_vc *nsvc = NULL, *tmp; + struct gprs_ns2_vc *nsvc = NULL; uint16_t bvci, nsei; uint8_t sducontrol = 0; @@ -319,16 +431,7 @@ int gprs_ns2_recv_prim(struct gprs_ns2_inst *nsi, struct osmo_prim_hdr *oph) if (!nse) return -EINVAL; - llist_for_each_entry(tmp, &nse->nsvc, list) { - if (!gprs_ns2_vc_is_unblocked(tmp)) - continue; - if (bvci == 0 && tmp->sig_weight == 0) - continue; - if (bvci != 0 && tmp->data_weight == 0) - continue; - - nsvc = tmp; - } + nsvc = ns2_load_sharing(nse, bvci, nsp->u.unitdata.link_selector); /* TODO: send a status primitive back */ if (!nsvc) @@ -957,6 +1060,20 @@ int ns2_recv_vc(struct gprs_ns2_vc *nsvc, return rc; } +/* summarize all active data nsvcs */ +void ns2_nse_data_sum(struct gprs_ns2_nse *nse) +{ + struct gprs_ns2_vc *nsvc; + nse->nsvc_data_count = 0; + + llist_for_each_entry(nsvc, &nse->nsvc, list) { + if (!gprs_ns2_vc_is_unblocked(nsvc)) + continue; + if (nsvc->data_weight > 0) + nse->nsvc_data_count++; + } +} + /*! Notify a nse about the change of a NS-VC. * \param[in] nsvc NS-VC which has detected the change (and shall not be notified). * \param[in] unblocked whether the NSE should be marked as unblocked (true) or blocked (false) */ @@ -965,6 +1082,8 @@ void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked) struct gprs_ns2_nse *nse = nsvc->nse; struct gprs_ns2_vc *tmp; + ns2_nse_data_sum(nse); + if (unblocked == nse->alive) return; diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h index e3c680d2..fc518278 100644 --- a/src/gb/gprs_ns2_internal.h +++ b/src/gb/gprs_ns2_internal.h @@ -118,6 +118,9 @@ struct gprs_ns2_nse { /*! llist head to hold all nsvc */ struct llist_head nsvc; + /*! count all active NSVCs with data capabilities */ + int nsvc_data_count; + /*! true if this NSE was created by VTY or pcu socket) */ bool persistent; @@ -154,7 +157,10 @@ struct gprs_ns2_vc { /*! signalling weight. 0 = don't use for signalling (BVCI == 0)*/ uint8_t sig_weight; - /*! signaling weight. 0 = don't use for user data (BVCI != 0) */ + /*! signalling packet counter for the load sharing function */ + uint8_t sig_counter; + + /*! data weight. 0 = don't use for user data (BVCI != 0) */ uint8_t data_weight; /*! can be used by the bind/driver of the virtual circuit. e.g. ipv4/ipv6/frgre/e1 */ -- cgit v1.2.3