From nobody Sat Feb 7 05:01:22 2026 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (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 3853B20B1E8 for ; Thu, 6 Mar 2025 09:57:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255028; cv=none; b=cZDtFHOslNKlNnzXa+/gPISHsqzA1vVwu1/t31Jtw6piX52szbcG1tsxhrET0McpW15zDZc1y2Lss0yJ3rGynn2RZ3IrFCyfn5VNfiFOj+3I54XA+JgjRS6ASLd5FWDt+1DWDX04ywDDQnNCv6PIrAAWqj8tOVozDqhHkltuIKA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255028; c=relaxed/simple; bh=9YjLGgOgxjd7KI1w2ki+88OGAWpCNcVzyGfWquchPGU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To; b=WBY3fVhBopqCHyW/af+DZFebhmKkBRK5Ek16FDucIsgh5wlCnhUEbXSs9YHENh0l4Sf53+QMyJcbv4AVP7jzxfgbIAp+hKEBvGRXWkga1Z16OkbaIWatA0rZ3fc+KYj4949OFAhrt7S/3xEmsnd0SDEX4fEYYs3hG74v2UquDJY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com; spf=pass smtp.mailfrom=daynix.com; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b=qNSbPQ1j; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=daynix.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b="qNSbPQ1j" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-223cc017ef5so6853555ad.0 for ; Thu, 06 Mar 2025 01:57:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=daynix-com.20230601.gappssmtp.com; s=20230601; t=1741255026; x=1741859826; darn=vger.kernel.org; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=b/qC0mfXfemQxtgPVvB5yWxcCoCf7wtW10EiI7eK4rY=; b=qNSbPQ1jqjyfhEOT4z+7Wgsx1G4ne2OrtJakl7SSjVhCbhAHPAxAm1jG9Kc++RUxK3 INEHVVX0+TVwhbojeyOS/fj4YGyyAP/qc8Rh4KApOWg9DK6r1xvTkXZxht4FwCAjcZRa 6Ct9EhDQMm4ZNrUlKxJ9/RMOe9wmoAdPBqbUH5XcHVTTg4f1nwqS8Z3xshSguaDa0Fde WXjjV1eCbX0DJV7GtunbOk6rqWVKfdZPMg/9vNWkvo/KrngadJub+1Q2W0o+Hre6AcCN yGgP0Z4FK8Z2/NpoU8wkdZqRWP5Fjb/G2ODYqLOrW78tkn825K0BH8vRsnrekLVcdtN+ FVew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741255026; x=1741859826; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=b/qC0mfXfemQxtgPVvB5yWxcCoCf7wtW10EiI7eK4rY=; b=uHA0DUwFlvMTssfNymI6OY/pqDk2u2wiRM5SjvzKe14glsWmDGDC7do7/e0Yx0F82R XufXoosJXjJLYeN7rTsIprTuaVGIOQF05hKpNecvXm3r41SS8PGeti2Pwr1UXQvx/xyi Q+rq7GQg9yzv++nbCWI372DIfnBbQdekrTrpgy2I/iIgdqMrL54tVVRk4Dcw4QWnMNA/ 6eTbP5kw7cx7TFN+rqwIfJUymC+Q3l+bB/qC/Khmrg6KDC5VO6mU/BKK7iqBb88Ma4rz +979ofD/9b3CxpIh1jL3NFZaX1sQWZ3ZODGUsFszW1nbUVzVrcPqI9H1WuJTl9AykyG2 ymMA== X-Forwarded-Encrypted: i=1; AJvYcCVe0tQy9uFUC+jikI3i6ifkXY16v1Nv01CoHlkqTTgTNznMq2t5DMNrmX9kcYj3timrP1LCA+3nR0NH8Is=@vger.kernel.org X-Gm-Message-State: AOJu0YwZEgZpBCaUlEnzcqrv4jAvjmo4drR0ZfWUcdD2XY+xsxtJaxo5 CBC+65psQHhWCg4kAJQA8+ERkTjdj4yj+Uh1OyOP8ERHvPMtlIJFqiHtJw/mJvk= X-Gm-Gg: ASbGncsmEg/rXbXXmhKEDvdWZqZ2gR/uWdGsdUZiWnHNK6iQjuhZ1Ew1ePjFVMk/vsG hQEH0YQfT6X3kKQhGkf6w7VzhRMnEAC1TjrUH96egFQg9sjRor42VvrM7hdmo6ttK4TLcwzg1nr Qy46elPLxYRJLmrB7u0BQu70sVlswrLayC8AXut1wIr2v9mdQBWQ32onI6nYbXKGKH3DKoBdE2k m8z6ghpIDR9t/5icoONZ6uxVrYUsKYaeOaBUrn+0fBjNit6IE2y9NfV2U07Wi2PB9744BpSQsFK zIcYr2tYeGFaieCOol98/Wm5bw3qsNEn52kNJiChVIAue1F8 X-Google-Smtp-Source: AGHT+IGGu/QoZAo2nhaA1potLpyXYJJNNJV8+aoiHR1iuEu2Ty1edcESMjQIzzVWhtXcrQeaP1hfZQ== X-Received: by 2002:a05:6a00:2d9a:b0:736:4ebd:e5a with SMTP id d2e1a72fcca58-73682cfa781mr9500647b3a.20.1741255026382; Thu, 06 Mar 2025 01:57:06 -0800 (PST) Received: from localhost ([157.82.207.107]) by smtp.gmail.com with UTF8SMTPSA id d2e1a72fcca58-7369844cfd8sm926795b3a.83.2025.03.06.01.57.02 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 06 Mar 2025 01:57:06 -0800 (PST) From: Akihiko Odaki Date: Thu, 06 Mar 2025 18:56:31 +0900 Subject: [PATCH net-next v8 1/6] virtio_net: Add functions for hashing Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250306-rss-v8-1-7ab4f56ff423@daynix.com> References: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> In-Reply-To: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> To: Jonathan Corbet , Willem de Bruijn , Jason Wang , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Michael S. Tsirkin" , Xuan Zhuo , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, kvm@vger.kernel.org, virtualization@lists.linux-foundation.org, linux-kselftest@vger.kernel.org, Yuri Benditovich , Andrew Melnychenko , Stephen Hemminger , gur.stavi@huawei.com, Lei Yang , Simon Horman , Akihiko Odaki X-Mailer: b4 0.14.2 They are useful to implement VIRTIO_NET_F_RSS and VIRTIO_NET_F_HASH_REPORT. Signed-off-by: Akihiko Odaki Tested-by: Lei Yang --- include/linux/virtio_net.h | 188 +++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 188 insertions(+) diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 02a9f4dc594d02372a6c1850cd600eff9d000d8d..426f33b4b82440d61b2af9fdc4c= 0b0d4c571b2c5 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -9,6 +9,194 @@ #include #include =20 +struct virtio_net_hash { + u32 value; + u16 report; +}; + +struct virtio_net_toeplitz_state { + u32 hash; + const u32 *key; +}; + +#define VIRTIO_NET_SUPPORTED_HASH_TYPES (VIRTIO_NET_RSS_HASH_TYPE_IPv4 | \ + VIRTIO_NET_RSS_HASH_TYPE_TCPv4 | \ + VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | \ + VIRTIO_NET_RSS_HASH_TYPE_IPv6 | \ + VIRTIO_NET_RSS_HASH_TYPE_TCPv6 | \ + VIRTIO_NET_RSS_HASH_TYPE_UDPv6) + +#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40 + +static inline void virtio_net_toeplitz_convert_key(u32 *input, size_t len) +{ + while (len >=3D sizeof(*input)) { + *input =3D be32_to_cpu((__force __be32)*input); + input++; + len -=3D sizeof(*input); + } +} + +static inline void virtio_net_toeplitz_calc(struct virtio_net_toeplitz_sta= te *state, + const __be32 *input, size_t len) +{ + while (len >=3D sizeof(*input)) { + for (u32 map =3D be32_to_cpu(*input); map; map &=3D (map - 1)) { + u32 i =3D ffs(map); + + state->hash ^=3D state->key[0] << (32 - i) | + (u32)((u64)state->key[1] >> i); + } + + state->key++; + input++; + len -=3D sizeof(*input); + } +} + +static inline u8 virtio_net_hash_key_length(u32 types) +{ + size_t len =3D 0; + + if (types & VIRTIO_NET_HASH_REPORT_IPv4) + len =3D max(len, + sizeof(struct flow_dissector_key_ipv4_addrs)); + + if (types & + (VIRTIO_NET_HASH_REPORT_TCPv4 | VIRTIO_NET_HASH_REPORT_UDPv4)) + len =3D max(len, + sizeof(struct flow_dissector_key_ipv4_addrs) + + sizeof(struct flow_dissector_key_ports)); + + if (types & VIRTIO_NET_HASH_REPORT_IPv6) + len =3D max(len, + sizeof(struct flow_dissector_key_ipv6_addrs)); + + if (types & + (VIRTIO_NET_HASH_REPORT_TCPv6 | VIRTIO_NET_HASH_REPORT_UDPv6)) + len =3D max(len, + sizeof(struct flow_dissector_key_ipv6_addrs) + + sizeof(struct flow_dissector_key_ports)); + + return len + sizeof(u32); +} + +static inline u32 virtio_net_hash_report(u32 types, + const struct flow_keys_basic *keys) +{ + switch (keys->basic.n_proto) { + case cpu_to_be16(ETH_P_IP): + if (!(keys->control.flags & FLOW_DIS_IS_FRAGMENT)) { + if (keys->basic.ip_proto =3D=3D IPPROTO_TCP && + (types & VIRTIO_NET_RSS_HASH_TYPE_TCPv4)) + return VIRTIO_NET_HASH_REPORT_TCPv4; + + if (keys->basic.ip_proto =3D=3D IPPROTO_UDP && + (types & VIRTIO_NET_RSS_HASH_TYPE_UDPv4)) + return VIRTIO_NET_HASH_REPORT_UDPv4; + } + + if (types & VIRTIO_NET_RSS_HASH_TYPE_IPv4) + return VIRTIO_NET_HASH_REPORT_IPv4; + + return VIRTIO_NET_HASH_REPORT_NONE; + + case cpu_to_be16(ETH_P_IPV6): + if (!(keys->control.flags & FLOW_DIS_IS_FRAGMENT)) { + if (keys->basic.ip_proto =3D=3D IPPROTO_TCP && + (types & VIRTIO_NET_RSS_HASH_TYPE_TCPv6)) + return VIRTIO_NET_HASH_REPORT_TCPv6; + + if (keys->basic.ip_proto =3D=3D IPPROTO_UDP && + (types & VIRTIO_NET_RSS_HASH_TYPE_UDPv6)) + return VIRTIO_NET_HASH_REPORT_UDPv6; + } + + if (types & VIRTIO_NET_RSS_HASH_TYPE_IPv6) + return VIRTIO_NET_HASH_REPORT_IPv6; + + return VIRTIO_NET_HASH_REPORT_NONE; + + default: + return VIRTIO_NET_HASH_REPORT_NONE; + } +} + +static inline void virtio_net_hash_rss(const struct sk_buff *skb, + u32 types, const u32 *key, + struct virtio_net_hash *hash) +{ + struct virtio_net_toeplitz_state toeplitz_state =3D { .key =3D key }; + struct flow_keys flow; + struct flow_keys_basic flow_basic; + u16 report; + + if (!skb_flow_dissect_flow_keys(skb, &flow, 0)) { + hash->report =3D VIRTIO_NET_HASH_REPORT_NONE; + return; + } + + flow_basic =3D (struct flow_keys_basic) { + .control =3D flow.control, + .basic =3D flow.basic + }; + + report =3D virtio_net_hash_report(types, &flow_basic); + + switch (report) { + case VIRTIO_NET_HASH_REPORT_IPv4: + virtio_net_toeplitz_calc(&toeplitz_state, + (__be32 *)&flow.addrs.v4addrs, + sizeof(flow.addrs.v4addrs)); + break; + + case VIRTIO_NET_HASH_REPORT_TCPv4: + virtio_net_toeplitz_calc(&toeplitz_state, + (__be32 *)&flow.addrs.v4addrs, + sizeof(flow.addrs.v4addrs)); + virtio_net_toeplitz_calc(&toeplitz_state, &flow.ports.ports, + sizeof(flow.ports.ports)); + break; + + case VIRTIO_NET_HASH_REPORT_UDPv4: + virtio_net_toeplitz_calc(&toeplitz_state, + (__be32 *)&flow.addrs.v4addrs, + sizeof(flow.addrs.v4addrs)); + virtio_net_toeplitz_calc(&toeplitz_state, &flow.ports.ports, + sizeof(flow.ports.ports)); + break; + + case VIRTIO_NET_HASH_REPORT_IPv6: + virtio_net_toeplitz_calc(&toeplitz_state, + (__be32 *)&flow.addrs.v6addrs, + sizeof(flow.addrs.v6addrs)); + break; + + case VIRTIO_NET_HASH_REPORT_TCPv6: + virtio_net_toeplitz_calc(&toeplitz_state, + (__be32 *)&flow.addrs.v6addrs, + sizeof(flow.addrs.v6addrs)); + virtio_net_toeplitz_calc(&toeplitz_state, &flow.ports.ports, + sizeof(flow.ports.ports)); + break; + + case VIRTIO_NET_HASH_REPORT_UDPv6: + virtio_net_toeplitz_calc(&toeplitz_state, + (__be32 *)&flow.addrs.v6addrs, + sizeof(flow.addrs.v6addrs)); + virtio_net_toeplitz_calc(&toeplitz_state, &flow.ports.ports, + sizeof(flow.ports.ports)); + break; + + default: + hash->report =3D VIRTIO_NET_HASH_REPORT_NONE; + return; + } + + hash->value =3D toeplitz_state.hash; + hash->report =3D report; +} + static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_ty= pe) { switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { --=20 2.48.1 From nobody Sat Feb 7 05:01:22 2026 Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) (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 0160720B814 for ; Thu, 6 Mar 2025 09:57:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255034; cv=none; b=r1bwv1KjR+TkYxKS/i9Twupx2AK3ADTg9cfVnMbYkTWhvufv7YvUsLixfDaK6vvzR0AZGtJkgROohvtUKyXnUeC4H6/M1T2dQ3OLZncmL4brE+jZpWPgg9Kt0RYJjwQPw7moXdhS5ShX/0ER2Oyev0XxTBR6PDiB0m5v+2t9pD4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255034; c=relaxed/simple; bh=6JPnLhL8s35dB03n039XAvB3PJPp3xjiscDs8gfFdqE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To; b=HgzzWs3igSHvUMj8Lmwbn0iUIDdWBEjYpDe2oLQBerkjFec7+5entA4MyJH5f61gAAE7U6aRFtpyY4UgX2QlrknrADSXMPAot88/sf6itSHK9pwXUnaLc4A4X287yMjD/UU9vPTWWo/506Zt2qPXIqrT/3Mqve2Pm16egZKRxts= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com; spf=pass smtp.mailfrom=daynix.com; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b=1f4Gjbzb; arc=none smtp.client-ip=209.85.214.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=daynix.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b="1f4Gjbzb" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-2240b4de12bso9165445ad.2 for ; Thu, 06 Mar 2025 01:57:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=daynix-com.20230601.gappssmtp.com; s=20230601; t=1741255032; x=1741859832; darn=vger.kernel.org; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=hY0V7oey/1irsg/oxeJVyzd/IX234qFLR74EBUPiqoM=; b=1f4GjbzbUQc2p7rObP8Rffq2hgjmxG5ITm/OnltgmggFk1HeiI5d00OnQ/FuL2k2Iz Nh8YEBYAXNdVn//VjatBjYFbzbE1QQjDVJ/HV7bS9fFT9c21xAVJnv9rztsSNfjXBGtb Noyz7bxrPpGYFGCCVSf9QkbrekXZBREZYZ9zSD3Xj3wOldG4JEx8dwfT+ThnhBCJfi2v 7p3iBBJF6UnuMv/7xID6Z9X9OGWyltMqGOwjDT3ynAdbnnK8gc1KarD6KmCJtbEu8q2P 0vdcHjZNfJGx5yhTI7hBtvdCru9KbRVRjBc5/C1lOdisJOjKoWjds+vieOUjWtaQ2Nrm 0MSA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741255032; x=1741859832; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hY0V7oey/1irsg/oxeJVyzd/IX234qFLR74EBUPiqoM=; b=i8f0+A5Ovr8GwTNIWvXJULF4lIsGvtKUxS113VBteq+b/JvZzFXye4OxwwI+p813v/ FTOzJj0xCNAXG+6OPm3UtOfu1hXDaFAzvwAuYB3svy/R7MQRKgWL+oNTcfmftH1L24RN tZoPd483zwaVgOfctnTPvHBK3PWDTlW+vT8Mx1OIIpvcViiOb2jjqX3lWVzGF14/aztv 6naO4/u+bAqCg/qd6Cf6t4z15OkHB7g5xvP2pfL42LGbjjYCSZDkOQoutaW8LUmp46G6 orZj5Bed/lpFYgzm6uUCkTHkBxbTzsED+5BsLuyoWXmhCbUaXiyvBeHrq/T/Msvv9KDI Hdbw== X-Forwarded-Encrypted: i=1; AJvYcCUNceQj7AyN2baTSGUu/3QQp2/IQgGXxAmExtbwJOV+oXLjfUHWDHuX2hF82Y56wekRtIgQEfmjH5NB700=@vger.kernel.org X-Gm-Message-State: AOJu0YwOWEgQbde3FUX8DFMjBXUviLfgjhl4oEDi/Vl5S+ctrWu1uyU6 7VS6eHAj5EWnGdhr6thhzjZKTuxJgsIecGBiMI4DAkLmEmyIBFgH8DDjXIqjZQo= X-Gm-Gg: ASbGnct7HFni8AuH9y8ljN4mBhP94Kke0DklIPGaqz7SKKdTlQB6r7E2Sj2TpmCN/OQ 7KXMnRamFvohc8PtiyoTqtRlIit8KspWNUIU4HYPtly/bW0wcKvMO3rD2N87ETTImah0Es0DrVm f3b+p56yawNhgc5ZVOCrkFI7SCMPyuP7hsSvopsaKwngDRYvap3jN75xB/9YM5FDrpCHvajUvHj r4rEvu3SckMpfVUptIQ27bc6mRZh1sMUbcO94/kIwdl6s7exbfCuI8P8T9l55yus44+BpOFqHdA rbARwyka/kmPtJXo8/htE9CgLNzDmSDdJ14uukuyvzHmyAMU X-Google-Smtp-Source: AGHT+IGlvJ3QJNQ7zdOmSl6WruunBWchSJlMnXfPdh91/T3HMnxY+JOOEGUyqA7YRqbtPgOIa/y+WA== X-Received: by 2002:a17:902:ea03:b0:223:f928:4553 with SMTP id d9443c01a7336-223f928486bmr88417775ad.44.1741255032170; Thu, 06 Mar 2025 01:57:12 -0800 (PST) Received: from localhost ([157.82.207.107]) by smtp.gmail.com with UTF8SMTPSA id d9443c01a7336-22410a7fc55sm8314515ad.138.2025.03.06.01.57.07 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 06 Mar 2025 01:57:11 -0800 (PST) From: Akihiko Odaki Date: Thu, 06 Mar 2025 18:56:32 +0900 Subject: [PATCH net-next v8 2/6] net: flow_dissector: Export flow_keys_dissector_symmetric Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250306-rss-v8-2-7ab4f56ff423@daynix.com> References: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> In-Reply-To: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> To: Jonathan Corbet , Willem de Bruijn , Jason Wang , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Michael S. Tsirkin" , Xuan Zhuo , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, kvm@vger.kernel.org, virtualization@lists.linux-foundation.org, linux-kselftest@vger.kernel.org, Yuri Benditovich , Andrew Melnychenko , Stephen Hemminger , gur.stavi@huawei.com, Lei Yang , Simon Horman , Akihiko Odaki X-Mailer: b4 0.14.2 flow_keys_dissector_symmetric is useful to derive a symmetric hash and to know its source such as IPv4, IPv6, TCP, and UDP. Signed-off-by: Akihiko Odaki Tested-by: Lei Yang --- include/net/flow_dissector.h | 1 + net/core/flow_dissector.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h index ced79dc8e8560e25a4dd567a04f5710b53452b45..d01c1ec77b7d21b17c14b05c47e= 3cdda39651bec 100644 --- a/include/net/flow_dissector.h +++ b/include/net/flow_dissector.h @@ -423,6 +423,7 @@ __be32 flow_get_u32_src(const struct flow_keys *flow); __be32 flow_get_u32_dst(const struct flow_keys *flow); =20 extern struct flow_dissector flow_keys_dissector; +extern struct flow_dissector flow_keys_dissector_symmetric; extern struct flow_dissector flow_keys_basic_dissector; =20 /* struct flow_keys_digest: diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 9cd8de6bebb543c3d672f576e03b29aa86b9d34a..32c7ee31330cf52df05d7a23b3e= 50d1a1bed9908 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -1862,7 +1862,8 @@ void make_flow_keys_digest(struct flow_keys_digest *d= igest, } EXPORT_SYMBOL(make_flow_keys_digest); =20 -static struct flow_dissector flow_keys_dissector_symmetric __read_mostly; +struct flow_dissector flow_keys_dissector_symmetric __read_mostly; +EXPORT_SYMBOL(flow_keys_dissector_symmetric); =20 u32 __skb_get_hash_symmetric_net(const struct net *net, const struct sk_bu= ff *skb) { --=20 2.48.1 From nobody Sat Feb 7 05:01:22 2026 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (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 011BF20AF87 for ; Thu, 6 Mar 2025 09:57:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255042; cv=none; b=tkrIOu4K4AgogGYQGSpWvyWs7QzVxO96DBOTcbmNmjgAcJ2gLbd7agM7ZuxwNY/qjX5zLZNYKt6vFfhky6ZKzWvaLwQUafbGTELg40LQmry5Uav8Lfexx/5r3AWtjzhNzp3XNI2lBIqak/KRBFyKNaWUgDoOsgGFvwZrqGoINtg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255042; c=relaxed/simple; bh=MMpbJXz9QV00ksgWCYgTHDqFr4mLF+GRGcfflinIYQc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To; b=MyB9zYII2oGBBhaFN2tllr1SkFmtVFU/TQp01KiZRjXH2xX1pqaeXb6S1KKVGpb3Wq3ymV5falQW4rj5ejF/s3B+/tj0iscb3RHXhr43q+jjg7Tmclq9IbAmIt6lbXe5MBv3r5rk2Be5jYMaw2uLA1QYAK2uR3AgSGMC+zakGos= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com; spf=pass smtp.mailfrom=daynix.com; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b=wppjJU+b; arc=none smtp.client-ip=209.85.214.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=daynix.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b="wppjJU+b" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-22185cddbffso31089245ad.1 for ; Thu, 06 Mar 2025 01:57:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=daynix-com.20230601.gappssmtp.com; s=20230601; t=1741255038; x=1741859838; darn=vger.kernel.org; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=lPWg8K/m0Ta5k6GYQ0gP01UZXN0a4f+PtljcMt7UTI8=; b=wppjJU+bz+wVQxRg8MKDwiwIT5YndD3hJHhlKWAmB79aVyvCK2QICYRYxYC1N6iBFq kl1w6q5HlrawgvBy8/0Xg4X1PBuvg7p3gXf7vw1PDCtMKHpXfrs+ZYylUcicWf65Y0Ey FfLdJls4UxHh2hXqOb8xARCDXN54zFEyE5i/C2c72FwWA9tNxuPXOdK6YQox9jYGHaa7 4Sl6pH2zZAXT3HJGjmdjVEI/Vn7PAAOAl+Y2rCNh5AK+d17d3ENai69HNa1VYbocXMkz 7/crFmYEZuEI3jHJ3gPxTo37Oc1RRPRU5BTDo5YKm034tZS+ps5dJvwmE1XQ7WZ+VdZj zAwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741255038; x=1741859838; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=lPWg8K/m0Ta5k6GYQ0gP01UZXN0a4f+PtljcMt7UTI8=; b=Lqwq8g3UoxRSeEHVj6tCoXg5DMeza+aAPLG7NW29S56/k3AUfZAyq/bt6GiZsFevpd d92KIgiCIetsB/nc+c9xsE+8RAsFYYIRhABfgWQ9bQREnBa9qTuuryCBHqneLLuzjWnV t09ibPMUuNI91u30QSPiOmAV0NQXPlnl+ypOhsOKd6eTZ0YsGXj1lvQb2yCTtTj66ZrN e9iv6x/VBYiVndQxaJfiN5K1ZEfq7uEkY0sJHMuFQ1YEgK5lruGFPcjay1jeCLSerr3R k+s3q2FhE1uj7wkjqyABu2MOked9AY0eTkhxv9TwRh6qPMRKR2FHFpIgr0cH2+xjLXT4 5jqA== X-Forwarded-Encrypted: i=1; AJvYcCWZXlHMbMp/1yXXA5NWEAAo+5fCQ3ZQA7YJhhXVIJ9YBWQmcvTrY4q1xlwhqkllTJyL7hVCD/2CMeTfIEQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyBMbPoqDqthHca9hUgDsr2+T3FBGZFUygDgc8Eb5psMV+FRFGs kGoMJSYIHPwobKPQESFMpv+Ay3fHxF5xwywuIX+Ru0MhDbbLBs+0/uYgeU8IKwc= X-Gm-Gg: ASbGncuZovR2LVYygW6dxkb7etFvjKb5fnxFM9bVjPc/ZlHKcTsVHUtTol7G+nbZBN4 VaoFDeXAYLxtF9t5M044S+Xnvv6XBZxwc/qUawdmMIUais0K+uSMued1sUdzkMWPjqb4kSTv+r9 IgtdFn8s4Dc+HARQ++E+3PAHum0T/nuXMiLqmPt84fqVn6zZhm6Bz9JH1FFdvmD/tLdTGq+xrih 3hE9QmfYxEWj/jgnO0n23yf7zBtiG1GtZk7RCZMieaUn+kXteeQXvukXq7Qg1WlhHAVVp/aeQUD ftufCPwktowYPUZbWS5nnGM107d1YcGQDLQ4aeoq/dUkuOs/ X-Google-Smtp-Source: AGHT+IESJ89B1GGiCdftGS1NutIDLl2nv5ieCWNvGAoMfgjQ8UwviY6HKRJDGBsWMwe6rGoHAo0dLw== X-Received: by 2002:a17:903:1cd:b0:221:751f:cfbe with SMTP id d9443c01a7336-2240946e7b2mr45966325ad.19.1741255038116; Thu, 06 Mar 2025 01:57:18 -0800 (PST) Received: from localhost ([157.82.207.107]) by smtp.gmail.com with UTF8SMTPSA id d9443c01a7336-22410aa5c36sm8263225ad.235.2025.03.06.01.57.13 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 06 Mar 2025 01:57:17 -0800 (PST) From: Akihiko Odaki Date: Thu, 06 Mar 2025 18:56:33 +0900 Subject: [PATCH net-next v8 3/6] tun: Introduce virtio-net hash feature Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250306-rss-v8-3-7ab4f56ff423@daynix.com> References: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> In-Reply-To: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> To: Jonathan Corbet , Willem de Bruijn , Jason Wang , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Michael S. Tsirkin" , Xuan Zhuo , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, kvm@vger.kernel.org, virtualization@lists.linux-foundation.org, linux-kselftest@vger.kernel.org, Yuri Benditovich , Andrew Melnychenko , Stephen Hemminger , gur.stavi@huawei.com, Lei Yang , Simon Horman , Akihiko Odaki X-Mailer: b4 0.14.2 Hash reporting =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Allow the guest to reuse the hash value to make receive steering consistent between the host and guest, and to save hash computation. RSS =3D=3D=3D RSS is a receive steering algorithm that can be negotiated to use with virtio_net. Conventionally the hash calculation was done by the VMM. However, computing the hash after the queue was chosen defeats the purpose of RSS. Another approach is to use eBPF steering program. This approach has another downside: it cannot report the calculated hash due to the restrictive nature of eBPF steering program. Introduce the code to perform RSS to the kernel in order to overcome thse challenges. An alternative solution is to extend the eBPF steering program so that it will be able to report to the userspace, but I didn't opt for it because extending the current mechanism of eBPF steering program as is because it relies on legacy context rewriting, and introducing kfunc-based eBPF will result in non-UAPI dependency while the other relevant virtualization APIs such as KVM and vhost_net are UAPIs. Signed-off-by: Akihiko Odaki Tested-by: Lei Yang --- Documentation/networking/tuntap.rst | 7 ++ drivers/net/Kconfig | 1 + drivers/net/tap.c | 67 ++++++++++++++- drivers/net/tun.c | 98 +++++++++++++++++----- drivers/net/tun_vnet.h | 159 ++++++++++++++++++++++++++++++++= ++-- include/linux/if_tap.h | 2 + include/linux/skbuff.h | 3 + include/uapi/linux/if_tun.h | 75 +++++++++++++++++ net/core/skbuff.c | 4 + 9 files changed, 385 insertions(+), 31 deletions(-) diff --git a/Documentation/networking/tuntap.rst b/Documentation/networking= /tuntap.rst index 4d7087f727be5e37dfbf5066a9e9c872cc98898d..86b4ae8caa8ad062c1e558920be= 42ce0d4217465 100644 --- a/Documentation/networking/tuntap.rst +++ b/Documentation/networking/tuntap.rst @@ -206,6 +206,13 @@ enable is true we enable it, otherwise we disable it:: return ioctl(fd, TUNSETQUEUE, (void *)&ifr); } =20 +3.4 Reference +------------- + +``linux/if_tun.h`` defines the interface described below: + +.. kernel-doc:: include/uapi/linux/if_tun.h + Universal TUN/TAP device driver Frequently Asked Question =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D =20 diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 1fd5acdc73c6af0e1a861867039c3624fc618e25..aecfd244dd83585fea2c5b815dc= d787c58166c28 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -395,6 +395,7 @@ config TUN tristate "Universal TUN/TAP device driver support" depends on INET select CRC32 + select SKB_EXTENSIONS help TUN/TAP provides packet reception and transmission for user space programs. It can be viewed as a simple Point-to-Point or Ethernet diff --git a/drivers/net/tap.c b/drivers/net/tap.c index d4ece538f1b23789ca60caa6232690e4d0a4d14a..e5c92259396b379f2532025a0fd= f1db36156a681 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -49,6 +49,10 @@ struct major_info { struct list_head next; }; =20 +struct tap_skb_cb { + struct virtio_net_hash hash; +}; + #define GOODCOPY_LEN 128 =20 static const struct proto_ops tap_socket_ops; @@ -179,6 +183,22 @@ static void tap_put_queue(struct tap_queue *q) sock_put(&q->sk); } =20 +static struct tap_skb_cb *tap_skb_cb(const struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct tap_skb_cb)); + return (struct tap_skb_cb *)skb->cb; +} + +static struct virtio_net_hash *tap_add_hash(struct sk_buff *skb) +{ + return &tap_skb_cb(skb)->hash; +} + +static const struct virtio_net_hash *tap_find_hash(const struct sk_buff *s= kb) +{ + return &tap_skb_cb(skb)->hash; +} + /* * Select a queue based on the rxq of the device on which this packet * arrived. If the incoming device is not mq, calculate a flow hash @@ -189,6 +209,7 @@ static void tap_put_queue(struct tap_queue *q) static struct tap_queue *tap_get_queue(struct tap_dev *tap, struct sk_buff *skb) { + struct flow_keys_basic keys_basic; struct tap_queue *queue =3D NULL; /* Access to taps array is protected by rcu, but access to numvtaps * isn't. Below we use it to lookup a queue, but treat it as a hint @@ -196,17 +217,47 @@ static struct tap_queue *tap_get_queue(struct tap_dev= *tap, * racing against queue removal. */ int numvtaps =3D READ_ONCE(tap->numvtaps); + struct tun_vnet_hash_container *vnet_hash =3D rcu_dereference(tap->vnet_h= ash); __u32 rxq; =20 + *tap_skb_cb(skb) =3D (struct tap_skb_cb) { + .hash =3D { .report =3D VIRTIO_NET_HASH_REPORT_NONE } + }; + if (!numvtaps) goto out; =20 if (numvtaps =3D=3D 1) goto single; =20 + if (vnet_hash) { + if ((vnet_hash->common.flags & TUN_VNET_HASH_RSS)) { + rxq =3D tun_vnet_rss_select_queue(numvtaps, vnet_hash, skb, tap_add_has= h); + queue =3D rcu_dereference(tap->taps[rxq]); + goto out; + } + + if (!skb->l4_hash && !skb->sw_hash) { + struct flow_keys keys; + + skb_flow_dissect_flow_keys(skb, &keys, FLOW_DISSECTOR_F_STOP_AT_FLOW_LA= BEL); + rxq =3D flow_hash_from_keys(&keys); + keys_basic =3D (struct flow_keys_basic) { + .control =3D keys.control, + .basic =3D keys.basic + }; + } else { + skb_flow_dissect_flow_keys_basic(NULL, skb, &keys_basic, NULL, 0, 0, 0, + FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL); + rxq =3D skb->hash; + } + } else { + rxq =3D skb_get_hash(skb); + } + /* Check if we can use flow to select a queue */ - rxq =3D skb_get_hash(skb); if (rxq) { + tun_vnet_hash_report(vnet_hash, skb, &keys_basic, rxq, tap_add_hash); queue =3D rcu_dereference(tap->taps[rxq % numvtaps]); goto out; } @@ -711,11 +762,12 @@ static ssize_t tap_put_user(struct tap_queue *q, int total; =20 if (q->flags & IFF_VNET_HDR) { - struct virtio_net_hdr vnet_hdr; + struct virtio_net_hdr_v1_hash vnet_hdr; =20 vnet_hdr_len =3D READ_ONCE(q->vnet_hdr_sz); =20 - ret =3D tun_vnet_hdr_from_skb(q->flags, NULL, skb, &vnet_hdr); + ret =3D tun_vnet_hdr_from_skb(vnet_hdr_len, q->flags, NULL, skb, + tap_find_hash, &vnet_hdr); if (ret) return ret; =20 @@ -992,6 +1044,15 @@ static long tap_ioctl(struct file *file, unsigned int= cmd, rtnl_unlock(); return ret; =20 + case TUNGETVNETHASHCAP: + return tun_vnet_ioctl_gethashcap(argp); + + case TUNSETVNETHASH: + rtnl_lock(); + tap =3D rtnl_dereference(q->tap); + ret =3D tap ? tun_vnet_ioctl_sethash(&tap->vnet_hash, true, argp) : -EBA= DFD; + rtnl_unlock(); + case SIOCGIFHWADDR: rtnl_lock(); tap =3D tap_get_tap_dev(q); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index d8f4d3e996a7a81d1f8b04635054081671a14f07..520013df416e93d3a50b46be9b5= 3ae9ab410eab4 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -209,6 +209,7 @@ struct tun_struct { struct bpf_prog __rcu *xdp_prog; struct tun_prog __rcu *steering_prog; struct tun_prog __rcu *filter_prog; + struct tun_vnet_hash_container __rcu *vnet_hash; struct ethtool_link_ksettings link_ksettings; /* init args */ struct file *file; @@ -451,20 +452,37 @@ static inline void tun_flow_save_rps_rxhash(struct tu= n_flow_entry *e, u32 hash) e->rps_rxhash =3D hash; } =20 +static struct virtio_net_hash *tun_add_hash(struct sk_buff *skb) +{ + return skb_ext_add(skb, SKB_EXT_TUN_VNET_HASH); +} + +static const struct virtio_net_hash *tun_find_hash(const struct sk_buff *s= kb) +{ + return skb_ext_find(skb, SKB_EXT_TUN_VNET_HASH); +} + /* We try to identify a flow through its rxhash. The reason that * we do not check rxq no. is because some cards(e.g 82599), chooses * the rxq based on the txq where the last packet of the flow comes. As * the userspace application move between processors, we may get a * different rxq no. here. */ -static u16 tun_automq_select_queue(struct tun_struct *tun, struct sk_buff = *skb) +static u16 tun_automq_select_queue(struct tun_struct *tun, + const struct tun_vnet_hash_container *vnet_hash, + struct sk_buff *skb) { + struct flow_keys keys; + struct flow_keys_basic keys_basic; struct tun_flow_entry *e; u32 txq, numqueues; =20 numqueues =3D READ_ONCE(tun->numqueues); =20 - txq =3D __skb_get_hash_symmetric(skb); + memset(&keys, 0, sizeof(keys)); + skb_flow_dissect(skb, &flow_keys_dissector_symmetric, &keys, 0); + + txq =3D flow_hash_from_keys(&keys); e =3D tun_flow_find(&tun->flows[tun_hashfn(txq)], txq); if (e) { tun_flow_save_rps_rxhash(e, txq); @@ -473,6 +491,13 @@ static u16 tun_automq_select_queue(struct tun_struct *= tun, struct sk_buff *skb) txq =3D reciprocal_scale(txq, numqueues); } =20 + keys_basic =3D (struct flow_keys_basic) { + .control =3D keys.control, + .basic =3D keys.basic + }; + tun_vnet_hash_report(vnet_hash, skb, &keys_basic, skb->l4_hash ? skb->has= h : txq, + tun_add_hash); + return txq; } =20 @@ -500,10 +525,17 @@ static u16 tun_select_queue(struct net_device *dev, s= truct sk_buff *skb, u16 ret; =20 rcu_read_lock(); - if (rcu_dereference(tun->steering_prog)) + if (rcu_dereference(tun->steering_prog)) { ret =3D tun_ebpf_select_queue(tun, skb); - else - ret =3D tun_automq_select_queue(tun, skb); + } else { + struct tun_vnet_hash_container *vnet_hash =3D rcu_dereference(tun->vnet_= hash); + + if (vnet_hash && (vnet_hash->common.flags & TUN_VNET_HASH_RSS)) + ret =3D tun_vnet_rss_select_queue(READ_ONCE(tun->numqueues), vnet_hash, + skb, tun_add_hash); + else + ret =3D tun_automq_select_queue(tun, vnet_hash, skb); + } rcu_read_unlock(); =20 return ret; @@ -1987,7 +2019,7 @@ static ssize_t tun_put_user_xdp(struct tun_struct *tu= n, ssize_t ret; =20 if (tun->flags & IFF_VNET_HDR) { - struct virtio_net_hdr gso =3D { 0 }; + struct virtio_net_hdr_v1_hash gso =3D { 0 }; =20 vnet_hdr_sz =3D READ_ONCE(tun->vnet_hdr_sz); ret =3D tun_vnet_hdr_put(vnet_hdr_sz, iter, &gso); @@ -2040,9 +2072,10 @@ static ssize_t tun_put_user(struct tun_struct *tun, } =20 if (vnet_hdr_sz) { - struct virtio_net_hdr gso; + struct virtio_net_hdr_v1_hash gso; =20 - ret =3D tun_vnet_hdr_from_skb(tun->flags, tun->dev, skb, &gso); + ret =3D tun_vnet_hdr_from_skb(vnet_hdr_sz, tun->flags, tun->dev, + skb, tun_find_hash, &gso); if (ret) return ret; =20 @@ -2223,6 +2256,7 @@ static void tun_free_netdev(struct net_device *dev) security_tun_dev_free_security(tun->security); __tun_set_ebpf(tun, &tun->steering_prog, NULL); __tun_set_ebpf(tun, &tun->filter_prog, NULL); + kfree_rcu_mightsleep(rcu_access_pointer(tun->vnet_hash)); } =20 static void tun_setup(struct net_device *dev) @@ -2921,13 +2955,9 @@ static int tun_set_queue(struct file *file, struct i= freq *ifr) } =20 static int tun_set_ebpf(struct tun_struct *tun, struct tun_prog __rcu **pr= og_p, - void __user *data) + int fd) { struct bpf_prog *prog; - int fd; - - if (copy_from_user(&fd, data, sizeof(fd))) - return -EFAULT; =20 if (fd =3D=3D -1) { prog =3D NULL; @@ -2993,7 +3023,9 @@ static long __tun_chr_ioctl(struct file *file, unsign= ed int cmd, int ifindex; int sndbuf; int ret; + int fd; bool do_notify =3D false; + struct tun_vnet_hash_container *vnet_hash; =20 if (cmd =3D=3D TUNSETIFF || cmd =3D=3D TUNSETQUEUE || (_IOC_TYPE(cmd) =3D=3D SOCK_IOC_TYPE && cmd !=3D SIOCGSKNS)) { @@ -3020,7 +3052,8 @@ static long __tun_chr_ioctl(struct file *file, unsign= ed int cmd, rtnl_lock(); =20 tun =3D tun_get(tfile); - if (cmd =3D=3D TUNSETIFF) { + switch (cmd) { + case TUNSETIFF: ret =3D -EEXIST; if (tun) goto unlock; @@ -3035,8 +3068,8 @@ static long __tun_chr_ioctl(struct file *file, unsign= ed int cmd, if (copy_to_user(argp, &ifr, ifreq_len)) ret =3D -EFAULT; goto unlock; - } - if (cmd =3D=3D TUNSETIFINDEX) { + + case TUNSETIFINDEX: ret =3D -EPERM; if (tun) goto unlock; @@ -3050,6 +3083,10 @@ static long __tun_chr_ioctl(struct file *file, unsig= ned int cmd, ret =3D 0; tfile->ifindex =3D ifindex; goto unlock; + + case TUNGETVNETHASHCAP: + ret =3D tun_vnet_ioctl_gethashcap(argp); + goto unlock; } =20 ret =3D -EBADFD; @@ -3230,11 +3267,27 @@ static long __tun_chr_ioctl(struct file *file, unsi= gned int cmd, break; =20 case TUNSETSTEERINGEBPF: - ret =3D tun_set_ebpf(tun, &tun->steering_prog, argp); + if (get_user(fd, (int __user *)argp)) { + ret =3D -EFAULT; + break; + } + + vnet_hash =3D rtnl_dereference(tun->vnet_hash); + if (fd !=3D -1 && vnet_hash && (vnet_hash->common.flags & TUN_VNET_HASH_= RSS)) { + ret =3D -EBUSY; + break; + } + + ret =3D tun_set_ebpf(tun, &tun->steering_prog, fd); break; =20 case TUNSETFILTEREBPF: - ret =3D tun_set_ebpf(tun, &tun->filter_prog, argp); + if (get_user(fd, (int __user *)argp)) { + ret =3D -EFAULT; + break; + } + + ret =3D tun_set_ebpf(tun, &tun->filter_prog, fd); break; =20 case TUNSETCARRIER: @@ -3252,8 +3305,15 @@ static long __tun_chr_ioctl(struct file *file, unsig= ned int cmd, ret =3D open_related_ns(&net->ns, get_net_ns); break; =20 + case TUNSETVNETHASH: + ret =3D tun_vnet_ioctl_sethash(&tun->vnet_hash, + !rtnl_dereference(tun->steering_prog), + argp); + break; + default: - ret =3D tun_vnet_ioctl(&tun->vnet_hdr_sz, &tun->flags, cmd, argp); + ret =3D tun_vnet_ioctl(&tun->vnet_hdr_sz, &tun->flags, + cmd, argp); break; } =20 diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h index 58b9ac7a5fc4084c789fe94fe36b5f8631bf1fa4..8e7d51fb0b4742cef56e7c5ad77= 8b156cc654bed 100644 --- a/drivers/net/tun_vnet.h +++ b/drivers/net/tun_vnet.h @@ -6,6 +6,16 @@ #define TUN_VNET_LE 0x80000000 #define TUN_VNET_BE 0x40000000 =20 +typedef struct virtio_net_hash *(*tun_vnet_hash_add)(struct sk_buff *); +typedef const struct virtio_net_hash *(*tun_vnet_hash_find)(const struct s= k_buff *); + +struct tun_vnet_hash_container { + struct tun_vnet_hash common; + struct tun_vnet_hash_rss rss; + u32 rss_key[VIRTIO_NET_RSS_MAX_KEY_SIZE]; + u16 rss_indirection_table[]; +}; + static inline bool tun_vnet_legacy_is_little_endian(unsigned int flags) { bool be =3D IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE) && @@ -107,6 +117,123 @@ static inline long tun_vnet_ioctl(int *vnet_hdr_sz, u= nsigned int *flags, } } =20 +static inline long tun_vnet_ioctl_gethashcap(void __user *argp) +{ + static const struct tun_vnet_hash cap =3D { + .flags =3D TUN_VNET_HASH_REPORT | TUN_VNET_HASH_RSS, + .types =3D VIRTIO_NET_SUPPORTED_HASH_TYPES + }; + + return copy_to_user(argp, &cap, sizeof(cap)) ? -EFAULT : 0; +} + +static inline long tun_vnet_ioctl_sethash(struct tun_vnet_hash_container _= _rcu **hashp, + bool can_rss, void __user *argp) +{ + struct tun_vnet_hash hash_buf; + struct tun_vnet_hash_container *hash; + + if (copy_from_user(&hash_buf, argp, sizeof(hash_buf))) + return -EFAULT; + argp =3D (struct tun_vnet_hash __user *)argp + 1; + + if (hash_buf.flags & TUN_VNET_HASH_RSS) { + struct tun_vnet_hash_rss rss; + size_t indirection_table_size; + size_t key_size; + size_t size; + + if (!can_rss) + return -EBUSY; + + if (copy_from_user(&rss, argp, sizeof(rss))) + return -EFAULT; + argp =3D (struct tun_vnet_hash_rss __user *)argp + 1; + + indirection_table_size =3D ((size_t)rss.indirection_table_mask + 1) * 2; + key_size =3D virtio_net_hash_key_length(hash_buf.types); + size =3D struct_size(hash, rss_indirection_table, + (size_t)rss.indirection_table_mask + 1); + + hash =3D kmalloc(size, GFP_KERNEL); + if (!hash) + return -ENOMEM; + + if (copy_from_user(hash->rss_indirection_table, + argp, indirection_table_size)) { + kfree(hash); + return -EFAULT; + } + argp =3D (u16 __user *)argp + rss.indirection_table_mask + 1; + + if (copy_from_user(hash->rss_key, argp, key_size)) { + kfree(hash); + return -EFAULT; + } + + virtio_net_toeplitz_convert_key(hash->rss_key, key_size); + hash->rss =3D rss; + } else { + hash =3D kmalloc(sizeof(hash->common), GFP_KERNEL); + if (!hash) + return -ENOMEM; + } + + hash->common =3D hash_buf; + kfree_rcu_mightsleep(rcu_replace_pointer_rtnl(*hashp, hash)); + return 0; +} + +static void tun_vnet_hash_report(const struct tun_vnet_hash_container *has= h, + struct sk_buff *skb, + const struct flow_keys_basic *keys, + u32 value, + tun_vnet_hash_add vnet_hash_add) +{ + struct virtio_net_hash *report; + + if (!hash || !(hash->common.flags & TUN_VNET_HASH_REPORT)) + return; + + report =3D vnet_hash_add(skb); + if (!report) + return; + + *report =3D (struct virtio_net_hash) { + .report =3D virtio_net_hash_report(hash->common.types, keys), + .value =3D value + }; +} + +static u16 tun_vnet_rss_select_queue(u32 numqueues, + const struct tun_vnet_hash_container *hash, + struct sk_buff *skb, + tun_vnet_hash_add vnet_hash_add) +{ + struct virtio_net_hash *report; + struct virtio_net_hash ret; + u16 txq, index; + + if (!numqueues) + return 0; + + virtio_net_hash_rss(skb, hash->common.types, hash->rss_key, &ret); + + if (!ret.report) + return hash->rss.unclassified_queue % numqueues; + + if (hash->common.flags & TUN_VNET_HASH_REPORT) { + report =3D vnet_hash_add(skb); + if (report) + *report =3D ret; + } + + index =3D ret.value & hash->rss.indirection_table_mask; + txq =3D READ_ONCE(hash->rss_indirection_table[index]); + + return txq % numqueues; +} + static inline int tun_vnet_hdr_get(int sz, unsigned int flags, struct iov_iter *from, struct virtio_net_hdr *hdr) @@ -135,15 +262,17 @@ static inline int tun_vnet_hdr_get(int sz, unsigned i= nt flags, } =20 static inline int tun_vnet_hdr_put(int sz, struct iov_iter *iter, - const struct virtio_net_hdr *hdr) + const struct virtio_net_hdr_v1_hash *hdr) { + int content_sz =3D MIN(sizeof(*hdr), sz); + if (unlikely(iov_iter_count(iter) < sz)) return -EINVAL; =20 - if (unlikely(copy_to_iter(hdr, sizeof(*hdr), iter) !=3D sizeof(*hdr))) + if (unlikely(copy_to_iter(hdr, content_sz, iter) !=3D content_sz)) return -EFAULT; =20 - if (iov_iter_zero(sz - sizeof(*hdr), iter) !=3D sz - sizeof(*hdr)) + if (iov_iter_zero(sz - content_sz, iter) !=3D sz - content_sz) return -EFAULT; =20 return 0; @@ -155,26 +284,38 @@ static inline int tun_vnet_hdr_to_skb(unsigned int fl= ags, struct sk_buff *skb, return virtio_net_hdr_to_skb(skb, hdr, tun_vnet_is_little_endian(flags)); } =20 -static inline int tun_vnet_hdr_from_skb(unsigned int flags, +static inline int tun_vnet_hdr_from_skb(int sz, unsigned int flags, const struct net_device *dev, const struct sk_buff *skb, - struct virtio_net_hdr *hdr) + tun_vnet_hash_find vnet_hash_find, + struct virtio_net_hdr_v1_hash *hdr) { int vlan_hlen =3D skb_vlan_tag_present(skb) ? VLAN_HLEN : 0; + const struct virtio_net_hash *report =3D sz < sizeof(struct virtio_net_hd= r_v1_hash) ? + NULL : vnet_hash_find(skb); + + *hdr =3D (struct virtio_net_hdr_v1_hash) { + .hash_report =3D VIRTIO_NET_HASH_REPORT_NONE + }; + + if (report) { + hdr->hash_value =3D cpu_to_le32(report->value); + hdr->hash_report =3D cpu_to_le16(report->report); + } =20 - if (virtio_net_hdr_from_skb(skb, hdr, + if (virtio_net_hdr_from_skb(skb, (struct virtio_net_hdr *)hdr, tun_vnet_is_little_endian(flags), true, vlan_hlen)) { struct skb_shared_info *sinfo =3D skb_shinfo(skb); =20 if (net_ratelimit()) { netdev_err(dev, "unexpected GSO type: 0x%x, gso_size %d, hdr_len %d\n", - sinfo->gso_type, tun_vnet16_to_cpu(flags, hdr->gso_size), - tun_vnet16_to_cpu(flags, hdr->hdr_len)); + sinfo->gso_type, tun_vnet16_to_cpu(flags, hdr->hdr.gso_size), + tun_vnet16_to_cpu(flags, hdr->hdr.hdr_len)); print_hex_dump(KERN_ERR, "tun: ", DUMP_PREFIX_NONE, 16, 1, skb->head, - min(tun_vnet16_to_cpu(flags, hdr->hdr_len), 64), true); + min(tun_vnet16_to_cpu(flags, hdr->hdr.hdr_len), 64), true); } WARN_ON_ONCE(1); return -EINVAL; diff --git a/include/linux/if_tap.h b/include/linux/if_tap.h index 553552fa635c3e1e53d1a63c203d32e4c4fd5a4f..7334c46a3f101675a0d4e5a0369= 87cfe18842f9f 100644 --- a/include/linux/if_tap.h +++ b/include/linux/if_tap.h @@ -31,6 +31,7 @@ static inline struct ptr_ring *tap_get_ptr_ring(struct fi= le *f) #define MAX_TAP_QUEUES 256 =20 struct tap_queue; +struct tun_vnet_hash_container; =20 struct tap_dev { struct net_device *dev; @@ -43,6 +44,7 @@ struct tap_dev { int numqueues; netdev_features_t tap_features; int minor; + struct tun_vnet_hash_container __rcu *vnet_hash; =20 void (*update_features)(struct tap_dev *tap, netdev_features_t features); void (*count_tx_dropped)(struct tap_dev *tap); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index bb2b751d274acff931281a72e8b4b0c699b4e8af..cdd793f1c360ad5f63fcc4cbf67= d845f5e2ccf6f 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -4842,6 +4842,9 @@ enum skb_ext_id { #endif #if IS_ENABLED(CONFIG_MCTP_FLOWS) SKB_EXT_MCTP, +#endif +#if IS_ENABLED(CONFIG_TUN) + SKB_EXT_TUN_VNET_HASH, #endif SKB_EXT_NUM, /* must be last */ }; diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index 287cdc81c9390c289a30545aa7ed23d81c3329d3..4887f97500a870c7ef3c96a5837= b2d0a5a225040 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h @@ -62,6 +62,42 @@ #define TUNSETCARRIER _IOW('T', 226, int) #define TUNGETDEVNETNS _IO('T', 227) =20 +/** + * define TUNGETVNETHASHCAP - ioctl to get virtio_net hashing capability. + * + * The argument is a pointer to &struct tun_vnet_hash which will store the + * maximal virtio_net hashing configuration. + */ +#define TUNGETVNETHASHCAP _IOR('T', 228, struct tun_vnet_hash) + +/** + * define TUNSETVNETHASH - ioctl to configure virtio_net hashing + * + * The argument is a pointer to &struct tun_vnet_hash. + * + * The argument is a pointer to the compound of the following in order if + * %TUN_VNET_HASH_RSS is set: + * + * 1. &struct tun_vnet_hash + * 2. &struct tun_vnet_hash_rss + * 3. Indirection table + * 4. Key + * + * The %TUN_VNET_HASH_REPORT flag set with this ioctl will be effective on= ly + * after calling the %TUNSETVNETHDRSZ ioctl with a number greater than or = equal + * to the size of &struct virtio_net_hdr_v1_hash. + * + * The members added to the legacy header by %TUN_VNET_HASH_REPORT flag wi= ll + * always be little-endian. + * + * This ioctl results in %EBADFD if the underlying device is deleted. It a= ffects + * all queues attached to the same device. + * + * This ioctl currently has no effect on XDP packets and packets with + * queue_mapping set by TC. + */ +#define TUNSETVNETHASH _IOW('T', 229, struct tun_vnet_hash) + /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 #define IFF_TAP 0x0002 @@ -115,4 +151,43 @@ struct tun_filter { __u8 addr[][ETH_ALEN]; }; =20 +/** + * define TUN_VNET_HASH_REPORT - Request virtio_net hash reporting for vho= st + */ +#define TUN_VNET_HASH_REPORT 0x0001 + +/** + * define TUN_VNET_HASH_RSS - Request virtio_net RSS + * + * This is mutually exclusive with eBPF steering program. + */ +#define TUN_VNET_HASH_RSS 0x0002 + +/** + * struct tun_vnet_hash - virtio_net hashing configuration + * @flags: + * Bitmask consists of %TUN_VNET_HASH_REPORT and %TUN_VNET_HASH_RSS + * @pad: + * Should be filled with zero before passing to %TUNSETVNETHASH + * @types: + * Bitmask of allowed hash types + */ +struct tun_vnet_hash { + __u16 flags; + __u8 pad[2]; + __u32 types; +}; + +/** + * struct tun_vnet_hash_rss - virtio_net RSS configuration + * @indirection_table_mask: + * Bitmask to be applied to the indirection table index + * @unclassified_queue: + * The index of the queue to place unclassified packets in + */ +struct tun_vnet_hash_rss { + __u16 indirection_table_mask; + __u16 unclassified_queue; +}; + #endif /* _UAPI__IF_TUN_H */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7b03b64fdcb276f68ce881d1d8da8e4c6b897efc..aa2a091b649f0c9d6e0196f34f3= 45ba78b5498fb 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -64,6 +64,7 @@ #include #include #include +#include =20 #include #include @@ -4969,6 +4970,9 @@ static const u8 skb_ext_type_len[] =3D { #if IS_ENABLED(CONFIG_MCTP_FLOWS) [SKB_EXT_MCTP] =3D SKB_EXT_CHUNKSIZEOF(struct mctp_flow), #endif +#if IS_ENABLED(CONFIG_TUN) + [SKB_EXT_TUN_VNET_HASH] =3D SKB_EXT_CHUNKSIZEOF(struct virtio_net_hash), +#endif }; =20 static __always_inline unsigned int skb_ext_total_length(void) --=20 2.48.1 From nobody Sat Feb 7 05:01:22 2026 Received: from mail-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.180]) (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 88C9520ADCF for ; Thu, 6 Mar 2025 09:57:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255046; cv=none; b=rcoU3+Z//HWkfd4KsK+VMiVHeJUKfRdm6Y8faQsoMCweFzVEEvw8RH2g/n+wMDNfzhYqTV/P9YP38hwctBQKz7s9r5Gg03nEAYVA1wVXz5yrEWmiLG4UP7JGJ83cpkaZtP/XyLXf6IHqF4Fp10Qi4NUeMDlYyhV+4atrMg4WHdM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255046; c=relaxed/simple; bh=nEE2/C77oF+j3LxMBPOVoqrwqXRBAnjdJN3gCt7XvfU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To; b=B/cKRpph+Ne5I/h3q5/X9zx/7J6wPoGP04Jj+bJfIiAbvAem57Xg/6JKJuo5p1wK0Sdc4d8wjCjq6CjYs9eAgAE4yyTk84Ijhyn0pi7dQgG+Qo5h8Xs6EFmRY5LiPP25cWFTB/+z4fB11fPsULtkiw+W4pVZytm2eqFWMTJYOGI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com; spf=pass smtp.mailfrom=daynix.com; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b=Hjzwn3Kj; arc=none smtp.client-ip=209.85.214.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=daynix.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b="Hjzwn3Kj" Received: by mail-pl1-f180.google.com with SMTP id d9443c01a7336-2235189adaeso7079025ad.0 for ; Thu, 06 Mar 2025 01:57:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=daynix-com.20230601.gappssmtp.com; s=20230601; t=1741255044; x=1741859844; darn=vger.kernel.org; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=N/24Tf2dJCQDhe+eHaFoVGEomrBLp8dkxojt6Qs+7G8=; b=Hjzwn3KjYw8Eou3QnNb6xowZIEmBUupqNIClkt/HBVzUdsnhmJnIXlyjcGNhs5m5Ga 0I/Dfr7IKdHMR77f4VWw8qtuiYq7RW4WCqyeXMN/x1wt6CaYL2tWsn2FRV9uU2xbfLRG s6cN4pZwOF7NJZ90XsIGlOM5Xnhz5nbobFyrgtdbvPYb6T2nAwRELsJe85ksNGH057yD FY+FyHmlpWYnG0QxEDpECDbYnzZ1sdquLrRHTtbGu6VzCW+OB2ZaBlCh0LytB8+HOjOH OZzOpllSPib8iVsJKVmIEcZ4tQ+PHK9QLUpTXWShCbWYdSaJIYIQzimpxctJSjPoPlUD QlIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741255044; x=1741859844; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=N/24Tf2dJCQDhe+eHaFoVGEomrBLp8dkxojt6Qs+7G8=; b=m78EqOCB2ukbYQrGIUqI+lxEm/HT6dUoyEtWEor4zpMwf5mx0fVuL6POiBAkrAKZuw 9pmdX/HoWyRwyGUlil3GqEf8SFwriiAf87sRA1fOECF4Z59pYP0JQU1G1MIvh7nkbvKI 8pC6lKv/x8a4YIoRmiuyd3nYYtMNgzijK/fWtAsp6zRfLv9fv2XjEFR4q/KO/gSZl4jJ +4beQbsOUNA/IiVa+JsFsa2czI8UNM2QoqNZjGRRK44vU5BVmJP5Ew6GnY2TckmwGzrh PfeWoFzqpz/1aFuEjDpsGkz6Sd+pkg6SD/wCVpW1omwffq/H6PTzitKGYZdL7d1GYajJ o5kg== X-Forwarded-Encrypted: i=1; AJvYcCVPc0+ReTohYTkPfzU+5RWaRr2l3GJ1UZAjuKFMpmHJ34wF3w28jDZSU8o035Io3jHDlPRa16ZN/UAo65Y=@vger.kernel.org X-Gm-Message-State: AOJu0YyDL78PSX6g2xVp2pOXtKKzXhcByPTWPBNbnruwYUeciD4guvtk V0Y6856aYJPz5OZ60HhOoL6Up05NipoouVrV+L6W2eTYyRL0XDdb1M9JJosFDRg= X-Gm-Gg: ASbGncsqgClcwnXyQ9Q7zjRaNtd0P0afVCakbXyIjEZTOY9NjCkpSAxXI/dWN2mkJOX 8HqRbWLc0titR8xgZiBPYYdrCE7r11vok3Z9/oGHmhEk3FFlpGdDb3+g0/ZbogIEriJhz8ImJGW NzrjlZRDVbIaGVblRs+WZ1N1raBn0eKwqpjA0diS5MBSv4tHjVVtzDZPYec+NEy7z+oSXY7BS0H QcDxRyj7aXRtlhAKlbqK2b07T0P5g1HR6guO6TgTxthHm7S1h0RC1Z4VeX7LQt/ow2ux5DLAgDz yJhnhLSt8dwnK1It4Ah3D6850ly1f3jgro10OXjj3QWpzMwt X-Google-Smtp-Source: AGHT+IF8DbatJk1Q6ANosIF8NkNqX8HFE0PqKk+dfYmnjOrPC/QDLXisFNDJhmMcDSA8YlhDXsU1RQ== X-Received: by 2002:a17:902:ea03:b0:220:ff82:1c60 with SMTP id d9443c01a7336-22409426beemr42036455ad.14.1741255043950; Thu, 06 Mar 2025 01:57:23 -0800 (PST) Received: from localhost ([157.82.207.107]) by smtp.gmail.com with UTF8SMTPSA id d2e1a72fcca58-736985387d3sm943032b3a.172.2025.03.06.01.57.19 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 06 Mar 2025 01:57:23 -0800 (PST) From: Akihiko Odaki Date: Thu, 06 Mar 2025 18:56:34 +0900 Subject: [PATCH net-next v8 4/6] selftest: tun: Test vnet ioctls without device Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250306-rss-v8-4-7ab4f56ff423@daynix.com> References: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> In-Reply-To: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> To: Jonathan Corbet , Willem de Bruijn , Jason Wang , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Michael S. Tsirkin" , Xuan Zhuo , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, kvm@vger.kernel.org, virtualization@lists.linux-foundation.org, linux-kselftest@vger.kernel.org, Yuri Benditovich , Andrew Melnychenko , Stephen Hemminger , gur.stavi@huawei.com, Lei Yang , Simon Horman , Akihiko Odaki X-Mailer: b4 0.14.2 Ensure that vnet ioctls result in EBADFD when the underlying device is deleted. Signed-off-by: Akihiko Odaki Tested-by: Lei Yang --- tools/testing/selftests/net/tun.c | 74 +++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 74 insertions(+) diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/ne= t/tun.c index fa83918b62d1be1c93adcd6c2f07654893cf97f8..463dd98f2b80b1bdcb398cee43c= 834e7dc5cf784 100644 --- a/tools/testing/selftests/net/tun.c +++ b/tools/testing/selftests/net/tun.c @@ -159,4 +159,78 @@ TEST_F(tun, reattach_close_delete) { EXPECT_EQ(tun_delete(self->ifname), 0); } =20 +FIXTURE(tun_deleted) +{ + char ifname[IFNAMSIZ]; + int fd; +}; + +FIXTURE_SETUP(tun_deleted) +{ + self->ifname[0] =3D 0; + self->fd =3D tun_alloc(self->ifname); + ASSERT_LE(0, self->fd); + + ASSERT_EQ(0, tun_delete(self->ifname)) + EXPECT_EQ(0, close(self->fd)); +} + +FIXTURE_TEARDOWN(tun_deleted) +{ + EXPECT_EQ(0, close(self->fd)); +} + +TEST_F(tun_deleted, getvnethdrsz) +{ + ASSERT_EQ(-1, ioctl(self->fd, TUNGETVNETHDRSZ)); + EXPECT_EQ(EBADFD, errno); +} + +TEST_F(tun_deleted, setvnethdrsz) +{ + ASSERT_EQ(-1, ioctl(self->fd, TUNSETVNETHDRSZ)); + EXPECT_EQ(EBADFD, errno); +} + +TEST_F(tun_deleted, getvnetle) +{ + ASSERT_EQ(-1, ioctl(self->fd, TUNGETVNETLE)); + EXPECT_EQ(EBADFD, errno); +} + +TEST_F(tun_deleted, setvnetle) +{ + ASSERT_EQ(-1, ioctl(self->fd, TUNSETVNETLE)); + EXPECT_EQ(EBADFD, errno); +} + +TEST_F(tun_deleted, getvnetbe) +{ + ASSERT_EQ(-1, ioctl(self->fd, TUNGETVNETBE)); + EXPECT_EQ(EBADFD, errno); +} + +TEST_F(tun_deleted, setvnetbe) +{ + ASSERT_EQ(-1, ioctl(self->fd, TUNSETVNETBE)); + EXPECT_EQ(EBADFD, errno); +} + +TEST_F(tun_deleted, getvnethashcap) +{ + struct tun_vnet_hash cap; + int i =3D ioctl(self->fd, TUNGETVNETHASHCAP, &cap); + + if (i =3D=3D -1 && errno =3D=3D EBADFD) + SKIP(return, "TUNGETVNETHASHCAP not supported"); + + EXPECT_EQ(0, i); +} + +TEST_F(tun_deleted, setvnethash) +{ + ASSERT_EQ(-1, ioctl(self->fd, TUNSETVNETHASH)); + EXPECT_EQ(EBADFD, errno); +} + TEST_HARNESS_MAIN --=20 2.48.1 From nobody Sat Feb 7 05:01:22 2026 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) (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 745C120DD7D for ; Thu, 6 Mar 2025 09:57:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255054; cv=none; b=BZuOUXnMm5Vm+j6sJDpVyNrSIluG3vP9svCEEq1kH1VZnjj94Fl2S2R+xBF5vEV+TNxEg6LtaZIP2ftVvPnfvLUlFVyU5renQgUZA0cAETFJi8We1YylCD1pV+ds+hWwtbFlZ/eKLZRR4T7ei2fSLTzlRUk1H+7Ud47415dJTR0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255054; c=relaxed/simple; bh=zmFVzver9kf4I8nu4iYc6c6/eOEuAQ30n1rNi4IX95s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To; b=bHXkGpo/+TfsqBK7KmMLGebQP9BsJ3m4mf44oC2luncbQk0S1P1sR7PZHsa64FQazOMX4/Xi9KKFSDATV8PLPAk6nmoyWD/EBkye2/BwDR9P4a4Ra73e4vRiNO9s044WJrvVM6NAMdl9xX4/MtkJVjaAwwmMbB/zzItuEM14lbU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com; spf=pass smtp.mailfrom=daynix.com; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b=NjzUZos4; arc=none smtp.client-ip=209.85.214.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=daynix.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b="NjzUZos4" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-22403cbb47fso7032145ad.0 for ; Thu, 06 Mar 2025 01:57:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=daynix-com.20230601.gappssmtp.com; s=20230601; t=1741255051; x=1741859851; darn=vger.kernel.org; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=7HLNLtip7z6AfdeftYfqeV53n28FrmHisyRZVCVIPJQ=; b=NjzUZos4vABTA2Gfp3BaLaI3j6T0RJWT7MFOp3dG+O3XFaPFVvw9si+hbvqTuCYjRL bcsDhQTFhWFIN6DA17cj1X2NePPQqpdGuoMbnjLWLngrbfKpsn2F7V4muzUeuIZFrZ5I UOIqTa8lp7LbpVlHgss5d/8mLwK+CbBNP7SgexkTxpy9uaBpqYgRDKcn7YGhL9FrYgV5 jHPe4cF3ByjhOgTbYKHKYBQS7fbpxWzZ6lcltZDbZkIWYWGGz7MbsIn8byfmfIL03/AS SZi+RkSKyOZci1KpJStLVj2AgB9Hu7QfUOQK5izbgMIOvegpe/icTnUKh7fNwZ3G6S2l t3NQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741255051; x=1741859851; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7HLNLtip7z6AfdeftYfqeV53n28FrmHisyRZVCVIPJQ=; b=v6jyLm5KUwaibMv42df3+nm0b6Uy3ZWS7J+D9cTIRTz70rmS+JA0Yhet2GsNiuWuj/ 6QQAzrLNPG1npcj1zuA90lAaDiecTPpiErV7jK5H6iWsG06JUoZSNECkCK76jfW3m94k KuXZjHYeo0vgbetsGobPyjynWMebsZ4ew/JVH//mu7vWOK3whR9+Bpbnn3oJzTUBozMu IW4ZNXqmKh4shG0D0gVqvXRakQmYgpIbI+3O7IXQS+eV2sERYMAO+Z67XSjAHo7e8m1H ZiMW3KSiRu/w9I61JYF4066+fe6/RDQbFUXqZX4HZWO1pT3vM7mhcHt50aRM1fGvSzxk VvQQ== X-Forwarded-Encrypted: i=1; AJvYcCXyRj8PQbEkwH7c6WZtITxr1qfUThH25wHWM+K6RyAqVLVZETMMdPQ8yMBaBRQImRD5SR61Hb091tzhFFk=@vger.kernel.org X-Gm-Message-State: AOJu0YyPo3teHcgmuO2H7EWGZses9KPyZMY/DiuacHurpV7OVPuBMTv1 +dvx7jcJJbIzWR/4UCfDObe/9JqzLGXODuHxiBDUq5TKu6AEYqzqS4fVL93bt4k= X-Gm-Gg: ASbGnctQpIowMHonLC+3PVzTzELjYA4sTqBCzZXbClvnAGcCyY9tCtRiYGcj26xLGog Ko9lJQJQjDE3jBXOH3A5cMV24nMXNkXvU4CI+1UvQreY5LEkKk+m4Z+2PFjyk182EjcuvwIVDvV rdxyqi9f1vLjQyHSI2EINFOlkbSF0yXrpV1YmB0SQ/mq2SBhGJm8RZ95X6ptOcSWm01MRgYumox X4Bq9MB3yR7gST+9/l+8ZubFuJkMN9rrPg3ZABgUsQC1Hn22czq/uSfoGfL1wybgof5Kw4deOZH vk1upFymMx9lYaZEUyKeZmuFxu/6ezq3B7TBHwXZO/JiejCG X-Google-Smtp-Source: AGHT+IGtPKOatxjbdRuu5ijpcnPDZUbbR29+sGCzJO/3CnoM37EjcrWeXH/XJwI+5M64Cj3NfNihvw== X-Received: by 2002:a05:6a21:3986:b0:1ee:8a68:f83e with SMTP id adf61e73a8af0-1f3494b4c84mr12241868637.20.1741255050746; Thu, 06 Mar 2025 01:57:30 -0800 (PST) Received: from localhost ([157.82.207.107]) by smtp.gmail.com with UTF8SMTPSA id 41be03b00d2f7-af281075ecfsm841172a12.5.2025.03.06.01.57.25 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 06 Mar 2025 01:57:30 -0800 (PST) From: Akihiko Odaki Date: Thu, 06 Mar 2025 18:56:35 +0900 Subject: [PATCH net-next v8 5/6] selftest: tun: Add tests for virtio-net hashing Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250306-rss-v8-5-7ab4f56ff423@daynix.com> References: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> In-Reply-To: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> To: Jonathan Corbet , Willem de Bruijn , Jason Wang , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Michael S. Tsirkin" , Xuan Zhuo , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, kvm@vger.kernel.org, virtualization@lists.linux-foundation.org, linux-kselftest@vger.kernel.org, Yuri Benditovich , Andrew Melnychenko , Stephen Hemminger , gur.stavi@huawei.com, Lei Yang , Simon Horman , Akihiko Odaki X-Mailer: b4 0.14.2 The added tests confirm tun can perform RSS and hash reporting, and reject invalid configurations for them. Signed-off-by: Akihiko Odaki Tested-by: Lei Yang --- tools/testing/selftests/net/Makefile | 2 +- tools/testing/selftests/net/tun.c | 584 +++++++++++++++++++++++++++++++= +++- 2 files changed, 576 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests= /net/Makefile index 73ee88d6b043004be23b444de667a1d99a6045de..9772f691a9a011d99212df32463= cdb930cf0a1a0 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -123,6 +123,6 @@ $(OUTPUT)/reuseport_bpf_numa: LDLIBS +=3D -lnuma $(OUTPUT)/tcp_mmap: LDLIBS +=3D -lpthread -lcrypto $(OUTPUT)/tcp_inq: LDLIBS +=3D -lpthread $(OUTPUT)/bind_bhash: LDLIBS +=3D -lpthread -$(OUTPUT)/io_uring_zerocopy_tx: CFLAGS +=3D -I../../../include/ +$(OUTPUT)/io_uring_zerocopy_tx $(OUTPUT)/tun: CFLAGS +=3D -I../../../inclu= de/ =20 include bpf.mk diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/ne= t/tun.c index 463dd98f2b80b1bdcb398cee43c834e7dc5cf784..acadeea7194eaea9416a605b47f= 99f7a5f1f80cd 100644 --- a/tools/testing/selftests/net/tun.c +++ b/tools/testing/selftests/net/tun.c @@ -2,21 +2,38 @@ =20 #define _GNU_SOURCE =20 +#include #include #include +#include +#include #include #include #include #include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include #include -#include -#include +#include +#include +#include +#include =20 #include "../kselftest_harness.h" =20 +#define TUN_HWADDR_SOURCE { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 } +#define TUN_HWADDR_DEST { 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 } +#define TUN_IPADDR_SOURCE htonl((172 << 24) | (17 << 16) | 0) +#define TUN_IPADDR_DEST htonl((172 << 24) | (17 << 16) | 1) + static int tun_attach(int fd, char *dev) { struct ifreq ifr; @@ -39,7 +56,7 @@ static int tun_detach(int fd, char *dev) return ioctl(fd, TUNSETQUEUE, (void *) &ifr); } =20 -static int tun_alloc(char *dev) +static int tun_alloc(char *dev, short flags) { struct ifreq ifr; int fd, err; @@ -52,7 +69,8 @@ static int tun_alloc(char *dev) =20 memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, dev); - ifr.ifr_flags =3D IFF_TAP | IFF_NAPI | IFF_MULTI_QUEUE; + ifr.ifr_flags =3D flags | IFF_TAP | IFF_NAPI | IFF_NO_PI | + IFF_MULTI_QUEUE; =20 err =3D ioctl(fd, TUNSETIFF, (void *) &ifr); if (err < 0) { @@ -64,6 +82,40 @@ static int tun_alloc(char *dev) return fd; } =20 +static bool tun_add_to_bridge(int local_fd, const char *name) +{ + struct ifreq ifreq =3D { + .ifr_name =3D "xbridge", + .ifr_ifindex =3D if_nametoindex(name) + }; + + if (!ifreq.ifr_ifindex) { + perror("if_nametoindex"); + return false; + } + + if (ioctl(local_fd, SIOCBRADDIF, &ifreq)) { + perror("SIOCBRADDIF"); + return false; + } + + return true; +} + +static bool tun_set_flags(int local_fd, const char *name, short flags) +{ + struct ifreq ifreq =3D { .ifr_flags =3D flags }; + + strcpy(ifreq.ifr_name, name); + + if (ioctl(local_fd, SIOCSIFFLAGS, &ifreq)) { + perror("SIOCSIFFLAGS"); + return false; + } + + return true; +} + static int tun_delete(char *dev) { struct { @@ -102,6 +154,156 @@ static int tun_delete(char *dev) return ret; } =20 +static uint32_t tun_sum(const void *buf, size_t len) +{ + const uint16_t *sbuf =3D buf; + uint32_t sum =3D 0; + + while (len > 1) { + sum +=3D *sbuf++; + len -=3D 2; + } + + if (len) + sum +=3D *(uint8_t *)sbuf; + + return sum; +} + +static uint16_t tun_build_ip_check(uint32_t sum) +{ + return ~((sum & 0xffff) + (sum >> 16)); +} + +static uint32_t tun_build_ip_pseudo_sum(const void *iphdr) +{ + uint16_t tot_len =3D ntohs(((struct iphdr *)iphdr)->tot_len); + + return tun_sum((char *)iphdr + offsetof(struct iphdr, saddr), 8) + + htons(((struct iphdr *)iphdr)->protocol) + + htons(tot_len - sizeof(struct iphdr)); +} + +static uint32_t tun_build_ipv6_pseudo_sum(const void *ipv6hdr) +{ + return tun_sum((char *)ipv6hdr + offsetof(struct ipv6hdr, saddr), 32) + + ((struct ipv6hdr *)ipv6hdr)->payload_len + + htons(((struct ipv6hdr *)ipv6hdr)->nexthdr); +} + +static void tun_build_ethhdr(struct ethhdr *ethhdr, uint16_t proto) +{ + *ethhdr =3D (struct ethhdr) { + .h_dest =3D TUN_HWADDR_DEST, + .h_source =3D TUN_HWADDR_SOURCE, + .h_proto =3D htons(proto) + }; +} + +static void tun_build_iphdr(void *dest, uint16_t len, uint8_t protocol) +{ + struct iphdr iphdr =3D { + .ihl =3D sizeof(iphdr) / 4, + .version =3D 4, + .tot_len =3D htons(sizeof(iphdr) + len), + .ttl =3D 255, + .protocol =3D protocol, + .saddr =3D TUN_IPADDR_SOURCE, + .daddr =3D TUN_IPADDR_DEST + }; + + iphdr.check =3D tun_build_ip_check(tun_sum(&iphdr, sizeof(iphdr))); + memcpy(dest, &iphdr, sizeof(iphdr)); +} + +static void tun_build_ipv6hdr(void *dest, uint16_t len, uint8_t protocol) +{ + struct ipv6hdr ipv6hdr =3D { + .version =3D 6, + .payload_len =3D htons(len), + .nexthdr =3D protocol, + .saddr =3D { + .s6_addr32 =3D { + htonl(0xffff0000), 0, 0, TUN_IPADDR_SOURCE + } + }, + .daddr =3D { + .s6_addr32 =3D { + htonl(0xffff0000), 0, 0, TUN_IPADDR_DEST + } + }, + }; + + memcpy(dest, &ipv6hdr, sizeof(ipv6hdr)); +} + +static void tun_build_tcphdr(void *dest, uint32_t sum) +{ + struct tcphdr tcphdr =3D { + .source =3D htons(9), + .dest =3D htons(9), + .fin =3D 1, + .doff =3D sizeof(tcphdr) / 4, + }; + uint32_t tcp_sum =3D tun_sum(&tcphdr, sizeof(tcphdr)); + + tcphdr.check =3D tun_build_ip_check(sum + tcp_sum); + memcpy(dest, &tcphdr, sizeof(tcphdr)); +} + +static void tun_build_udphdr(void *dest, uint32_t sum) +{ + struct udphdr udphdr =3D { + .source =3D htons(9), + .dest =3D htons(9), + .len =3D htons(sizeof(udphdr)), + }; + uint32_t udp_sum =3D tun_sum(&udphdr, sizeof(udphdr)); + + udphdr.check =3D tun_build_ip_check(sum + udp_sum); + memcpy(dest, &udphdr, sizeof(udphdr)); +} + +static bool tun_vnet_hash_check(int source_fd, const int *dest_fds, + const void *buffer, size_t len, + uint8_t flags, + uint16_t hash_report, uint32_t hash_value) +{ + size_t read_len =3D sizeof(struct virtio_net_hdr_v1_hash) + len; + struct virtio_net_hdr_v1_hash *read_buffer; + struct virtio_net_hdr_v1_hash hdr =3D { + .hdr =3D { .flags =3D flags }, + .hash_value =3D htole32(hash_value), + .hash_report =3D htole16(hash_report) + }; + int ret; + int txq =3D hash_report ? hash_value & 1 : 2; + + if (write(source_fd, buffer, len) !=3D len) { + perror("write"); + return false; + } + + read_buffer =3D malloc(read_len); + if (!read_buffer) { + perror("malloc"); + return false; + } + + ret =3D read(dest_fds[txq], read_buffer, read_len); + if (ret !=3D read_len) { + perror("read"); + free(read_buffer); + return false; + } + + ret =3D !memcmp(read_buffer, &hdr, sizeof(*read_buffer)) && + !memcmp(read_buffer + 1, buffer, len); + + free(read_buffer); + return ret; +} + FIXTURE(tun) { char ifname[IFNAMSIZ]; @@ -112,10 +314,10 @@ FIXTURE_SETUP(tun) { memset(self->ifname, 0, sizeof(self->ifname)); =20 - self->fd =3D tun_alloc(self->ifname); + self->fd =3D tun_alloc(self->ifname, 0); ASSERT_GE(self->fd, 0); =20 - self->fd2 =3D tun_alloc(self->ifname); + self->fd2 =3D tun_alloc(self->ifname, 0); ASSERT_GE(self->fd2, 0); } =20 @@ -168,7 +370,7 @@ FIXTURE(tun_deleted) FIXTURE_SETUP(tun_deleted) { self->ifname[0] =3D 0; - self->fd =3D tun_alloc(self->ifname); + self->fd =3D tun_alloc(self->ifname, 0); ASSERT_LE(0, self->fd); =20 ASSERT_EQ(0, tun_delete(self->ifname)) @@ -233,4 +435,368 @@ TEST_F(tun_deleted, setvnethash) EXPECT_EQ(EBADFD, errno); } =20 -TEST_HARNESS_MAIN +FIXTURE(tun_vnet_hash) +{ + int local_fd; + int source_fd; + int dest_fds[3]; +}; + +FIXTURE_SETUP(tun_vnet_hash) +{ + static const struct { + struct tun_vnet_hash hdr; + struct tun_vnet_hash_rss rss; + uint16_t rss_indirection_table[2]; + uint8_t rss_key[40]; + } vnet_hash =3D { + .hdr =3D { + .flags =3D TUN_VNET_HASH_REPORT | TUN_VNET_HASH_RSS, + .types =3D VIRTIO_NET_RSS_HASH_TYPE_IPv4 | + VIRTIO_NET_RSS_HASH_TYPE_TCPv4 | + VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | + VIRTIO_NET_RSS_HASH_TYPE_IPv6 | + VIRTIO_NET_RSS_HASH_TYPE_TCPv6 | + VIRTIO_NET_RSS_HASH_TYPE_UDPv6 + }, + .rss =3D { .indirection_table_mask =3D 1, .unclassified_queue =3D 5 }, + .rss_indirection_table =3D { 3, 4 }, + .rss_key =3D { + 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, + 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, + 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, + 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, + 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa + } + }; + + struct { + struct virtio_net_hdr_v1_hash vnet_hdr; + struct ethhdr ethhdr; + struct arphdr arphdr; + unsigned char sender_hwaddr[6]; + uint32_t sender_ipaddr; + unsigned char target_hwaddr[6]; + uint32_t target_ipaddr; + } __packed packet =3D { + .ethhdr =3D { + .h_source =3D TUN_HWADDR_SOURCE, + .h_dest =3D { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + .h_proto =3D htons(ETH_P_ARP) + }, + .arphdr =3D { + .ar_hrd =3D htons(ARPHRD_ETHER), + .ar_pro =3D htons(ETH_P_IP), + .ar_hln =3D ETH_ALEN, + .ar_pln =3D 4, + .ar_op =3D htons(ARPOP_REQUEST) + }, + .sender_hwaddr =3D TUN_HWADDR_DEST, + .sender_ipaddr =3D TUN_IPADDR_DEST, + .target_ipaddr =3D TUN_IPADDR_DEST + }; + + struct tun_vnet_hash cap; + char source_ifname[IFNAMSIZ] =3D ""; + char dest_ifname[IFNAMSIZ] =3D ""; + int i; + + self->local_fd =3D socket(AF_LOCAL, SOCK_STREAM, 0); + ASSERT_LE(0, self->local_fd); + + self->source_fd =3D tun_alloc(source_ifname, 0); + ASSERT_LE(0, self->source_fd) { + EXPECT_EQ(0, close(self->local_fd)); + } + + i =3D ioctl(self->source_fd, TUNGETVNETHASHCAP, &cap); + if (i =3D=3D -1 && errno =3D=3D EINVAL) { + EXPECT_EQ(0, close(self->local_fd)); + SKIP(return, "TUNGETVNETHASHCAP not supported"); + } + + ASSERT_EQ(0, i) + EXPECT_EQ(0, close(self->local_fd)); + + if ((cap.flags & vnet_hash.hdr.flags) !=3D vnet_hash.hdr.flags) { + EXPECT_EQ(0, close(self->local_fd)); + SKIP(return, "Lacks some hash flag support"); + } + + if ((cap.types & vnet_hash.hdr.types) !=3D vnet_hash.hdr.types) { + EXPECT_EQ(0, close(self->local_fd)); + SKIP(return, "Lacks some hash type support"); + } + + ASSERT_TRUE(tun_set_flags(self->local_fd, source_ifname, IFF_UP)) + EXPECT_EQ(0, close(self->local_fd)); + + self->dest_fds[0] =3D tun_alloc(dest_ifname, IFF_VNET_HDR); + ASSERT_LE(0, self->dest_fds[0]) { + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + i =3D sizeof(struct virtio_net_hdr_v1_hash); + ASSERT_EQ(0, ioctl(self->dest_fds[0], TUNSETVNETHDRSZ, &i)) { + EXPECT_EQ(0, close(self->dest_fds[0])); + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + i =3D 1; + ASSERT_EQ(0, ioctl(self->dest_fds[0], TUNSETVNETLE, &i)) { + EXPECT_EQ(0, close(self->dest_fds[0])); + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + ASSERT_TRUE(tun_set_flags(self->local_fd, dest_ifname, IFF_UP)) { + EXPECT_EQ(0, close(self->dest_fds[0])); + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + ASSERT_EQ(sizeof(packet), + write(self->dest_fds[0], &packet, sizeof(packet))) { + EXPECT_EQ(0, close(self->dest_fds[0])); + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + ASSERT_EQ(0, ioctl(self->dest_fds[0], TUNSETVNETHASH, &vnet_hash)) { + EXPECT_EQ(0, close(self->dest_fds[0])); + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + for (i =3D 1; i < ARRAY_SIZE(self->dest_fds); i++) { + self->dest_fds[i] =3D tun_alloc(dest_ifname, IFF_VNET_HDR); + ASSERT_LE(0, self->dest_fds[i]) { + while (i) { + i--; + EXPECT_EQ(0, close(self->local_fd)); + } + + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + } + + ASSERT_EQ(0, ioctl(self->local_fd, SIOCBRADDBR, "xbridge")) { + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); + + for (i =3D 0; i < ARRAY_SIZE(self->dest_fds); i++) + EXPECT_EQ(0, close(self->dest_fds[i])); + + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + ASSERT_TRUE(tun_add_to_bridge(self->local_fd, source_ifname)) { + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); + + for (i =3D 0; i < ARRAY_SIZE(self->dest_fds); i++) + EXPECT_EQ(0, close(self->dest_fds[i])); + + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + ASSERT_TRUE(tun_add_to_bridge(self->local_fd, dest_ifname)) { + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); + + for (i =3D 0; i < ARRAY_SIZE(self->dest_fds); i++) + EXPECT_EQ(0, close(self->dest_fds[i])); + + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + ASSERT_TRUE(tun_set_flags(self->local_fd, "xbridge", IFF_UP)) { + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); + + for (i =3D 0; i < ARRAY_SIZE(self->dest_fds); i++) + EXPECT_EQ(0, close(self->dest_fds[i])); + + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } +} + +FIXTURE_TEARDOWN(tun_vnet_hash) +{ + ASSERT_TRUE(tun_set_flags(self->local_fd, "xbridge", 0)) { + for (size_t i =3D 0; i < ARRAY_SIZE(self->dest_fds); i++) + EXPECT_EQ(0, close(self->dest_fds[i])); + + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); + } + + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); + + for (size_t i =3D 0; i < ARRAY_SIZE(self->dest_fds); i++) + EXPECT_EQ(0, close(self->dest_fds[i])); + + EXPECT_EQ(0, close(self->source_fd)); + EXPECT_EQ(0, close(self->local_fd)); +} + +TEST_F(tun_vnet_hash, unclassified) +{ + struct { + struct ethhdr ethhdr; + struct iphdr iphdr; + } __packed packet; + + tun_build_ethhdr(&packet.ethhdr, ETH_P_LOOPBACK); + + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, + &packet, sizeof(packet), 0, + VIRTIO_NET_HASH_REPORT_NONE, 0)); +} + +TEST_F(tun_vnet_hash, ipv4) +{ + struct { + struct ethhdr ethhdr; + struct iphdr iphdr; + } __packed packet; + + tun_build_ethhdr(&packet.ethhdr, ETH_P_IP); + tun_build_iphdr(&packet.iphdr, 0, 253); + + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, + &packet, sizeof(packet), 0, + VIRTIO_NET_HASH_REPORT_IPv4, + 0x6e45d952)); +} + +TEST_F(tun_vnet_hash, tcpv4) +{ + struct { + struct ethhdr ethhdr; + struct iphdr iphdr; + struct tcphdr tcphdr; + } __packed packet; + + tun_build_ethhdr(&packet.ethhdr, ETH_P_IP); + tun_build_iphdr(&packet.iphdr, sizeof(struct tcphdr), IPPROTO_TCP); + + tun_build_tcphdr(&packet.tcphdr, + tun_build_ip_pseudo_sum(&packet.iphdr)); + + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, + &packet, sizeof(packet), + VIRTIO_NET_HDR_F_DATA_VALID, + VIRTIO_NET_HASH_REPORT_TCPv4, + 0xfb63539a)); +} + +TEST_F(tun_vnet_hash, udpv4) +{ + struct { + struct ethhdr ethhdr; + struct iphdr iphdr; + struct udphdr udphdr; + } __packed packet; + + tun_build_ethhdr(&packet.ethhdr, ETH_P_IP); + tun_build_iphdr(&packet.iphdr, sizeof(struct udphdr), IPPROTO_UDP); + + tun_build_udphdr(&packet.udphdr, + tun_build_ip_pseudo_sum(&packet.iphdr)); + + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, + &packet, sizeof(packet), + VIRTIO_NET_HDR_F_DATA_VALID, + VIRTIO_NET_HASH_REPORT_UDPv4, + 0xfb63539a)); +} + +TEST_F(tun_vnet_hash, ipv6) +{ + struct { + struct ethhdr ethhdr; + struct ipv6hdr ipv6hdr; + } __packed packet; + + tun_build_ethhdr(&packet.ethhdr, ETH_P_IPV6); + tun_build_ipv6hdr(&packet.ipv6hdr, 0, 253); + + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, + &packet, sizeof(packet), 0, + VIRTIO_NET_HASH_REPORT_IPv6, + 0xd6eb560f)); +} + +TEST_F(tun_vnet_hash, tcpv6) +{ + struct { + struct ethhdr ethhdr; + struct ipv6hdr ipv6hdr; + struct tcphdr tcphdr; + } __packed packet; + + tun_build_ethhdr(&packet.ethhdr, ETH_P_IPV6); + tun_build_ipv6hdr(&packet.ipv6hdr, sizeof(struct tcphdr), IPPROTO_TCP); + + tun_build_tcphdr(&packet.tcphdr, + tun_build_ipv6_pseudo_sum(&packet.ipv6hdr)); + + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, + &packet, sizeof(packet), + VIRTIO_NET_HDR_F_DATA_VALID, + VIRTIO_NET_HASH_REPORT_TCPv6, + 0xc2b9f251)); +} + +TEST_F(tun_vnet_hash, udpv6) +{ + struct { + struct ethhdr ethhdr; + struct ipv6hdr ipv6hdr; + struct udphdr udphdr; + } __packed packet; + + tun_build_ethhdr(&packet.ethhdr, ETH_P_IPV6); + tun_build_ipv6hdr(&packet.ipv6hdr, sizeof(struct udphdr), IPPROTO_UDP); + + tun_build_udphdr(&packet.udphdr, + tun_build_ipv6_pseudo_sum(&packet.ipv6hdr)); + + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, + &packet, sizeof(packet), + VIRTIO_NET_HDR_F_DATA_VALID, + VIRTIO_NET_HASH_REPORT_UDPv6, + 0xc2b9f251)); +} + +int main(int argc, char **argv) +{ + FILE *file; + + if (unshare(CLONE_NEWNET)) { + perror("unshare"); + return KSFT_FAIL; + } + + file =3D fopen("/proc/sys/net/ipv6/conf/default/disable_ipv6", "w"); + if (file) { + if (fputc('1', file) !=3D '1') { + perror("fputc"); + return KSFT_FAIL; + } + + if (fclose(file)) { + perror("fclose"); + return KSFT_FAIL; + } + } else if (errno !=3D ENOENT) { + perror("fopen"); + return KSFT_FAIL; + } + + return test_harness_run(argc, argv); +} --=20 2.48.1 From nobody Sat Feb 7 05:01:22 2026 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (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 74A5420E03A for ; Thu, 6 Mar 2025 09:57:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255058; cv=none; b=J7jBNAwbAlX8Ab8T2HfWRlIu5zISXgnkNQ9+JORbC9i/3hl7gpvxYsFRJIi1wjFj5nBamWuh2Eiw8DpnwEoSNlbxr8X1+h7iE8Ffv5LbF8Tvcjf4TC3mJ6CYfMEjpIhjyeSEYPXLgVJlEATYugvX1As2ha+helDlnhnPNU+vfvU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741255058; c=relaxed/simple; bh=efUQp3xn6WYS8yAIVpG+8nNESAbDYNoZwtefI4rJo8g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To; b=N0YHYbIaTIFuNRY3kcwZbCkDdCI+L8y628usVO3HYJihH53scX1f5SxhLspvWgbwzQ02BPNwcDYei7CwDt25jGH1IPeNyEpYp3xFfI+fzLKS8xkZFNUDZMHjGIujUwQwP1z2P7yrqoCPhZ96mDULUFhOzcL7pWHf+hUf0+NpoR0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com; spf=pass smtp.mailfrom=daynix.com; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b=liTDfdTi; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=daynix.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=daynix-com.20230601.gappssmtp.com header.i=@daynix-com.20230601.gappssmtp.com header.b="liTDfdTi" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-22334203781so32930485ad.0 for ; Thu, 06 Mar 2025 01:57:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=daynix-com.20230601.gappssmtp.com; s=20230601; t=1741255057; x=1741859857; darn=vger.kernel.org; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=CnuaMjhFxZ/6ziAA9dAFSZFZKEXkUeKJopwW9aMJd8c=; b=liTDfdTiAJZ/79okWEDZNcbgi7+Hp66nqq9EZCfTbn2l7x4IhDeyq5Hn/bi1lpczj0 ORpVll2qWNQ+89fV7LDV/Eh1lR8HK015UvQZu/eitRX4VLlosUTS6em93Awo6Ofqz0d8 m37RFcDHHI+c0Gbm2ZKVUwbAm54vMtsM6Mut8pwWAMDDORC48Sqm0wmsnYHKnxh+pX5o iZNrWzyJaESgGSI6LdUqfhVKnrb2k1Tbyf1jkCBYqCp8LenuvXvtzJZv50Fg+V9NLtLg dBtaf+U2HIV//gwDRQQHm3wvNZTA/sIFJf6JhUen7QfpiCZnwHEXSA3G4OcHc3/Pdwfp zBTQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741255057; x=1741859857; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=CnuaMjhFxZ/6ziAA9dAFSZFZKEXkUeKJopwW9aMJd8c=; b=tCTAyOj6S06oeo6Nkma0bCvv2/mPP9ISJaxSuhn9kpIY143h/jnVVGFN6YVB/gFSrE EVAwLwvUCFCZRymzqtceUktm+r+FIbdcW7Z0k2CH0cj9+ZLm8T0aW0ngLFELrk5XxUfU 0nA65FEXgwOJcY0+JM32Lz236ON45o04iISkGHVgvoxjLIj9VS4Pull/ZkXWfURGG0cN Y69DqfuTb/krag7JSbEap2QLC9rjmGVYHd/QeXK+jj9H6iCPUkaqfTLFMGxFocUBy1S+ zCHEdZh6Z+YVIRlvmkdRrbwEs44cJAbP0IgqN8G1uNx7tAHGHvywZTJ9vl3PtfdvB+Bf sCcw== X-Forwarded-Encrypted: i=1; AJvYcCVkVZQpKJjlJURq/HniyTGW1z7SbCf/OfasJnlvBt/AhJqfgyIeDdiThLysvVwtIpHToUY4QzTHg/V5iP4=@vger.kernel.org X-Gm-Message-State: AOJu0Ywlrc0ISLFCMz6jKM0wOMX2hWEjOpiCswM1ft3ALhhCr0n/nBjb NlLuS48F8qKqmm19+LZmgr2DEt05sIWYK6FOeD/w5wjCz52jAWd4A7q9aqj+dqY= X-Gm-Gg: ASbGnct3CzyM97fPWVh9G2PW9W9U0DfV9252jo0isJELeo1CJmA9YXuNraohtJkzmiW m4yLQnbKImx/5mUFzJivn+7qkc9ciifM4dRCBrPPrMgAvZymihoYv+WkcTlVmxxAAvgtgHlnTni Pa6Vq8I5hYJetWAgtIvNE7y2b04GuT3lM64GG++1PEzb1BEpUqLkSd0juxORFRL4qW2n9KbmK58 l5iGx9Jh+nbYpN/idT39u/kyL8CqG49lQgPXvmFu7CaUV/grk/CrnjLhdCfStQ3cJMPpl6BPxMW Wye5+13ZEmt4zTv0w1BQ7C5WUPsejTszWAlNzL4EJy6W3EE3 X-Google-Smtp-Source: AGHT+IHJGksHinhnEKP7Vul6y/8p6O/KEECmCOaw6lnVXA484Y6gp9ZfN9uJUdcGYFrU6DtyYorN0g== X-Received: by 2002:a05:6a00:3397:b0:734:26c6:26d3 with SMTP id d2e1a72fcca58-73693e85ddcmr5480443b3a.5.1741255056584; Thu, 06 Mar 2025 01:57:36 -0800 (PST) Received: from localhost ([157.82.207.107]) by smtp.gmail.com with UTF8SMTPSA id d2e1a72fcca58-73698514f58sm948820b3a.133.2025.03.06.01.57.32 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 06 Mar 2025 01:57:36 -0800 (PST) From: Akihiko Odaki Date: Thu, 06 Mar 2025 18:56:36 +0900 Subject: [PATCH net-next v8 6/6] vhost/net: Support VIRTIO_NET_F_HASH_REPORT Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250306-rss-v8-6-7ab4f56ff423@daynix.com> References: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> In-Reply-To: <20250306-rss-v8-0-7ab4f56ff423@daynix.com> To: Jonathan Corbet , Willem de Bruijn , Jason Wang , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Michael S. Tsirkin" , Xuan Zhuo , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, kvm@vger.kernel.org, virtualization@lists.linux-foundation.org, linux-kselftest@vger.kernel.org, Yuri Benditovich , Andrew Melnychenko , Stephen Hemminger , gur.stavi@huawei.com, Lei Yang , Simon Horman , Akihiko Odaki X-Mailer: b4 0.14.2 VIRTIO_NET_F_HASH_REPORT allows to report hash values calculated on the host. When VHOST_NET_F_VIRTIO_NET_HDR is employed, it will report no hash values (i.e., the hash_report member is always set to VIRTIO_NET_HASH_REPORT_NONE). Otherwise, the values reported by the underlying socket will be reported. VIRTIO_NET_F_HASH_REPORT requires VIRTIO_F_VERSION_1. Signed-off-by: Akihiko Odaki Tested-by: Lei Yang --- drivers/vhost/net.c | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index b9b9e9d40951856d881d77ac74331d914473cd56..16b241b44f89820a42c302f3586= ea6bb5e0d4289 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -73,6 +73,7 @@ enum { VHOST_NET_FEATURES =3D VHOST_FEATURES | (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) | (1ULL << VIRTIO_NET_F_MRG_RXBUF) | + (1ULL << VIRTIO_NET_F_HASH_REPORT) | (1ULL << VIRTIO_F_ACCESS_PLATFORM) | (1ULL << VIRTIO_F_RING_RESET) }; @@ -1097,9 +1098,11 @@ static void handle_rx(struct vhost_net *net) .msg_controllen =3D 0, .msg_flags =3D MSG_DONTWAIT, }; - struct virtio_net_hdr hdr =3D { - .flags =3D 0, - .gso_type =3D VIRTIO_NET_HDR_GSO_NONE + struct virtio_net_hdr_v1_hash hdr =3D { + .hdr =3D { + .flags =3D 0, + .gso_type =3D VIRTIO_NET_HDR_GSO_NONE + } }; size_t total_len =3D 0; int err, mergeable; @@ -1110,7 +1113,6 @@ static void handle_rx(struct vhost_net *net) bool set_num_buffers; struct socket *sock; struct iov_iter fixup; - __virtio16 num_buffers; int recv_pkts =3D 0; =20 mutex_lock_nested(&vq->mutex, VHOST_NET_VQ_RX); @@ -1191,30 +1193,30 @@ static void handle_rx(struct vhost_net *net) vhost_discard_vq_desc(vq, headcount); continue; } + hdr.hdr.num_buffers =3D cpu_to_vhost16(vq, headcount); /* Supply virtio_net_hdr if VHOST_NET_F_VIRTIO_NET_HDR */ if (unlikely(vhost_hlen)) { - if (copy_to_iter(&hdr, sizeof(hdr), - &fixup) !=3D sizeof(hdr)) { + if (copy_to_iter(&hdr, vhost_hlen, + &fixup) !=3D vhost_hlen) { vq_err(vq, "Unable to write vnet_hdr " "at addr %p\n", vq->iov->iov_base); goto out; } - } else { + } else if (likely(set_num_buffers)) { /* Header came from socket; we'll need to patch * ->num_buffers over if VIRTIO_NET_F_MRG_RXBUF */ - iov_iter_advance(&fixup, sizeof(hdr)); + iov_iter_advance(&fixup, offsetof(struct virtio_net_hdr_v1, num_buffers= )); + + if (copy_to_iter(&hdr.hdr.num_buffers, sizeof(hdr.hdr.num_buffers), + &fixup) !=3D sizeof(hdr.hdr.num_buffers)) { + vq_err(vq, "Failed num_buffers write"); + vhost_discard_vq_desc(vq, headcount); + goto out; + } } /* TODO: Should check and handle checksum. */ =20 - num_buffers =3D cpu_to_vhost16(vq, headcount); - if (likely(set_num_buffers) && - copy_to_iter(&num_buffers, sizeof num_buffers, - &fixup) !=3D sizeof num_buffers) { - vq_err(vq, "Failed num_buffers write"); - vhost_discard_vq_desc(vq, headcount); - goto out; - } nvq->done_idx +=3D headcount; if (nvq->done_idx > VHOST_NET_BATCH) vhost_net_signal_used(nvq); @@ -1607,10 +1609,13 @@ static int vhost_net_set_features(struct vhost_net = *n, u64 features) size_t vhost_hlen, sock_hlen, hdr_len; int i; =20 - hdr_len =3D (features & ((1ULL << VIRTIO_NET_F_MRG_RXBUF) | - (1ULL << VIRTIO_F_VERSION_1))) ? - sizeof(struct virtio_net_hdr_mrg_rxbuf) : - sizeof(struct virtio_net_hdr); + if (features & (1ULL << VIRTIO_NET_F_HASH_REPORT)) + hdr_len =3D sizeof(struct virtio_net_hdr_v1_hash); + else if (features & ((1ULL << VIRTIO_NET_F_MRG_RXBUF) | + (1ULL << VIRTIO_F_VERSION_1))) + hdr_len =3D sizeof(struct virtio_net_hdr_mrg_rxbuf); + else + hdr_len =3D sizeof(struct virtio_net_hdr); if (features & (1 << VHOST_NET_F_VIRTIO_NET_HDR)) { /* vhost provides vnet_hdr */ vhost_hlen =3D hdr_len; @@ -1691,6 +1696,10 @@ static long vhost_net_ioctl(struct file *f, unsigned= int ioctl, return -EFAULT; if (features & ~VHOST_NET_FEATURES) return -EOPNOTSUPP; + if ((features & ((1ULL << VIRTIO_F_VERSION_1) | + (1ULL << VIRTIO_NET_F_HASH_REPORT))) =3D=3D + (1ULL << VIRTIO_NET_F_HASH_REPORT)) + return -EINVAL; return vhost_net_set_features(n, features); case VHOST_GET_BACKEND_FEATURES: features =3D VHOST_NET_BACKEND_FEATURES; --=20 2.48.1