{"id":2215693,"url":"http://patchwork.ozlabs.org/api/patches/2215693/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-cifs-client/patch/72c4d87ed7fce4ed7e06d8f76a3ceaf7869e91e3.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":"<72c4d87ed7fce4ed7e06d8f76a3ceaf7869e91e3.1774410440.git.lucien.xin@gmail.com>","list_archive_url":null,"date":"2026-03-25T03:47:15","name":"[net-next,v11,10/15] quic: add packet number space","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"0aff19f8a54e04b0f40610e6faf1d1907d67192f","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/72c4d87ed7fce4ed7e06d8f76a3ceaf7869e91e3.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/2215693/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2215693/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <linux-cifs+bounces-10512-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=HTEGNMbE;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c04:e001:36c::12fc:5321; helo=tor.lore.kernel.org;\n envelope-from=linux-cifs+bounces-10512-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=\"HTEGNMbE\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.160.178","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 tor.lore.kernel.org (tor.lore.kernel.org\n [IPv6:2600:3c04:e001:36c::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fgY0f6MVTz1y1G\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 25 Mar 2026 14:53:22 +1100 (AEDT)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id C20C330D9442\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 25 Mar 2026 03:49:55 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 7B5B12E282B;\n\tWed, 25 Mar 2026 03:49:51 +0000 (UTC)","from mail-qt1-f178.google.com (mail-qt1-f178.google.com\n [209.85.160.178])\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 6D830286D70\n\tfor <linux-cifs@vger.kernel.org>; Wed, 25 Mar 2026 03:49:49 +0000 (UTC)","by mail-qt1-f178.google.com with SMTP id\n d75a77b69052e-50b2b289925so50608551cf.2\n        for <linux-cifs@vger.kernel.org>;\n Tue, 24 Mar 2026 20:49:49 -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.45\n        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n        Tue, 24 Mar 2026 20:49:47 -0700 (PDT)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1774410591; cv=none;\n b=IOoIyauSpodoYqy2AQU0CoK8hbklWPonXiji9+dud7ikXLF7xMQsrBx8XyQkScMkkIZNh4Y4V87SA4x7NdDdHy6oIT/2lVSlDiT3WvtQ1x4smAY/QeKQeNXlBocq3jm6DjqrcmYlLbwcybN/9d5M+Pgiz+QF2fn5FvFKfmZqX20=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1774410591; c=relaxed/simple;\n\tbh=ngFPwXkkurbSJi167Sr4VLCpev1Q2dqSQoazmHQB9Qs=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=feKQQsjPfcsDLJ68/oLmkNy5EjpxJPFWoevjXuivON2SwvVerKS8j4VzN2M3diw9srUFlG7xtOjouNypfBdyVWuqeITtcV8HhkOD7NAQturvTv0vzZ0scW8da+l4ZxP5qsDMEU3K0ljxmCcjlpDTqRUnJpneDOrlpK/I6mtgMCQ=","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=HTEGNMbE; arc=none smtp.client-ip=209.85.160.178","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=gmail.com; s=20251104; t=1774410588; x=1775015388;\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=sWXysmmFoumyN1OfpS1Dlk5sIIO7cNibhRi90Ty/B1E=;\n        b=HTEGNMbEYloU7RpsGvhMpbKYgonoC+4/4iwz/EvAZOK0Q9xw/b3RRbCiAbWDEqDr4T\n         Zyl+yhcmVdD2Fe6qJT2dxwAHaMEZjW3fCMEDDyrleYKBI1beDTxQzPyOQrpw73uWEANI\n         OYlEEPeSjuZd/lb5qleE9E9UwyxzcrIEbwZnr88I1yp8/Sd1R5erT1SW2Gad+bQiv88q\n         JOySD3wwalrlitRFVX7QUlzwUJxFvDhjVtkYq6NOecyNcSBPAQYqXyaasWQLE1X0YQFn\n         cEmyBpbJ8Ekq3/BuNmDtfrrBQS5ASpvln3fwh33tOvPJu0OwYHAKcdmwwCwQkPBC45qU\n         +MqA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1774410588; x=1775015388;\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=sWXysmmFoumyN1OfpS1Dlk5sIIO7cNibhRi90Ty/B1E=;\n        b=HR5PE9XgmTO6/el4Re6Pp0gW1yytkjkfugQW5EF0a896bLY8UtbnkZyCPCEV4G0xEV\n         3vpIc/nJ6fctnFPbhqWKSAOztaPQS6Cu0rMSV+Kh48319sewFh3GP+yziZkggNLpFOzo\n         iSI5L9LHyqgfTmUyJF3UOjuOXa+sHvgtMGakCEC+K0Hp3FKsi+m9CsKXGF1PieuVHFi8\n         nK4BR1hTKUkspTL1hVvbzIE3AXXwdAqM9hWSzWhIcn+WEPZlIFvw7iUc0N9RLmv71k9y\n         lR30DnShrTEbJQL9lz5ptwzT6uL8xSr9Jhxllmv4Ot6Ju5a/rhHULxdorDbAa4iWrNT6\n         lIGw==","X-Forwarded-Encrypted":"i=1;\n AJvYcCUrcZNUbcLT9z/jjgE+SsMqOfJHLGKAGNTMd9JkDJRj9zgYIMOF9UoSwvF6iafh+Zta3D4/HiwCfDOR@vger.kernel.org","X-Gm-Message-State":"AOJu0YxdEh8AMbTRuqMamQ0ptk8u3zWvY1CSmMAp22ofKLbLjNo/0Hs/\n\tOSEIlbAlBfnA9hzWxhK7A+8/7KwvYreRWCutbDb/0zj9LvSjJnWYCLO4duNsiaOUSek=","X-Gm-Gg":"ATEYQzyS7On276bFagkJssoJNttkg2pjcyv6fco3u6M8Lrua+2b5O1PPitV5HjjvO4Q\n\tlKMObtdfK3sVopGHwe34QDAG6CpCd5c9mqjY4+8qH7fy6tKBQzBle8C8fbKXw2mquPJWH5+cNo7\n\t7sWdr3WcKYSc1b9YIb6nPixhLDm8ldobFl2QEuHFBQ6zU+9yoqNyxYNPKkcXkxCqfgohZxE358H\n\tkKymkvRnp8vQEgBkwyvKZViNQjlHcP5A3tshrr1Uv6/aof+BzIxDBh2akEsmgd96f8mDIQf1YTU\n\tHWdY6MdiRQYRGf7SR0gmrKI24KD8FZmNKz3rk1xfWB7kW0YwvrjAjN68rwpRdNDsU7dP73EmIfe\n\tj5S3xuA0cuoyXC3r/RQ/LPBl45a6mOUIzEUTQ/+eIcfOS/DDrbFRd5Kgbi7GdINd5VHxxO3l9Sk\n\tW1IhZYW8nrpSe6ZO4eCshZxF7wa9mZZ4kettPemmFkab31a1UEInJ/E9kaFgZFFMmyGpy1yvrub\n\tRaV4kUzxVUAFlazUq+n08PIjwxomzWmx6DU/FICs9MFo8N4Cq/UlfPHFSJ+RpNzeQ==","X-Received":"by 2002:a05:622a:22a9:b0:50b:4b2f:1606 with SMTP id\n d75a77b69052e-50b80c9ad0amr29393041cf.15.1774410588361;\n        Tue, 24 Mar 2026 20:49:48 -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 10/15] quic: add packet number space","Date":"Tue, 24 Mar 2026 23:47:15 -0400","Message-ID":"\n <72c4d87ed7fce4ed7e06d8f76a3ceaf7869e91e3.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_pnspace', which manages per packet number\nspace members.\n\nIt maintains the next packet number to assign, tracks the total length of\nframes currently in flight, and records the time when the next packet may\nbe considered lost. It also keeps track of the largest acknowledged packet\nnumber, the time it was acknowledged, and when the most recent ack\neliciting packet was sent. These fields are useful for loss detection,\nRTT estimation, and congestion control.\n\nTo support ACK frame generation, quic_pnspace includes a packet number\nacknowledgment map (pn_ack_map) that tracks received packet numbers.\nSupporting functions are provided to validate and mark received packet\nnumbers and compute the number of gap blocks needed during ACK frame\nconstruction.\n\n- quic_pnspace_check(): Validates a received packet number.\n\n- quic_pnspace_mark(): Marks a received packet number in the ACK map.\n\n- quic_pnspace_num_gabs(): Returns the gap ACK blocks for constructing\n  ACK frames.\n\nNote QUIC uses separate packet number spaces for each encryption level\n(APP, INITIAL, HANDSHAKE, EARLY) except EARLY and all generations of\nAPP keys use the same packet number space, as describe in\nrfc9002#section-4.1.\n\nSigned-off-by: Xin Long <lucien.xin@gmail.com>\nAcked-by: Paolo Abeni <pabeni@redhat.com>\n---\nv5:\n  - Change timestamp variables from u32 to u64 and use quic_ktime_get_us()\n    to set max_pn_acked_time, as jiffies_to_usecs() is not accurate enough.\n  - Reorder some members in quic_pnspace to reduce 32-bit holes (noted\n    by Paolo).\nv6:\n  - Note for AI reviews: it's safe to do cast (u16)(pn - space->base_pn)\n    in quic_pnspace_mark(), as the pn < base_pn + QUIC_PN_MAP_SIZE (4096)\n    validation is always done in quic_pnspace_check(), which will always\n    be called before quic_pnspace_mark() in a later patchset.\n  - Note for AI reviews: failures in quic_pnspace_init() do not result in a\n    pn_map leak in quic_init_sock(), because quic_destroy_sock() is always\n    called to free it in err path, either via inet/6_create() or through\n    quic_accept() in a later patchset.\nv8:\n  - Replace bitfields with plain u8 in struct quic_pnspace.\nv10:\n  - Fix a grammar error in the comment of quic_pnspace_check().\nv11:\n  - Note for AI reviews: RFC 9000 does not define integer IDs for packet\n    number spaces. In this implementation, App=0, Initial=1,\n    Handshake=2, and Early maps to 0 (3 % 3).\n  - Set maximum line length to 80 characters.\n  - clear space->pn_map pointer after free in quic_pnspace_free().\n  - Change quic_pnspace_grow() to return negative errno on failure or 0\n    on success.\n  - Change return type of quic_pnspace_next_gap_ack() and\n    quic_pnspace_set_ecn_count() to bool.\n  - Return -EINVAL instead of -1 on failure in quic_pnspace_check().\n---\n net/quic/Makefile  |   2 +-\n net/quic/pnspace.c | 250 +++++++++++++++++++++++++++++++++++++++++++++\n net/quic/pnspace.h | 157 ++++++++++++++++++++++++++++\n net/quic/socket.c  |  12 +++\n net/quic/socket.h  |   7 ++\n 5 files changed, 427 insertions(+), 1 deletion(-)\n create mode 100644 net/quic/pnspace.c\n create mode 100644 net/quic/pnspace.h","diff":"diff --git a/net/quic/Makefile b/net/quic/Makefile\nindex 4d4a42c6d565..9d8e18297911 100644\n--- a/net/quic/Makefile\n+++ b/net/quic/Makefile\n@@ -6,4 +6,4 @@\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-\t  cong.o\n+\t  cong.o pnspace.o\ndiff --git a/net/quic/pnspace.c b/net/quic/pnspace.c\nnew file mode 100644\nindex 000000000000..d8f19f4e0fc6\n--- /dev/null\n+++ b/net/quic/pnspace.c\n@@ -0,0 +1,250 @@\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/slab.h>\n+\n+#include \"common.h\"\n+#include \"pnspace.h\"\n+\n+int quic_pnspace_init(struct quic_pnspace *space)\n+{\n+\tif (!space->pn_map) {\n+\t\tspace->pn_map = kzalloc(BITS_TO_BYTES(QUIC_PN_MAP_INITIAL),\n+\t\t\t\t\tGFP_KERNEL);\n+\t\tif (!space->pn_map)\n+\t\t\treturn -ENOMEM;\n+\t\tspace->pn_map_len = QUIC_PN_MAP_INITIAL;\n+\t} else {\n+\t\tbitmap_zero(space->pn_map, space->pn_map_len);\n+\t}\n+\n+\tspace->max_time_limit = QUIC_PNSPACE_TIME_LIMIT;\n+\tspace->next_pn = QUIC_PNSPACE_NEXT_PN;\n+\tspace->base_pn = -1;\n+\treturn 0;\n+}\n+\n+void quic_pnspace_free(struct quic_pnspace *space)\n+{\n+\tspace->pn_map_len = 0;\n+\tkfree(space->pn_map);\n+\tspace->pn_map = NULL;\n+}\n+\n+/* Expand the bitmap tracking received packet numbers.  Ensures the pn_map\n+ * bitmap can cover at least @size packet numbers.  Allocates a larger bitmap,\n+ * copies existing data, and updates metadata.\n+ *\n+ * Return: 0 on success, or a negative errno value on failure.\n+ */\n+static int quic_pnspace_grow(struct quic_pnspace *space, u16 size)\n+{\n+\tu16 len, inc, offset;\n+\tunsigned long *new;\n+\n+\tif (size > QUIC_PN_MAP_SIZE)\n+\t\treturn -EINVAL;\n+\n+\tinc = ALIGN((size - space->pn_map_len), BITS_PER_LONG) +\n+\t      QUIC_PN_MAP_INCREMENT;\n+\tlen = (u16)min(space->pn_map_len + inc, QUIC_PN_MAP_SIZE);\n+\n+\tnew = kzalloc(BITS_TO_BYTES(len), GFP_ATOMIC);\n+\tif (!new)\n+\t\treturn -ENOMEM;\n+\n+\toffset = (u16)(space->max_pn_seen + 1 - space->base_pn);\n+\tbitmap_copy(new, space->pn_map, offset);\n+\tkfree(space->pn_map);\n+\tspace->pn_map = new;\n+\tspace->pn_map_len = len;\n+\n+\treturn 0;\n+}\n+\n+/* Check if a packet number has been received.\n+ *\n+ * Returns: 0 if the packet number has not been received.  1 if it has already\n+ * been received. -EINVAL if the packet number is too old or too far in the\n+ * future to track.\n+ */\n+int quic_pnspace_check(struct quic_pnspace *space, s64 pn)\n+{\n+\tif (space->base_pn == -1) /* No packet number received yet. */\n+\t\treturn 0;\n+\n+\tif (pn < space->min_pn_seen || pn >= space->base_pn + QUIC_PN_MAP_SIZE)\n+\t\treturn -EINVAL;\n+\n+\tif (pn < space->base_pn)\n+\t\treturn 1;\n+\tif (pn - space->base_pn < space->pn_map_len &&\n+\t    test_bit(pn - space->base_pn, space->pn_map))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n+/* Advance base_pn past contiguous received packet numbers.  Finds the next gap\n+ * (unreceived packet) beyond @pn, shifts the bitmap, and updates base_pn\n+ * accordingly.\n+ */\n+static void quic_pnspace_move(struct quic_pnspace *space, s64 pn)\n+{\n+\tu16 offset;\n+\n+\toffset = (u16)(pn + 1 - space->base_pn);\n+\toffset = (u16)find_next_zero_bit(space->pn_map, space->pn_map_len,\n+\t\t\t\t\t offset);\n+\tspace->base_pn += offset;\n+\tbitmap_shift_right(space->pn_map, space->pn_map, offset,\n+\t\t\t   space->pn_map_len);\n+}\n+\n+/* Mark a packet number as received. Updates the packet number map to record\n+ * reception of @pn.  Advances base_pn if possible, and updates max/min/last\n+ * seen fields as needed.\n+ *\n+ * Returns: 0 on success or if the packet was already marked, or a negative\n+ * error returned by bitmap growth when expanding the map.\n+ */\n+int quic_pnspace_mark(struct quic_pnspace *space, s64 pn)\n+{\n+\ts64 last_max_pn_seen;\n+\tu64 last_max_pn_time;\n+\tu16 gap;\n+\tint err;\n+\n+\tif (space->base_pn == -1) {\n+\t\t/* Initialize base_pn based on the peer's first packet number\n+\t\t * since peer's packet numbers may start at a non-zero value.\n+\t\t */\n+\t\tquic_pnspace_set_base_pn(space, pn + 1);\n+\t\treturn 0;\n+\t}\n+\n+\t/* Ignore packets with number less than current base (already\n+\t * processed).\n+\t */\n+\tif (pn < space->base_pn)\n+\t\treturn 0;\n+\n+\t/* If gap is beyond current map length, try to grow the bitmap to\n+\t * accommodate.\n+\t */\n+\tgap = (u16)(pn - space->base_pn);\n+\tif (gap >= space->pn_map_len) {\n+\t\terr = quic_pnspace_grow(space, gap + 1);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\tif (space->max_pn_seen < pn) {\n+\t\tspace->max_pn_seen = pn;\n+\t\tspace->max_pn_time = space->time;\n+\t}\n+\n+\tif (space->base_pn == pn) { /* PN is next expected packet. */\n+\t\tif (quic_pnspace_has_gap(space)) /* Advance to next gap. */\n+\t\t\tquic_pnspace_move(space, pn);\n+\t\telse /* Fast path: increment base_pn if no gaps. */\n+\t\t\tspace->base_pn++;\n+\t} else { /* Mark this packet as received in the bitmap. */\n+\t\tset_bit(gap, space->pn_map);\n+\t}\n+\n+\t/* Only update min and last_max_pn_seen if this packet is the current\n+\t * max_pn.\n+\t */\n+\tif (space->max_pn_seen != pn)\n+\t\treturn 0;\n+\n+\t/* Check if enough time has elapsed or enough packets have been\n+\t * received to update tracking.\n+\t */\n+\tlast_max_pn_seen = min_t(s64, space->last_max_pn_seen, space->base_pn);\n+\tlast_max_pn_time = space->last_max_pn_time;\n+\tif (space->max_pn_time < last_max_pn_time + space->max_time_limit &&\n+\t    space->max_pn_seen <= last_max_pn_seen + QUIC_PN_MAP_LIMIT)\n+\t\treturn 0;\n+\n+\t/* Advance base_pn if last_max_pn_seen is ahead of current base_pn.\n+\t * This is needed because QUIC doesn't retransmit packets;\n+\t * retransmitted frames are carried in new packets, so we move forward.\n+\t */\n+\tif (space->last_max_pn_seen + 1 > space->base_pn)\n+\t\tquic_pnspace_move(space, space->last_max_pn_seen);\n+\n+\tspace->min_pn_seen = space->last_max_pn_seen;\n+\tspace->last_max_pn_seen = space->max_pn_seen;\n+\tspace->last_max_pn_time = space->max_pn_time;\n+\treturn 0;\n+}\n+\n+/* Find the next gap in received packet numbers. Scans pn_map for a gap\n+ * starting from *@iter. A gap is a contiguous block of unreceived packets\n+ * between received ones.\n+ *\n+ * Returns: true if a gap was found, false if no more gaps exist or are\n+ * relevant.\n+ */\n+static bool quic_pnspace_next_gap_ack(const struct quic_pnspace *space,\n+\t\t\t\t      s64 *iter, u16 *start, u16 *end)\n+{\n+\tu16 start_ = 0, end_ = 0, offset = (u16)(*iter - space->base_pn);\n+\n+\tstart_ = (u16)find_next_zero_bit(space->pn_map, space->pn_map_len,\n+\t\t\t\t\t offset);\n+\tif (space->max_pn_seen <= space->base_pn + start_)\n+\t\treturn false;\n+\n+\tend_ = (u16)find_next_bit(space->pn_map, space->pn_map_len, start_);\n+\tif (space->max_pn_seen <= space->base_pn + end_ - 1)\n+\t\treturn false;\n+\n+\t*start = start_ + 1;\n+\t*end = end_;\n+\t*iter = space->base_pn + *end;\n+\treturn true;\n+}\n+\n+/* Generate gap acknowledgment blocks (GABs).  GABs describe ranges of\n+ * unacknowledged packets between received ones, and are used in ACK frames.\n+ *\n+ * Returns: Number of generated GABs (up to QUIC_PN_MAP_MAX_GABS).\n+ */\n+u16 quic_pnspace_num_gabs(struct quic_pnspace *space,\n+\t\t\t  struct quic_gap_ack_block *gabs)\n+{\n+\tu16 start, end, ngaps = 0;\n+\ts64 iter;\n+\n+\tif (!quic_pnspace_has_gap(space))\n+\t\treturn 0;\n+\n+\titer = space->base_pn;\n+\t/* Loop through all gaps until the end of the window or max allowed\n+\t * gaps.\n+\t */\n+\twhile (quic_pnspace_next_gap_ack(space, &iter, &start, &end)) {\n+\t\tgabs[ngaps].start = start;\n+\t\tif (ngaps == QUIC_PN_MAP_MAX_GABS - 1) {\n+\t\t\tgabs[ngaps].end =\n+\t\t\t\t(u16)(space->max_pn_seen - space->base_pn);\n+\t\t\tngaps++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tgabs[ngaps].end = end;\n+\t\tngaps++;\n+\t}\n+\treturn ngaps;\n+}\ndiff --git a/net/quic/pnspace.h b/net/quic/pnspace.h\nnew file mode 100644\nindex 000000000000..0961add68401\n--- /dev/null\n+++ b/net/quic/pnspace.h\n@@ -0,0 +1,157 @@\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_PN_MAP_MAX_GABS\t32\n+\n+#define QUIC_PN_MAP_INITIAL\t64\n+#define QUIC_PN_MAP_INCREMENT\tQUIC_PN_MAP_INITIAL\n+#define QUIC_PN_MAP_SIZE\t4096\n+#define QUIC_PN_MAP_LIMIT\t(QUIC_PN_MAP_SIZE * 3 / 4)\n+\n+#define QUIC_PNSPACE_MAX\t(QUIC_CRYPTO_MAX - 1)\n+#define QUIC_PNSPACE_NEXT_PN\t0\n+#define QUIC_PNSPACE_TIME_LIMIT\t(333000 * 3)\n+\n+enum {\n+\tQUIC_ECN_ECT1,\n+\tQUIC_ECN_ECT0,\n+\tQUIC_ECN_CE,\n+\tQUIC_ECN_MAX\n+};\n+\n+enum {\n+\tQUIC_ECN_LOCAL, /* ECN bits from incoming IP headers */\n+\tQUIC_ECN_PEER,  /* ECN bits reported by peer in ACK frames */\n+\tQUIC_ECN_DIR_MAX\n+};\n+\n+/* Represents a gap (range of missing packets) in the ACK map.  The values are\n+ * offsets from base_pn, with both 'start' and 'end' being +1.\n+ */\n+struct quic_gap_ack_block {\n+\tu16 start;\n+\tu16 end;\n+};\n+\n+/* Packet Number Map (pn_map) Layout:\n+ *\n+ *     min_pn_seen -->++-----------------------+---------------------+---\n+ *         base_pn -----^   last_max_pn_seen --^       max_pn_seen --^\n+ *\n+ * Map Advancement Logic:\n+ *   - min_pn_seen = last_max_pn_seen;\n+ *   - base_pn = first zero bit after last_max_pn_seen;\n+ *   - last_max_pn_seen = max_pn_seen;\n+ *   - last_max_pn_time = current time;\n+ *\n+ * Conditions to Advance pn_map:\n+ *   - (max_pn_time - last_max_pn_time) >= max_time_limit, or\n+ *   - (max_pn_seen - last_max_pn_seen) > QUIC_PN_MAP_LIMIT\n+ *\n+ * Gap Search Range:\n+ *   - From (base_pn - 1) to max_pn_seen\n+ */\n+struct quic_pnspace {\n+\t/* ECN counters indexed by dir and ECN codepoint (ECT1, ECT0, CE) */\n+\tu64 ecn_count[QUIC_ECN_DIR_MAX][QUIC_ECN_MAX];\n+\tunsigned long *pn_map; /* Received PN bitmap for ACK generation */\n+\tu16 pn_map_len;        /* Length of the PN bit map (in bits) */\n+\tu8  need_sack;         /* Flag indicating a SACK frame should be sent */\n+\tu8  sack_path;         /* Path used for sending the SACK frame */\n+\n+\ts64 last_max_pn_seen; /* Largest PN seen before pn_map advance */\n+\tu64 last_max_pn_time; /* Timestamp last_max_pn_seen was received */\n+\ts64 min_pn_seen;      /* Smallest PN received */\n+\ts64 max_pn_seen;      /* Largest PN received */\n+\tu64 max_pn_time;      /* Timestamp max_pn_seen was received */\n+\ts64 base_pn; /* PN corresponding to the start of the pn_map */\n+\tu64 time;    /* Cached now, or latest socket accept timestamp */\n+\n+\ts64 max_pn_acked_seen; /* Largest PN ACKed by peer */\n+\tu64 max_pn_acked_time; /* Timestamp max_pn_acked_seen was ACKed */\n+\tu64 last_sent_time;    /* Timestamp last ack-eliciting packet sent */\n+\tu64 loss_time;         /* Timestamp the packet can be declared lost */\n+\ts64 next_pn;           /* Next PN to send */\n+\n+\tu32 max_time_limit; /* Time threshold to trigger pn_map advance */\n+\tu32 inflight;       /* Ack-eliciting bytes in flight */\n+};\n+\n+static inline void\n+quic_pnspace_set_max_pn_acked_seen(struct quic_pnspace *space,\n+\t\t\t\t   s64 max_pn_acked_seen)\n+{\n+\tif (space->max_pn_acked_seen >= max_pn_acked_seen)\n+\t\treturn;\n+\tspace->max_pn_acked_seen = max_pn_acked_seen;\n+\tspace->max_pn_acked_time = quic_ktime_get_us();\n+}\n+\n+static inline void quic_pnspace_set_base_pn(struct quic_pnspace *space, s64 pn)\n+{\n+\tspace->base_pn = pn;\n+\tspace->max_pn_seen = space->base_pn - 1;\n+\tspace->last_max_pn_seen = space->max_pn_seen;\n+\tspace->min_pn_seen = space->max_pn_seen;\n+\n+\tspace->max_pn_time = space->time;\n+\tspace->last_max_pn_time = space->max_pn_time;\n+}\n+\n+static inline bool quic_pnspace_has_gap(const struct quic_pnspace *space)\n+{\n+\treturn space->base_pn != space->max_pn_seen + 1;\n+}\n+\n+static inline void quic_pnspace_inc_ecn_count(struct quic_pnspace *space,\n+\t\t\t\t\t      u8 ecn)\n+{\n+\tif (!ecn)\n+\t\treturn;\n+\tspace->ecn_count[QUIC_ECN_LOCAL][ecn - 1]++;\n+}\n+\n+/* Check if any ECN-marked packets were received. */\n+static inline bool quic_pnspace_has_ecn_count(struct quic_pnspace *space)\n+{\n+\treturn space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT0] ||\n+\t       space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT1] ||\n+\t       space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_CE];\n+}\n+\n+/* Updates the stored ECN counters based on values received in the peer's ACK\n+ * frame. Each counter is updated only if the new value is higher.\n+ *\n+ * Returns: true if CE count was increased (congestion indicated), false\n+ * otherwise.\n+ */\n+static inline bool quic_pnspace_set_ecn_count(struct quic_pnspace *space,\n+\t\t\t\t\t      u64 *ecn_count)\n+{\n+\tu64 *count = space->ecn_count[QUIC_ECN_PEER];\n+\n+\tif (count[QUIC_ECN_ECT0] < ecn_count[QUIC_ECN_ECT0])\n+\t\tcount[QUIC_ECN_ECT0] = ecn_count[QUIC_ECN_ECT0];\n+\tif (count[QUIC_ECN_ECT1] < ecn_count[QUIC_ECN_ECT1])\n+\t\tcount[QUIC_ECN_ECT1] = ecn_count[QUIC_ECN_ECT1];\n+\tif (count[QUIC_ECN_CE] < ecn_count[QUIC_ECN_CE]) {\n+\t\tcount[QUIC_ECN_CE] = ecn_count[QUIC_ECN_CE];\n+\t\treturn true;\n+\t}\n+\treturn false;\n+}\n+\n+u16 quic_pnspace_num_gabs(struct quic_pnspace *space,\n+\t\t\t  struct quic_gap_ack_block *gabs);\n+int quic_pnspace_check(struct quic_pnspace *space, s64 pn);\n+int quic_pnspace_mark(struct quic_pnspace *space, s64 pn);\n+\n+void quic_pnspace_free(struct quic_pnspace *space);\n+int quic_pnspace_init(struct quic_pnspace *space);\ndiff --git a/net/quic/socket.c b/net/quic/socket.c\nindex 7a4a00498b54..3b66cf8a942a 100644\n--- a/net/quic/socket.c\n+++ b/net/quic/socket.c\n@@ -38,6 +38,8 @@ static void quic_write_space(struct sock *sk)\n \n static int quic_init_sock(struct sock *sk)\n {\n+\tu8 i;\n+\n \tsk->sk_destruct = inet_sock_destruct;\n \tsk->sk_write_space = quic_write_space;\n \tsock_set_flag(sk, SOCK_USE_WRITE_QUEUE);\n@@ -52,11 +54,21 @@ static int quic_init_sock(struct sock *sk)\n \tif (quic_stream_init(quic_streams(sk)))\n \t\treturn -ENOMEM;\n \n+\tfor (i = 0; i < QUIC_PNSPACE_MAX; i++) {\n+\t\tif (quic_pnspace_init(quic_pnspace(sk, i)))\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n \treturn 0;\n }\n \n static void quic_destroy_sock(struct sock *sk)\n {\n+\tu8 i;\n+\n+\tfor (i = 0; i < QUIC_PNSPACE_MAX; i++)\n+\t\tquic_pnspace_free(quic_pnspace(sk, i));\n+\n \tquic_path_unbind(sk, quic_paths(sk), 0);\n \tquic_path_unbind(sk, quic_paths(sk), 1);\n \ndiff --git a/net/quic/socket.h b/net/quic/socket.h\nindex 9201ca3edad0..68c7b22d1e88 100644\n--- a/net/quic/socket.h\n+++ b/net/quic/socket.h\n@@ -12,6 +12,7 @@\n #include <linux/quic.h>\n \n #include \"common.h\"\n+#include \"pnspace.h\"\n #include \"family.h\"\n #include \"stream.h\"\n #include \"connid.h\"\n@@ -43,6 +44,7 @@ struct quic_sock {\n \tstruct quic_conn_id_set\t\tdest;\n \tstruct quic_path_group\t\tpaths;\n \tstruct quic_cong\t\tcong;\n+\tstruct quic_pnspace\t\tspace[QUIC_PNSPACE_MAX];\n };\n \n struct quic6_sock {\n@@ -105,6 +107,11 @@ static inline struct quic_cong *quic_cong(const struct sock *sk)\n \treturn &quic_sk(sk)->cong;\n }\n \n+static inline struct quic_pnspace *quic_pnspace(const struct sock *sk, u8 level)\n+{\n+\treturn &quic_sk(sk)->space[level % QUIC_CRYPTO_EARLY];\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","10/15"]}