{"id":2215686,"url":"http://patchwork.ozlabs.org/api/patches/2215686/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-cifs-client/patch/3bd2b698c0f1a66d31a19b8e43820a391fc92a0a.1774410440.git.lucien.xin@gmail.com/","project":{"id":12,"url":"http://patchwork.ozlabs.org/api/projects/12/?format=json","name":"Linux CIFS Client","link_name":"linux-cifs-client","list_id":"linux-cifs.vger.kernel.org","list_email":"linux-cifs@vger.kernel.org","web_url":"","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<3bd2b698c0f1a66d31a19b8e43820a391fc92a0a.1774410440.git.lucien.xin@gmail.com>","list_archive_url":null,"date":"2026-03-25T03:47:14","name":"[net-next,v11,09/15] quic: add congestion control","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"432ce16b8053599bf8d2920e932ff35025c883e5","submitter":{"id":61073,"url":"http://patchwork.ozlabs.org/api/people/61073/?format=json","name":"Xin Long","email":"lucien.xin@gmail.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-cifs-client/patch/3bd2b698c0f1a66d31a19b8e43820a391fc92a0a.1774410440.git.lucien.xin@gmail.com/mbox/","series":[{"id":497380,"url":"http://patchwork.ozlabs.org/api/series/497380/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-cifs-client/list/?series=497380","date":"2026-03-25T03:47:06","name":"net: introduce QUIC infrastructure and core subcomponents","version":11,"mbox":"http://patchwork.ozlabs.org/series/497380/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2215686/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2215686/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <linux-cifs+bounces-10511-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-cifs@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=BC2z4UVP;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c09:e001:a7::12fc:5321; helo=sto.lore.kernel.org;\n envelope-from=linux-cifs+bounces-10511-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=\"BC2z4UVP\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.160.177","smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=gmail.com"],"Received":["from sto.lore.kernel.org (sto.lore.kernel.org\n [IPv6:2600:3c09:e001:a7::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fgXxK143xz1y1G\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 25 Mar 2026 14:50:29 +1100 (AEDT)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sto.lore.kernel.org (Postfix) with ESMTP id 41C7C305CDD8\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 25 Mar 2026 03:49:53 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id A14FF1428F4;\n\tWed, 25 Mar 2026 03:49:48 +0000 (UTC)","from mail-qt1-f177.google.com (mail-qt1-f177.google.com\n [209.85.160.177])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 7ABB22DCF72\n\tfor <linux-cifs@vger.kernel.org>; Wed, 25 Mar 2026 03:49:46 +0000 (UTC)","by mail-qt1-f177.google.com with SMTP id\n d75a77b69052e-50917e02472so53571601cf.2\n        for <linux-cifs@vger.kernel.org>;\n Tue, 24 Mar 2026 20:49:46 -0700 (PDT)","from wsfd-netdev58.anl.eng.rdu2.dc.redhat.com ([66.187.232.140])\n        by smtp.gmail.com with ESMTPSA id\n d75a77b69052e-50b36cb2e29sm150093001cf.1.2026.03.24.20.49.43\n        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n        Tue, 24 Mar 2026 20:49:44 -0700 (PDT)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1774410588; cv=none;\n b=LqGmVbN4IfHVMbFZ/lIiPUd8kzkYzE/7QJ31cVMtHBZ4K+Q0NvjPqJ9X3evqKDZzY7TJPKqMzqCAJZ4ZEtrUTf8SyL0hcCwidQDIhE9PC6UMuOHqptuEegn/QygH8PQJ/NV0WBx8aw2D91UoI6oXt1gt2kgo16S4Z14mhnYTIO8=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1774410588; c=relaxed/simple;\n\tbh=nMquJgLXHbR6Js2vYh2ECm3mS8pOTvCfDPO7Tukdej8=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=fWpLcbqyBc2K57FA8qZIEHMz86Fy6nEAwke9GuJucGdy7bJyjf5TTcL3GiB3TTalrXNnDU1CPFZNeuchCnLvu0dfhM0uq8Mr+QHqvQ6HWhzYY1F11GB7jqEuimh7ld5EnJ2KfQjtc9lwxsmN6ioOyMSDbCgyBIFntFI8Dlth1LY=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com;\n spf=pass smtp.mailfrom=gmail.com;\n dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=BC2z4UVP; arc=none smtp.client-ip=209.85.160.177","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=gmail.com; s=20251104; t=1774410585; x=1775015385;\n darn=vger.kernel.org;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n         :message-id:reply-to;\n        bh=rV+uTx4KK+FveSSb8Ez+o9lhljpgRn4pqxdZ3P0u33E=;\n        b=BC2z4UVPZ7WWTU2LpVVNddDf3QN0ijoNZhult7A1GCGltYLoddu6FIqfONq9oz94mV\n         U+CafIfwuIoF9ejTjys/ccmC2Y0pQ3wR7//9S638V3VgOIvkPz5TgN08WiFph6fu50bH\n         Tc1iD/NBdPvkJNmjvEepXnc7yb9POca+H1m4YQYIZABkKK7YYvN+q0bOGCZVPhYTU4im\n         QOH8Tq7mNDG0U2u+S76ktc6Fn7nbx+lSR1ppJ0ZWm/2LkyM6cNGL4aEkiaspll5q3P0f\n         UqmATNPwDzV3JZcXNiRupY8TkWuBlTL/88eTIT57/ltcl0FoYxm0lKKffi0B6+I82237\n         VQKw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1774410585; x=1775015385;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n         :to:cc:subject:date:message-id:reply-to;\n        bh=rV+uTx4KK+FveSSb8Ez+o9lhljpgRn4pqxdZ3P0u33E=;\n        b=VK6kL1C5miXTdcFxUCuKu5AVQGnzB4WpmZsBdiO+iCdSbQwMGAv4YtcJ3zZrQnnhuM\n         8DxLqtuO08l6Cl5JHUw5xE2QumKa1qCxDd0fe8OOwmitiKDgoQGqEP9pQNoL+MugNt3R\n         bUi5wG29x699iKt0rqkTvDXkE7dZEXaNqw0vRmLku3/YZHYCgf06Kg42n2jVse29ihYb\n         7PTUKSfhWWUdUqT7hrP5Zg2oWmynBnwurjug+FJS/sddQJYS3uuar1T6d/vCotTpEyzx\n         GY2tm36S6x9zkGKKD5rynYDSFz5ufGBXIHQGE0Uzh4qFxzctILN+9Lu6XE+2C4Tg4Cq4\n         0Law==","X-Forwarded-Encrypted":"i=1;\n AJvYcCWlbOeoZRKBrwD1nP0HhcpRMt82d+nMQ3QIb1hLAWVVqzeEDST2087OlmICPJ9QTHxPm/2EH4m0hBOb@vger.kernel.org","X-Gm-Message-State":"AOJu0YzrPCFJlFJi+uKxKkhyVO1mTrvjCrMTnO82hrLjulil+eBM8oli\n\tHKK0+tvy3ILUsmsho9+H1EokZbGLT8ZQnmQf3Z7w0yIwGZ3I9SOwKWgH","X-Gm-Gg":"ATEYQzzIESU1/t2Z2LGjqp7DQxSttXw+ZdXkFyW0mOTfNuXd7U+SavpMnIB0VNwW92c\n\tCbKWN/gtIYaCM3tbHVwDjXZeR2+0jRiFPfYBxQJG3HkqpoyG9yypsma32sxIOc9kjPC4RNd0x2p\n\tjUbG/yluZPwL6TLnoegKLJGmnccteCU257E+toWmPeJkJdNOCXfezG4PyhI9UM1KuBXgqTawloM\n\tiXhVTKwCffBQmRftSA2e8pJD1AjUn0oTxbT+KhFl+Rr+7ixUCKUNG0c4Ud5CxZpwVoWWPKRbYuz\n\tcBVsPpk/2rqHq4SCYiqNMmkHZoz0vwqk/jIHQQeChFDiyCO9g63NIarDW4l2+fGgPD0Xg+EeSae\n\tdh12dh0F6uzBtLL1QC57Tm29AnZ4tX4SSokSJvDRVTLE3UftBBzD1upHT+aTsMepcOs3547wyz9\n\tqjlYXw4xFqlrlp0XG1uL4C1gFl+Mficne3R4YMVfClTO2+NFdNESPL2UGwgmN+99B6U/o+EZY2i\n\tW4n/7WpXqAF22VNzgsr7pB+LcKzd5nQ9X9BILPpyCx8LGCVGZP6fOBJfSOcOF/Qmg==","X-Received":"by 2002:ac8:59d6:0:b0:50b:3c78:ddd6 with SMTP id\n d75a77b69052e-50b80e0b744mr30930501cf.38.1774410585401;\n        Tue, 24 Mar 2026 20:49:45 -0700 (PDT)","From":"Xin Long <lucien.xin@gmail.com>","To":"network dev <netdev@vger.kernel.org>,\n\tquic@lists.linux.dev","Cc":"davem@davemloft.net,\n\tkuba@kernel.org,\n\tEric Dumazet <edumazet@google.com>,\n\tPaolo Abeni <pabeni@redhat.com>,\n\tSimon Horman <horms@kernel.org>,\n\tStefan Metzmacher <metze@samba.org>,\n\tMoritz Buhl <mbuhl@openbsd.org>,\n\tTyler Fanelli <tfanelli@redhat.com>,\n\tPengtao He <hepengtao@xiaomi.com>,\n\tThomas Dreibholz <dreibh@simula.no>,\n\tlinux-cifs@vger.kernel.org,\n\tSteve French <smfrench@gmail.com>,\n\tNamjae Jeon <linkinjeon@kernel.org>,\n\tPaulo Alcantara <pc@manguebit.com>,\n\tTom Talpey <tom@talpey.com>,\n\tkernel-tls-handshake@lists.linux.dev,\n\tChuck Lever <chuck.lever@oracle.com>,\n\tJeff Layton <jlayton@kernel.org>,\n\tSteve Dickson <steved@redhat.com>,\n\tHannes Reinecke <hare@suse.de>,\n\tAlexander Aring <aahringo@redhat.com>,\n\tDavid Howells <dhowells@redhat.com>,\n\tMatthieu Baerts <matttbe@kernel.org>,\n\tJohn Ericson <mail@johnericson.me>,\n\tCong Wang <xiyou.wangcong@gmail.com>,\n\t\"D . Wythe\" <alibuda@linux.alibaba.com>,\n\tJason Baron <jbaron@akamai.com>,\n\tilliliti <illiliti@protonmail.com>,\n\tSabrina Dubroca <sd@queasysnail.net>,\n\tMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>,\n\tDaniel Stenberg <daniel@haxx.se>,\n\tAndy Gospodarek <andrew.gospodarek@broadcom.com>,\n\t\"Marc E . Fiuczynski\" <marc@fiuczynski.com>","Subject":"[PATCH net-next v11 09/15] quic: add congestion control","Date":"Tue, 24 Mar 2026 23:47:14 -0400","Message-ID":"\n <3bd2b698c0f1a66d31a19b8e43820a391fc92a0a.1774410440.git.lucien.xin@gmail.com>","X-Mailer":"git-send-email 2.47.1","In-Reply-To":"<cover.1774410440.git.lucien.xin@gmail.com>","References":"<cover.1774410440.git.lucien.xin@gmail.com>","Precedence":"bulk","X-Mailing-List":"linux-cifs@vger.kernel.org","List-Id":"<linux-cifs.vger.kernel.org>","List-Subscribe":"<mailto:linux-cifs+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-cifs+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit"},"content":"This patch introduces 'quic_cong' for RTT measurement and congestion\ncontrol. The 'quic_cong_ops' is added to define the congestion\ncontrol algorithm.\n\nIt implements a congestion control state machine with slow start,\ncongestion avoidance, and recovery phases, and currently introduces\nthe New Reno algorithm only.\n\nThe implementation updates RTT estimates when packets are acknowledged,\nreacts to loss and ECN signals, and adjusts the congestion window\naccordingly during packet transmission and acknowledgment processing.\n\n- quic_cong_rtt_update(): Performs RTT measurement, invoked when a\n  packet is acknowledged by the largest number in the ACK frame.\n\n- quic_cong_on_packet_acked(): Invoked when a packet is acknowledged.\n\n- quic_cong_on_packet_lost(): Invoked when a packet is marked as lost.\n\n- quic_cong_on_process_ecn(): Invoked when an ACK_ECN frame is received.\n\n- quic_cong_on_packet_sent(): Invoked when a packet is transmitted.\n\n- quic_cong_on_ack_recv(): Invoked when an ACK frame is received.\n\nSigned-off-by: Xin Long <lucien.xin@gmail.com>\n---\nv4:\n  - Remove the CUBIC congestion algorithm support for this version\n    (suggested by Paolo).\nv5:\n  - Do not update the pacing rate when !cong->smoothed_rtt in\n    quic_cong_pace_update() (suggested by Paolo).\n  - Change timestamp variables from u32 to u64, as RTT is measured in\n    microseconds and u64 provides sufficient precision for timestamps\n    in microsecond.\nv8:\n  - Add a comment in quic_reno_on_packet_acked() clarifying cong->window\n    is never zero (noted by AI review).\nv9:\n  - Use abs_diff() to simplify RTT variance calculation (noted by AI\n    review).\n  - Fix a small typo in the comment for struct quic_cong::time (noted\n    by AI review).\n  - Fix another small typo in quic_cong_check_persistent_congestion().\nv10:\n  - Note for AI reviews: cong->time will always be set before processing\n    the packet on the RX path in the future patch series.\n  - Add initial_srtt and algo to struct quic_cong to store the initial\n    smoothed RTT and congestion control algorithm, previously kept in\n    quic_sock.config.\nv11:\n  - Note for AI reviews: later patches guarantee cong->time is set\n    before any quic_cong function uses it.\n  - Note for AI reviews: RFC 9002 section 5.3 does NOT specify that\n    rttvar_sample must use the previous smoothed_rtt value.\n  - Set maximum line length to 80 characters.\n  - Change return type of quic_cong_check_persistent_congestion() to bool.\n  - Add a check for algo in quic_cong_set_algo().\n  - Extract quic_reno_handle_packet_lost() from quic_reno_on_packet_lost()\n    and quic_reno_on_process_ecn() (noted by AI review).\n---\n net/quic/Makefile |   3 +-\n net/quic/cong.c   | 314 ++++++++++++++++++++++++++++++++++++++++++++++\n net/quic/cong.h   | 129 +++++++++++++++++++\n net/quic/socket.c |   1 +\n net/quic/socket.h |   7 ++\n 5 files changed, 453 insertions(+), 1 deletion(-)\n create mode 100644 net/quic/cong.c\n create mode 100644 net/quic/cong.h","diff":"diff --git a/net/quic/Makefile b/net/quic/Makefile\nindex 1565fb5cef9d..4d4a42c6d565 100644\n--- a/net/quic/Makefile\n+++ b/net/quic/Makefile\n@@ -5,4 +5,5 @@\n \n obj-$(CONFIG_IP_QUIC) += quic.o\n \n-quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o\n+quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o \\\n+\t  cong.o\ndiff --git a/net/quic/cong.c b/net/quic/cong.c\nnew file mode 100644\nindex 000000000000..85c3dedf6a60\n--- /dev/null\n+++ b/net/quic/cong.c\n@@ -0,0 +1,314 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ *    Xin Long <lucien.xin@gmail.com>\n+ */\n+\n+#include <linux/quic.h>\n+\n+#include \"common.h\"\n+#include \"cong.h\"\n+\n+static bool quic_cong_check_persistent_congestion(struct quic_cong *cong,\n+\t\t\t\t\t\t  u64 time)\n+{\n+\tu32 ssthresh;\n+\n+\t/* rfc9002#section-7.6.1:\n+\t *   (smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay) *\n+\t *      kPersistentCongestionThreshold\n+\t */\n+\tssthresh = cong->smoothed_rtt +\n+\t\t   max(4 * cong->rttvar, QUIC_KGRANULARITY);\n+\tssthresh = (ssthresh + cong->max_ack_delay) *\n+\t\t   QUIC_KPERSISTENT_CONGESTION_THRESHOLD;\n+\tif (cong->time - time <= ssthresh)\n+\t\treturn false;\n+\n+\tpr_debug(\"%s: persistent congestion, cwnd: %u, ssth: %u\\n\",\n+\t\t __func__, cong->window, cong->ssthresh);\n+\tcong->min_rtt_valid = 0;\n+\tcong->window = cong->min_window;\n+\tcong->state = QUIC_CONG_SLOW_START;\n+\treturn true;\n+}\n+\n+/* NEW RENO APIs */\n+static void quic_reno_handle_packet_lost(struct quic_cong *cong)\n+{\n+\tswitch (cong->state) {\n+\tcase QUIC_CONG_SLOW_START:\n+\t\tpr_debug(\"%s: slow_start -> recovery, cwnd: %u, ssth: %u\\n\",\n+\t\t\t __func__, cong->window, cong->ssthresh);\n+\t\tbreak;\n+\tcase QUIC_CONG_RECOVERY_PERIOD:\n+\t\treturn;\n+\tcase QUIC_CONG_CONGESTION_AVOIDANCE:\n+\t\tpr_debug(\"%s: cong_avoid -> recovery, cwnd: %u, ssth: %u\\n\",\n+\t\t\t __func__, cong->window, cong->ssthresh);\n+\t\tbreak;\n+\tdefault:\n+\t\tpr_debug(\"%s: wrong congestion state: %d\\n\", __func__,\n+\t\t\t cong->state);\n+\t\treturn;\n+\t}\n+\n+\tcong->recovery_time = cong->time;\n+\tcong->state = QUIC_CONG_RECOVERY_PERIOD;\n+\tcong->ssthresh = max(cong->window >> 1U, cong->min_window);\n+\tcong->window = cong->ssthresh;\n+}\n+\n+static void quic_reno_on_packet_lost(struct quic_cong *cong, u64 time,\n+\t\t\t\t     u32 bytes, s64 number)\n+{\n+\tif (quic_cong_check_persistent_congestion(cong, time))\n+\t\treturn;\n+\n+\tquic_reno_handle_packet_lost(cong);\n+}\n+\n+static void quic_reno_on_packet_acked(struct quic_cong *cong, u64 time,\n+\t\t\t\t      u32 bytes, s64 number)\n+{\n+\tswitch (cong->state) {\n+\tcase QUIC_CONG_SLOW_START:\n+\t\tcong->window = min_t(u32, cong->window + bytes,\n+\t\t\t\t     cong->max_window);\n+\t\tif (cong->window < cong->ssthresh)\n+\t\t\tbreak;\n+\t\tcong->state = QUIC_CONG_CONGESTION_AVOIDANCE;\n+\t\tpr_debug(\"%s: slow_start -> cong_avoid, cwnd: %u, ssth: %u\\n\",\n+\t\t\t __func__, cong->window, cong->ssthresh);\n+\t\tbreak;\n+\tcase QUIC_CONG_RECOVERY_PERIOD:\n+\t\tif (cong->recovery_time >= time)\n+\t\t\tbreak;\n+\t\tcong->state = QUIC_CONG_CONGESTION_AVOIDANCE;\n+\t\tpr_debug(\"%s: recovery -> cong_avoid, cwnd: %u, ssth: %u\\n\",\n+\t\t\t __func__, cong->window, cong->ssthresh);\n+\t\tbreak;\n+\tcase QUIC_CONG_CONGESTION_AVOIDANCE:\n+\t\t/* cong->window is never zero; it is initialized by\n+\t\t * quic_packet_route() during connect/accept.\n+\t\t */\n+\t\tcong->window += cong->mss * bytes / cong->window;\n+\t\tbreak;\n+\tdefault:\n+\t\tpr_debug(\"%s: wrong congestion state: %d\\n\", __func__,\n+\t\t\t cong->state);\n+\t\treturn;\n+\t}\n+}\n+\n+static void quic_reno_on_process_ecn(struct quic_cong *cong)\n+{\n+\tquic_reno_handle_packet_lost(cong);\n+}\n+\n+static void quic_reno_on_init(struct quic_cong *cong)\n+{\n+}\n+\n+static struct quic_cong_ops quic_congs[] = {\n+\t{ /* QUIC_CONG_ALG_RENO */\n+\t\t.on_packet_acked = quic_reno_on_packet_acked,\n+\t\t.on_packet_lost = quic_reno_on_packet_lost,\n+\t\t.on_process_ecn = quic_reno_on_process_ecn,\n+\t\t.on_init = quic_reno_on_init,\n+\t},\n+};\n+\n+/* COMMON APIs */\n+void quic_cong_on_packet_lost(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t      s64 number)\n+{\n+\tcong->ops->on_packet_lost(cong, time, bytes, number);\n+}\n+\n+void quic_cong_on_packet_acked(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t       s64 number)\n+{\n+\tcong->ops->on_packet_acked(cong, time, bytes, number);\n+}\n+\n+void quic_cong_on_process_ecn(struct quic_cong *cong)\n+{\n+\tcong->ops->on_process_ecn(cong);\n+}\n+\n+/* Update Probe Timeout (PTO) and loss detection delay based on RTT stats. */\n+static void quic_cong_pto_update(struct quic_cong *cong)\n+{\n+\tu32 pto, loss_delay;\n+\n+\t/* rfc9002#section-6.2.1:\n+\t *   PTO = smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay\n+\t */\n+\tpto = cong->smoothed_rtt + max(4 * cong->rttvar, QUIC_KGRANULARITY);\n+\tcong->pto = pto + cong->max_ack_delay;\n+\n+\t/* rfc9002#section-6.1.2:\n+\t *   max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity)\n+\t */\n+\tloss_delay = QUIC_KTIME_THRESHOLD(max(cong->smoothed_rtt,\n+\t\t\t\t\t      cong->latest_rtt));\n+\tcong->loss_delay = max(loss_delay, QUIC_KGRANULARITY);\n+\n+\tpr_debug(\"%s: update pto: %u\\n\", __func__, pto);\n+}\n+\n+/* Update pacing timestamp after sending 'bytes' bytes.\n+ *\n+ * This function tracks when the next packet is allowed to be sent based on\n+ * pacing rate.\n+ */\n+static void quic_cong_update_pacing_time(struct quic_cong *cong, u32 bytes)\n+{\n+\tu64 prior_time, credit, len_ns, rate = READ_ONCE(cong->pacing_rate);\n+\n+\tif (!rate)\n+\t\treturn;\n+\n+\tprior_time = cong->pacing_time;\n+\tcong->pacing_time = max(cong->pacing_time, ktime_get_ns());\n+\tcredit = cong->pacing_time - prior_time;\n+\n+\t/* take into account OS jitter */\n+\tlen_ns = div64_ul((u64)bytes * NSEC_PER_SEC, rate);\n+\tlen_ns -= min_t(u64, len_ns / 2, credit);\n+\tcong->pacing_time += len_ns;\n+}\n+\n+/* Compute and update the pacing rate based on congestion window and smoothed\n+ * RTT.\n+ */\n+static void quic_cong_pace_update(struct quic_cong *cong, u32 bytes,\n+\t\t\t\t  u64 max_rate)\n+{\n+\tu64 rate;\n+\n+\tif (unlikely(!cong->smoothed_rtt))\n+\t\treturn;\n+\n+\t/* rate = N * congestion_window / smoothed_rtt */\n+\trate = div64_ul((u64)cong->window * USEC_PER_SEC * 2,\n+\t\t\tcong->smoothed_rtt);\n+\n+\tWRITE_ONCE(cong->pacing_rate, min_t(u64, rate, max_rate));\n+\tpr_debug(\"%s: update pacing rate: %llu, max rate: %llu, srtt: %u\\n\",\n+\t\t __func__, cong->pacing_rate, max_rate, cong->smoothed_rtt);\n+}\n+\n+void quic_cong_on_packet_sent(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t      s64 number)\n+{\n+\tif (!bytes)\n+\t\treturn;\n+\tif (cong->ops->on_packet_sent)\n+\t\tcong->ops->on_packet_sent(cong, time, bytes, number);\n+\tquic_cong_update_pacing_time(cong, bytes);\n+}\n+\n+void quic_cong_on_ack_recv(struct quic_cong *cong, u32 bytes, u64 max_rate)\n+{\n+\tif (!bytes)\n+\t\treturn;\n+\tif (cong->ops->on_ack_recv)\n+\t\tcong->ops->on_ack_recv(cong, bytes, max_rate);\n+\tquic_cong_pace_update(cong, bytes, max_rate);\n+}\n+\n+/* rfc9002#section-5: Estimating the Round-Trip Time */\n+void quic_cong_rtt_update(struct quic_cong *cong, u64 time, u32 ack_delay)\n+{\n+\tu32 adjusted_rtt, rttvar_sample;\n+\n+\t/* Ignore RTT sample if ACK delay is suspiciously large. */\n+\tif (ack_delay > cong->max_ack_delay * 2)\n+\t\treturn;\n+\n+\t/* rfc9002#section-5.1:\n+\t *   latest_rtt = ack_time - send_time_of_largest_acked\n+\t */\n+\tcong->latest_rtt = cong->time - time;\n+\n+\t/* rfc9002#section-5.2: Estimating min_rtt */\n+\tif (!cong->min_rtt_valid) {\n+\t\tcong->min_rtt = cong->latest_rtt;\n+\t\tcong->min_rtt_valid = 1;\n+\t}\n+\tif (cong->min_rtt > cong->latest_rtt)\n+\t\tcong->min_rtt = cong->latest_rtt;\n+\n+\tif (!cong->is_rtt_set) {\n+\t\t/* rfc9002#section-5.3:\n+\t\t *   smoothed_rtt = latest_rtt\n+\t\t *   rttvar = latest_rtt / 2\n+\t\t */\n+\t\tcong->smoothed_rtt = cong->latest_rtt;\n+\t\tcong->rttvar = cong->smoothed_rtt / 2;\n+\t\tquic_cong_pto_update(cong);\n+\t\tcong->is_rtt_set = 1;\n+\t\treturn;\n+\t}\n+\n+\t/* rfc9002#section-5.3:\n+\t *   adjusted_rtt = latest_rtt\n+\t *   if (latest_rtt >= min_rtt + ack_delay):\n+\t *     adjusted_rtt = latest_rtt - ack_delay\n+\t *   smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt\n+\t *   rttvar_sample = abs(smoothed_rtt - adjusted_rtt)\n+\t *   rttvar = 3/4 * rttvar + 1/4 * rttvar_sample\n+\t */\n+\tadjusted_rtt = cong->latest_rtt;\n+\tif (cong->latest_rtt >= cong->min_rtt + ack_delay)\n+\t\tadjusted_rtt = cong->latest_rtt - ack_delay;\n+\n+\tcong->smoothed_rtt = (cong->smoothed_rtt * 7 + adjusted_rtt) / 8;\n+\trttvar_sample = abs_diff(cong->smoothed_rtt, adjusted_rtt);\n+\tcong->rttvar = (cong->rttvar * 3 + rttvar_sample) / 4;\n+\tquic_cong_pto_update(cong);\n+\n+\tif (cong->ops->on_rtt_update)\n+\t\tcong->ops->on_rtt_update(cong);\n+}\n+\n+void quic_cong_set_algo(struct quic_cong *cong, u8 algo)\n+{\n+\t/* The caller must ensure algo < QUIC_CONG_ALG_MAX. */\n+\tif (WARN_ON_ONCE(algo >= QUIC_CONG_ALG_MAX))\n+\t\treturn;\n+\tcong->algo = algo;\n+\tcong->state = QUIC_CONG_SLOW_START;\n+\tcong->ssthresh = U32_MAX;\n+\tcong->ops = &quic_congs[algo];\n+\tcong->ops->on_init(cong);\n+}\n+\n+void quic_cong_set_srtt(struct quic_cong *cong, u32 srtt)\n+{\n+\t/* rfc9002#section-5.3:\n+\t *   smoothed_rtt = kInitialRtt\n+\t *   rttvar = kInitialRtt / 2\n+\t */\n+\tcong->initial_srtt = srtt;\n+\tcong->latest_rtt = srtt;\n+\tcong->smoothed_rtt = cong->latest_rtt;\n+\tcong->rttvar = cong->smoothed_rtt / 2;\n+\tquic_cong_pto_update(cong);\n+}\n+\n+void quic_cong_init(struct quic_cong *cong)\n+{\n+\tcong->max_ack_delay = QUIC_DEF_ACK_DELAY;\n+\tcong->max_window = S32_MAX / 2;\n+\tquic_cong_set_algo(cong, QUIC_CONG_ALG_RENO);\n+\tquic_cong_set_srtt(cong, QUIC_RTT_INIT);\n+}\ndiff --git a/net/quic/cong.h b/net/quic/cong.h\nnew file mode 100644\nindex 000000000000..aef765d097f2\n--- /dev/null\n+++ b/net/quic/cong.h\n@@ -0,0 +1,129 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ *    Xin Long <lucien.xin@gmail.com>\n+ */\n+\n+#define QUIC_KPERSISTENT_CONGESTION_THRESHOLD\t3\n+#define QUIC_KPACKET_THRESHOLD\t\t\t3\n+#define QUIC_KTIME_THRESHOLD(rtt)\t\t((rtt) * 9 / 8)\n+#define QUIC_KGRANULARITY\t\t\t1000U\n+\n+#define QUIC_RTT_INIT\t\t333000U\n+#define QUIC_RTT_MAX\t\t2000000U\n+#define QUIC_RTT_MIN\t\tQUIC_KGRANULARITY\n+\n+/* rfc9002#section-7.3: Congestion Control States\n+ *\n+ *                  New path or      +------------+\n+ *             persistent congestion |   Slow     |\n+ *         (O)---------------------->|   Start    |\n+ *                                   +------------+\n+ *                                         |\n+ *                                 Loss or |\n+ *                         ECN-CE increase |\n+ *                                         v\n+ *  +------------+     Loss or       +------------+\n+ *  | Congestion |  ECN-CE increase  |  Recovery  |\n+ *  | Avoidance  |------------------>|   Period   |\n+ *  +------------+                   +------------+\n+ *            ^                            |\n+ *            |                            |\n+ *            +----------------------------+\n+ *               Acknowledgment of packet\n+ *                 sent during recovery\n+ */\n+enum quic_cong_state {\n+\tQUIC_CONG_SLOW_START,\n+\tQUIC_CONG_RECOVERY_PERIOD,\n+\tQUIC_CONG_CONGESTION_AVOIDANCE,\n+};\n+\n+struct quic_cong {\n+\t/* RTT tracking */\n+\tu32 max_ack_delay;\t/* max_ack_delay from rfc9000#section-18.2 */\n+\tu32 smoothed_rtt;\t/* Smoothed RTT */\n+\tu32 latest_rtt;\t\t/* Latest RTT sample */\n+\tu32 min_rtt;\t\t/* Lowest observed RTT */\n+\tu32 rttvar;\t\t/* RTT variation */\n+\tu32 pto;\t\t/* Probe timeout */\n+\n+\t/* Timing & pacing */\n+\tu64 recovery_time;\t/* Recovery period start timestamp */\n+\tu64 pacing_rate;\t/* Packet sending speed Bytes/sec */\n+\tu64 pacing_time;\t/* Next scheduled send timestamp (ns) */\n+\tu64 time;\t\t/* Cached current timestamp */\n+\n+\t/* Congestion window */\n+\tu32 max_window;\t\t/* Max growth cap */\n+\tu32 min_window;\t\t/* Min window limit */\n+\tu32 loss_delay;\t\t/* Time before marking loss */\n+\tu32 ssthresh;\t\t/* Slow start threshold */\n+\tu32 window;\t\t/* Bytes in flight allowed */\n+\tu32 mss;\t\t/* QUIC MSS (excl. UDP) */\n+\n+\t/* Algorithm-specific */\n+\tstruct quic_cong_ops *ops;\n+\tu64 priv[8];\t\t/* Algo private data */\n+\n+\tu32 initial_srtt;\t/* Initial smoothed RTT */\n+\tu8 algo;\t\t/* Congestion control algorithm */\n+\n+\t/* Flags & state */\n+\tu8 min_rtt_valid;\t/* min_rtt initialized */\n+\tu8 is_rtt_set;\t\t/* RTT samples exist */\n+\tu8 state;\t\t/* State machine in rfc9002#section-7.3 */\n+};\n+\n+/* Hooks for congestion control algorithms */\n+struct quic_cong_ops {\n+\tvoid (*on_packet_acked)(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t\ts64 number);\n+\tvoid (*on_packet_lost)(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t       s64 number);\n+\tvoid (*on_process_ecn)(struct quic_cong *cong);\n+\tvoid (*on_init)(struct quic_cong *cong);\n+\n+\t/* Optional callbacks */\n+\tvoid (*on_packet_sent)(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t       s64 number);\n+\tvoid (*on_ack_recv)(struct quic_cong *cong, u32 bytes, u64 max_rate);\n+\tvoid (*on_rtt_update)(struct quic_cong *cong);\n+};\n+\n+static inline void quic_cong_set_mss(struct quic_cong *cong, u32 mss)\n+{\n+\tif (cong->mss == mss)\n+\t\treturn;\n+\n+\t/* rfc9002#section-7.2: Initial and Minimum Congestion Window */\n+\tcong->mss = mss;\n+\tcong->min_window = max(min(mss * 10, 14720U), mss * 2);\n+\n+\tif (cong->window < cong->min_window)\n+\t\tcong->window = cong->min_window;\n+}\n+\n+static inline void *quic_cong_priv(struct quic_cong *cong)\n+{\n+\treturn (void *)cong->priv;\n+}\n+\n+void quic_cong_on_packet_acked(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t       s64 number);\n+void quic_cong_on_packet_lost(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t      s64 number);\n+void quic_cong_on_process_ecn(struct quic_cong *cong);\n+\n+void quic_cong_on_packet_sent(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t      s64 number);\n+void quic_cong_on_ack_recv(struct quic_cong *cong, u32 bytes, u64 max_rate);\n+void quic_cong_rtt_update(struct quic_cong *cong, u64 time, u32 ack_delay);\n+\n+void quic_cong_set_srtt(struct quic_cong *cong, u32 srtt);\n+void quic_cong_set_algo(struct quic_cong *cong, u8 algo);\n+void quic_cong_init(struct quic_cong *cong);\ndiff --git a/net/quic/socket.c b/net/quic/socket.c\nindex d5ac77c02861..7a4a00498b54 100644\n--- a/net/quic/socket.c\n+++ b/net/quic/socket.c\n@@ -47,6 +47,7 @@ static int quic_init_sock(struct sock *sk)\n \n \tquic_conn_id_set_init(quic_source(sk), 1);\n \tquic_conn_id_set_init(quic_dest(sk), 0);\n+\tquic_cong_init(quic_cong(sk));\n \n \tif (quic_stream_init(quic_streams(sk)))\n \t\treturn -ENOMEM;\ndiff --git a/net/quic/socket.h b/net/quic/socket.h\nindex 91338601905e..9201ca3edad0 100644\n--- a/net/quic/socket.h\n+++ b/net/quic/socket.h\n@@ -16,6 +16,7 @@\n #include \"stream.h\"\n #include \"connid.h\"\n #include \"path.h\"\n+#include \"cong.h\"\n \n #include \"protocol.h\"\n \n@@ -41,6 +42,7 @@ struct quic_sock {\n \tstruct quic_conn_id_set\t\tsource;\n \tstruct quic_conn_id_set\t\tdest;\n \tstruct quic_path_group\t\tpaths;\n+\tstruct quic_cong\t\tcong;\n };\n \n struct quic6_sock {\n@@ -98,6 +100,11 @@ static inline bool quic_is_serv(const struct sock *sk)\n \treturn !!sk->sk_max_ack_backlog;\n }\n \n+static inline struct quic_cong *quic_cong(const struct sock *sk)\n+{\n+\treturn &quic_sk(sk)->cong;\n+}\n+\n static inline bool quic_is_establishing(struct sock *sk)\n {\n \treturn sk->sk_state == QUIC_SS_ESTABLISHING;\n","prefixes":["net-next","v11","09/15"]}