From d8a8d980535468d1f8642cdcf796b2d675c7b8df Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Mon, 15 Feb 2021 01:07:45 +0100 Subject: [PATCH] gprs_ns2: implement a simple load sharing for UDP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the load sharing based on modulo of the LSP. As long the gprs_ns2 doesn't support the resource distribution function (48.016 ยง 4.4a) this simple approach is good enought. Fixes: OS#4836 Change-Id: I8c2fe5d647694886ac600470fca6ea5d5d210a85 --- src/gb/gprs_ns2.c | 41 ++++++++++++++++- tests/gb/gprs_ns2_test.c | 94 +++++++++++++++++++++++++++++++++++++++ tests/gb/gprs_ns2_test.ok | 8 ++++ 3 files changed, 142 insertions(+), 1 deletion(-) diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c index bc4db53ed..e29073434 100644 --- a/src/gb/gprs_ns2.c +++ b/src/gb/gprs_ns2.c @@ -384,6 +384,45 @@ static struct gprs_ns2_vc *ns2_load_sharing_modulor( return NULL; } +/* 4.4.2 Load Sharing function for the IP Sub-Network + * + * Implement a simple approach for UDP load sharing of data weight based on the modulo of the lsp. + * + * E.g. 3 NSVC: 1st weight 5, 2nd weight 3, 3rd weight 1, lsp = 3. + * sum all weights = 9 + * target_weight = lsp % sum = 3 + * + * 1st NSVC will be the target for 0-4 + * 2nd NSVC will be the target for 5-7 + * 3rd NSVC will be the target for 8 + * + * The 1st NSVC will be used. + * E.g. lsp = 7. The 2nd NSVC will used. + */ +static struct gprs_ns2_vc *ns2_load_sharing_weight_modulo( + struct gprs_ns2_nse *nse, + uint16_t bvci, + uint32_t load_selector) +{ + struct gprs_ns2_vc *tmp; + uint32_t mod; + uint32_t i = 0; + + if (nse->nsvc_count == 0) + return NULL; + + mod = (bvci + load_selector) % nse->sum_data_weight; + llist_for_each_entry(tmp, &nse->nsvc, list) { + if (!ns2_vc_is_unblocked(tmp)) + continue; + if (i == mod || mod < i + tmp->data_weight) + return tmp; + i += tmp->data_weight; + } + + return NULL; +} + /* pick the first available data NSVC - no load sharing */ struct gprs_ns2_vc *ns2_load_sharing_first(struct gprs_ns2_nse *nse) { @@ -421,7 +460,7 @@ static struct gprs_ns2_vc *ns2_load_sharing( nsvc = ns2_load_sharing_signal(nse); } else { /* data with load sharing parameter */ - nsvc = ns2_load_sharing_first(nse); + nsvc = ns2_load_sharing_weight_modulo(nse, bvci, link_selector); } break; } diff --git a/tests/gb/gprs_ns2_test.c b/tests/gb/gprs_ns2_test.c index 6d71a8c63..d8ed06fa6 100644 --- a/tests/gb/gprs_ns2_test.c +++ b/tests/gb/gprs_ns2_test.c @@ -60,6 +60,18 @@ static int ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx) return 0; } +static int gp_send_to_ns(struct gprs_ns2_inst *nsi, struct msgb *msg, uint16_t nsei, uint16_t bvci, uint32_t lsp) +{ + struct osmo_gprs_ns2_prim nsp = {}; + nsp.nsei = nsei; + nsp.bvci = bvci; + nsp.u.unitdata.link_selector = lsp; + osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, + PRIM_OP_REQUEST, msg); + return gprs_ns2_recv_prim(nsi, &nsp.oph); +} + + static struct msgb *get_pdu(struct gprs_ns2_vc_bind *bind, enum ns_pdu_type pdu_type) { struct gprs_ns_hdr *nsh; @@ -88,6 +100,12 @@ static bool find_pdu(struct gprs_ns2_vc_bind *bind, enum ns_pdu_type pdu_type) return false; } +static unsigned int count_pdus(struct gprs_ns2_vc_bind *bind) +{ + struct osmo_wqueue *queue = bind->priv; + return llist_count(&queue->msg_queue); +} + static void clear_pdus(struct gprs_ns2_vc_bind *bind) { struct osmo_wqueue *queue = bind->priv; @@ -370,6 +388,81 @@ void test_unitdata(void *ctx) printf("--- Finish unitdata test\n"); } +void test_unitdata_weights(void *ctx) +{ + struct gprs_ns2_inst *nsi; + struct gprs_ns2_vc_bind *bind[3]; + struct gprs_ns2_vc_bind *loopbind; + struct gprs_ns2_nse *nse; + struct gprs_ns2_vc *nsvc[3]; + struct gprs_ns2_vc *loop[3]; + + struct msgb *msg, *other; + char idbuf[32]; + int i; + + printf("--- Testing unitdata weight test\n"); + osmo_wqueue_clear(unitdata); + printf("---- Create NSE + Binds\n"); + nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL); + bind[0] = dummy_bind(nsi, "bblock1"); + bind[1] = dummy_bind(nsi, "bblock2"); + bind[2] = dummy_bind(nsi, "bblock3"); + loopbind = loopback_bind(nsi, "loopback"); + nse = gprs_ns2_create_nse(nsi, 1004, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_ALIVE); + OSMO_ASSERT(nse); + + /* data weights are + * nsvc[0] = 1 + * nsvc[1] = 2 + * nsvc[2] = 3 + */ + for (i = 0; i < 3; i++) { + printf("---- Create NSVC[%d]\n", i); + snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i); + nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_ALIVE, idbuf); + loop[i] = loopback_nsvc(loopbind, nsvc[i]); + OSMO_ASSERT(nsvc[i]); + nsvc[i]->data_weight = i + 1; + ns2_vc_fsm_start(nsvc[i]); + OSMO_ASSERT(!ns2_vc_is_unblocked(nsvc[i])); + ns2_tx_alive_ack(loop[i]); + OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i])); + } + + /* all nsvcs are alive */ + printf("---- Send UNITDATA to all NSVCs\n"); + for (i = 0; i < 3; i++) { + msg = generate_unitdata("test_unitdata_weight"); + ns2_recv_vc(nsvc[i], msg); + other = msgb_dequeue(&unitdata->msg_queue); + OSMO_ASSERT(msg == other); + other = msgb_dequeue(&unitdata->msg_queue); + OSMO_ASSERT(NULL == other); + msgb_free(msg); + } + + /* nsvc[1] should be still good */ + printf("---- Send BSSGP data to the NSE to test unitdata over NSVC[1]\n"); + for (i = 0; i < 3; i++) + clear_pdus(bind[i]); + + for (i = 0; i < 12; i++) { + msg = generate_unitdata("test_unitdata_weight2"); + gp_send_to_ns(nsi, msg, 1004, 1, i + 1); + } + + for (i = 0; i < 3; i++) + fprintf(stderr, "count_pdus(bind[%d]) = %d\n", i, count_pdus(bind[i])); + + for (i = 0; i < 3; i++) { + OSMO_ASSERT(count_pdus(bind[i]) == nsvc[i]->data_weight * 2); + } + + gprs_ns2_free(nsi); + printf("--- Finish unitdata weight test\n"); +} + void test_mtu(void *ctx) { struct gprs_ns2_inst *nsi; @@ -440,6 +533,7 @@ int main(int argc, char **argv) test_nse_transfer_cap(ctx); test_block_unblock_nsvc(ctx); test_unitdata(ctx); + test_unitdata_weights(ctx); test_mtu(ctx); printf("===== NS2 protocol test END\n\n"); diff --git a/tests/gb/gprs_ns2_test.ok b/tests/gb/gprs_ns2_test.ok index f40579f3e..40a1528bb 100644 --- a/tests/gb/gprs_ns2_test.ok +++ b/tests/gb/gprs_ns2_test.ok @@ -20,6 +20,14 @@ ---- Try to receive over blocked NSVC[0] ---- Receive over NSVC[1] --- Finish unitdata test +--- Testing unitdata weight test +---- Create NSE + Binds +---- Create NSVC[0] +---- Create NSVC[1] +---- Create NSVC[2] +---- Send UNITDATA to all NSVCs +---- Send BSSGP data to the NSE to test unitdata over NSVC[1] +--- Finish unitdata weight test --- Testing mtu test ---- Create NSE + Binds ---- Create NSVC[0]