blob: 7745bd097415ed29318002f5bbf603f52a211bb1 [file] [log] [blame]
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +01001/* Copied from tbf.cpp
2 *
3 * Copyright (C) 2012 Ivan Klyuchnikov
4 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
5 * Copyright (C) 2013 by Holger Hans Peter Freyther
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010016 */
17
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010018
Holger Hans Peter Freyther857281f2013-11-13 14:56:55 +010019#include <stdio.h>
20
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010021#include <osmocom/core/msgb.h>
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010022
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020023#include "bts.h"
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +020024#include "gprs_ms.h"
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010025#include "pcu_utils.h"
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020026#include "llc.h"
27
28void llc_init(struct gprs_llc *llc)
29{
30 llc_reset(llc);
31}
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010032
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010033/* reset LLC frame */
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020034void llc_reset(struct gprs_llc *llc)
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010035{
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020036 llc->index = 0;
37 llc->length = 0;
Holger Hans Peter Freythera42b2ad2013-11-26 16:39:47 +010038
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020039 memset(llc->frame, 0x42, sizeof(llc->frame));
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010040}
41
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020042void llc_reset_frame_space(struct gprs_llc *llc)
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010043{
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020044 llc->index = 0;
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010045}
46
Jacob Erlbeck612e93e2015-03-20 13:57:27 +010047/* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020048void llc_put_dummy_frame(struct gprs_llc *llc, size_t req_len)
Jacob Erlbeck612e93e2015-03-20 13:57:27 +010049{
50 /* The shortest dummy command (the spec requests at least 6 octets) */
51 static const uint8_t llc_dummy_command[] = {
52 0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
53 };
Jacob Erlbeck3db617f2015-07-07 13:43:44 +020054 static const size_t max_dummy_command_len = 79;
Jacob Erlbeck612e93e2015-03-20 13:57:27 +010055
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020056 llc_put_frame(llc, llc_dummy_command, sizeof(llc_dummy_command));
Jacob Erlbeck612e93e2015-03-20 13:57:27 +010057
58 if (req_len > max_dummy_command_len)
59 req_len = max_dummy_command_len;
60
61 /* Add further stuffing, if the requested length exceeds the minimum
62 * dummy command length */
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020063 if (llc->length < req_len) {
64 memset(&llc->frame[llc->length], 0x2b, req_len - llc->length);
65 llc->length = req_len;
Pau Espin Pedrola70bf722021-03-02 12:28:32 +010066 }
Jacob Erlbeck612e93e2015-03-20 13:57:27 +010067}
68
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020069void llc_put_frame(struct gprs_llc *llc, const uint8_t *data, size_t len)
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010070{
Holger Hans Peter Freyther857281f2013-11-13 14:56:55 +010071 /* only put frames when we are empty */
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020072 OSMO_ASSERT(llc->index == 0 && llc->length == 0);
73 llc_append_frame(llc, data, len);
Holger Hans Peter Freythere2310262013-11-13 16:56:15 +010074}
Holger Hans Peter Freyther857281f2013-11-13 14:56:55 +010075
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020076void llc_append_frame(struct gprs_llc *llc, const uint8_t *data, size_t len)
Holger Hans Peter Freythere2310262013-11-13 16:56:15 +010077{
Holger Hans Peter Freyther857281f2013-11-13 14:56:55 +010078 /* TODO: bounds check */
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +020079 memcpy(llc->frame + llc->length, data, len);
80 llc->length += len;
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +010081}
82
Pau Espin Pedrol1111aa12023-01-26 15:09:48 +010083static bool llc_pdu_can_be_discarded(const uint8_t *data, size_t len)
Jacob Erlbeck6dbe8222015-05-29 10:37:09 +020084{
Pau Espin Pedrol1111aa12023-01-26 15:09:48 +010085 const unsigned keep_small_thresh = 60;
86
87 /* Is the frame small, perhaps only a TCP ACK? */
88 if (len <= keep_small_thresh)
Jacob Erlbeck6dbe8222015-05-29 10:37:09 +020089 return false;
90
91 if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
92 return false;
93
Jacob Erlbeckd0aee852015-06-11 11:47:06 +020094 if ((data[0] & 0xe0) != 0xc0 /* LLC UI */)
95 /* It is not an LLC UI frame, see TS 44.064, 6.3 */
Jacob Erlbeck6dbe8222015-05-29 10:37:09 +020096 return false;
97
98 return true;
99}
100
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200101void llc_queue_init(struct gprs_llc_queue *q, struct GprsMs *ms)
Jacob Erlbeck6dbe8222015-05-29 10:37:09 +0200102{
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200103 unsigned int i;
104
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200105 q->ms = ms;
Pau Espin Pedrol14633832022-03-31 19:08:07 +0200106 q->queue_size = 0;
107 q->queue_octets = 0;
108 q->avg_queue_delay = 0;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200109 for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
110 INIT_LLIST_HEAD(&q->pq[i].queue);
111 gprs_codel_init(&q->pq[i].codel_state);
112 }
Jacob Erlbeck6dbe8222015-05-29 10:37:09 +0200113}
114
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200115/* interval=0 -> don't use codel in the LLC queue */
116void llc_queue_set_codel_interval(struct gprs_llc_queue *q, unsigned int interval)
117{
118 unsigned int i;
119 if (interval == LLC_CODEL_DISABLE) {
120 q->use_codel = false;
121 return;
122 }
123 q->use_codel = true;
124 for (i = 0; i < ARRAY_SIZE(q->pq); i++)
125 gprs_codel_set_interval(&q->pq[i].codel_state, interval);
126}
Pau Espin Pedrol5fc6e012020-02-26 20:05:33 +0100127
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200128static enum gprs_llc_queue_prio llc_sapi2prio(uint8_t sapi)
129{
130 switch (sapi) {
131 case 1:
132 return LLC_QUEUE_PRIO_GMM;
133 case 2:
134 case 7:
135 case 8:
136 return LLC_QUEUE_PRIO_TOM_SMS;
137 default:
138 return LLC_QUEUE_PRIO_OTHER;
139 }
140}
141
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200142void llc_queue_enqueue(struct gprs_llc_queue *q, struct msgb *llc_msg, const struct timespec *expire_time)
Jacob Erlbeck6dbe8222015-05-29 10:37:09 +0200143{
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200144 struct MetaInfo *meta_storage;
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200145 struct gprs_llc_hdr *llc_hdr = (struct gprs_llc_hdr *)msgb_data(llc_msg);
146 enum gprs_llc_queue_prio prio;
Jacob Erlbeckb671dbf2015-06-15 14:32:33 +0200147
Pau Espin Pedrol5fc6e012020-02-26 20:05:33 +0100148 osmo_static_assert(sizeof(*meta_storage) <= sizeof(llc_msg->cb), info_does_not_fit);
Jacob Erlbeckb671dbf2015-06-15 14:32:33 +0200149
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200150 prio = llc_sapi2prio(llc_hdr->sapi);
151
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200152 q->queue_size += 1;
153 q->queue_octets += msgb_length(llc_msg);
Jacob Erlbeckb671dbf2015-06-15 14:32:33 +0200154
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200155 meta_storage = (struct MetaInfo *)&llc_msg->cb[0];
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100156 osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
Pau Espin Pedrol5fc6e012020-02-26 20:05:33 +0100157 meta_storage->expire_time = *expire_time;
Jacob Erlbeckb671dbf2015-06-15 14:32:33 +0200158
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200159 msgb_enqueue(&q->pq[prio].queue, llc_msg);
Jacob Erlbeck6dbe8222015-05-29 10:37:09 +0200160}
161
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100162void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +0100163{
164 struct msgb *msg;
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200165 unsigned int i;
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +0100166
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200167 for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
168 while ((msg = msgb_dequeue(&q->pq[i].queue))) {
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200169 if (bts)
170 bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
171 msgb_free(msg);
172 }
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +0100173 }
Holger Hans Peter Freyther550bb882013-12-04 17:10:54 +0100174
Pau Espin Pedrol14633832022-03-31 19:08:07 +0200175 q->queue_size = 0;
176 q->queue_octets = 0;
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +0100177}
178
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100179void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o)
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200180{
181 struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
182 struct llist_head new_queue;
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200183 unsigned int i;
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200184 size_t queue_size = 0;
185 size_t queue_octets = 0;
186 INIT_LLIST_HEAD(&new_queue);
187
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200188 for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200189 while (1) {
190 if (msg1 == NULL)
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200191 msg1 = msgb_dequeue(&q->pq[i].queue);
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200192
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200193 if (msg2 == NULL)
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200194 msg2 = msgb_dequeue(&o->pq[i].queue);
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200195
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200196 if (msg1 == NULL && msg2 == NULL)
197 break;
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200198
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200199 if (msg1 == NULL) {
200 msg = msg2;
201 msg2 = NULL;
202 } else if (msg2 == NULL) {
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200203 msg = msg1;
204 msg1 = NULL;
205 } else {
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200206 const struct MetaInfo *mi1 = (struct MetaInfo *)&msg1->cb[0];
207 const struct MetaInfo *mi2 = (struct MetaInfo *)&msg2->cb[0];
208
209 if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) {
210 msg = msg1;
211 msg1 = NULL;
212 } else {
213 msg = msg2;
214 msg2 = NULL;
215 }
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200216 }
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200217
218 msgb_enqueue(&new_queue, msg);
219 queue_size += 1;
220 queue_octets += msgb_length(msg);
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200221 }
222
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200223 OSMO_ASSERT(llist_empty(&q->pq[i].queue));
224 OSMO_ASSERT(llist_empty(&o->pq[i].queue));
225 llist_splice_init(&new_queue, &q->pq[i].queue);
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200226 }
227
Pau Espin Pedrol14633832022-03-31 19:08:07 +0200228 o->queue_size = 0;
229 o->queue_octets = 0;
Pau Espin Pedrol14633832022-03-31 19:08:07 +0200230 q->queue_size = queue_size;
231 q->queue_octets = queue_octets;
Jacob Erlbeck257b6302015-08-21 18:07:47 +0200232}
233
Daniel Willmann9c623892013-12-04 18:11:47 +0100234#define ALPHA 0.5f
235
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200236static struct msgb *llc_queue_pick_msg(struct gprs_llc_queue *q, enum gprs_llc_queue_prio *prio)
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +0100237{
Daniel Willmann9c623892013-12-04 18:11:47 +0100238 struct msgb *msg;
Pau Espin Pedrole0a84882023-01-26 18:20:08 +0100239 struct timespec tv_now, tv_result;
Daniel Willmann9c623892013-12-04 18:11:47 +0100240 uint32_t lifetime;
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200241 unsigned int i;
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200242 const struct MetaInfo *meta_storage;
Daniel Willmann9c623892013-12-04 18:11:47 +0100243
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200244 for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
245 if ((msg = msgb_dequeue(&q->pq[i].queue))) {
246 *prio = (enum gprs_llc_queue_prio)i;
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200247 break;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200248 }
Pau Espin Pedrol5c598c72022-03-31 20:22:05 +0200249 }
Daniel Willmann9c623892013-12-04 18:11:47 +0100250 if (!msg)
251 return NULL;
252
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200253 meta_storage = (struct MetaInfo *)&msg->cb[0];
Jacob Erlbeckb671dbf2015-06-15 14:32:33 +0200254
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200255 q->queue_size -= 1;
256 q->queue_octets -= msgb_length(msg);
Daniel Willmann9c623892013-12-04 18:11:47 +0100257
258 /* take the second time */
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100259 osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100260 timespecsub(&tv_now, &meta_storage->recv_time, &tv_result);
Daniel Willmann9c623892013-12-04 18:11:47 +0100261
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100262 lifetime = tv_result.tv_sec*1000 + tv_result.tv_nsec/1000000;
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200263 q->avg_queue_delay = q->avg_queue_delay * ALPHA + lifetime * (1-ALPHA);
Daniel Willmann9c623892013-12-04 18:11:47 +0100264
265 return msg;
Holger Hans Peter Freyther096f6f92013-11-07 07:21:06 +0100266}
Holger Hans Peter Freytherfce431c2013-11-13 15:17:12 +0100267
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200268struct msgb *llc_queue_dequeue(struct gprs_llc_queue *q)
269{
270 struct msgb *msg;
271 struct timespec tv_now, tv_now2;
272 uint32_t octets = 0, frames = 0;
273 struct gprs_rlcmac_bts *bts = q->ms->bts;
274 struct gprs_pcu *pcu = bts->pcu;
275 struct timespec hyst_delta = {0, 0};
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200276 enum gprs_llc_queue_prio prio;
277
278 if (pcu->vty.llc_discard_csec)
279 csecs_to_timespec(pcu->vty.llc_discard_csec, &hyst_delta);
280
281 osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
282 timespecadd(&tv_now, &hyst_delta, &tv_now2);
283
284 while ((msg = llc_queue_pick_msg(q, &prio))) {
285 const struct MetaInfo *info = (const struct MetaInfo *)&msg->cb[0];
286 const struct timespec *tv_disc = &info->expire_time;
287 const struct timespec *tv_recv = &info->recv_time;
288
289 gprs_bssgp_update_queue_delay(tv_recv, &tv_now);
290
291 if (q->use_codel) {
292 int bytes = llc_queue_octets(q);
293 if (gprs_codel_control(&q->pq[prio].codel_state, tv_recv, &tv_now, bytes))
294 goto drop_frame;
295 }
296
297 /* Is the age below the low water mark? */
298 if (!llc_queue_is_frame_expired(&tv_now2, tv_disc))
299 break;
300
301 /* Is the age below the high water mark */
302 if (!llc_queue_is_frame_expired(&tv_now, tv_disc)) {
303 /* Has the previous message not been dropped? */
304 if (frames == 0)
305 break;
306
307 /* Hysteresis mode, try to discard LLC messages until
308 * the low water mark has been reached */
309
Pau Espin Pedrol1111aa12023-01-26 15:09:48 +0100310 /* Check whether to abort the hysteresis mode:
311 * Can the PDU be discarded according to its type? */
312 if (!llc_pdu_can_be_discarded(msg->data, msg->len))
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200313 break;
314 }
315
316 bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_TIMEDOUT);
317drop_frame:
318 frames++;
319 octets += msg->len;
320 msgb_free(msg);
321 bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
322 continue;
323 }
324
325 if (frames) {
326 LOGPMS(q->ms, DTBFDL, LOGL_NOTICE, "Discarding LLC PDU "
327 "because lifetime limit reached, "
328 "count=%u new_queue_size=%zu\n",
329 frames, llc_queue_size(q));
330 if (frames > 0xff)
331 frames = 0xff;
332 if (octets > 0xffffff)
333 octets = 0xffffff;
334 if (pcu->bssgp.bctx)
335 bssgp_tx_llc_discarded(pcu->bssgp.bctx, ms_tlli(q->ms), frames, octets);
336 }
337
338 return msg;
339}
340
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200341void llc_queue_calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec, struct timespec *tv)
Holger Hans Peter Freytherfce431c2013-11-13 15:17:12 +0100342{
343 uint16_t delay_csec;
Pau Espin Pedrolf473ec92021-01-14 14:45:14 +0100344 if (bts->pcu->vty.force_llc_lifetime)
345 delay_csec = bts->pcu->vty.force_llc_lifetime;
Holger Hans Peter Freytherfce431c2013-11-13 15:17:12 +0100346 else
347 delay_csec = pdu_delay_csec;
348
Holger Hans Peter Freyther5a7f6362013-11-21 21:59:32 +0100349 /* keep timestamp at 0 for infinite delay */
Holger Hans Peter Freyther985fd112013-11-13 15:19:39 +0100350 if (delay_csec == 0xffff) {
351 memset(tv, 0, sizeof(*tv));
352 return;
353 }
354
355 /* calculate timestamp of timeout */
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100356 struct timespec now, csec;
357 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
358 csecs_to_timespec(delay_csec, &csec);
Holger Hans Peter Freyther51e093b2013-11-13 15:35:45 +0100359
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100360 timespecadd(&now, &csec, tv);
Holger Hans Peter Freytherfce431c2013-11-13 15:17:12 +0100361}
Holger Hans Peter Freytherb1302b02013-11-13 17:15:26 +0100362
Pau Espin Pedrol4f8384b2022-03-31 19:36:12 +0200363bool llc_queue_is_frame_expired(const struct timespec *tv_now, const struct timespec *tv)
Holger Hans Peter Freytherb1302b02013-11-13 17:15:26 +0100364{
365 /* Timeout is infinite */
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100366 if (tv->tv_sec == 0 && tv->tv_nsec == 0)
Holger Hans Peter Freytherb1302b02013-11-13 17:15:26 +0100367 return false;
368
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100369 return timespeccmp(tv_now, tv, >);
Holger Hans Peter Freytherb1302b02013-11-13 17:15:26 +0100370}