[PATCH net-next v14 13/15] quic: add timer management
Xin Long <[email protected]> Wed, 15 Jul 2026 16:27:16 -0400
| Newsgroups | dev.linux.lists.quic,dev.linux.lists.kernel-tls-handshake,org.kernel.vger.linux-cifs,org.kernel.vger.netdev |
|---|---|
| Message-ID | <70e0bd6a967cbb4111b9f4f107b8ba0c3a3c0991.1784147238.git.lucien.xin@gmail.com> |
This patch introduces 'quic_timer' to unify and manage the five main timers used in QUIC: loss detection, delayed ACK, path validation, PMTU probing, and pacing. These timers are critical for driving retransmissions, connection liveness, and flow control. Each timer type is initialized, started, reset, or stopped using a common set of operations. - quic_timer_reset(): Reset a timer with type and timeout - quic_timer_start(): Start a timer with type and timeout - quic_timer_stop(): Stop a timer with type Although handler functions for each timer are defined, they are currently placeholders; their logic will be implemented in upcoming patches for packet transmission and outqueue handling. Deferred timer actions are also integrated through quic_release_cb(), which dispatches to the appropriate handler when timers expire. Signed-off-by: Tyler Fanelli <[email protected]> Signed-off-by: Xin Long <[email protected]> Acked-by: Paolo Abeni <[email protected]> --- v5: - Rename QUIC_TSQ_DEFERRED to QUIC_PACE_DEFERRED. v11: - Note for AI reviews: it is safe to avoid synchronous timer shutdown, as the timer holds a socket reference and handlers will not access pnspace/crypto/cong data once sk_state is closed in later patches. - Note for AI reviews: QUIC_F_MTU_REDUCED_DEFERRED will be used in a later patch; handling in quic_release_cb() will be added then. - Set maximum line length to 80 characters. - Add a check for type in quic_timer_reset(). - Extract quic_timer_timeout() from quic_timer_sack/loss/path/pmtu/pace_timeout() (noted by AI review). v12: - Remove quic_timer_reset_path() as it is no longer used. --- net/quic/Makefile | 2 +- net/quic/socket.c | 33 ++++++++++ net/quic/socket.h | 33 ++++++++++ net/quic/timer.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/timer.h | 45 ++++++++++++++ 5 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 net/quic/timer.c create mode 100644 net/quic/timer.h diff --git a/net/quic/Makefile b/net/quic/Makefile index 58bb18f7926d..2ccf01ad9e22 100644 --- a/net/quic/Makefile +++ b/net/quic/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_IP_QUIC) += quic.o quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o \ - cong.o pnspace.o crypto.o + cong.o pnspace.o crypto.o timer.o diff --git a/net/quic/socket.c b/net/quic/socket.c index 66d810dbaac9..08a1b2f64ef3 100644 --- a/net/quic/socket.c +++ b/net/quic/socket.c @@ -67,6 +67,8 @@ static int quic_init_sock(struct sock *sk) quic_conn_id_set_init(quic_dest(sk), false); quic_cong_init(quic_cong(sk)); + quic_timer_init(sk); + if (quic_stream_init(quic_streams(sk))) return -ENOMEM; @@ -82,6 +84,8 @@ static void quic_destroy_sock(struct sock *sk) { u8 i; + quic_timer_free(sk); + for (i = 0; i < QUIC_PNSPACE_MAX; i++) quic_pnspace_free(quic_pnspace(sk, i)); @@ -213,6 +217,35 @@ static int quic_getsockopt(struct sock *sk, int level, int optname, static void quic_release_cb(struct sock *sk) { + /* Similar to tcp_release_cb(). */ + unsigned long nflags, flags = smp_load_acquire(&sk->sk_tsq_flags); + + do { + if (!(flags & QUIC_DEFERRED_ALL)) + return; + nflags = flags & ~QUIC_DEFERRED_ALL; + } while (!try_cmpxchg(&sk->sk_tsq_flags, &flags, nflags)); + + if (flags & QUIC_F_LOSS_DEFERRED) { + quic_timer_loss_handler(sk); + __sock_put(sk); + } + if (flags & QUIC_F_SACK_DEFERRED) { + quic_timer_sack_handler(sk); + __sock_put(sk); + } + if (flags & QUIC_F_PATH_DEFERRED) { + quic_timer_path_handler(sk); + __sock_put(sk); + } + if (flags & QUIC_F_PMTU_DEFERRED) { + quic_timer_pmtu_handler(sk); + __sock_put(sk); + } + if (flags & QUIC_F_PACE_DEFERRED) { + quic_timer_pace_handler(sk); + __sock_put(sk); + } } static int quic_disconnect(struct sock *sk, int flags) diff --git a/net/quic/socket.h b/net/quic/socket.h index d7811391cc8b..c5654fdc06b5 100644 --- a/net/quic/socket.h +++ b/net/quic/socket.h @@ -21,6 +21,7 @@ #include "cong.h" #include "protocol.h" +#include "timer.h" extern struct proto quic_prot; extern struct proto quicv6_prot; @@ -32,6 +33,31 @@ enum quic_state { QUIC_SS_ESTABLISHED = TCP_ESTABLISHED, }; +enum quic_tsq_enum { + QUIC_MTU_REDUCED_DEFERRED, + QUIC_LOSS_DEFERRED, + QUIC_SACK_DEFERRED, + QUIC_PATH_DEFERRED, + QUIC_PMTU_DEFERRED, + QUIC_PACE_DEFERRED, +}; + +enum quic_tsq_flags { + QUIC_F_MTU_REDUCED_DEFERRED = BIT(QUIC_MTU_REDUCED_DEFERRED), + QUIC_F_LOSS_DEFERRED = BIT(QUIC_LOSS_DEFERRED), + QUIC_F_SACK_DEFERRED = BIT(QUIC_SACK_DEFERRED), + QUIC_F_PATH_DEFERRED = BIT(QUIC_PATH_DEFERRED), + QUIC_F_PMTU_DEFERRED = BIT(QUIC_PMTU_DEFERRED), + QUIC_F_PACE_DEFERRED = BIT(QUIC_PACE_DEFERRED), +}; + +#define QUIC_DEFERRED_ALL (QUIC_F_MTU_REDUCED_DEFERRED | \ + QUIC_F_LOSS_DEFERRED | \ + QUIC_F_SACK_DEFERRED | \ + QUIC_F_PATH_DEFERRED | \ + QUIC_F_PMTU_DEFERRED | \ + QUIC_F_PACE_DEFERRED) + struct quic_sock { struct inet_sock inet; struct list_head reqs; @@ -47,6 +73,8 @@ struct quic_sock { struct quic_cong cong; struct quic_pnspace space[QUIC_PNSPACE_MAX]; struct quic_crypto crypto[QUIC_CRYPTO_MAX]; + + struct quic_timer timers[QUIC_TIMER_MAX]; }; struct quic6_sock { @@ -119,6 +147,11 @@ static inline struct quic_crypto *quic_crypto(const struct sock *sk, u8 level) return &quic_sk(sk)->crypto[level]; } +static inline void *quic_timer(const struct sock *sk, u8 type) +{ + return (void *)&quic_sk(sk)->timers[type]; +} + static inline bool quic_is_establishing(struct sock *sk) { return sk->sk_state == QUIC_SS_ESTABLISHING; diff --git a/net/quic/timer.c b/net/quic/timer.c new file mode 100644 index 000000000000..0dd6d6580bbd --- /dev/null +++ b/net/quic/timer.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* QUIC kernel implementation + * (C) Copyright Red Hat Corp. 2023 + * + * This file is part of the QUIC kernel implementation + * + * Initialization/cleanup for QUIC protocol support. + * + * Written or modified by: + * Xin Long <[email protected]> + */ + +#include "socket.h" + +static void quic_timer_timeout(struct quic_timer *t, int type, int defer_bit, + void (*handler)(struct sock *sk)) +{ + struct quic_sock *qs = container_of(t, struct quic_sock, timers[type]); + struct sock *sk = &qs->inet.sk; + + bh_lock_sock(sk); + if (sock_owned_by_user(sk)) { + if (!test_and_set_bit(defer_bit, &sk->sk_tsq_flags)) + sock_hold(sk); + goto out; + } + + handler(sk); +out: + bh_unlock_sock(sk); + sock_put(sk); +} + +void quic_timer_sack_handler(struct sock *sk) +{ +} + +static void quic_timer_sack_timeout(struct timer_list *t) +{ + quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_SACK, + QUIC_SACK_DEFERRED, quic_timer_sack_handler); +} + +void quic_timer_loss_handler(struct sock *sk) +{ +} + +static void quic_timer_loss_timeout(struct timer_list *t) +{ + quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_LOSS, + QUIC_LOSS_DEFERRED, quic_timer_loss_handler); +} + +void quic_timer_path_handler(struct sock *sk) +{ +} + +static void quic_timer_path_timeout(struct timer_list *t) +{ + quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PATH, + QUIC_PATH_DEFERRED, quic_timer_path_handler); +} + +void quic_timer_pmtu_handler(struct sock *sk) +{ +} + +static void quic_timer_pmtu_timeout(struct timer_list *t) +{ + quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PMTU, + QUIC_PMTU_DEFERRED, quic_timer_pmtu_handler); +} + +void quic_timer_pace_handler(struct sock *sk) +{ +} + +static enum hrtimer_restart quic_timer_pace_timeout(struct hrtimer *hr) +{ + quic_timer_timeout((struct quic_timer *)hr, QUIC_TIMER_PACE, + QUIC_PACE_DEFERRED, quic_timer_pace_handler); + return HRTIMER_NORESTART; +} + +void quic_timer_reset(struct sock *sk, u8 type, u64 timeout) +{ + struct timer_list *t = quic_timer(sk, type); + + /* Note that type must never be QUIC_TIMER_PACE for this helper. */ + if (WARN_ON_ONCE(type == QUIC_TIMER_PACE)) + return; + if (timeout && !mod_timer(t, jiffies + usecs_to_jiffies(timeout))) + sock_hold(sk); +} + +void quic_timer_start(struct sock *sk, u8 type, u64 timeout) +{ + struct timer_list *t; + struct hrtimer *hr; + + if (type == QUIC_TIMER_PACE) { + hr = quic_timer(sk, type); + + if (!hrtimer_is_queued(hr)) { + hrtimer_start(hr, ns_to_ktime(timeout), + HRTIMER_MODE_ABS_PINNED_SOFT); + sock_hold(sk); + } + return; + } + + t = quic_timer(sk, type); + if (timeout && !timer_pending(t)) { + if (!mod_timer(t, jiffies + usecs_to_jiffies(timeout))) + sock_hold(sk); + } +} + +void quic_timer_stop(struct sock *sk, u8 type) +{ + if (type == QUIC_TIMER_PACE) { + if (hrtimer_try_to_cancel(quic_timer(sk, type)) == 1) + sock_put(sk); + return; + } + if (timer_delete(quic_timer(sk, type))) + sock_put(sk); +} + +void quic_timer_init(struct sock *sk) +{ + timer_setup(quic_timer(sk, QUIC_TIMER_LOSS), quic_timer_loss_timeout, + 0); + timer_setup(quic_timer(sk, QUIC_TIMER_SACK), quic_timer_sack_timeout, + 0); + timer_setup(quic_timer(sk, QUIC_TIMER_PATH), quic_timer_path_timeout, + 0); + timer_setup(quic_timer(sk, QUIC_TIMER_PMTU), quic_timer_pmtu_timeout, + 0); + /* Use hrtimer for pace timer, ensuring precise control over send + * timing. + */ + hrtimer_setup(quic_timer(sk, QUIC_TIMER_PACE), quic_timer_pace_timeout, + CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED_SOFT); +} + +void quic_timer_free(struct sock *sk) +{ + quic_timer_stop(sk, QUIC_TIMER_LOSS); + quic_timer_stop(sk, QUIC_TIMER_SACK); + quic_timer_stop(sk, QUIC_TIMER_PATH); + quic_timer_stop(sk, QUIC_TIMER_PMTU); + quic_timer_stop(sk, QUIC_TIMER_PACE); +} diff --git a/net/quic/timer.h b/net/quic/timer.h new file mode 100644 index 000000000000..4f6366037602 --- /dev/null +++ b/net/quic/timer.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* QUIC kernel implementation + * (C) Copyright Red Hat Corp. 2023 + * + * This file is part of the QUIC kernel implementation + * + * Written or modified by: + * Xin Long <[email protected]> + */ + +enum { + QUIC_TIMER_LOSS, /* Loss detection timer: retransmit on packet loss */ + QUIC_TIMER_SACK, /* ACK delay timer, also used as idle timer alias */ + QUIC_TIMER_PATH, /* Path validation timer: verifies path connectivity */ + QUIC_TIMER_PMTU, /* PLPMTUD probing timer */ + QUIC_TIMER_PACE, /* Pacing timer: controls packet transmission pacing */ + QUIC_TIMER_MAX, + QUIC_TIMER_IDLE = QUIC_TIMER_SACK, +}; + +struct quic_timer { + union { + struct timer_list t; + struct hrtimer hr; + }; +}; + +#define QUIC_MIN_PROBE_TIMEOUT 5000000 + +#define QUIC_MIN_PATH_TIMEOUT 1500000 + +#define QUIC_MIN_IDLE_TIMEOUT 1000000 +#define QUIC_DEF_IDLE_TIMEOUT 30000000 + +void quic_timer_reset(struct sock *sk, u8 type, u64 timeout); +void quic_timer_start(struct sock *sk, u8 type, u64 timeout); +void quic_timer_stop(struct sock *sk, u8 type); +void quic_timer_init(struct sock *sk); +void quic_timer_free(struct sock *sk); + +void quic_timer_loss_handler(struct sock *sk); +void quic_timer_pace_handler(struct sock *sk); +void quic_timer_path_handler(struct sock *sk); +void quic_timer_sack_handler(struct sock *sk); +void quic_timer_pmtu_handler(struct sock *sk); -- 2.47.1