From nobody Fri Nov 29 22:57:27 2024 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 898E68F49 for ; Sun, 15 Sep 2024 01:19:56 +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=1726363199; cv=none; b=MXCdC2/SbaiHdX6cctHuboxLekVketKOjbiMG6J0rvf6p2HkowZTv5bBgrOYyoO/FabetofJULAfLWjPjJUkKfyCv12s0mj5UYbLkSFRbrXoGYddGduiesYKvWNEFIdl+exUOg748gWN/jzDfxZ5QwDiQuy80kBuLfUXSs+6rEU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726363199; c=relaxed/simple; bh=xKAQjQfEXzGVSgAW6KjLXs1ZmXqmjpJWYhD2AKwH81w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To; b=kpr4/NRNkDAaOPppg8nUNNvLruwB7Auvs5OQ4eXWEzu4/ntxGZU9ddFXWPe1B2HhLFIVczlI636BnIejVwf9GuhOpfuUeQ099RIMkUJAxuPQbSQsZrspJ0TFbyo6kZHzi+gMJKxjYvGLdI//t8wGZ97EFZJ5DOm1se/VjSWM2TA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=daynix.com; spf=none 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=Z0kb3VaP; 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=none 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="Z0kb3VaP" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-207397d1000so33138345ad.0 for ; Sat, 14 Sep 2024 18:19:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=daynix-com.20230601.gappssmtp.com; s=20230601; t=1726363196; x=1726967996; 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=+abuVJ6BOsm0J5HlDpKBrW4vbDWaVftBJtrI/m0/jU0=; b=Z0kb3VaPrV+Pt+Pz6QsCVsmrKFZq3RpraNDM3oAn33u+QcKXAkKjczXoaZOwB+n24h IniBst43dxGdVYJaU4Kc7OZKWDPdl4vdqs6spD4hFd9Sc8KX/VSuFPiCPU1Mxnoc3fgm gQNnLwdp6G6gZ69NhbenX7uI1jfbrQ8FOPId1TO8jMWHi/26aEsHGV5zvMOjjkOGIKDd 0HoOxfgpPEHPDPioFP7DWsa99UmRTOAeeEtKknTQ5JW4Dj4ILdJdYILtGsHyS5SK5Zm0 yt+D5kiNXJhfsd7jDliBffMLli7odUVks6qcIyCYlJZcSv5FKsGUkTWk4Urzx/Xx+DUe Ii7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726363196; x=1726967996; 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=+abuVJ6BOsm0J5HlDpKBrW4vbDWaVftBJtrI/m0/jU0=; b=oUcBqhT359hVWzRr2aFgaT7gCF7DHwCuYSThar+kUOcpWUCdZMjHjLOaOI5ZfQiB2G PfvBjOr/TBL2NTsgLJDuMLSxQc4X7g+OO14TKdQHdCDaskl/HO5PunMGk6Xoii2ddGmx mzFswC/WCGgQptQv1PyoZ6Rj9XTiG7tagMgxJYNu+o+bVcLP3/4iA+ZHYeeDeaXM+9TN HEiBDCmyYON/ry7EF3UJcXjFIHCqM78dlbT+i1O8PWNRFw6038B1nw6hkjQ1SsP/NTZa 7ubrYmiWjpBOq6cZuiYTtzNCaeDiQtY6KfHoQBNXgclTOZYXyZrynjM6/tEEw0GNjwn5 R3Iw== X-Forwarded-Encrypted: i=1; AJvYcCXc05Zr7vw5LUxOvkHpinJShhuZQzJdJWPAH1RrQ0pjLGw7yZbPXVQEBvCNcV7kpqGoURGvKWVzFPp0lE4=@vger.kernel.org X-Gm-Message-State: AOJu0YxXBWyMM6nmss9HU/tVvdNv1wK4gC/uNZryt4W03bAdOjqwPhFz AezbEjXmepk1YLDZh/qFuo0ibzrEP1jNULOiabX8YtS8KYWj4jBQdw+k94+F/rM= X-Google-Smtp-Source: AGHT+IE1i3eg5lF77b/VqpPShWujILyuOG1U3fn7zxi8mIz+yCiQ1vTyuvQ6/4Ws73CpDcw3dhmsPQ== X-Received: by 2002:a17:902:da8a:b0:206:b5b8:25dd with SMTP id d9443c01a7336-2076e5f80ecmr186906355ad.23.1726363195647; Sat, 14 Sep 2024 18:19:55 -0700 (PDT) Received: from localhost ([210.160.217.68]) by smtp.gmail.com with UTF8SMTPSA id d9443c01a7336-2079472fb5csm14765945ad.252.2024.09.14.18.19.41 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sat, 14 Sep 2024 18:19:55 -0700 (PDT) From: Akihiko Odaki Date: Sun, 15 Sep 2024 10:17:45 +0900 Subject: [PATCH RFC v3 6/9] tun: Introduce virtio-net hash reporting 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: <20240915-rss-v3-6-c630015db082@daynix.com> References: <20240915-rss-v3-0-c630015db082@daynix.com> In-Reply-To: <20240915-rss-v3-0-c630015db082@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 , Akihiko Odaki X-Mailer: b4 0.14-dev-fd6e3 Allow the guest to reuse the hash value to make receive steering consistent between the host and guest, and to save hash computation. Signed-off-by: Akihiko Odaki --- Documentation/networking/tuntap.rst | 7 ++ drivers/net/Kconfig | 1 + drivers/net/tun.c | 146 +++++++++++++++++++++++++++++++-= ---- include/uapi/linux/if_tun.h | 44 +++++++++++ 4 files changed, 180 insertions(+), 18 deletions(-) diff --git a/Documentation/networking/tuntap.rst b/Documentation/networking= /tuntap.rst index 4d7087f727be..86b4ae8caa8a 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 9920b3a68ed1..e2a7bd703550 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/tun.c b/drivers/net/tun.c index 9d93ab9ee58f..b8fcd71becac 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -173,6 +173,10 @@ struct tun_prog { struct bpf_prog *prog; }; =20 +struct tun_vnet_hash_container { + struct tun_vnet_hash common; +}; + /* Since the socket were moved to tun_file, to preserve the behavior of pe= rsist * device, socket filter, sndbuf and vnet header size were restore when the * file were attached to a persist device. @@ -210,6 +214,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; @@ -221,6 +226,11 @@ struct veth { __be16 h_vlan_TCI; }; =20 +static const struct tun_vnet_hash tun_vnet_hash_cap =3D { + .flags =3D TUN_VNET_HASH_REPORT, + .types =3D VIRTIO_NET_SUPPORTED_HASH_TYPES +}; + static void tun_flow_init(struct tun_struct *tun); static void tun_flow_uninit(struct tun_struct *tun); =20 @@ -322,10 +332,17 @@ static long tun_set_vnet_be(struct tun_struct *tun, i= nt __user *argp) if (get_user(be, argp)) return -EFAULT; =20 - if (be) + if (be) { + struct tun_vnet_hash_container *vnet_hash =3D rtnl_dereference(tun->vnet= _hash); + + if (!(tun->flags & TUN_VNET_LE) && + vnet_hash && (vnet_hash->flags & TUN_VNET_HASH_REPORT)) + return -EBUSY; + tun->flags |=3D TUN_VNET_BE; - else + } else { tun->flags &=3D ~TUN_VNET_BE; + } =20 return 0; } @@ -522,14 +539,20 @@ static inline void tun_flow_save_rps_rxhash(struct tu= n_flow_entry *e, u32 hash) * 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, struct sk_buff = *skb, + const struct tun_vnet_hash_container *vnet_hash) { + struct tun_vnet_hash_ext *ext; + struct flow_keys keys; 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); @@ -538,6 +561,16 @@ static u16 tun_automq_select_queue(struct tun_struct *= tun, struct sk_buff *skb) txq =3D reciprocal_scale(txq, numqueues); } =20 + if (vnet_hash && (vnet_hash->common.flags & TUN_VNET_HASH_REPORT)) { + ext =3D skb_ext_add(skb, SKB_EXT_TUN_VNET_HASH); + if (ext) { + u32 types =3D vnet_hash->common.types; + + ext->report =3D virtio_net_hash_report(types, keys.basic); + ext->value =3D skb->l4_hash ? skb->hash : txq; + } + } + return txq; } =20 @@ -565,10 +598,13 @@ 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); + + ret =3D tun_automq_select_queue(tun, skb, vnet_hash); + } rcu_read_unlock(); =20 return ret; @@ -2120,33 +2156,63 @@ static ssize_t tun_put_user(struct tun_struct *tun, } =20 if (vnet_hdr_sz) { - struct virtio_net_hdr gso; + struct tun_vnet_hash_ext *ext; + size_t vnet_hdr_content_sz =3D sizeof(struct virtio_net_hdr); + union { + struct virtio_net_hdr hdr; + struct virtio_net_hdr_v1_hash hdr_v1_hash; + } vnet_hdr; + int ret; =20 if (iov_iter_count(iter) < vnet_hdr_sz) return -EINVAL; =20 - if (virtio_net_hdr_from_skb(skb, &gso, - tun_is_little_endian(tun), true, - vlan_hlen)) { + ext =3D vnet_hdr_sz < sizeof(vnet_hdr.hdr_v1_hash) ? + NULL : skb_ext_find(skb, SKB_EXT_TUN_VNET_HASH); + + if (ext) { + struct virtio_net_hash hash =3D { + .value =3D ext->value, + .report =3D ext->report, + }; + + vnet_hdr_content_sz =3D sizeof(vnet_hdr.hdr_v1_hash); + ret =3D virtio_net_hdr_v1_hash_from_skb(skb, + &vnet_hdr.hdr_v1_hash, + true, + vlan_hlen, + &hash); + } else { + vnet_hdr_content_sz =3D sizeof(struct virtio_net_hdr); + ret =3D virtio_net_hdr_from_skb(skb, + &vnet_hdr.hdr, + tun_is_little_endian(tun), + true, + vlan_hlen); + } + + if (ret) { struct skb_shared_info *sinfo =3D skb_shinfo(skb); =20 if (net_ratelimit()) { netdev_err(tun->dev, "unexpected GSO type: 0x%x, gso_size %d, hdr_len = %d\n", - sinfo->gso_type, tun16_to_cpu(tun, gso.gso_size), - tun16_to_cpu(tun, gso.hdr_len)); + sinfo->gso_type, + tun16_to_cpu(tun, vnet_hdr.hdr.gso_size), + tun16_to_cpu(tun, vnet_hdr.hdr.hdr_len)); print_hex_dump(KERN_ERR, "tun: ", DUMP_PREFIX_NONE, 16, 1, skb->head, - min((int)tun16_to_cpu(tun, gso.hdr_len), 64), true); + min(tun16_to_cpu(tun, vnet_hdr.hdr.hdr_len), 64), + true); } WARN_ON_ONCE(1); return -EINVAL; } =20 - if (copy_to_iter(&gso, sizeof(gso), iter) !=3D sizeof(gso)) + if (copy_to_iter(&vnet_hdr, vnet_hdr_content_sz, iter) !=3D vnet_hdr_con= tent_sz) return -EFAULT; =20 - iov_iter_zero(vnet_hdr_sz - sizeof(gso), iter); + iov_iter_zero(vnet_hdr_sz - vnet_hdr_content_sz, iter); } =20 if (vlan_hlen) { @@ -3094,6 +3160,8 @@ static long __tun_chr_ioctl(struct file *file, unsign= ed int cmd, int le; int ret; bool do_notify =3D false; + struct tun_vnet_hash vnet_hash_common; + 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)) { @@ -3115,6 +3183,9 @@ static long __tun_chr_ioctl(struct file *file, unsign= ed int cmd, if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; return open_related_ns(&net->ns, get_net_ns); + } else if (cmd =3D=3D TUNGETVNETHASHCAP) { + return copy_to_user(argp, &tun_vnet_hash_cap, sizeof(tun_vnet_hash_cap))= ? + -EFAULT : 0; } =20 rtnl_lock(); @@ -3314,6 +3385,13 @@ static long __tun_chr_ioctl(struct file *file, unsig= ned int cmd, break; } =20 + vnet_hash =3D rtnl_dereference(tun->vnet_hash); + if (vnet_hash && (vnet_hash->common.flags & TUN_VNET_HASH_REPORT) && + vnet_hdr_sz < (int)sizeof(struct virtio_net_hdr_v1_hash)) { + ret =3D -EBUSY; + break; + } + tun->vnet_hdr_sz =3D vnet_hdr_sz; break; =20 @@ -3328,10 +3406,18 @@ static long __tun_chr_ioctl(struct file *file, unsi= gned int cmd, ret =3D -EFAULT; break; } - if (le) + if (le) { tun->flags |=3D TUN_VNET_LE; - else + } else { + vnet_hash =3D rtnl_dereference(tun->vnet_hash); + if (vnet_hash && (vnet_hash->common.flags & TUN_VNET_HASH_REPORT) && + !tun_legacy_is_little_endian(tun)) { + ret =3D -EBUSY; + break; + } + tun->flags &=3D ~TUN_VNET_LE; + } break; =20 case TUNGETVNETBE: @@ -3396,6 +3482,30 @@ 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: + if (copy_from_user(&vnet_hash_common, argp, sizeof(vnet_hash_common))) { + ret =3D -EFAULT; + break; + } + argp =3D (struct tun_vnet_hash __user *)argp + 1; + + if ((vnet_hash_common.flags & TUN_VNET_HASH_REPORT) && + (tun->vnet_hdr_sz < sizeof(struct virtio_net_hdr_v1_hash) || + !tun_is_little_endian(tun))) { + ret =3D -EBUSY; + break; + } + + vnet_hash =3D kmalloc(sizeof(vnet_hash->common), GFP_KERNEL); + if (!vnet_hash) { + ret =3D -ENOMEM; + break; + } + + vnet_hash->common =3D vnet_hash_common; + kfree_rcu_mightsleep(rcu_replace_pointer_rtnl(tun->vnet_hash, vnet_hash)= ); + break; + default: ret =3D -EINVAL; break; diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index 287cdc81c939..1561e8ce0a0a 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h @@ -62,6 +62,30 @@ #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. + * + * %TUNSETVNETHDRSZ ioctl must be called with a number greater than or equ= al to + * the size of &struct virtio_net_hdr_v1_hash before calling this ioctl wi= th + * %TUN_VNET_HASH_REPORT. + * + * The virtio_net header must be configured as little-endian before callin= g this + * ioctl with %TUN_VNET_HASH_REPORT. + * + * This ioctl currently has no effect on XDP packets. + */ +#define TUNSETVNETHASH _IOW('T', 229, struct tun_vnet_hash) + /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 #define IFF_TAP 0x0002 @@ -115,4 +139,24 @@ 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 + +/** + * 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; +}; + #endif /* _UAPI__IF_TUN_H */ --=20 2.46.0