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,