From 5c598c76cd73012670d5613bb79e389790955f63 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Thu, 31 Mar 2022 20:22:05 +0200 Subject: [PATCH] llc: schedule frames to MS based on SAPI priority The CoDel state is still applied globally, which could cause higher prio messages (GMM) to clear dropping state (since those are left for a smaller period of time inside the queue). CoDel state will be moved per prio-queue in a follow-up patch. Related: OS#5508 Related: SYS#5908 Change-Id: Ie8bd91eeac4fa7487d4f11b808dea95737041c7e --- src/llc.c | 100 +++++++++++++++++++++++++++++++++++------------------- src/llc.h | 27 +++++++++++++-- 2 files changed, 90 insertions(+), 37 deletions(-) diff --git a/src/llc.c b/src/llc.c index 700585ab..c8d74d89 100644 --- a/src/llc.c +++ b/src/llc.c @@ -96,19 +96,40 @@ bool llc_is_user_data_frame(const uint8_t *data, size_t len) void llc_queue_init(struct gprs_llc_queue *q) { - INIT_LLIST_HEAD(&q->queue); + unsigned int i; + q->queue_size = 0; q->queue_octets = 0; q->avg_queue_delay = 0; + for (i = 0; i < ARRAY_SIZE(q->queue); i++) + INIT_LLIST_HEAD(&q->queue[i]); } +static enum gprs_llc_queue_prio llc_sapi2prio(uint8_t sapi) +{ + switch (sapi) { + case 1: + return LLC_QUEUE_PRIO_GMM; + case 2: + case 7: + case 8: + return LLC_QUEUE_PRIO_TOM_SMS; + default: + return LLC_QUEUE_PRIO_OTHER; + } +} + void llc_queue_enqueue(struct gprs_llc_queue *q, struct msgb *llc_msg, const struct timespec *expire_time) { struct MetaInfo *meta_storage; + struct gprs_llc_hdr *llc_hdr = (struct gprs_llc_hdr *)msgb_data(llc_msg); + enum gprs_llc_queue_prio prio; osmo_static_assert(sizeof(*meta_storage) <= sizeof(llc_msg->cb), info_does_not_fit); + prio = llc_sapi2prio(llc_hdr->sapi); + q->queue_size += 1; q->queue_octets += msgb_length(llc_msg); @@ -116,17 +137,20 @@ void llc_queue_enqueue(struct gprs_llc_queue *q, struct msgb *llc_msg, const str osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time); meta_storage->expire_time = *expire_time; - msgb_enqueue(&q->queue, llc_msg); + msgb_enqueue(&q->queue[prio], llc_msg); } void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts) { struct msgb *msg; + unsigned int i; - while ((msg = msgb_dequeue(&q->queue))) { - if (bts) - bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED); - msgb_free(msg); + for (i = 0; i < ARRAY_SIZE(q->queue); i++) { + while ((msg = msgb_dequeue(&q->queue[i]))) { + if (bts) + bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED); + msgb_free(msg); + } } q->queue_size = 0; @@ -137,51 +161,53 @@ void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o { struct msgb *msg, *msg1 = NULL, *msg2 = NULL; struct llist_head new_queue; + unsigned int i; size_t queue_size = 0; size_t queue_octets = 0; INIT_LLIST_HEAD(&new_queue); - while (1) { - if (msg1 == NULL) - msg1 = msgb_dequeue(&q->queue); + for (i = 0; i < ARRAY_SIZE(q->queue); i++) { + while (1) { + if (msg1 == NULL) + msg1 = msgb_dequeue(&q->queue[i]); - if (msg2 == NULL) - msg2 = msgb_dequeue(&o->queue); + if (msg2 == NULL) + msg2 = msgb_dequeue(&o->queue[i]); - if (msg1 == NULL && msg2 == NULL) - break; + if (msg1 == NULL && msg2 == NULL) + break; - if (msg1 == NULL) { - msg = msg2; - msg2 = NULL; - } else if (msg2 == NULL) { - msg = msg1; - msg1 = NULL; - } else { - const struct MetaInfo *mi1 = (struct MetaInfo *)&msg1->cb[0]; - const struct MetaInfo *mi2 = (struct MetaInfo *)&msg2->cb[0]; - - if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) { + if (msg1 == NULL) { + msg = msg2; + msg2 = NULL; + } else if (msg2 == NULL) { msg = msg1; msg1 = NULL; } else { - msg = msg2; - msg2 = NULL; + const struct MetaInfo *mi1 = (struct MetaInfo *)&msg1->cb[0]; + const struct MetaInfo *mi2 = (struct MetaInfo *)&msg2->cb[0]; + + if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) { + msg = msg1; + msg1 = NULL; + } else { + msg = msg2; + msg2 = NULL; + } } + + msgb_enqueue(&new_queue, msg); + queue_size += 1; + queue_octets += msgb_length(msg); } - msgb_enqueue(&new_queue, msg); - queue_size += 1; - queue_octets += msgb_length(msg); + OSMO_ASSERT(llist_empty(&q->queue[i])); + OSMO_ASSERT(llist_empty(&o->queue[i])); + llist_splice_init(&new_queue, &q->queue[i]); } - OSMO_ASSERT(llist_empty(&q->queue)); - OSMO_ASSERT(llist_empty(&o->queue)); - o->queue_size = 0; o->queue_octets = 0; - - llist_splice_init(&new_queue, &q->queue); q->queue_size = queue_size; q->queue_octets = queue_octets; } @@ -193,9 +219,13 @@ struct msgb *llc_queue_dequeue(struct gprs_llc_queue *q, const struct MetaInfo * struct msgb *msg; struct timespec *tv, tv_now, tv_result; uint32_t lifetime; + unsigned int i; const struct MetaInfo *meta_storage; - msg = msgb_dequeue(&q->queue); + for (i = 0; i < ARRAY_SIZE(q->queue); i++) { + if ((msg = msgb_dequeue(&q->queue[i]))) + break; + } if (!msg) return NULL; diff --git a/src/llc.h b/src/llc.h index bd542c0c..adfab657 100644 --- a/src/llc.h +++ b/src/llc.h @@ -1,4 +1,4 @@ -/* +/* 3GPP TS 44.064 * Copyright (C) 2013 by Holger Hans Peter Freyther * Copyright (C) 2022 by by Sysmocom s.f.m.c. GmbH * @@ -23,13 +23,30 @@ extern "C" { #include #include +#include #include #include +#include #define LLC_MAX_LEN 1543 struct gprs_rlcmac_bts; +struct gprs_llc_hdr { +#if OSMO_IS_LITTLE_ENDIAN + union { /* 5.2, 6.2.0 */ + uint8_t address; + uint8_t sapi:4, unused:2, c_r:1, pd:1; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */ + union { + uint8_t address; + uint8_t pd:1, c_r:1, unused:2, sapi:4; +#endif + }; + uint8_t control[0]; +} __attribute__ ((packed)); + /** * I represent the LLC data to a MS */ @@ -87,11 +104,17 @@ struct MetaInfo { /** * I store the LLC frames that come from the SGSN. */ +enum gprs_llc_queue_prio { /* lowest value has highest prio */ + LLC_QUEUE_PRIO_GMM = 0, /* SAPI 1 */ + LLC_QUEUE_PRIO_TOM_SMS, /* SAPI 2,7,8 */ + LLC_QUEUE_PRIO_OTHER, /* Other SAPIs */ + _LLC_QUEUE_PRIO_SIZE /* used to calculate size of enum */ +}; struct gprs_llc_queue { uint32_t avg_queue_delay; /* Average delay of data going through the queue */ size_t queue_size; size_t queue_octets; - struct llist_head queue; /* queued LLC DL data */ + struct llist_head queue[_LLC_QUEUE_PRIO_SIZE]; /* queued LLC DL data. See enum gprs_llc_queue_prio. */ }; void llc_queue_calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec,