aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Couzens <lynxis@fe80.eu>2020-10-27 20:01:23 +0100
committerAlexander Couzens <lynxis@fe80.eu>2020-10-27 20:01:23 +0100
commit2453a0baa8541dfdbc3b88ac5a858f6fc9e6b2e6 (patch)
treee9ffb4265b5c19407896642b9c345a423a676a33
parent447b03428469fe790a13e29e8e6a7d7cdc09d82b (diff)
gprs_ns2: implement a load sharing distribution for E1lynxis/wip-fr
-rw-r--r--include/osmocom/gprs/gprs_ns2.h1
-rw-r--r--src/gb/gprs_ns2.c141
-rw-r--r--src/gb/gprs_ns2_internal.h8
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 */