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
This commit is contained in:
Pau Espin 2022-03-31 20:22:05 +02:00
parent 1cf38fcbe8
commit 5c598c76cd
2 changed files with 90 additions and 37 deletions

100
src/llc.c
View File

@ -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;

View File

@ -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 <string.h>
#include <time.h>
#include <osmocom/core/endian.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/endian.h>
#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,