From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qv1-f44.google.com (mail-qv1-f44.google.com [209.85.219.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 939CA33AD82 for ; Mon, 5 Jan 2026 14:08:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.44 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767622121; cv=none; b=UEAXJ+iXyy+KM+SZrSZ4GUeGzV0iXbcJT20qzFjMK/zSPv/wy5SpQlUllC2W6TFCB4ZbOTKArWNHK1ZeLYiOLBIggS4hKmkD9VlWzFI/TGJXyadp6w7uAMeOvKYfI1szuqo+TXAKt6/7RGVv1P4dMQGDY55ZfHNVlek5kFqpMlg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767622121; c=relaxed/simple; bh=UVdt4O3MXXGZWpPUvZJL+BEeg/ROxMGZcGZGQPuT86Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kokI3vo9WzMEfMvx4i+8PEfzzxTI9T9CoMLzBRC6NB/o16u8/Gku6DgnluF70vKIGlNanbXlk1rIgQIb5Q1gum219A2ryGtENCIOW2equ/qxGL60t3zL9KnPzV2qzk4fLQ21sXTgO2kkhUTkQ7ymsXwZwCyTqLUoTyXoyyoKnCk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gOD6/wwr; arc=none smtp.client-ip=209.85.219.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gOD6/wwr" Received: by mail-qv1-f44.google.com with SMTP id 6a1803df08f44-88a3d2f3299so166195516d6.2 for ; Mon, 05 Jan 2026 06:08:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1767622111; x=1768226911; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=NYW82/OQPizvlnuIFpJI9bh1i/PGfyIpeKywW7eClrI=; b=gOD6/wwr4CbPmaY+58B/Zantv5tWcdCJNrORREmWTP9HHuyBRRfC3UPkiPoD1VFBk6 nVuiFJCVgdqvQrUFyobU48HCBVS4zWUEVh2qTF4Lifsdx3+I2+D2OWO8S8KBlXqklwma 6StOYolvlTCj4orGRNdtgbZddK1BFqqpelQIDPXu8Qsuk7El9OlDR8ZxtQV+9mf7wNms mspOgdySz/gbXTFzJ53Ezq1jt17xQs9EI27Likgz4BulnAwTuOLQih7Z/V0cG0q0r6QV 05FDC3L4sDpS/wKML5jWa47ZvMCqThArBwEnZlsp5XhW/Ulz+nGF6jJsbqFrQwULYfYo 2a0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767622111; x=1768226911; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=NYW82/OQPizvlnuIFpJI9bh1i/PGfyIpeKywW7eClrI=; b=QsvAY7M8FTi6mX3byKxJjlKKdvEL/IOZnNTdspVdI6NduyKwS5Nut4Q75fqibkHzyu kiWjt8OUjosusjqhqOs+xGsAy35sumsArnNRsAVRz54VNdWSA9+HuNMSoA/PL/SU/bZ9 KtrmgNqA8gp8pH6/9KR0si2Z6zdcSZ4bvHjRdHZ19EcveacMVZZiLT4GVpuBIGeL0OZn IXTzPXSNju3c/RN5VklABNRZgc4BSxTgc12Ym5lM06Qbiz974IYdbVOmxhjzAffNP973 HFFp3tBYU/9UMSL1WJUhOtcqfKJR4QnRNWehm1J06GyYQe4cObW+shJemwnkY2E211kE aaaA== X-Forwarded-Encrypted: i=1; AJvYcCWMzv5qVsvq0Zs2fT06pmu/PimU5+/aj8Jh0geQUJLwkR9vWH9sErfNZvJZQN3ICLTM/3gL@lists.linux.dev X-Gm-Message-State: AOJu0YzNhTx84Tu0PLZl/2+SFzR29slO7AoeSsOLcUrGocj0iZXEzHKS dI+evBoIEpvQgnxo2Uer01tz7g57OdqB33r84plJHQ9vL/H3BoZgrFXS X-Gm-Gg: AY/fxX73k6ke/Rfdqyzs/4kSeMeE0he4aZhc3fsABTPTmVrEzFPEqKPwr0Y4Z9PlP7F SS3vfdDotqxZ0a9Ld0MPBqQER29Ht6HfYHbFfxx4UFRQ2SUsLEGAegB6cVfMeu9ukF4cwen3yx4 6G8oWiEoxmr3astUTwMEcmlCZcEynOfFunW3J0CrnqohE/V+gO2NCcrj8nLg3QNpMu74gOKdcb+ Nwve5addbmtYYFhp3yDnH0vFJdELvh8JruZOv/m78qKnjhbTjt0wA6DjGMpkK12t7Dwci/RGqCx BGJlhhBwk4eCje6OdtoNLI7HNxORnGHzXOCajV0bxznQQtCirTWCSetaZhB8B4DGf223Cboff5M fOXDfOFu88iJFw3y9M0XP1B8YQMKpi7y6F5Kz8/K+B/Tge5QxeOEOTwG0jtGVoxD3WXv0m/VMoH pPjQ1daPXZl3XdET+a3yKRLXfXnrnfGBYbh0HGGGqK78bhIg03heo= X-Google-Smtp-Source: AGHT+IEUingJGfOq2s82LE9dZ5jdvMJFktjgTZ3muMZ3bac9oXIPcET0q6f9P3eC/0aoQ4A+p/qYaQ== X-Received: by 2002:a05:6214:124d:b0:880:5730:d3db with SMTP id 6a1803df08f44-88d84b20ebemr763302176d6.21.1767622110617; Mon, 05 Jan 2026 06:08:30 -0800 (PST) Received: from wsfd-netdev58.anl.eng.rdu2.dc.redhat.com ([66.187.232.140]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-4f4ac64a47esm368957221cf.24.2026.01.05.06.08.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 05 Jan 2026 06:08:29 -0800 (PST) From: Xin Long To: network dev , quic@lists.linux.dev Cc: davem@davemloft.net, kuba@kernel.org, Eric Dumazet , Paolo Abeni , Simon Horman , Stefan Metzmacher , Moritz Buhl , Tyler Fanelli , Pengtao He , Thomas Dreibholz , linux-cifs@vger.kernel.org, Steve French , Namjae Jeon , Paulo Alcantara , Tom Talpey , kernel-tls-handshake@lists.linux.dev, Chuck Lever , Jeff Layton , Steve Dickson , Hannes Reinecke , Alexander Aring , David Howells , Matthieu Baerts , John Ericson , Cong Wang , "D . Wythe" , Jason Baron , illiliti , Sabrina Dubroca , Marcelo Ricardo Leitner , Daniel Stenberg , Andy Gospodarek Subject: [PATCH net-next v6 13/16] quic: add timer management Date: Mon, 5 Jan 2026 09:04:39 -0500 Message-ID: <8f4613919d2af6399f0c7ba29b819374c2f9b1ca.1767621882.git.lucien.xin@gmail.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: quic@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Xin Long Acked-by: Paolo Abeni --- v5: - Rename QUIC_TSQ_DEFERRED to QUIC_PACE_DEFERRED. --- net/quic/Makefile | 2 +- net/quic/socket.c | 33 ++++++++ net/quic/socket.h | 33 ++++++++ net/quic/timer.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/timer.h | 47 +++++++++++ 5 files changed, 310 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 895fbcbdce22..0a6e59f85d32 100644 --- a/net/quic/socket.c +++ b/net/quic/socket.c @@ -47,6 +47,8 @@ static int quic_init_sock(struct sock *sk) quic_conn_id_set_init(quic_dest(sk), 0); quic_cong_init(quic_cong(sk)); + quic_timer_init(sk); + if (quic_stream_init(quic_streams(sk))) return -ENOMEM; @@ -68,6 +70,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)); for (i = 0; i < QUIC_CRYPTO_MAX; i++) @@ -204,6 +208,35 @@ EXPORT_SYMBOL_GPL(quic_kernel_getsockopt); 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 fc203eecbb8b..5e9b21430f42 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; @@ -48,6 +74,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 { @@ -125,6 +153,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..6f957385a341 --- /dev/null +++ b/net/quic/timer.c @@ -0,0 +1,196 @@ +// 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 + */ + +#include "socket.h" + +void quic_timer_sack_handler(struct sock *sk) +{ +} + +static void quic_timer_sack_timeout(struct timer_list *t) +{ + struct quic_sock *qs = container_of(t, struct quic_sock, timers[QUIC_TIMER_SACK].t); + struct sock *sk = &qs->inet.sk; + + bh_lock_sock(sk); + if (sock_owned_by_user(sk)) { + if (!test_and_set_bit(QUIC_SACK_DEFERRED, &sk->sk_tsq_flags)) + sock_hold(sk); + goto out; + } + + quic_timer_sack_handler(sk); +out: + bh_unlock_sock(sk); + sock_put(sk); +} + +void quic_timer_loss_handler(struct sock *sk) +{ +} + +static void quic_timer_loss_timeout(struct timer_list *t) +{ + struct quic_sock *qs = container_of(t, struct quic_sock, timers[QUIC_TIMER_LOSS].t); + struct sock *sk = &qs->inet.sk; + + bh_lock_sock(sk); + if (sock_owned_by_user(sk)) { + if (!test_and_set_bit(QUIC_LOSS_DEFERRED, &sk->sk_tsq_flags)) + sock_hold(sk); + goto out; + } + + quic_timer_loss_handler(sk); +out: + bh_unlock_sock(sk); + sock_put(sk); +} + +void quic_timer_path_handler(struct sock *sk) +{ +} + +static void quic_timer_path_timeout(struct timer_list *t) +{ + struct quic_sock *qs = container_of(t, struct quic_sock, timers[QUIC_TIMER_PATH].t); + struct sock *sk = &qs->inet.sk; + + bh_lock_sock(sk); + if (sock_owned_by_user(sk)) { + if (!test_and_set_bit(QUIC_PATH_DEFERRED, &sk->sk_tsq_flags)) + sock_hold(sk); + goto out; + } + + quic_timer_path_handler(sk); +out: + bh_unlock_sock(sk); + sock_put(sk); +} + +void quic_timer_reset_path(struct sock *sk) +{ + struct quic_cong *cong = quic_cong(sk); + u64 timeout = cong->pto * 2; + + /* Calculate timeout based on cong.pto, but enforce a lower bound. */ + if (timeout < QUIC_MIN_PATH_TIMEOUT) + timeout = QUIC_MIN_PATH_TIMEOUT; + quic_timer_reset(sk, QUIC_TIMER_PATH, timeout); +} + +void quic_timer_pmtu_handler(struct sock *sk) +{ +} + +static void quic_timer_pmtu_timeout(struct timer_list *t) +{ + struct quic_sock *qs = container_of(t, struct quic_sock, timers[QUIC_TIMER_PMTU].t); + struct sock *sk = &qs->inet.sk; + + bh_lock_sock(sk); + if (sock_owned_by_user(sk)) { + if (!test_and_set_bit(QUIC_PMTU_DEFERRED, &sk->sk_tsq_flags)) + sock_hold(sk); + goto out; + } + + quic_timer_pmtu_handler(sk); +out: + bh_unlock_sock(sk); + sock_put(sk); +} + +void quic_timer_pace_handler(struct sock *sk) +{ +} + +static enum hrtimer_restart quic_timer_pace_timeout(struct hrtimer *hr) +{ + struct quic_sock *qs = container_of(hr, struct quic_sock, timers[QUIC_TIMER_PACE].hr); + struct sock *sk = &qs->inet.sk; + + bh_lock_sock(sk); + if (sock_owned_by_user(sk)) { + if (!test_and_set_bit(QUIC_PACE_DEFERRED, &sk->sk_tsq_flags)) + sock_hold(sk); + goto out; + } + + quic_timer_pace_handler(sk); +out: + bh_unlock_sock(sk); + sock_put(sk); + return HRTIMER_NORESTART; +} + +void quic_timer_reset(struct sock *sk, u8 type, u64 timeout) +{ + struct timer_list *t = quic_timer(sk, type); + + 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..61b094325334 --- /dev/null +++ b/net/quic/timer.h @@ -0,0 +1,47 @@ +/* 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 + */ + +enum { + QUIC_TIMER_LOSS, /* Loss detection timer: triggers retransmission on packet loss */ + QUIC_TIMER_SACK, /* ACK delay timer, also used as idle timer alias */ + QUIC_TIMER_PATH, /* Path validation timer: verifies network path connectivity */ + QUIC_TIMER_PMTU, /* Packetization Layer Path MTU Discovery 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_reset_path(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