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 d717efca9b9e..53a2f76c9bda 100644
--- a/net/quic/socket.c
+++ b/net/quic/socket.c
@@ -85,6 +85,8 @@ static int quic_init_sock(struct sock *sk)
quic_path_init(quic_paths(sk));
quic_cong_init(quic_cong(sk));
+ quic_timer_init(sk);
+
if (quic_stream_init(quic_streams(sk)))
return -ENOMEM;
@@ -117,6 +119,8 @@ static void quic_destroy_sock(struct sock *sk)
struct quic_pnspace *space;
u8 i;
+ quic_timer_free(sk);
+
for (i = 0; i < QUIC_PNSPACE_MAX; i++) {
space = quic_pnspace(sk, i);
if (!space)@@ -253,6 +257,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 ece21b9120fc..7ce29974baf4 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..e40157e790ee
--- /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
+ *
+ * Timer management.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#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 <lucien.xin@gmail.com>
+ */
+
+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