2013-11-07 06:21:06 +00:00
|
|
|
/* Copied from tbf.cpp
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
|
|
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
|
|
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2013-11-13 13:56:55 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
2013-11-07 06:21:06 +00:00
|
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
#include "bts.h"
|
2022-04-04 11:45:56 +00:00
|
|
|
#include "gprs_ms.h"
|
2020-03-11 13:04:52 +00:00
|
|
|
#include "pcu_utils.h"
|
2022-03-31 17:36:12 +00:00
|
|
|
#include "llc.h"
|
|
|
|
|
|
|
|
void llc_init(struct gprs_llc *llc)
|
|
|
|
{
|
|
|
|
llc_reset(llc);
|
|
|
|
}
|
2020-03-11 13:04:52 +00:00
|
|
|
|
2013-11-07 06:21:06 +00:00
|
|
|
/* reset LLC frame */
|
2022-03-31 17:36:12 +00:00
|
|
|
void llc_reset(struct gprs_llc *llc)
|
2013-11-07 06:21:06 +00:00
|
|
|
{
|
2022-03-31 17:36:12 +00:00
|
|
|
llc->index = 0;
|
|
|
|
llc->length = 0;
|
2013-11-26 15:39:47 +00:00
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
memset(llc->frame, 0x42, sizeof(llc->frame));
|
2013-11-07 06:21:06 +00:00
|
|
|
}
|
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
void llc_reset_frame_space(struct gprs_llc *llc)
|
2013-11-07 06:21:06 +00:00
|
|
|
{
|
2022-03-31 17:36:12 +00:00
|
|
|
llc->index = 0;
|
2013-11-07 06:21:06 +00:00
|
|
|
}
|
|
|
|
|
2015-03-20 12:57:27 +00:00
|
|
|
/* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
|
2022-03-31 17:36:12 +00:00
|
|
|
void llc_put_dummy_frame(struct gprs_llc *llc, size_t req_len)
|
2015-03-20 12:57:27 +00:00
|
|
|
{
|
|
|
|
/* The shortest dummy command (the spec requests at least 6 octets) */
|
|
|
|
static const uint8_t llc_dummy_command[] = {
|
|
|
|
0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
|
|
|
|
};
|
2015-07-07 11:43:44 +00:00
|
|
|
static const size_t max_dummy_command_len = 79;
|
2015-03-20 12:57:27 +00:00
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
llc_put_frame(llc, llc_dummy_command, sizeof(llc_dummy_command));
|
2015-03-20 12:57:27 +00:00
|
|
|
|
|
|
|
if (req_len > max_dummy_command_len)
|
|
|
|
req_len = max_dummy_command_len;
|
|
|
|
|
|
|
|
/* Add further stuffing, if the requested length exceeds the minimum
|
|
|
|
* dummy command length */
|
2022-03-31 17:36:12 +00:00
|
|
|
if (llc->length < req_len) {
|
|
|
|
memset(&llc->frame[llc->length], 0x2b, req_len - llc->length);
|
|
|
|
llc->length = req_len;
|
2021-03-02 11:28:32 +00:00
|
|
|
}
|
2015-03-20 12:57:27 +00:00
|
|
|
}
|
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
void llc_put_frame(struct gprs_llc *llc, const uint8_t *data, size_t len)
|
2013-11-07 06:21:06 +00:00
|
|
|
{
|
2013-11-13 13:56:55 +00:00
|
|
|
/* only put frames when we are empty */
|
2022-03-31 17:36:12 +00:00
|
|
|
OSMO_ASSERT(llc->index == 0 && llc->length == 0);
|
|
|
|
llc_append_frame(llc, data, len);
|
2013-11-13 15:56:15 +00:00
|
|
|
}
|
2013-11-13 13:56:55 +00:00
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
void llc_append_frame(struct gprs_llc *llc, const uint8_t *data, size_t len)
|
2013-11-13 15:56:15 +00:00
|
|
|
{
|
2013-11-13 13:56:55 +00:00
|
|
|
/* TODO: bounds check */
|
2022-03-31 17:36:12 +00:00
|
|
|
memcpy(llc->frame + llc->length, data, len);
|
|
|
|
llc->length += len;
|
2015-05-29 08:37:09 +00:00
|
|
|
}
|
2013-11-07 06:21:06 +00:00
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
bool llc_is_user_data_frame(const uint8_t *data, size_t len)
|
2015-05-29 08:37:09 +00:00
|
|
|
{
|
|
|
|
if (len < 2)
|
|
|
|
return false;
|
2013-12-04 16:10:54 +00:00
|
|
|
|
2015-05-29 08:37:09 +00:00
|
|
|
if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
|
|
|
|
return false;
|
|
|
|
|
2015-06-11 09:47:06 +00:00
|
|
|
if ((data[0] & 0xe0) != 0xc0 /* LLC UI */)
|
|
|
|
/* It is not an LLC UI frame, see TS 44.064, 6.3 */
|
2015-05-29 08:37:09 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
2013-11-07 06:21:06 +00:00
|
|
|
}
|
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
void llc_queue_init(struct gprs_llc_queue *q, struct GprsMs *ms)
|
2013-11-07 06:21:06 +00:00
|
|
|
{
|
2022-03-31 18:22:05 +00:00
|
|
|
unsigned int i;
|
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
q->ms = ms;
|
2022-03-31 17:08:07 +00:00
|
|
|
q->queue_size = 0;
|
|
|
|
q->queue_octets = 0;
|
|
|
|
q->avg_queue_delay = 0;
|
2022-04-04 11:45:56 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
|
|
|
|
INIT_LLIST_HEAD(&q->pq[i].queue);
|
|
|
|
gprs_codel_init(&q->pq[i].codel_state);
|
|
|
|
}
|
2015-05-29 08:37:09 +00:00
|
|
|
}
|
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
/* interval=0 -> don't use codel in the LLC queue */
|
|
|
|
void llc_queue_set_codel_interval(struct gprs_llc_queue *q, unsigned int interval)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
if (interval == LLC_CODEL_DISABLE) {
|
|
|
|
q->use_codel = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
q->use_codel = true;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(q->pq); i++)
|
|
|
|
gprs_codel_set_interval(&q->pq[i].codel_state, interval);
|
|
|
|
}
|
2020-02-26 19:05:33 +00:00
|
|
|
|
2022-03-31 18:22:05 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
void llc_queue_enqueue(struct gprs_llc_queue *q, struct msgb *llc_msg, const struct timespec *expire_time)
|
2015-05-29 08:37:09 +00:00
|
|
|
{
|
2022-03-31 17:36:12 +00:00
|
|
|
struct MetaInfo *meta_storage;
|
2022-03-31 18:22:05 +00:00
|
|
|
struct gprs_llc_hdr *llc_hdr = (struct gprs_llc_hdr *)msgb_data(llc_msg);
|
|
|
|
enum gprs_llc_queue_prio prio;
|
2015-06-15 12:32:33 +00:00
|
|
|
|
2020-02-26 19:05:33 +00:00
|
|
|
osmo_static_assert(sizeof(*meta_storage) <= sizeof(llc_msg->cb), info_does_not_fit);
|
2015-06-15 12:32:33 +00:00
|
|
|
|
2022-03-31 18:22:05 +00:00
|
|
|
prio = llc_sapi2prio(llc_hdr->sapi);
|
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
q->queue_size += 1;
|
|
|
|
q->queue_octets += msgb_length(llc_msg);
|
2015-06-15 12:32:33 +00:00
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
meta_storage = (struct MetaInfo *)&llc_msg->cb[0];
|
2020-03-11 13:04:52 +00:00
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
|
2020-02-26 19:05:33 +00:00
|
|
|
meta_storage->expire_time = *expire_time;
|
2015-06-15 12:32:33 +00:00
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
msgb_enqueue(&q->pq[prio].queue, llc_msg);
|
2015-05-29 08:37:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 15:48:38 +00:00
|
|
|
void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
|
2015-05-29 08:37:09 +00:00
|
|
|
{
|
|
|
|
struct msgb *msg;
|
2022-03-31 18:22:05 +00:00
|
|
|
unsigned int i;
|
2015-05-29 08:37:09 +00:00
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
|
|
|
|
while ((msg = msgb_dequeue(&q->pq[i].queue))) {
|
2022-03-31 18:22:05 +00:00
|
|
|
if (bts)
|
|
|
|
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
|
|
|
|
msgb_free(msg);
|
|
|
|
}
|
2015-05-29 08:37:09 +00:00
|
|
|
}
|
|
|
|
|
2022-03-31 17:08:07 +00:00
|
|
|
q->queue_size = 0;
|
|
|
|
q->queue_octets = 0;
|
2013-11-07 06:21:06 +00:00
|
|
|
}
|
|
|
|
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o)
|
2015-08-21 16:07:47 +00:00
|
|
|
{
|
|
|
|
struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
|
|
|
|
struct llist_head new_queue;
|
2022-03-31 18:22:05 +00:00
|
|
|
unsigned int i;
|
2015-08-21 16:07:47 +00:00
|
|
|
size_t queue_size = 0;
|
|
|
|
size_t queue_octets = 0;
|
|
|
|
INIT_LLIST_HEAD(&new_queue);
|
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
|
2022-03-31 18:22:05 +00:00
|
|
|
while (1) {
|
|
|
|
if (msg1 == NULL)
|
2022-04-04 11:45:56 +00:00
|
|
|
msg1 = msgb_dequeue(&q->pq[i].queue);
|
2015-08-21 16:07:47 +00:00
|
|
|
|
2022-03-31 18:22:05 +00:00
|
|
|
if (msg2 == NULL)
|
2022-04-04 11:45:56 +00:00
|
|
|
msg2 = msgb_dequeue(&o->pq[i].queue);
|
2015-08-21 16:07:47 +00:00
|
|
|
|
2022-03-31 18:22:05 +00:00
|
|
|
if (msg1 == NULL && msg2 == NULL)
|
|
|
|
break;
|
2015-08-21 16:07:47 +00:00
|
|
|
|
2022-03-31 18:22:05 +00:00
|
|
|
if (msg1 == NULL) {
|
|
|
|
msg = msg2;
|
|
|
|
msg2 = NULL;
|
|
|
|
} else if (msg2 == NULL) {
|
2015-08-21 16:07:47 +00:00
|
|
|
msg = msg1;
|
|
|
|
msg1 = NULL;
|
|
|
|
} else {
|
2022-03-31 18:22:05 +00:00
|
|
|
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;
|
|
|
|
}
|
2015-08-21 16:07:47 +00:00
|
|
|
}
|
2022-03-31 18:22:05 +00:00
|
|
|
|
|
|
|
msgb_enqueue(&new_queue, msg);
|
|
|
|
queue_size += 1;
|
|
|
|
queue_octets += msgb_length(msg);
|
2015-08-21 16:07:47 +00:00
|
|
|
}
|
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
OSMO_ASSERT(llist_empty(&q->pq[i].queue));
|
|
|
|
OSMO_ASSERT(llist_empty(&o->pq[i].queue));
|
|
|
|
llist_splice_init(&new_queue, &q->pq[i].queue);
|
2015-08-21 16:07:47 +00:00
|
|
|
}
|
|
|
|
|
2022-03-31 17:08:07 +00:00
|
|
|
o->queue_size = 0;
|
|
|
|
o->queue_octets = 0;
|
|
|
|
q->queue_size = queue_size;
|
|
|
|
q->queue_octets = queue_octets;
|
2015-08-21 16:07:47 +00:00
|
|
|
}
|
|
|
|
|
2013-12-04 17:11:47 +00:00
|
|
|
#define ALPHA 0.5f
|
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
static struct msgb *llc_queue_pick_msg(struct gprs_llc_queue *q, enum gprs_llc_queue_prio *prio)
|
2013-11-07 06:21:06 +00:00
|
|
|
{
|
2013-12-04 17:11:47 +00:00
|
|
|
struct msgb *msg;
|
2020-03-11 13:04:52 +00:00
|
|
|
struct timespec *tv, tv_now, tv_result;
|
2013-12-04 17:11:47 +00:00
|
|
|
uint32_t lifetime;
|
2022-03-31 18:22:05 +00:00
|
|
|
unsigned int i;
|
2022-03-31 17:36:12 +00:00
|
|
|
const struct MetaInfo *meta_storage;
|
2013-12-04 17:11:47 +00:00
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
|
|
|
|
if ((msg = msgb_dequeue(&q->pq[i].queue))) {
|
|
|
|
*prio = (enum gprs_llc_queue_prio)i;
|
2022-03-31 18:22:05 +00:00
|
|
|
break;
|
2022-04-04 11:45:56 +00:00
|
|
|
}
|
2022-03-31 18:22:05 +00:00
|
|
|
}
|
2013-12-04 17:11:47 +00:00
|
|
|
if (!msg)
|
|
|
|
return NULL;
|
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
meta_storage = (struct MetaInfo *)&msg->cb[0];
|
2015-06-15 12:32:33 +00:00
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
q->queue_size -= 1;
|
|
|
|
q->queue_octets -= msgb_length(msg);
|
2013-12-04 17:11:47 +00:00
|
|
|
|
|
|
|
/* take the second time */
|
2020-03-11 13:04:52 +00:00
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
|
|
|
|
tv = (struct timespec *)&msg->data[sizeof(*tv)];
|
|
|
|
timespecsub(&tv_now, &meta_storage->recv_time, &tv_result);
|
2013-12-04 17:11:47 +00:00
|
|
|
|
2020-03-11 13:04:52 +00:00
|
|
|
lifetime = tv_result.tv_sec*1000 + tv_result.tv_nsec/1000000;
|
2022-03-31 17:36:12 +00:00
|
|
|
q->avg_queue_delay = q->avg_queue_delay * ALPHA + lifetime * (1-ALPHA);
|
2013-12-04 17:11:47 +00:00
|
|
|
|
|
|
|
return msg;
|
2013-11-07 06:21:06 +00:00
|
|
|
}
|
2013-11-13 14:17:12 +00:00
|
|
|
|
2022-04-04 11:45:56 +00:00
|
|
|
struct msgb *llc_queue_dequeue(struct gprs_llc_queue *q)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
struct timespec tv_now, tv_now2;
|
|
|
|
uint32_t octets = 0, frames = 0;
|
|
|
|
struct gprs_rlcmac_bts *bts = q->ms->bts;
|
|
|
|
struct gprs_pcu *pcu = bts->pcu;
|
|
|
|
struct timespec hyst_delta = {0, 0};
|
|
|
|
const unsigned keep_small_thresh = 60;
|
|
|
|
enum gprs_llc_queue_prio prio;
|
|
|
|
|
|
|
|
if (pcu->vty.llc_discard_csec)
|
|
|
|
csecs_to_timespec(pcu->vty.llc_discard_csec, &hyst_delta);
|
|
|
|
|
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
|
|
|
|
timespecadd(&tv_now, &hyst_delta, &tv_now2);
|
|
|
|
|
|
|
|
while ((msg = llc_queue_pick_msg(q, &prio))) {
|
|
|
|
const struct MetaInfo *info = (const struct MetaInfo *)&msg->cb[0];
|
|
|
|
const struct timespec *tv_disc = &info->expire_time;
|
|
|
|
const struct timespec *tv_recv = &info->recv_time;
|
|
|
|
|
|
|
|
gprs_bssgp_update_queue_delay(tv_recv, &tv_now);
|
|
|
|
|
|
|
|
if (q->use_codel) {
|
|
|
|
int bytes = llc_queue_octets(q);
|
|
|
|
if (gprs_codel_control(&q->pq[prio].codel_state, tv_recv, &tv_now, bytes))
|
|
|
|
goto drop_frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Is the age below the low water mark? */
|
|
|
|
if (!llc_queue_is_frame_expired(&tv_now2, tv_disc))
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Is the age below the high water mark */
|
|
|
|
if (!llc_queue_is_frame_expired(&tv_now, tv_disc)) {
|
|
|
|
/* Has the previous message not been dropped? */
|
|
|
|
if (frames == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Hysteresis mode, try to discard LLC messages until
|
|
|
|
* the low water mark has been reached */
|
|
|
|
|
|
|
|
/* Check whether to abort the hysteresis mode */
|
|
|
|
|
|
|
|
/* Is the frame small, perhaps only a TCP ACK? */
|
|
|
|
if (msg->len <= keep_small_thresh)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Is it a GMM message? */
|
|
|
|
if (!llc_is_user_data_frame(msg->data, msg->len))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_TIMEDOUT);
|
|
|
|
drop_frame:
|
|
|
|
frames++;
|
|
|
|
octets += msg->len;
|
|
|
|
msgb_free(msg);
|
|
|
|
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frames) {
|
|
|
|
LOGPMS(q->ms, DTBFDL, LOGL_NOTICE, "Discarding LLC PDU "
|
|
|
|
"because lifetime limit reached, "
|
|
|
|
"count=%u new_queue_size=%zu\n",
|
|
|
|
frames, llc_queue_size(q));
|
|
|
|
if (frames > 0xff)
|
|
|
|
frames = 0xff;
|
|
|
|
if (octets > 0xffffff)
|
|
|
|
octets = 0xffffff;
|
|
|
|
if (pcu->bssgp.bctx)
|
|
|
|
bssgp_tx_llc_discarded(pcu->bssgp.bctx, ms_tlli(q->ms), frames, octets);
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
void llc_queue_calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec, struct timespec *tv)
|
2013-11-13 14:17:12 +00:00
|
|
|
{
|
|
|
|
uint16_t delay_csec;
|
2021-01-14 13:45:14 +00:00
|
|
|
if (bts->pcu->vty.force_llc_lifetime)
|
|
|
|
delay_csec = bts->pcu->vty.force_llc_lifetime;
|
2013-11-13 14:17:12 +00:00
|
|
|
else
|
|
|
|
delay_csec = pdu_delay_csec;
|
|
|
|
|
2013-11-21 20:59:32 +00:00
|
|
|
/* keep timestamp at 0 for infinite delay */
|
2013-11-13 14:19:39 +00:00
|
|
|
if (delay_csec == 0xffff) {
|
|
|
|
memset(tv, 0, sizeof(*tv));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* calculate timestamp of timeout */
|
2020-03-11 13:04:52 +00:00
|
|
|
struct timespec now, csec;
|
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
csecs_to_timespec(delay_csec, &csec);
|
2013-11-13 14:35:45 +00:00
|
|
|
|
2020-03-11 13:04:52 +00:00
|
|
|
timespecadd(&now, &csec, tv);
|
2013-11-13 14:17:12 +00:00
|
|
|
}
|
2013-11-13 16:15:26 +00:00
|
|
|
|
2022-03-31 17:36:12 +00:00
|
|
|
bool llc_queue_is_frame_expired(const struct timespec *tv_now, const struct timespec *tv)
|
2013-11-13 16:15:26 +00:00
|
|
|
{
|
|
|
|
/* Timeout is infinite */
|
2020-03-11 13:04:52 +00:00
|
|
|
if (tv->tv_sec == 0 && tv->tv_nsec == 0)
|
2013-11-13 16:15:26 +00:00
|
|
|
return false;
|
|
|
|
|
2020-03-11 13:04:52 +00:00
|
|
|
return timespeccmp(tv_now, tv, >);
|
2013-11-13 16:15:26 +00:00
|
|
|
}
|