virtio: introduce GSO over UDP tunnel
Subsystems (6)
Paolo Abeni
maintainerv4 cover virtio: introduce GSO over UDP tunnel
Some virtualized deployments use UDP tunnel pervasively and are impacted negatively by the lack of GSO support for such kind of traffic in the virtual NIC driver.
The virtio_net specification recently introduced support for GSO over UDP tunnel, this series updates the virtio implementation to support such a feature.
Currently the kernel virtio support limits the feature space to 64, while the virtio specification allows for a larger number of features. Specifically the GSO-over-UDP-tunnel-related virtio features use bits 65-69.
The first four patches in this series rework the virtio and vhost feature support to cope with up to 128 bits. The limit is set by a define and could be easily raised in future, as needed.
This implementation choice is aimed at keeping the code churn as limited as possible. For the same reason, only the virtio_net driver is reworked to leverage the extended feature space; all other virtio/vhost drivers are unaffected, but could be upgraded to support the extended features space in a later time.
The last four patches bring in the actual GSO over UDP tunnel support. As per specification, some additional fields are introduced into the virtio net header to support the new offload. The presence of such fields depends on the negotiated features.
New helpers are introduced to convert the UDP-tunneled skb metadata to an extended virtio net header and vice versa. Such helpers are used by the tun and virtio_net driver to cope with the newly supported offloads.
Tested with basic stream transfer with all the possible permutations of host kernel/qemu/guest kernel with/without GSO over UDP tunnel support.
---
v3 -> v4:
- vnet sockopt cleanup
- fixed offset for UDP-tunnel related field
- use dev->features instead of flags
v3: https://lore.kernel.org/netdev/[email protected]/
v2 -> v3:
- uint128_t -> u64[2]
- dropped related ifdef
- define and use vnet_hdr with tunnel layouts
v2: https://lore.kernel.org/netdev/[email protected]/
v1 -> v2:
- fix build failures
- many comment clarification
- changed the vhost_net ioctl API
- fixed some hdr <> skb helper bugs
v1: https://lore.kernel.org/netdev/[email protected]/
Paolo Abeni (8):
virtio: introduce extended features
virtio_pci_modern: allow configuring extended features
vhost-net: allow configuring extended features
virtio_net: add supports for extended offloads
net: implement virtio helpers to handle UDP GSO tunneling.
virtio_net: enable gso over UDP tunnel support.
tun: enable gso over UDP tunnel support. vhost/net: enable gso over UDP tunnel support.
drivers/net/tun.c | 70 +++++++-- drivers/net/tun_vnet.h | 88 +++++++++-- drivers/net/virtio_net.c | 109 +++++++++++--- drivers/vhost/net.c | 95 +++++++++--- drivers/vhost/vhost.c | 2 +- drivers/vhost/vhost.h | 4 +- drivers/virtio/virtio.c | 43 +++--- drivers/virtio/virtio_debug.c | 27 ++-- drivers/virtio/virtio_pci_modern.c | 10 +- drivers/virtio/virtio_pci_modern_dev.c | 69 +++++---- include/linux/virtio.h | 5 +- include/linux/virtio_config.h | 41 +++--- include/linux/virtio_features.h | 88 +++++++++++ include/linux/virtio_net.h | 196 ++++++++++++++++++++++++- include/linux/virtio_pci_modern.h | 43 +++++- include/uapi/linux/if_tun.h | 9 ++ include/uapi/linux/vhost.h | 7 + include/uapi/linux/vhost_types.h | 5 + include/uapi/linux/virtio_net.h | 33 +++++ 19 files changed, 780 insertions(+), 164 deletions(-) create mode 100644 include/linux/virtio_features.h
Signature · 1 line
2.49.0
Paolo Abeni
maintainerPATCH v4 1/8 virtio: introduce extended features
The virtio specifications allows for up to 128 bits for the device features. Soon we are going to use some of the 'extended' bits features (above 64) for the virtio_net driver.
Introduce extended features as a fixed size array of u64. To minimize the diffstat allows legacy driver to access the low 64 bits via a transparent union.
Introduce an extended get_extended_features configuration callback that devices supporting the extended features range must implement in place of the traditional one.
Note that legacy and transport features don't need any change, as they are always in the low 64 bit range.
---
v3 -> v4:
- moved bit sanity check in virtio_features_*
- replaced BUG_ON with WARN_ON_ONCE
- *_and_not -> _andnot
- short circuit features comparison
v2 -> v3:
- uint128_t -> u64[2];
v1 -> v2:
- let u64 VIRTIO_BIT() cope with higher bit values
- add .get_features128 instead of changing .get_features signature
--- drivers/virtio/virtio.c | 43 +++++++++------- drivers/virtio/virtio_debug.c | 27 +++++----- include/linux/virtio.h | 5 +- include/linux/virtio_config.h | 41 +++++++-------- include/linux/virtio_features.h | 88 +++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 53 deletions(-) create mode 100644 include/linux/virtio_features.h
drivers/virtio/virtio.c +26 -17
@@ -53,7 +53,7 @@ static ssize_t features_show(struct device *_d,/* We actually represent this as a bitstring, as it could be * arbitrary length in future. */ -for (i =0 ; i <sizeof (dev->features)*8 ; i++) +for (i =0 ; i <VIRTIO_FEATURES_MAX ; i++) len +=sysfs_emit_at (buf, len,"%c" ,__virtio_test_bit (dev, i) ?'1' :'0' ); len +=sysfs_emit_at (buf, len,"\n" );
@@ -272,22 +272,22 @@ static int virtio_dev_probe(struct device *_d)int err, i;struct virtio_device *dev =dev_to_virtio (_d);struct virtio_driver *drv =drv_to_virtio (dev->dev.driver); -u64 device_features; -u64 driver_features; +u64 device_features[VIRTIO_FEATURES_DWORDS ]; +u64 driver_features[VIRTIO_FEATURES_DWORDS ];u64 driver_features_legacy;/* We have a driver! */ virtio_add_status (dev,VIRTIO_CONFIG_S_DRIVER );/* Figure out what features the device supports. */ - device_features = dev->config->get_features (dev); +virtio_get_features (dev, device_features);/* Figure out what features the driver supports. */ - driver_features =0 ; +virtio_features_zero (driver_features);for (i =0 ; i < drv->feature_table_size; i++) {unsigned int f = drv->feature_table[i]; -BUG_ON (f >=64 ); - driver_features |= (1ULL << f); +if (!WARN_ON_ONCE (f >=VIRTIO_FEATURES_MAX )) +virtio_features_set_bit (driver_features, f); }/* Some drivers have a separate feature table for virtio v1.0 */
@@ -295,24 +295,29 @@ static int virtio_dev_probe(struct device *_d) driver_features_legacy =0 ;for (i =0 ; i < drv->feature_table_size_legacy; i++) {unsigned int f = drv->feature_table_legacy[i]; -BUG_ON (f >=64 ); - driver_features_legacy |= (1ULL << f); +if (!WARN_ON_ONCE (f >=64 )) + driver_features_legacy |= (1ULL << f); } }else { - driver_features_legacy = driver_features; + driver_features_legacy = driver_features[0 ]; } -if (device_features & (1ULL <<VIRTIO_F_VERSION_1 )) - dev->features = driver_features & device_features; -else - dev->features = driver_features_legacy & device_features; +if (virtio_features_test_bit (device_features,VIRTIO_F_VERSION_1 )) { +for (i =0 ; i <VIRTIO_FEATURES_DWORDS ; ++i) + dev->features_array[i] = driver_features[i] & + device_features[i]; + }else { +virtio_features_from_u64 (dev->features_array, + driver_features_legacy & + device_features[0 ]); + }/* When debugging, user may filter some features by hand. */ virtio_debug_device_filter_features (dev);/* Transport features always preserved to pass to finalize_features. */ for (i =VIRTIO_TRANSPORT_F_START ; i <VIRTIO_TRANSPORT_F_END ; i++) -if (device_features & (1ULL << i)) +if (virtio_features_test_bit (device_features, i))__virtio_set_bit (dev, i); err = dev->config->finalize_features (dev);
@@ -320,14 +325,15 @@ static int virtio_dev_probe(struct device *_d) goto err;if (drv->validate) { -u64 features = dev->features; +u64 features[VIRTIO_FEATURES_DWORDS ]; +virtio_features_copy (features, dev->features_array); err = drv->validate (dev);if (err) goto err;/* Did validation change any features? Then write them again. */ -if (features != dev->features) { +if (!virtio_features_equal (features, dev->features_array)) { err = dev->config->finalize_features (dev);if (err) goto err;
@@ -701,6 +707,9 @@ EXPORT_SYMBOL_GPL(virtio_device_reset_done);static int virtio_init (void ) { +BUILD_BUG_ON (offsetof(struct virtio_device , features) != + offsetof(struct virtio_device, features_array[0])); +if (bus_register (&virtio_bus) !=0 )panic ("virtio bus registration failed" );virtio_debug_init ();
drivers/virtio/virtio_debug.c +15 -12
@@ -8,13 +8,13 @@ static struct dentry *virtio_debugfs_dir;static int virtio_debug_device_features_show (struct seq_file *s,void *data) { +u64 device_features[VIRTIO_FEATURES_DWORDS ];struct virtio_device *dev = s->private; -u64 device_features;unsigned int i; - device_features = dev->config->get_features (dev); -for (i =0 ; i <BITS_PER_LONG_LONG ; i++) { -if (device_features & (1ULL << i)) +virtio_get_features (dev, device_features); +for (i =0 ; i <VIRTIO_FEATURES_MAX ; i++) { +if (virtio_features_test_bit (device_features, i))seq_printf (s,"%u\n" , i); }return 0 ;
@@ -26,8 +26,8 @@ static int virtio_debug_filter_features_show(struct seq_file *s, void *data)struct virtio_device *dev = s->private;unsigned int i; -for (i =0 ; i <BITS_PER_LONG_LONG ; i++) { -if (dev->debugfs_filter_features & (1ULL << i)) +for (i =0 ; i <VIRTIO_FEATURES_MAX ; i++) { +if (virtio_features_test_bit (dev->debugfs_filter_features, i))seq_printf (s,"%u\n" , i); }return 0 ;
@@ -39,7 +39,7 @@ static int virtio_debug_filter_features_clear(void *data, u64 val)struct virtio_device *dev = data;if (val ==1 ) - dev->debugfs_filter_features =0 ; +virtio_features_zero (dev->debugfs_filter_features);return 0 ; }
@@ -50,9 +50,10 @@ static int virtio_debug_filter_feature_add(void *data, u64 val) {struct virtio_device *dev = data; -if (val >=BITS_PER_LONG_LONG ) +if (val >=VIRTIO_FEATURES_MAX )return -EINVAL ; - dev->debugfs_filter_features |=BIT_ULL_MASK (val); + +virtio_features_set_bit (dev->debugfs_filter_features, val);return 0 ; }
@@ -63,9 +64,10 @@ static int virtio_debug_filter_feature_del(void *data, u64 val) {struct virtio_device *dev = data; -if (val >=BITS_PER_LONG_LONG ) +if (val >=VIRTIO_FEATURES_MAX )return -EINVAL ; - dev->debugfs_filter_features &= ~BIT_ULL_MASK (val); + +virtio_features_clear_bit (dev->debugfs_filter_features, val);return 0 ; }
@@ -91,7 +93,8 @@ EXPORT_SYMBOL_GPL(virtio_debug_device_init);void virtio_debug_device_filter_features (struct virtio_device *dev) { - dev->features &= ~dev->debugfs_filter_features; +virtio_features_andnot (dev->features_array, dev->features_array, + dev->debugfs_filter_features); }EXPORT_SYMBOL_GPL (virtio_debug_device_filter_features);
include/linux/virtio.h +3 -2
@@ -11,6 +11,7 @@#include <linux/gfp.h> #include <linux/dma-mapping.h> #include <linux/completion.h> +#include <linux/virtio_features.h> /** * struct virtqueue - a queue to register buffers for sending or receiving.
@@ -159,11 +160,11 @@ struct virtio_device {const struct virtio_config_ops *config;const struct vringh_config_ops *vringh_config;struct list_head vqs; -u64 features; +VIRTIO_DECLARE_FEATURES (features);void *priv;#ifdef CONFIG_VIRTIO_DEBUG struct dentry *debugfs_dir; -u64 debugfs_filter_features; +u64 debugfs_filter_features[VIRTIO_FEATURES_DWORDS ];#endif };
include/linux/virtio_config.h +19 -22
77 lines not on this page · raw message
include/linux/virtio_features.h new file +88 -0
88 lines not on this page · raw message
Signature · 1 line
2.49.0
Paolo Abeni
maintainerPATCH v4 2/8 virtio_pci_modern: allow configuring extended features
In reply to Paolo AbeniThe virtio specifications allows for up to 128 bits for the device features. Soon we are going to use some of the 'extended' bits features (above 64) for the virtio_net driver.
Extend the virtio pci modern driver to support configuring the full virtio features range, replacing the unrolled loops reading and writing the features space with explicit one bounded to the actual features space size in word and implementing the get_extended_features callback.
Note that in vp_finalize_features() we only need to cache the lower 64 features bits, to process the transport features.
---
v3 -> v4:
- dropped unneeded check in vp_modern_get_features()
v2 -> v3:
- virtio_features_t -> u64 *
v1 -> v2:
- use get_extended_features
--- drivers/virtio/virtio_pci_modern.c | 10 ++-- drivers/virtio/virtio_pci_modern_dev.c | 69 +++++++++++++++----------- include/linux/virtio_pci_modern.h | 43 ++++++++++++++-- 3 files changed, 84 insertions(+), 38 deletions(-)
drivers/virtio/virtio_pci_modern.c +5 -5
@@ -22,11 +22,11 @@#define VIRTIO_AVQ_SGS_MAX 4 -static u64 vp_get_features (struct virtio_device *vdev) +static void vp_get_features (struct virtio_device *vdev,u64 *features) {struct virtio_pci_device *vp_dev =to_vp_device (vdev); -return vp_modern_get_features (&vp_dev->mdev); +vp_modern_get_extended_features (&vp_dev->mdev, features); }static int vp_avq_index (struct virtio_device *vdev,u16 *index,u16 *num)
@@ -437,7 +437,7 @@ static int vp_finalize_features(struct virtio_device *vdev)if (vp_check_common_size (vdev))return -EINVAL ; -vp_modern_set_features (&vp_dev->mdev, vdev->features); +vp_modern_set_extended_features (&vp_dev->mdev, vdev->features_array);return 0 ; }
@@ -1234,7 +1234,7 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = { .find_vqs = vp_modern_find_vqs, .del_vqs = vp_del_vqs, .synchronize_cbs = vp_synchronize_vectors, - .get_features = vp_get_features, + .get_extended_features = vp_get_features, .finalize_features = vp_finalize_features, .bus_name = vp_bus_name, .set_vq_affinity = vp_set_vq_affinity,
@@ -1254,7 +1254,7 @@ static const struct virtio_config_ops virtio_pci_config_ops = { .find_vqs = vp_modern_find_vqs, .del_vqs = vp_del_vqs, .synchronize_cbs = vp_synchronize_vectors, - .get_features = vp_get_features, + .get_extended_features = vp_get_features, .finalize_features = vp_finalize_features, .bus_name = vp_bus_name, .set_vq_affinity = vp_set_vq_affinity,
drivers/virtio/virtio_pci_modern_dev.c +40 -29
103 lines not on this page · raw message
include/linux/virtio_pci_modern.h +39 -4
55 lines not on this page · raw message
Signature · 1 line
2.49.0
Paolo Abeni
maintainerPATCH v4 3/8 vhost-net: allow configuring extended features
In reply to Paolo AbeniUse the extended feature type for 'acked_features' and implement two new ioctls operation allowing the user-space to set/query an unbounded amount of features.
The actual number of processed features is limited by VIRTIO_FEATURES_MAX and attempts to set features above such limit fail with EOPNOTSUPP.
Note that: the legacy ioctls implicitly truncate the negotiated features to the lower 64 bits range and the 'acked_backend_features' field don't need conversion, as the only negotiated feature there is in the low 64 bit range.
---
v3 -> v4:
- use a single static (lower case) constant instead of enum and old def
- simpler VHOST_GET_FEATURES_ARRAY impl
- avoid using virtio_features_and_not
v2 -> v3:
- virtio_features_t -> u64[2]
- add __counted_by annotation to vhost_features_array
v1 -> v2:
- change the ioctl to use an extensible API
--- drivers/vhost/net.c | 87 ++++++++++++++++++++++++-------- drivers/vhost/vhost.c | 2 +- drivers/vhost/vhost.h | 4 +- include/uapi/linux/vhost.h | 7 +++ include/uapi/linux/vhost_types.h | 5 ++ 5 files changed, 82 insertions(+), 23 deletions(-)
drivers/vhost/net.c +67 -20
@@ -69,12 +69,12 @@ MODULE_PARM_DESC(experimental_zcopytx, "Enable Zero Copy TX;"#define VHOST_DMA_IS_DONE (len) ((__force u32)(len) >= (__force u32)VHOST_DMA_DONE_LEN) -enum { -VHOST_NET_FEATURES =VHOST_FEATURES | - (1ULL <<VHOST_NET_F_VIRTIO_NET_HDR ) | - (1ULL <<VIRTIO_NET_F_MRG_RXBUF ) | - (1ULL <<VIRTIO_F_ACCESS_PLATFORM ) | - (1ULL <<VIRTIO_F_RING_RESET ) +static const u64 vhost_net_features[VIRTIO_FEATURES_DWORDS ] = { +VHOST_FEATURES | + (1ULL <<VHOST_NET_F_VIRTIO_NET_HDR ) | + (1ULL <<VIRTIO_NET_F_MRG_RXBUF ) | + (1ULL <<VIRTIO_F_ACCESS_PLATFORM ) | + (1ULL <<VIRTIO_F_RING_RESET ), };enum {
@@ -1614,16 +1614,17 @@ static long vhost_net_reset_owner(struct vhost_net *n)return err; } -static int vhost_net_set_features (struct vhost_net *n,u64 features) +static int vhost_net_set_features (struct vhost_net *n,const u64 *features) {size_t vhost_hlen, sock_hlen, hdr_len;int i; - hdr_len = (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 & (1 <<VHOST_NET_F_VIRTIO_NET_HDR )) { + hdr_len =virtio_features_test_bit (features,VIRTIO_NET_F_MRG_RXBUF ) || +virtio_features_test_bit (features,VIRTIO_F_VERSION_1 ) ? +sizeof (struct virtio_net_hdr_mrg_rxbuf ) : +sizeof (struct virtio_net_hdr ); + +if (virtio_features_test_bit (features,VHOST_NET_F_VIRTIO_NET_HDR )) {/* vhost provides vnet_hdr */ vhost_hlen = hdr_len; sock_hlen =0 ;
@@ -1633,18 +1634,19 @@ static int vhost_net_set_features(struct vhost_net *n, u64 features) sock_hlen = hdr_len; }mutex_lock (&n->dev.mutex); -if ((features & (1 <<VHOST_F_LOG_ALL )) && +if (virtio_features_test_bit (features,VHOST_F_LOG_ALL ) && !vhost_log_access_ok (&n->dev)) goto out_unlock; -if ((features & (1ULL <<VIRTIO_F_ACCESS_PLATFORM ))) { +if (virtio_features_test_bit (features,VIRTIO_F_ACCESS_PLATFORM )) {if (vhost_init_device_iotlb (&n->dev)) goto out_unlock; }for (i =0 ; i <VHOST_NET_VQ_MAX ; ++i) {mutex_lock (&n->vqs[i].vq.mutex); - n->vqs[i].vq.acked_features = features; +virtio_features_copy (n->vqs[i].vq.acked_features_array, + features); n->vqs[i].vhost_hlen = vhost_hlen; n->vqs[i].sock_hlen = sock_hlen;mutex_unlock (&n->vqs[i].vq.mutex);
@@ -1681,12 +1683,13 @@ static long vhost_net_set_owner(struct vhost_net *n)static long vhost_net_ioctl (struct file *f,unsigned int ioctl,unsigned long arg) { +u64 all_features[VIRTIO_FEATURES_DWORDS ];struct vhost_net *n = f->private_data;void __user *argp = (void __user *)arg;u64 __user *featurep = argp;struct vhost_vring_file backend; -u64 features; -int r; +u64 features, count, copied; +int r, i;switch (ioctl) {case VHOST_NET_SET_BACKEND :
@@ -1694,16 +1697,60 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,return -EFAULT ;return vhost_net_set_backend (n, backend.index, backend.fd);case VHOST_GET_FEATURES : - features =VHOST_NET_FEATURES ; + features = vhost_net_features[0 ];if (copy_to_user (featurep, &features,sizeof features))return -EFAULT ;return 0 ;case VHOST_SET_FEATURES :if (copy_from_user (&features, featurep,sizeof features))return -EFAULT ; -if (features & ~VHOST_NET_FEATURES ) +if (features & ~vhost_net_features[0 ])return -EOPNOTSUPP ; -return vhost_net_set_features (n, features); + +virtio_features_from_u64 (all_features, features); +return vhost_net_set_features (n, all_features); +case VHOST_GET_FEATURES_ARRAY : +if (copy_from_user (&count, argp,sizeof (u64))) +return -EFAULT ; + +/* Copy the net features, up to the user-provided buffer size */ + argp +=sizeof (u64); + copied =min (count,VIRTIO_FEATURES_DWORDS ); +if (copy_to_user (argp, vhost_net_features, + copied *sizeof (u64))) +return -EFAULT ; + +/* Zero the trailing space provided by user-space, if any */ +if (clear_user (argp, (count - copied) *sizeof (u64))) +return -EFAULT ; +return 0 ; +case VHOST_SET_FEATURES_ARRAY : +if (copy_from_user (&count, argp,sizeof (u64))) +return -EFAULT ; + +virtio_features_zero (all_features); +for (i =0 ; i <min (count,VIRTIO_FEATURES_DWORDS ); ++i) { + argp +=sizeof (u64); +if (copy_from_user (&all_features[i], argp, +sizeof (u64))) +return -EFAULT ; + } + +/* Any feature specified by user-space above VIRTIO_FEATURES_MAX is +* not supported by definition. +*/ +for (; i < count; ++i) { +if (copy_from_user (&features, argp,sizeof (u64))) +return -EFAULT ; +if (features) +return -EOPNOTSUPP ; + } + +for (i =1 ; i <VIRTIO_FEATURES_DWORDS ; i++) +if (all_features[i] & ~vhost_net_features[i]) +return -EOPNOTSUPP ; + +return vhost_net_set_features (n, all_features);case VHOST_GET_BACKEND_FEATURES : features =VHOST_NET_BACKEND_FEATURES ;if (copy_to_user (featurep, &features,sizeof (features)))
drivers/vhost/vhost.c +1 -1
@@ -372,7 +372,7 @@ static void vhost_vq_reset(struct vhost_dev *dev, vq->log_used = false; vq->log_addr =-1ull ; vq->private_data =NULL ; - vq->acked_features =0 ; +virtio_features_zero (vq->acked_features_array); vq->acked_backend_features =0 ; vq->log_base =NULL ; vq->error_ctx =NULL ;
drivers/vhost/vhost.h +2 -2
@@ -133,7 +133,7 @@ struct vhost_virtqueue {struct vhost_iotlb *umem;struct vhost_iotlb *iotlb;void *private_data; -u64 acked_features; +VIRTIO_DECLARE_FEATURES (acked_features);u64 acked_backend_features;/* Log write descriptors */ void __user *log_base;
@@ -291,7 +291,7 @@ static inline void *vhost_vq_get_backend(struct vhost_virtqueue *vq)static inline bool vhost_has_feature (struct vhost_virtqueue *vq,int bit) { -return vq->acked_features & (1ULL << bit); +return virtio_features_test_bit (vq->acked_features_array, bit); }static inline bool vhost_backend_has_feature (struct vhost_virtqueue *vq,int bit)
include/uapi/linux/vhost.h +7 -0
@@ -235,4 +235,11 @@*/ #define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x82, \ struct vhost_vring_state) + +/* Extended features manipulation */ +#define VHOST_GET_FEATURES_ARRAY _IOR(VHOST_VIRTIO, 0x83, \ + struct vhost_features_array) +#define VHOST_SET_FEATURES_ARRAY _IOW(VHOST_VIRTIO, 0x83, \ + struct vhost_features_array) +#endif
include/uapi/linux/vhost_types.h +5 -0
@@ -110,6 +110,11 @@ struct vhost_msg_v2 { }; }; +struct vhost_features_array { +__u64 count;/* number of entries present in features array */ + __u64 features[] __counted_by(count); +}; +struct vhost_memory_region {__u64 guest_phys_addr;__u64 memory_size;/* bytes */
Signature · 1 line
2.49.0
Paolo Abeni
maintainerPATCH v4 5/8 net: implement virtio helpers to handle UDP GSO tunneling.
In reply to Paolo AbeniThe virtio specification are introducing support for GSO over UDP tunnel.
This patch brings in the needed defines and the additional virtio hdr parsing/building helpers.
The UDP tunnel support uses additional fields in the virtio hdr, and such fields location can change depending on other negotiated features - specifically VIRTIO_NET_F_HASH_REPORT.
Try to be as conservative as possible with the new field validation.
Existing implementation for plain GSO offloads allow for invalid/ self-contradictory values of such fields. With GSO over UDP tunnel we can be more strict, with no need to deal with legacy implementation.
Since the checksum-related field validation is asymmetric in the driver and in the device, introduce a separate helper to implement the new checks (to be used only on the driver side).
Note that while the feature space exceeds the 64-bit boundaries, the guest offload space is fixed by the specification of the VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET command to a 64-bit size.
Prior to the UDP tunnel GSO support, each guest offload bit corresponded to the feature bit with the same value and vice versa.
Due to the limited 'guest offload' space, relevant features in the high 64 bits are 'mapped' to free bits in the lower range. That is simpler than defining a new command (and associated features) to exchange an extended guest offloads set.
As a consequence, the uAPIs also specify the mapped guest offload value corresponding to the UDP tunnel GSO features.
--
v3 -> v4:
- fixed offset for UDP GSO tunnel, update accordingly the helpers
- tried to clarified vlan_hlen semantic
- virtio_net_chk_data_valid() -> virtio_net_handle_csum_offload()
v2 -> v3:
- add definitions for possible vnet hdr layouts with tunnel support
v1 -> v2:
- 'relay' -> 'rely' typo
- less unclear comment WRT enforced inner GSO checks
- inner header fields are allowed only with 'modern' virtio, thus are always le
- clarified in the commit message the need for 'mapped features' defines
- assume little_endian is true when UDP GSO is enabled.
- fix inner proto type value
--- include/linux/virtio_net.h | 196 ++++++++++++++++++++++++++++++-- include/uapi/linux/virtio_net.h | 33 ++++++ 2 files changed, 221 insertions(+), 8 deletions(-)
include/linux/virtio_net.h +188 -8
@@ -47,9 +47,9 @@ static inline int virtio_net_hdr_set_proto(struct sk_buff *skb,return 0 ; } -static inline int virtio_net_hdr_to_skb (struct sk_buff *skb, -const struct virtio_net_hdr *hdr, -bool little_endian) +static inline int __virtio_net_hdr_to_skb (struct sk_buff *skb, +const struct virtio_net_hdr *hdr, +bool little_endian,u8 hdr_gso_type) {unsigned int nh_min_len =sizeof (struct iphdr );unsigned int gso_type =0 ;
@@ -57,8 +57,8 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,unsigned int p_off =0 ;unsigned int ip_proto; -if (hdr->gso_type !=VIRTIO_NET_HDR_GSO_NONE ) { -switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN ) { +if (hdr_gso_type !=VIRTIO_NET_HDR_GSO_NONE ) { +switch (hdr_gso_type & ~VIRTIO_NET_HDR_GSO_ECN ) {case VIRTIO_NET_HDR_GSO_TCPV4 : gso_type =SKB_GSO_TCPV4 ; ip_proto =IPPROTO_TCP ;
@@ -84,7 +84,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,return -EINVAL ; } -if (hdr->gso_type &VIRTIO_NET_HDR_GSO_ECN ) +if (hdr_gso_type &VIRTIO_NET_HDR_GSO_ECN ) gso_type |=SKB_GSO_TCP_ECN ;if (hdr->gso_size ==0 )
@@ -122,7 +122,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,if (!protocol)virtio_net_hdr_set_proto (skb, hdr); -else if (!virtio_net_hdr_match_proto (protocol, hdr->gso_type)) +else if (!virtio_net_hdr_match_proto (protocol, hdr_gso_type))return -EINVAL ;else skb->protocol = protocol;
@@ -153,7 +153,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, } } -if (hdr->gso_type !=VIRTIO_NET_HDR_GSO_NONE ) { +if (hdr_gso_type !=VIRTIO_NET_HDR_GSO_NONE ) {u16 gso_size =__virtio16_to_cpu (little_endian, hdr->gso_size);unsigned int nh_off = p_off;struct skb_shared_info *shinfo =skb_shinfo (skb);
@@ -199,6 +199,13 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,return 0 ; } +static inline int virtio_net_hdr_to_skb (struct sk_buff *skb, +const struct virtio_net_hdr *hdr, +bool little_endian) +{ +return __virtio_net_hdr_to_skb (skb, hdr, little_endian, hdr->gso_type); +} + static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, struct virtio_net_hdr *hdr, bool little_endian,
@@ -242,4 +249,177 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,return 0 ; } +static inline unsigned int virtio_l3min (bool is_ipv6) +{ +return is_ipv6 ?sizeof (struct ipv6hdr ) :sizeof (struct iphdr ); +} + +static inline int +virtio_net_hdr_tnl_to_skb (struct sk_buff *skb, +const struct virtio_net_hdr_v1_hash_tunnel *vhdr, +bool tnl_hdr_negotiated, +bool tnl_csum_negotiated, +bool little_endian) +{ +const struct virtio_net_hdr *hdr = (const struct virtio_net_hdr *)vhdr; +unsigned int inner_nh, outer_th, inner_th; +unsigned int inner_l3min, outer_l3min; +u8 gso_inner_type, gso_tunnel_type; +bool outer_isv6, inner_isv6; +int ret; + + gso_tunnel_type = hdr->gso_type &VIRTIO_NET_HDR_GSO_UDP_TUNNEL ; +if (!gso_tunnel_type) +return virtio_net_hdr_to_skb (skb, hdr, little_endian); + +/* Tunnel not supported/negotiated, but the hdr asks for it. */ +if (!tnl_hdr_negotiated) +return -EINVAL ; + +/* Either ipv4 or ipv6. */ +if (gso_tunnel_type ==VIRTIO_NET_HDR_GSO_UDP_TUNNEL ) +return -EINVAL ; + +/* The UDP tunnel must carry a GSO packet, but no UFO. */ + gso_inner_type = hdr->gso_type & ~(VIRTIO_NET_HDR_GSO_ECN | +VIRTIO_NET_HDR_GSO_UDP_TUNNEL ); +if (!gso_inner_type || gso_inner_type ==VIRTIO_NET_HDR_GSO_UDP ) +return -EINVAL ; + +/* Rely on csum being present. */ +if (!(hdr->flags &VIRTIO_NET_HDR_F_NEEDS_CSUM )) +return -EINVAL ; + +/* Validate offsets. */ + outer_isv6 = gso_tunnel_type &VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 ; + inner_isv6 = gso_inner_type ==VIRTIO_NET_HDR_GSO_TCPV6 ; + inner_l3min =virtio_l3min (inner_isv6); + outer_l3min =ETH_HLEN +virtio_l3min (outer_isv6); + + inner_th =__virtio16_to_cpu (little_endian, hdr->csum_start); + inner_nh =le16_to_cpu (vhdr->inner_nh_offset); + outer_th =le16_to_cpu (vhdr->outer_th_offset); +if (outer_th < outer_l3min || + inner_nh < outer_th +sizeof (struct udphdr ) || + inner_th < inner_nh + inner_l3min) +return -EINVAL ; + +/* Let the basic parsing deal with plain GSO features. */ + ret =__virtio_net_hdr_to_skb (skb, hdr, true, + hdr->gso_type & ~gso_tunnel_type); +if (ret) +return ret; + +/* In case of USO, the inner protocol is still unknown and +* `inner_isv6` is just a guess, additional parsing is needed. +* The previous validation ensures that accessing an ipv4 inner +* network header is safe. +*/ +if (gso_inner_type ==VIRTIO_NET_HDR_GSO_UDP_L4 ) { +struct iphdr *iphdr = (struct iphdr *)(skb->data + inner_nh); + + inner_isv6 = iphdr->version ==6 ; + inner_l3min =virtio_l3min (inner_isv6); +if (inner_th < inner_nh + inner_l3min) +return -EINVAL ; + } + +skb_set_inner_protocol (skb, inner_isv6 ?htons (ETH_P_IPV6 ) : +htons (ETH_P_IP )); +if (hdr->flags &VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM ) { +if (!tnl_csum_negotiated) +return -EINVAL ; + +skb_shinfo (skb)->gso_type |=SKB_GSO_UDP_TUNNEL_CSUM ; + }else { +skb_shinfo (skb)->gso_type |=SKB_GSO_UDP_TUNNEL ; + } + + skb->inner_transport_header = inner_th +skb_headroom (skb); + skb->inner_network_header = inner_nh +skb_headroom (skb); + skb->inner_mac_header = inner_nh +skb_headroom (skb); + skb->transport_header = outer_th +skb_headroom (skb); + skb->encapsulation =1 ; +return 0 ; +} + +/* Checksum-related fields validation for the driver */ +static inline int virtio_net_handle_csum_offload (struct sk_buff *skb, +struct virtio_net_hdr *hdr, +bool tnl_csum_negotiated) +{ +if (!(hdr->gso_type &VIRTIO_NET_HDR_GSO_UDP_TUNNEL )) { +if (!(hdr->flags &VIRTIO_NET_HDR_F_DATA_VALID )) +return 0 ; + + skb->ip_summed =CHECKSUM_UNNECESSARY ; +if (!(hdr->flags &VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM )) +return 0 ; + +/* tunnel csum packets are invalid when the related +* feature has not been negotiated +*/ +if (!tnl_csum_negotiated) +return -EINVAL ; + skb->csum_level =1 ; +return 0 ; + } + +/* DATA_VALID is mutually exclusive with NEEDS_CSUM, and GSO +* over UDP tunnel requires the latter +*/ +if (hdr->flags &VIRTIO_NET_HDR_F_DATA_VALID ) +return -EINVAL ; +return 0 ; +} + +/* +* vlan_hlen always refers to the outermost MAC header. That also +* means it refers to the only MAC header, if the packet does not carry +* any encapsulation. +*/ +static inline int +virtio_net_hdr_tnl_from_skb (const struct sk_buff *skb, +struct virtio_net_hdr_v1_hash_tunnel *vhdr, +bool tnl_hdr_negotiated, +bool little_endian, +int vlan_hlen) +{ +struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)vhdr; +unsigned int inner_nh, outer_th; +int tnl_gso_type; +int ret; + + tnl_gso_type =skb_shinfo (skb)->gso_type & (SKB_GSO_UDP_TUNNEL | +SKB_GSO_UDP_TUNNEL_CSUM ); +if (!tnl_gso_type) +return virtio_net_hdr_from_skb (skb, hdr, little_endian, false, + vlan_hlen); + +/* Tunnel support not negotiated but skb ask for it. */ +if (!tnl_hdr_negotiated) +return -EINVAL ; + +/* Let the basic parsing deal with plain GSO features. */ +skb_shinfo (skb)->gso_type &= ~tnl_gso_type; + ret =virtio_net_hdr_from_skb (skb, hdr, true, false, vlan_hlen); +skb_shinfo (skb)->gso_type |= tnl_gso_type; +if (ret) +return ret; + +if (skb->protocol ==htons (ETH_P_IPV6 )) + hdr->gso_type |=VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 ; +else + hdr->gso_type |=VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 ; + +if (skb_shinfo (skb)->gso_type &SKB_GSO_UDP_TUNNEL_CSUM ) + hdr->flags |=VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM ; + + inner_nh = skb->inner_network_header -skb_headroom (skb); + outer_th = skb->transport_header -skb_headroom (skb); + vhdr->inner_nh_offset =cpu_to_le16 (inner_nh); + vhdr->outer_th_offset =cpu_to_le16 (outer_th); +return 0 ; +} +#endif /* _LINUX_VIRTIO_NET_H */
include/uapi/linux/virtio_net.h +33 -0
@@ -70,6 +70,28 @@* with the same MAC. */ #define VIRTIO_NET_F_SPEED_DUPLEX 63/* Device set linkspeed and duplex */ +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO 65/* Driver can receive +* GSO-over-UDP-tunnel packets +*/ +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM 66/* Driver handles +* GSO-over-UDP-tunnel +* packets with partial csum +* for the outer header +*/ +#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO 67/* Device can receive +* GSO-over-UDP-tunnel packets +*/ +#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM 68/* Device handles +* GSO-over-UDP-tunnel +* packets with partial csum +* for the outer header +*/ + +/* Offloads bits corresponding to VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO{,_CSUM} +* features +*/ +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED 46 +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED 47#ifndef VIRTIO_NET_NO_LEGACY #define VIRTIO_NET_F_GSO 6/* Host handles pkts w/ any GSO type */
@@ -131,12 +153,17 @@ struct virtio_net_hdr_v1 {#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1/* Use csum_start, csum_offset */ #define VIRTIO_NET_HDR_F_DATA_VALID 2/* Csum is valid */ #define VIRTIO_NET_HDR_F_RSC_INFO 4/* rsc info in csum_ fields */ +#define VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM 8/* UDP tunnel requires csum offload */ __u8 flags;#define VIRTIO_NET_HDR_GSO_NONE 0/* Not a GSO frame */ #define VIRTIO_NET_HDR_GSO_TCPV4 1/* GSO frame, IPv4 TCP (TSO) */ #define VIRTIO_NET_HDR_GSO_UDP 3/* GSO frame, IPv4 UDP (UFO) */ #define VIRTIO_NET_HDR_GSO_TCPV6 4/* GSO frame, IPv6 TCP */ #define VIRTIO_NET_HDR_GSO_UDP_L4 5/* GSO frame, IPv4& IPv6 UDP (USO) */ +#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 0x20/* UDP over IPv4 tunnel present */ +#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 0x40/* UDP over IPv6 tunnel present */ +#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL (VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 | \ + VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6)#define VIRTIO_NET_HDR_GSO_ECN 0x80/* TCP has ECN set */ __u8 gso_type;__virtio16 hdr_len;/* Ethernet + IP + tcp/udp hdrs */
@@ -181,6 +208,12 @@ struct virtio_net_hdr_v1_hash {__le16 padding; }; +struct virtio_net_hdr_v1_hash_tunnel { +struct virtio_net_hdr_v1_hash hash_hdr; +__le16 outer_th_offset; +__le16 inner_nh_offset; +}; +#ifndef VIRTIO_NET_NO_LEGACY /* This header comes first in the scatter-gather list. * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must
Signature · 1 line
2.49.0
Paolo Abeni
maintainerPATCH v4 4/8 virtio_net: add supports for extended offloads
In reply to Paolo AbeniThe virtio_net driver needs it to implement GSO over UDP tunnel offload.
The only missing piece is mapping them to/from the extended features.
---
v2 -> v3:
- offload to feature conversion is now an inline function
v1 -> v2:
- drop unused macro
- restrict the offload remap range as per latest spec update
--- drivers/net/virtio_net.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
drivers/net/virtio_net.c +22 -2
@@ -35,6 +35,22 @@ module_param(csum, bool, 0444);module_param (gso, bool,0444 );module_param (napi_tx, bool,0644 ); +#define VIRTIO_OFFLOAD_MAP_MIN 46 +#define VIRTIO_OFFLOAD_MAP_MAX 47 +#define VIRTIO_FEATURES_MAP_MIN 65 +#define VIRTIO_O2F_DELTA (VIRTIO_FEATURES_MAP_MIN - VIRTIO_OFFLOAD_MAP_MIN) + +static bool virtio_is_mapped_offload (unsigned int obit) +{ +return obit >=VIRTIO_OFFLOAD_MAP_MIN && + obit <=VIRTIO_OFFLOAD_MAP_MAX ; +} + +static unsigned int virtio_offload_to_feature (unsigned int obit) +{ +return virtio_is_mapped_offload (obit) ? obit +VIRTIO_O2F_DELTA : obit; +} +/* FIXME: MTU in config. */ #define GOOD_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN)#define GOOD_COPY_LEN 128
@@ -7026,9 +7042,13 @@ static int virtnet_probe(struct virtio_device *vdev)netif_carrier_on (dev); } -for (i =0 ; i <ARRAY_SIZE (guest_offloads); i++) -if (virtio_has_feature (vi->vdev, guest_offloads[i])) +for (i =0 ; i <ARRAY_SIZE (guest_offloads); i++) { +unsigned int fbit; + + fbit =virtio_offload_to_feature (guest_offloads[i]); +if (virtio_has_feature (vi->vdev, fbit))set_bit (guest_offloads[i], &vi->guest_offloads); + } vi->guest_offloads_capable = vi->guest_offloads;rtnl_unlock ();
Signature · 1 line
2.49.0
Paolo Abeni
maintainerPATCH v4 6/8 virtio_net: enable gso over UDP tunnel support.
In reply to Paolo AbeniIf the related virtio feature is set, enable transmission and reception of gso over UDP tunnel packets.
Most of the work is done by the previously introduced helper, just need to determine the UDP tunnel features inside the virtio_net_hdr and update accordingly the virtio net hdr size.
---
v3 -> v4:
- tnl header fields at constant offset, update the parsing accordingly
- keep separate status for tnl offload tx and tnl offload rx
- drop redundant comment
v2 -> v3:
- drop the VIRTIO_HAS_EXTENDED_FEATURES conditionals
v1 -> v2:
- test for UDP_TUNNEL_GSO* only on builds with extended features support
- comment indentation cleanup
- rebased on top of virtio helpers changes
- dump more information in case of bad offloads
--- drivers/net/virtio_net.c | 85 ++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 21 deletions(-)
drivers/net/virtio_net.c +64 -21
@@ -78,15 +78,19 @@ static const unsigned long guest_offloads[] = { VIRTIO_NET_F_GUEST_CSUM,VIRTIO_NET_F_GUEST_USO4 ,VIRTIO_NET_F_GUEST_USO6 , -VIRTIO_NET_F_GUEST_HDRLEN +VIRTIO_NET_F_GUEST_HDRLEN , +VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED , + VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED, };#define GUEST_OFFLOAD_GRO_HW_MASK ((1ULL << VIRTIO_NET_F_GUEST_TSO4) | \ - (1ULL << VIRTIO_NET_F_GUEST_TSO6) | \ - (1ULL << VIRTIO_NET_F_GUEST_ECN) | \ - (1ULL << VIRTIO_NET_F_GUEST_UFO) | \ - (1ULL << VIRTIO_NET_F_GUEST_USO4) | \ - (1ULL << VIRTIO_NET_F_GUEST_USO6)) + (1ULL << VIRTIO_NET_F_GUEST_TSO6) | \ + (1ULL << VIRTIO_NET_F_GUEST_ECN) | \ + (1ULL << VIRTIO_NET_F_GUEST_UFO) | \ + (1ULL << VIRTIO_NET_F_GUEST_USO4) | \ + (1ULL << VIRTIO_NET_F_GUEST_USO6) | \ + (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED) | \ + (1ULL << VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED))struct virtnet_stat_desc {char desc[ETH_GSTRING_LEN ];
@@ -439,6 +443,13 @@ struct virtnet_info {/* Work struct for delayed refilling if we run low on memory. */ struct delayed_work refill; +/* UDP tunnel support */ +bool tx_tnl; + +bool rx_tnl; + +bool rx_tnl_csum; +/* Is delayed refill enabled? */ bool refill_enabled;
@@ -498,6 +509,7 @@ struct virtio_net_common_hdr {struct virtio_net_hdr hdr;struct virtio_net_hdr_mrg_rxbuf mrg_hdr;struct virtio_net_hdr_v1_hash hash_v1_hdr; +struct virtio_net_hdr_v1_hash_tunnel tnl_hdr; }; };
@@ -2531,14 +2543,21 @@ static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *if (dev->features &NETIF_F_RXHASH && vi->has_rss_hash_report)virtio_skb_set_hash (&hdr->hash_v1_hdr, skb); -if (flags &VIRTIO_NET_HDR_F_DATA_VALID ) - skb->ip_summed =CHECKSUM_UNNECESSARY ; + hdr->hdr.flags = flags; +if (virtio_net_handle_csum_offload (skb, &hdr->hdr, vi->rx_tnl_csum)) { +net_warn_ratelimited ("%s: bad csum: flags: %x, gso_type: %x rx_tnl_csum %d\n" , + dev->name, hdr->hdr.flags, + hdr->hdr.gso_type, vi->rx_tnl_csum); + goto frame_err; + } -if (virtio_net_hdr_to_skb (skb, &hdr->hdr, -virtio_is_little_endian (vi->vdev))) { -net_warn_ratelimited ("%s: bad gso: type: %u, size: %u\n" , +if (virtio_net_hdr_tnl_to_skb (skb, &hdr->tnl_hdr, vi->rx_tnl, + vi->rx_tnl_csum, +virtio_is_little_endian (vi->vdev))) { +net_warn_ratelimited ("%s: bad gso: type: %x, size: %u, flags %x tunnel %d tnl csum %d\n" , dev->name, hdr->hdr.gso_type, - hdr->hdr.gso_size); + hdr->hdr.gso_size, hdr->hdr.flags, + vi->rx_tnl, vi->rx_tnl_csum); goto frame_err; }
@@ -3250,9 +3269,9 @@ static int virtnet_poll_tx(struct napi_struct *napi, int budget)static int xmit_skb (struct send_queue *sq,struct sk_buff *skb,bool orphan) { -struct virtio_net_hdr_mrg_rxbuf *hdr;const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;struct virtnet_info *vi = sq->vq->vdev->priv; +struct virtio_net_hdr_v1_hash_tunnel *hdr;int num_sg;unsigned hdr_len = vi->hdr_len;bool can_push;
@@ -3265,17 +3284,17 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)/* Even if we can, don't push here yet as this would skew * csum_start offset below. */ if (can_push) - hdr = (struct virtio_net_hdr_mrg_rxbuf *)(skb->data - hdr_len); + hdr = (struct virtio_net_hdr_v1_hash_tunnel *)(skb->data - + hdr_len);else - hdr = &skb_vnet_common_hdr (skb)->mrg_hdr; + hdr = &skb_vnet_common_hdr (skb)->tnl_hdr; -if (virtio_net_hdr_from_skb (skb, &hdr->hdr, -virtio_is_little_endian (vi->vdev), false, -0 )) +if (virtio_net_hdr_tnl_from_skb (skb, hdr, vi->tx_tnl, +virtio_is_little_endian (vi->vdev),0 ))return -EPROTO ;if (vi->mergeable_rx_bufs) - hdr->num_buffers =0 ; + hdr->hash_hdr.hdr.num_buffers =0 ;sg_init_table (sq->sg,skb_shinfo (skb)->nr_frags + (can_push ?1 :2 ));if (can_push) {
@@ -6764,10 +6783,20 @@ static int virtnet_probe(struct virtio_device *vdev)if (virtio_has_feature (vdev,VIRTIO_NET_F_HOST_USO )) dev->hw_features |=NETIF_F_GSO_UDP_L4 ; +if (virtio_has_feature (vdev,VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO )) { + dev->hw_features |=NETIF_F_GSO_UDP_TUNNEL ; + dev->hw_enc_features = dev->hw_features; + } +if (dev->hw_features &NETIF_F_GSO_UDP_TUNNEL && +virtio_has_feature (vdev,VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM )) { + dev->hw_features |=NETIF_F_GSO_UDP_TUNNEL_CSUM ; + dev->hw_enc_features |=NETIF_F_GSO_UDP_TUNNEL_CSUM ; + } + dev->features |=NETIF_F_GSO_ROBUST ;if (gso) - dev->features |= dev->hw_features &NETIF_F_ALL_TSO ; + dev->features |= dev->hw_features;/* (!csum && gso) case will be fixed by register_netdev() */ }
@@ -6860,7 +6889,10 @@ static int virtnet_probe(struct virtio_device *vdev) dev->xdp_metadata_ops = &virtnet_xdp_metadata_ops; } -if (vi->has_rss_hash_report) +if (virtio_has_feature (vdev,VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO ) || +virtio_has_feature (vdev,VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO )) + vi->hdr_len =sizeof (struct virtio_net_hdr_v1_hash_tunnel ); +else if (vi->has_rss_hash_report) vi->hdr_len =sizeof (struct virtio_net_hdr_v1_hash );else if (virtio_has_feature (vdev,VIRTIO_NET_F_MRG_RXBUF ) ||virtio_has_feature (vdev,VIRTIO_F_VERSION_1 ))
@@ -6868,6 +6900,13 @@ static int virtnet_probe(struct virtio_device *vdev)else vi->hdr_len =sizeof (struct virtio_net_hdr ); +if (virtio_has_feature (vdev,VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM )) + vi->rx_tnl_csum = true; +if (virtio_has_feature (vdev,VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO )) + vi->rx_tnl = true; +if (virtio_has_feature (vdev,VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO )) + vi->tx_tnl = true; +if (virtio_has_feature (vdev,VIRTIO_F_ANY_LAYOUT ) ||virtio_has_feature (vdev,VIRTIO_F_VERSION_1 )) vi->any_header_sg = true;
@@ -7178,6 +7217,10 @@ static struct virtio_device_id id_table[] = {static unsigned int features[] = {VIRTNET_FEATURES , +VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO , +VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM , +VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO , +VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM , };static unsigned int features_legacy[] = {
Signature · 1 line
2.49.0
Paolo Abeni
maintainerPATCH v4 7/8 tun: enable gso over UDP tunnel support.
In reply to Paolo AbeniAdd new tun features to represent the newly introduced virtio GSO over UDP tunnel offload. Allows detection and selection of such features via the existing TUNSETOFFLOAD ioctl and compute the expected virtio header size and tunnel header offset using the current netdev features, so that we can plug almost seamless the newly introduced virtio helpers to serialize the extended virtio header.
---
v3 -> v4:
- virtio tnl-related fields are at fixed offset, cleanup the code accordingly.
- use netdev features instead of flags bit to check for the configured offload
- drop packet in case of enabled features/configured hdr size mismatch
v2 -> v3:
- cleaned-up uAPI comments
- use explicit struct layout instead of raw buf.
--- drivers/net/tun.c | 70 ++++++++++++++++++++++++----- drivers/net/tun_vnet.h | 88 +++++++++++++++++++++++++++++++++---- include/uapi/linux/if_tun.h | 9 ++++ 3 files changed, 148 insertions(+), 19 deletions(-)
drivers/net/tun.c +60 -10
@@ -186,7 +186,8 @@ struct tun_struct {struct net_device *dev;netdev_features_t set_features;#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \ - NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4) + NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4 | \ + NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM)int align;int vnet_hdr_sz;
@@ -925,6 +926,7 @@ static int tun_net_init(struct net_device *dev) dev->hw_features =NETIF_F_SG |NETIF_F_FRAGLIST |TUN_USER_FEATURES |NETIF_F_HW_VLAN_CTAG_TX |NETIF_F_HW_VLAN_STAG_TX ; + dev->hw_enc_features = dev->hw_features; dev->features = dev->hw_features; dev->vlan_features = dev->features & ~(NETIF_F_HW_VLAN_CTAG_TX |
@@ -1698,7 +1700,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,struct sk_buff *skb;size_t total_len =iov_iter_count (from);size_t len = total_len, align = tun->align, linear; -struct virtio_net_hdr gso = {0 }; +struct virtio_net_hdr_v1_hash_tunnel hdr; +struct virtio_net_hdr *gso;int good_linear;int copylen;int hdr_len =0 ;
@@ -1708,6 +1711,15 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,int skb_xdp =1 ;bool frags =tun_napi_frags_enabled (tfile);enum skb_drop_reason drop_reason =SKB_DROP_REASON_NOT_SPECIFIED ; +netdev_features_t features =0 ; + +/* +* Keep it easy and always zero the whole buffer, even if the +* tunnel-related field will be touched only when the feature +* is enabled and the hdr size id compatible. +*/ +memset (&hdr,0 ,sizeof (hdr)); + gso = (struct virtio_net_hdr *)&hdr;if (!(tun->flags &IFF_NO_PI )) {if (len <sizeof (pi))
@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz); - hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ; + + hdr_len =__tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, + features, from, gso);if (hdr_len <0 )return hdr_len;
@@ -1755,7 +1772,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,* (e.g gso or jumbo packet), we will do it at after * skb was created with generic XDP routine. */ - skb =tun_build_skb (tun, tfile, from, &gso, len, &skb_xdp); + skb =tun_build_skb (tun, tfile, from, gso, len, &skb_xdp); err =PTR_ERR_OR_ZERO (skb);if (err) goto drop;
@@ -1799,7 +1816,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } } -if (tun_vnet_hdr_to_skb (tun->flags, skb, &gso)) { +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, &hdr)) {atomic_long_inc (&tun->rx_frame_errors); err = -EINVAL ; goto free_skb;
@@ -2050,13 +2067,21 @@ static ssize_t tun_put_user(struct tun_struct *tun, }if (vnet_hdr_sz) { -struct virtio_net_hdr gso; +struct virtio_net_hdr_v1_hash_tunnel hdr; +struct virtio_net_hdr *gso; - ret =tun_vnet_hdr_from_skb (tun->flags, tun->dev, skb, &gso); + ret =tun_vnet_hdr_tnl_from_skb (tun->flags, tun->dev, skb, + &hdr);if (ret)return ret; - ret =tun_vnet_hdr_put (vnet_hdr_sz, iter, &gso); +/* +* Drop the packet if the configured header size is too small +* WRT the enabled offloads. +*/ + gso = (struct virtio_net_hdr *)&hdr; + ret =__tun_vnet_hdr_put (vnet_hdr_sz, tun->dev->features, + iter, gso);if (ret)return ret; }
@@ -2357,7 +2382,9 @@ static int tun_xdp_one(struct tun_struct *tun, {unsigned int datasize = xdp->data_end - xdp->data;struct tun_xdp_hdr *hdr = xdp->data_hard_start; +struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr;struct virtio_net_hdr *gso = &hdr->gso; +netdev_features_t features =0 ;struct bpf_prog *xdp_prog;struct sk_buff *skb =NULL ;struct sk_buff_head *queue;
@@ -2426,7 +2453,17 @@ static int tun_xdp_one(struct tun_struct *tun,if (metasize >0 )skb_metadata_set (skb, metasize); -if (tun_vnet_hdr_to_skb (tun->flags, skb, gso)) { +/* +* Assume tunnel offloads are enabled if the received hdr is large +* enough. +*/ +if (READ_ONCE (tun->vnet_hdr_sz) >=TUN_VNET_TNL_SIZE && + xdp->data - xdp->data_hard_start >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ; + + tnl_hdr = (struct virtio_net_hdr_v1_hash_tunnel *)gso; +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, tnl_hdr)) {atomic_long_inc (&tun->rx_frame_errors);kfree_skb (skb); ret = -EINVAL ;
@@ -2812,6 +2849,8 @@ static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) } +#define PLAIN_GSO (NETIF_F_GSO_UDP_L4 | NETIF_F_TSO | NETIF_F_TSO6) +/* This is like a cut-down ethtool ops, except done via tun fd so no * privs required. */ static int set_offload (struct tun_struct *tun,unsigned long arg)
@@ -2841,6 +2880,18 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) features |=NETIF_F_GSO_UDP_L4 ; arg &= ~(TUN_F_USO4 |TUN_F_USO6 ); } + +/* +* Tunnel offload is allowed only if some plain offload is +* available, too. +*/ +if (features &PLAIN_GSO && arg &TUN_F_UDP_TUNNEL_GSO ) { + features |=NETIF_F_GSO_UDP_TUNNEL ; +if (arg &TUN_F_UDP_TUNNEL_GSO_CSUM ) + features |=NETIF_F_GSO_UDP_TUNNEL_CSUM ; + arg &= ~(TUN_F_UDP_TUNNEL_GSO | +TUN_F_UDP_TUNNEL_GSO_CSUM ); + } }/* This gives the user a way to test for new features in future by
@@ -2852,7 +2903,6 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) tun->dev->wanted_features &= ~TUN_USER_FEATURES ; tun->dev->wanted_features |= features;netdev_update_features (tun->dev); -return 0 ; }
drivers/net/tun_vnet.h +79 -9
137 lines not on this page · raw message
include/uapi/linux/if_tun.h +9 -0
@@ -93,6 +93,15 @@#define TUN_F_USO4 0x20/* I can handle USO for IPv4 packets */ #define TUN_F_USO6 0x40/* I can handle USO for IPv6 packets */ +/* I can handle TSO/USO for UDP tunneled packets */ +#define TUN_F_UDP_TUNNEL_GSO 0x080 + +/* +* I can handle TSO/USO for UDP tunneled packets requiring csum offload for +* the outer header +*/ +#define TUN_F_UDP_TUNNEL_GSO_CSUM 0x100 +/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ #define TUN_PKT_STRIP 0x0001struct tun_pi {
Signature · 1 line
2.49.0
Paolo Abeni
maintainerPATCH v4 8/8 vhost/net: enable gso over UDP tunnel support.
In reply to Paolo AbeniVhost net need to know the exact virtio net hdr size to be able to copy such header correctly. Teach it about the newly defined UDP tunnel-related option and update the hdr size computation accordingly.
---
v3 -> v4:
- rebased
--- drivers/vhost/net.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/vhost/net.c +8 -0
@@ -75,6 +75,8 @@ static const u64 vhost_net_features[VIRTIO_FEATURES_DWORDS] = { (1ULL <<VIRTIO_NET_F_MRG_RXBUF ) | (1ULL <<VIRTIO_F_ACCESS_PLATFORM ) | (1ULL <<VIRTIO_F_RING_RESET ), +VIRTIO_BIT (VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO ) | + VIRTIO_BIT(VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO), }; enum {
@@ -1624,6 +1626,12 @@ static int vhost_net_set_features(struct vhost_net *n, const u64 *features) sizeof(struct virtio_net_hdr_mrg_rxbuf) :sizeof (struct virtio_net_hdr ); +if (virtio_features_test_bit (features, +VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO ) || +virtio_features_test_bit (features, +VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO )) + hdr_len =sizeof (struct virtio_net_hdr_v1_hash_tunnel ); +if (virtio_features_test_bit (features,VHOST_NET_F_VIRTIO_NET_HDR )) {/* vhost provides vnet_hdr */ vhost_hlen = hdr_len;
Signature · 1 line
2.49.0
Michael S. Tsirkin
maintainerQuoted 28 lines · Paolo Abeni
Some virtualized deployments use UDP tunnel pervasively and are impacted negatively by the lack of GSO support for such kind of traffic in the virtual NIC driver.
The
virtio_netspecification recently introduced support for GSO over UDP tunnel, this series updates the virtio implementation to support such a feature.Currently the kernel virtio support limits the feature space to 64, while the virtio specification allows for a larger number of features. Specifically the GSO-over-UDP-tunnel-related virtio features use bits 65-69.
The first four patches in this series rework the virtio and vhost feature support to cope with up to 128 bits. The limit is set by a define and could be easily raised in future, as needed.
This implementation choice is aimed at keeping the code churn as limited as possible. For the same reason, only the
virtio_netdriver is reworked to leverage the extended feature space; all other virtio/vhost drivers are unaffected, but could be upgraded to support the extended features space in a later time.The last four patches bring in the actual GSO over UDP tunnel support. As per specification, some additional fields are introduced into the virtio net header to support the new offload. The presence of such fields depends on the negotiated features.
Quoted 7 lines
New helpers are introduced to convert the UDP-tunneled skb metadata to an extended virtio net header and vice versa. Such helpers are used by the tun and
virtio_netdriver to cope with the newly supported offloads.Tested with basic stream transfer with all the possible permutations of host
kernel/qemu/guestkernel with/without GSO over UDP tunnel support.
Hi!
I'm out sick. Hope to get to this next week. Sorry about the delay.
Quoted 54 lines
---
v3 -> v4:
- vnet sockopt cleanup
- fixed offset for UDP-tunnel related field
- usedev->featuresinstead of flags
v3: https://lore.kernel.org/netdev/[email protected]/v2 -> v3:
-uint128_t-> u64[2]
- dropped related ifdef
- define and usevnet_hdrwith tunnel layouts
v2: https://lore.kernel.org/netdev/[email protected]/v1 -> v2:
- fix build failures
- many comment clarification
- changed thevhost_netioctl API
- fixed some hdr <> skb helper bugs
v1: https://lore.kernel.org/netdev/[email protected]/Paolo Abeni (8):
virtio: introduce extended features
virtio_pci_modern: allow configuring extended features
vhost-net: allow configuring extended features
virtio_net: add supports for extended offloads
net: implement virtio helpers to handle UDP GSO tunneling.
virtio_net: enable gso over UDP tunnel support.
tun: enable gso over UDP tunnel support. vhost/net: enable gso over UDP tunnel support.drivers/net/tun.c | 70 +++++++-- drivers/net/tun_vnet.h | 88 +++++++++-- drivers/net/virtio_net.c | 109 +++++++++++--- drivers/vhost/net.c | 95 +++++++++--- drivers/vhost/vhost.c | 2 +- drivers/vhost/vhost.h | 4 +- drivers/virtio/virtio.c | 43 +++--- drivers/virtio/virtio_debug.c | 27 ++-- drivers/virtio/virtio_pci_modern.c | 10 +- drivers/virtio/virtio_pci_modern_dev.c | 69 +++++---- include/linux/virtio.h | 5 +- include/linux/virtio_config.h | 41 +++--- include/linux/virtio_features.h | 88 +++++++++++ include/linux/virtio_net.h | 196 ++++++++++++++++++++++++- include/linux/virtio_pci_modern.h | 43 +++++- include/uapi/linux/if_tun.h | 9 ++ include/uapi/linux/vhost.h | 7 + include/uapi/linux/vhost_types.h | 5 + include/uapi/linux/virtio_net.h | 33 +++++ 19 files changed, 780 insertions(+), 164 deletions(-) create mode 100644 include/linux/virtio_features.hSignature · 1 line
2.49.0
Re: [PATCH v4 net-next 1/8] virtio: introduce extended features
In reply to Paolo AbeniQuoted 65 lines · Paolo Abeni
The virtio specifications allows for up to 128 bits for the device features. Soon we are going to use some of the 'extended' bits features (above 64) for the
virtio_netdriver.Introduce extended features as a fixed size array of u64. To minimize the diffstat allows legacy driver to access the low 64 bits via a transparent union.
Introduce an extended
get_extended_featuresconfiguration callback that devices supporting the extended features range must implement in place of the traditional one.Note that legacy and transport features don't need any change, as they are always in the low 64 bit range.
Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- moved bit sanity check in virtio_features_*
- replacedBUG_ONwithWARN_ON_ONCE
-*_and_not->_andnot
- short circuit features comparison
v2 -> v3:
-uint128_t-> u64[2];
v1 -> v2:
- let u64VIRTIO_BIT()cope with higher bit values
- add .get_features128 instead of changing .get_features signature--- drivers/virtio/virtio.c | 43 +++++++++------- drivers/virtio/virtio_debug.c | 27 +++++----- include/linux/virtio.h | 5 +- include/linux/virtio_config.h | 41 +++++++-------- include/linux/virtio_features.h | 88 +++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 53 deletions(-) create mode 100644 include/linux/virtio_features.hdrivers/virtio/virtio.c +3
-4@@ -53,7 +53,7 @@ static ssize_t features_show(struct device *_d,/* We actually represent this as a bitstring, as it could be * arbitrary length in future. */ -for (i =0 ; i <sizeof (dev->features)*8 ; i++) +for (i =0 ; i <VIRTIO_FEATURES_MAX ; i++) len +=sysfs_emit_at (buf, len,"%c" ,__virtio_test_bit (dev, i) ?'1' :'0' ); len +=sysfs_emit_at (buf, len,"\n" );@@ -272,22 +272,22 @@ static int virtio_dev_probe(struct device *_d)int err, i;struct virtio_device *dev =dev_to_virtio (_d);struct virtio_driver *drv =drv_to_virtio (dev->dev.driver); -u64 device_features; -u64 driver_features; +u64 device_features[VIRTIO_FEATURES_DWORDS ]; +u64 driver_features[VIRTIO_FEATURES_DWORDS ];u64 driver_features_legacy;/* We have a driver! */ virtio_add_status (dev,VIRTIO_CONFIG_S_DRIVER );/* Figure out what features the device supports. */ - device_features = dev->config->get_features (dev);
drivers/virtio/virtio.c static int virtio_dev_probe(struct device *_d) · line 289
Quoted 10 lines
drivers/virtio/virtio.c +3
-3+virtio_get_features (dev, device_features);/* Figure out what features the driver supports. */ - driver_features =0 ; +virtio_features_zero (driver_features);for (i =0 ; i < drv->feature_table_size; i++) {unsigned int f = drv->feature_table[i]; -BUG_ON (f >=64 ); - driver_features |= (1ULL << f); +if (!WARN_ON_ONCE (f >=VIRTIO_FEATURES_MAX ))
Nit: Any reason why switching to use WARN_ON_ONCE()?
Other than this.
Thanks
Re: [PATCH v4 net-next 2/8] virtio_pci_modern: allow configuring extended features
In reply to Paolo AbeniQuoted 102 lines · Paolo Abeni
The virtio specifications allows for up to 128 bits for the device features. Soon we are going to use some of the 'extended' bits features (above 64) for the
virtio_netdriver.Extend the virtio pci modern driver to support configuring the full virtio features range, replacing the unrolled loops reading and writing the features space with explicit one bounded to the actual features space size in word and implementing the
get_extended_featurescallback.Note that in
vp_finalize_features()we only need to cache the lower 64 features bits, to process the transport features.Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- dropped unneeded check invp_modern_get_features()v2 -> v3:
-virtio_features_t-> u64 *v1 -> v2:
- useget_extended_features--- drivers/virtio/virtio_pci_modern.c | 10 ++-- drivers/virtio/virtio_pci_modern_dev.c | 69 +++++++++++++++----------- include/linux/virtio_pci_modern.h | 43 ++++++++++++++-- 3 files changed, 84 insertions(+), 38 deletions(-)drivers/virtio/virtio_pci_modern.c +5
-5@@ -22,11 +22,11 @@#define VIRTIO_AVQ_SGS_MAX 4 -static u64 vp_get_features (struct virtio_device *vdev) +static void vp_get_features (struct virtio_device *vdev,u64 *features) {struct virtio_pci_device *vp_dev =to_vp_device (vdev); -return vp_modern_get_features (&vp_dev->mdev); +vp_modern_get_extended_features (&vp_dev->mdev, features); }static int vp_avq_index (struct virtio_device *vdev,u16 *index,u16 *num)@@ -437,7 +437,7 @@ static int vp_finalize_features(struct virtio_device *vdev)if (vp_check_common_size (vdev))return -EINVAL ; -vp_modern_set_features (&vp_dev->mdev, vdev->features); +vp_modern_set_extended_features (&vp_dev->mdev, vdev->features_array);return 0 ; }@@ -1234,7 +1234,7 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = { .find_vqs = vp_modern_find_vqs, .del_vqs = vp_del_vqs, .synchronize_cbs = vp_synchronize_vectors, - .get_features = vp_get_features, + .get_extended_features = vp_get_features, .finalize_features = vp_finalize_features, .bus_name = vp_bus_name, .set_vq_affinity = vp_set_vq_affinity,@@ -1254,7 +1254,7 @@ static const struct virtio_config_ops virtio_pci_config_ops = { .find_vqs = vp_modern_find_vqs, .del_vqs = vp_del_vqs, .synchronize_cbs = vp_synchronize_vectors, - .get_features = vp_get_features, + .get_extended_features = vp_get_features, .finalize_features = vp_finalize_features, .bus_name = vp_bus_name, .set_vq_affinity = vp_set_vq_affinity,drivers/virtio/virtio_pci_modern_dev.c +9
-4@@ -388,63 +388,74 @@ void vp_modern_remove(struct virtio_pci_modern_device *mdev)EXPORT_SYMBOL_GPL (vp_modern_remove);/* -* vp_modern_get_features - get features from device +* vp_modern_get_extended_features - get features from device * @mdev: the modern virtio-pci device +* @features: the features array to be filled * -* Returns the features read from the device +* Fill the specified features array with the features read from the device */ -u64 vp_modern_get_features (struct virtio_pci_modern_device *mdev) +void vp_modern_get_extended_features (struct virtio_pci_modern_device *mdev, +u64 *features) {struct virtio_pci_common_cfg __iomem *cfg = mdev->common; +int i; -u64 features; +virtio_features_zero (features); +for (i =0 ; i <VIRTIO_FEATURES_WORDS ; i++) { +u64 cur;
drivers/virtio/virtio_pci_modern_dev.c void vp_modern_remove(struct virtio_pci_modern_device *mdev) · line 409
Quoted 10 lines
drivers/virtio/virtio_pci_modern_dev.c +3
-6-vp_iowrite32 (0 , &cfg->device_feature_select); - features =vp_ioread32 (&cfg->device_feature); -vp_iowrite32 (1 , &cfg->device_feature_select); - features |= ((u64 )vp_ioread32 (&cfg->device_feature) <<32 ); - -return features; +vp_iowrite32 (i, &cfg->device_feature_select); + cur =vp_ioread32 (&cfg->device_feature); + features[i >>1 ] |= cur << (32 * (i &1 ));
Nit: why not simply cast features to u32 * then everything is simplified.
Others look good.
Thanks
Re: [PATCH v4 net-next 4/8] virtio_net: add supports for extended offloads
In reply to Paolo AbeniQuoted 9 lines · Paolo Abeni
The
virtio_netdriver needs it to implement GSO over UDP tunnel offload.The only missing piece is mapping them to/from the extended features.
Signed-off-by Paolo Abeni <[email protected]>---
Thanks
Re: [PATCH v4 net-next 5/8] net: implement virtio helpers to handle UDP GSO tunneling.
In reply to Paolo AbeniQuoted 257 lines · Paolo Abeni
The virtio specification are introducing support for GSO over UDP tunnel.
This patch brings in the needed defines and the additional virtio hdr parsing/building helpers.
The UDP tunnel support uses additional fields in the virtio hdr, and such fields location can change depending on other negotiated features - specifically
VIRTIO_NET_F_HASH_REPORT.Try to be as conservative as possible with the new field validation.
Existing implementation for plain GSO offloads allow for invalid/ self-contradictory values of such fields. With GSO over UDP tunnel we can be more strict, with no need to deal with legacy implementation.
Since the checksum-related field validation is asymmetric in the driver and in the device, introduce a separate helper to implement the new checks (to be used only on the driver side).
Note that while the feature space exceeds the 64-bit boundaries, the guest offload space is fixed by the specification of the
VIRTIO_NET_CTRL_GUEST_OFFLOADS_SETcommand to a 64-bit size.Prior to the UDP tunnel GSO support, each guest offload bit corresponded to the feature bit with the same value and vice versa.
Due to the limited 'guest offload' space, relevant features in the high 64 bits are 'mapped' to free bits in the lower range. That is simpler than defining a new command (and associated features) to exchange an extended guest offloads set.
As a consequence, the uAPIs also specify the mapped guest offload value corresponding to the UDP tunnel GSO features.
Signed-off-by Paolo Abeni <[email protected]>--
v3 -> v4:
- fixed offset for UDP GSO tunnel, update accordingly the helpers
- tried to clarifiedvlan_hlensemantic
-virtio_net_chk_data_valid()->virtio_net_handle_csum_offload()v2 -> v3:
- add definitions for possible vnet hdr layouts with tunnel supportv1 -> v2:
- 'relay' -> 'rely' typo
- less unclear comment WRT enforced inner GSO checks
- inner header fields are allowed only with 'modern' virtio, thus are always le
- clarified in the commit message the need for 'mapped features' defines
- assumelittle_endianis true when UDP GSO is enabled.
- fix inner proto type value--- include/linux/virtio_net.h | 196 ++++++++++++++++++++++++++++++-- include/uapi/linux/virtio_net.h | 33 ++++++ 2 files changed, 221 insertions(+), 8 deletions(-)include/linux/virtio_net.h +139
-8@@ -47,9 +47,9 @@ static inline int virtio_net_hdr_set_proto(struct sk_buff *skb,return 0 ; } -static inline int virtio_net_hdr_to_skb (struct sk_buff *skb, -const struct virtio_net_hdr *hdr, -bool little_endian) +static inline int __virtio_net_hdr_to_skb (struct sk_buff *skb, +const struct virtio_net_hdr *hdr, +bool little_endian,u8 hdr_gso_type) {unsigned int nh_min_len =sizeof (struct iphdr );unsigned int gso_type =0 ;@@ -57,8 +57,8 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,unsigned int p_off =0 ;unsigned int ip_proto; -if (hdr->gso_type !=VIRTIO_NET_HDR_GSO_NONE ) { -switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN ) { +if (hdr_gso_type !=VIRTIO_NET_HDR_GSO_NONE ) { +switch (hdr_gso_type & ~VIRTIO_NET_HDR_GSO_ECN ) {case VIRTIO_NET_HDR_GSO_TCPV4 : gso_type =SKB_GSO_TCPV4 ; ip_proto =IPPROTO_TCP ;@@ -84,7 +84,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,return -EINVAL ; } -if (hdr->gso_type &VIRTIO_NET_HDR_GSO_ECN ) +if (hdr_gso_type &VIRTIO_NET_HDR_GSO_ECN ) gso_type |=SKB_GSO_TCP_ECN ;if (hdr->gso_size ==0 )@@ -122,7 +122,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,if (!protocol)virtio_net_hdr_set_proto (skb, hdr); -else if (!virtio_net_hdr_match_proto (protocol, hdr->gso_type)) +else if (!virtio_net_hdr_match_proto (protocol, hdr_gso_type))return -EINVAL ;else skb->protocol = protocol;@@ -153,7 +153,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, } } -if (hdr->gso_type !=VIRTIO_NET_HDR_GSO_NONE ) { +if (hdr_gso_type !=VIRTIO_NET_HDR_GSO_NONE ) {u16 gso_size =__virtio16_to_cpu (little_endian, hdr->gso_size);unsigned int nh_off = p_off;struct skb_shared_info *shinfo =skb_shinfo (skb);@@ -199,6 +199,13 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,return 0 ; } +static inline int virtio_net_hdr_to_skb (struct sk_buff *skb, +const struct virtio_net_hdr *hdr, +bool little_endian) +{ +return __virtio_net_hdr_to_skb (skb, hdr, little_endian, hdr->gso_type); +} + static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, struct virtio_net_hdr *hdr, bool little_endian,@@ -242,4 +249,177 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,return 0 ; } +static inline unsigned int virtio_l3min (bool is_ipv6) +{ +return is_ipv6 ?sizeof (struct ipv6hdr ) :sizeof (struct iphdr ); +} + +static inline int +virtio_net_hdr_tnl_to_skb (struct sk_buff *skb, +const struct virtio_net_hdr_v1_hash_tunnel *vhdr, +bool tnl_hdr_negotiated, +bool tnl_csum_negotiated, +bool little_endian) +{ +const struct virtio_net_hdr *hdr = (const struct virtio_net_hdr *)vhdr; +unsigned int inner_nh, outer_th, inner_th; +unsigned int inner_l3min, outer_l3min; +u8 gso_inner_type, gso_tunnel_type; +bool outer_isv6, inner_isv6; +int ret; + + gso_tunnel_type = hdr->gso_type &VIRTIO_NET_HDR_GSO_UDP_TUNNEL ; +if (!gso_tunnel_type) +return virtio_net_hdr_to_skb (skb, hdr, little_endian); + +/* Tunnel not supported/negotiated, but the hdr asks for it. */ +if (!tnl_hdr_negotiated) +return -EINVAL ; + +/* Either ipv4 or ipv6. */ +if (gso_tunnel_type ==VIRTIO_NET_HDR_GSO_UDP_TUNNEL ) +return -EINVAL ; + +/* The UDP tunnel must carry a GSO packet, but no UFO. */ + gso_inner_type = hdr->gso_type & ~(VIRTIO_NET_HDR_GSO_ECN | +VIRTIO_NET_HDR_GSO_UDP_TUNNEL ); +if (!gso_inner_type || gso_inner_type ==VIRTIO_NET_HDR_GSO_UDP ) +return -EINVAL ; + +/* Rely on csum being present. */ +if (!(hdr->flags &VIRTIO_NET_HDR_F_NEEDS_CSUM )) +return -EINVAL ; + +/* Validate offsets. */ + outer_isv6 = gso_tunnel_type &VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 ; + inner_isv6 = gso_inner_type ==VIRTIO_NET_HDR_GSO_TCPV6 ; + inner_l3min =virtio_l3min (inner_isv6); + outer_l3min =ETH_HLEN +virtio_l3min (outer_isv6); + + inner_th =__virtio16_to_cpu (little_endian, hdr->csum_start); + inner_nh =le16_to_cpu (vhdr->inner_nh_offset); + outer_th =le16_to_cpu (vhdr->outer_th_offset); +if (outer_th < outer_l3min || + inner_nh < outer_th +sizeof (struct udphdr ) || + inner_th < inner_nh + inner_l3min) +return -EINVAL ; + +/* Let the basic parsing deal with plain GSO features. */ + ret =__virtio_net_hdr_to_skb (skb, hdr, true, + hdr->gso_type & ~gso_tunnel_type); +if (ret) +return ret; + +/* In case of USO, the inner protocol is still unknown and +* `inner_isv6` is just a guess, additional parsing is needed. +* The previous validation ensures that accessing an ipv4 inner +* network header is safe. +*/ +if (gso_inner_type ==VIRTIO_NET_HDR_GSO_UDP_L4 ) { +struct iphdr *iphdr = (struct iphdr *)(skb->data + inner_nh); + + inner_isv6 = iphdr->version ==6 ; + inner_l3min =virtio_l3min (inner_isv6); +if (inner_th < inner_nh + inner_l3min) +return -EINVAL ; + } + +skb_set_inner_protocol (skb, inner_isv6 ?htons (ETH_P_IPV6 ) : +htons (ETH_P_IP )); +if (hdr->flags &VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM ) { +if (!tnl_csum_negotiated) +return -EINVAL ; + +skb_shinfo (skb)->gso_type |=SKB_GSO_UDP_TUNNEL_CSUM ; + }else { +skb_shinfo (skb)->gso_type |=SKB_GSO_UDP_TUNNEL ; + } + + skb->inner_transport_header = inner_th +skb_headroom (skb); + skb->inner_network_header = inner_nh +skb_headroom (skb); + skb->inner_mac_header = inner_nh +skb_headroom (skb); + skb->transport_header = outer_th +skb_headroom (skb); + skb->encapsulation =1 ; +return 0 ; +} + +/* Checksum-related fields validation for the driver */ +static inline int virtio_net_handle_csum_offload (struct sk_buff *skb, +struct virtio_net_hdr *hdr, +bool tnl_csum_negotiated) +{ +if (!(hdr->gso_type &VIRTIO_NET_HDR_GSO_UDP_TUNNEL )) { +if (!(hdr->flags &VIRTIO_NET_HDR_F_DATA_VALID )) +return 0 ; + + skb->ip_summed =CHECKSUM_UNNECESSARY ; +if (!(hdr->flags &VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM )) +return 0 ; + +/* tunnel csum packets are invalid when the related +* feature has not been negotiated +*/ +if (!tnl_csum_negotiated) +return -EINVAL ; + skb->csum_level =1 ; +return 0 ; + } + +/* DATA_VALID is mutually exclusive with NEEDS_CSUM, and GSO +* over UDP tunnel requires the latter +*/ +if (hdr->flags &VIRTIO_NET_HDR_F_DATA_VALID ) +return -EINVAL ; +return 0 ; +} +
include/linux/virtio_net.h static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, · line 385
Quoted 10 lines
include/linux/virtio_net.h +10
-0+/* +* vlan_hlen always refers to the outermost MAC header. That also +* means it refers to the only MAC header, if the packet does not carry +* any encapsulation. +*/ +static inline int +virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb, + struct virtio_net_hdr_v1_hash_tunnel *vhdr, + bool tnl_hdr_negotiated, + bool little_endian,
Nit: it looks to me we can just accept netdev_features_t here.
Others look good.
Thanks
Re: [PATCH v4 net-next 6/8] virtio_net: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 18 lines · Paolo Abeni
If the related virtio feature is set, enable transmission and reception of gso over UDP tunnel packets.
Most of the work is done by the previously introduced helper, just need to determine the UDP tunnel features inside the
virtio_net_hdrand update accordingly the virtio net hdr size.Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- tnl header fields at constant offset, update the parsing accordingly
- keep separate status for tnl offload tx and tnl offload rx
- drop redundant commentv2 -> v3:
- drop theVIRTIO_HAS_EXTENDED_FEATURESconditionals
Quoted 6 lines
v1 -> v2:
- test for UDP_TUNNEL_GSO* only on builds with extended features support
- comment indentation cleanup
- rebased on top of virtio helpers changes
- dump more information in case of bad offloads
---
Thanks
Re: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 19 lines · Paolo Abeni
Add new tun features to represent the newly introduced virtio GSO over UDP tunnel offload. Allows detection and selection of such features via the existing TUNSETOFFLOAD ioctl and compute the expected virtio header size and tunnel header offset using the current netdev features, so that we can plug almost seamless the newly introduced virtio helpers to serialize the extended virtio header.
Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- virtio tnl-related fields are at fixed offset, cleanup the code accordingly.
- use netdev features instead of flags bit to check for the configured offload
- drop packet in case of enabled features/configured hdr size mismatch
Quoted 4 lines
v2 -> v3:
- cleaned-up uAPI comments
- use explicit struct layout instead of raw buf.
---
Thanks
Re: [PATCH v4 net-next 8/8] vhost/net: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 10 lines · Paolo Abeni
Vhost net need to know the exact virtio net hdr size to be able to copy such header correctly. Teach it about the newly defined UDP tunnel-related option and update the hdr size computation accordingly.
Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- rebased
Thanks
Paolo Abeni
maintainerRe: [PATCH v4 net-next 1/8] virtio: introduce extended features
In reply to Jason WangQuoted 68 lines · Jason Wang
Quoted 67 lines · Paolo Abeni
The virtio specifications allows for up to 128 bits for the device features. Soon we are going to use some of the 'extended' bits features (above 64) for the
virtio_netdriver.Introduce extended features as a fixed size array of u64. To minimize the diffstat allows legacy driver to access the low 64 bits via a transparent union.
Introduce an extended
get_extended_featuresconfiguration callback that devices supporting the extended features range must implement in place of the traditional one.Note that legacy and transport features don't need any change, as they are always in the low 64 bit range.
Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- moved bit sanity check in virtio_features_*
- replacedBUG_ONwithWARN_ON_ONCE
-*_and_not->_andnot
- short circuit features comparison
v2 -> v3:
-uint128_t-> u64[2];
v1 -> v2:
- let u64VIRTIO_BIT()cope with higher bit values
- add .get_features128 instead of changing .get_features signature--- drivers/virtio/virtio.c | 43 +++++++++------- drivers/virtio/virtio_debug.c | 27 +++++----- include/linux/virtio.h | 5 +- include/linux/virtio_config.h | 41 +++++++-------- include/linux/virtio_features.h | 88 +++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 53 deletions(-) create mode 100644 include/linux/virtio_features.hdrivers/virtio/virtio.c +4
-4@@ -53,7 +53,7 @@ static ssize_t features_show(struct device *_d,/* We actually represent this as a bitstring, as it could be * arbitrary length in future. */ -for (i =0 ; i <sizeof (dev->features)*8 ; i++) +for (i =0 ; i <VIRTIO_FEATURES_MAX ; i++) len +=sysfs_emit_at (buf, len,"%c" ,__virtio_test_bit (dev, i) ?'1' :'0' ); len +=sysfs_emit_at (buf, len,"\n" );@@ -272,22 +272,22 @@ static int virtio_dev_probe(struct device *_d)int err, i;struct virtio_device *dev =dev_to_virtio (_d);struct virtio_driver *drv =drv_to_virtio (dev->dev.driver); -u64 device_features; -u64 driver_features; +u64 device_features[VIRTIO_FEATURES_DWORDS ]; +u64 driver_features[VIRTIO_FEATURES_DWORDS ];u64 driver_features_legacy;/* We have a driver! */ virtio_add_status (dev,VIRTIO_CONFIG_S_DRIVER );/* Figure out what features the device supports. */ - device_features = dev->config->get_features (dev); +virtio_get_features (dev, device_features);
Quoted 10 lines
drivers/virtio/virtio.c static int virtio_dev_probe(struct device *_d) · line 289
Quoted 8 lines
drivers/virtio/virtio.c +2
-3/* Figure out what features the driver supports. */ - driver_features =0 ; +virtio_features_zero (driver_features);for (i =0 ; i < drv->feature_table_size; i++) {unsigned int f = drv->feature_table[i]; -BUG_ON (f >=64 ); - driver_features |= (1ULL << f); +if (!WARN_ON_ONCE (f >=VIRTIO_FEATURES_MAX ))Nit: Any reason why switching to use
WARN_ON_ONCE()?
BUG_ON() are strongly discouraged. Since I touched the nearby code checkpatch urged me to take action ;)
/P
Jakub Kicinski
maintainerRe: [PATCH v4 net-next 1/8] virtio: introduce extended features
In reply to Paolo Abeniinclude/linux/virtio.h struct virtio_device { · line 163
Quoted 2 lines · On Tue, 17 Jun 2025 18:12:08 +0200 Paolo Abeni wrote:
include/linux/virtio.h +1
-1-u64 features; +VIRTIO_DECLARE_FEATURES (features);
FWIW this makes kdoc upset. There is a list of regexps in scripts/kernel-doc.pl which make it understand the declaration macros. I think VIRTIO_DECLARE_FEATURES() needs to be added to that list.
Paolo Abeni
maintainerRe: [PATCH v4 net-next 1/8] virtio: introduce extended features
Quoted 7 lines · Jakub Kicinski
include/linux/virtio.h struct virtio_device { · line 163
Quoted 2 lines · On Tue, 17 Jun 2025 18:12:08 +0200 Paolo Abeni wrote:
include/linux/virtio.h +1
-1-u64 features; +VIRTIO_DECLARE_FEATURES (features);FWIW this makes kdoc upset. There is a list of regexps in
scripts/kernel-doc.plwhich make it understand the declaration macros. I thinkVIRTIO_DECLARE_FEATURES()needs to be added to that list.
I guess the preferred course of action is sharing a v5 including a scripts/kernel-doc.pl patch, right?
Also I'll include a stable reference on a public git tree, so that this could be pulled both into the net-next and virtio tree.
Thanks,
Paolo
Re: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 76 lines · Paolo Abeni
Add new tun features to represent the newly introduced virtio GSO over UDP tunnel offload. Allows detection and selection of such features via the existing TUNSETOFFLOAD ioctl and compute the expected virtio header size and tunnel header offset using the current netdev features, so that we can plug almost seamless the newly introduced virtio helpers to serialize the extended virtio header.
Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- virtio tnl-related fields are at fixed offset, cleanup the code accordingly.
- use netdev features instead of flags bit to check for the configured offload
- drop packet in case of enabled features/configured hdr size mismatchv2 -> v3:
- cleaned-up uAPI comments
- use explicit struct layout instead of raw buf.--- drivers/net/tun.c | 70 ++++++++++++++++++++++++----- drivers/net/tun_vnet.h | 88 +++++++++++++++++++++++++++++++++---- include/uapi/linux/if_tun.h | 9 ++++ 3 files changed, 148 insertions(+), 19 deletions(-)drivers/net/tun.c +14
-2@@ -186,7 +186,8 @@ struct tun_struct {struct net_device *dev;netdev_features_t set_features;#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \ - NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4) + NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4 | \ + NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM)int align;int vnet_hdr_sz;@@ -925,6 +926,7 @@ static int tun_net_init(struct net_device *dev) dev->hw_features =NETIF_F_SG |NETIF_F_FRAGLIST |TUN_USER_FEATURES |NETIF_F_HW_VLAN_CTAG_TX |NETIF_F_HW_VLAN_STAG_TX ; + dev->hw_enc_features = dev->hw_features; dev->features = dev->hw_features; dev->vlan_features = dev->features & ~(NETIF_F_HW_VLAN_CTAG_TX |@@ -1698,7 +1700,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,struct sk_buff *skb;size_t total_len =iov_iter_count (from);size_t len = total_len, align = tun->align, linear; -struct virtio_net_hdr gso = {0 }; +struct virtio_net_hdr_v1_hash_tunnel hdr; +struct virtio_net_hdr *gso;int good_linear;int copylen;int hdr_len =0 ;@@ -1708,6 +1711,15 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,int skb_xdp =1 ;bool frags =tun_napi_frags_enabled (tfile);enum skb_drop_reason drop_reason =SKB_DROP_REASON_NOT_SPECIFIED ; +netdev_features_t features =0 ; + +/* +* Keep it easy and always zero the whole buffer, even if the +* tunnel-related field will be touched only when the feature +* is enabled and the hdr size id compatible. +*/ +memset (&hdr,0 ,sizeof (hdr)); + gso = (struct virtio_net_hdr *)&hdr;if (!(tun->flags &IFF_NO_PI )) {if (len <sizeof (pi))
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1738
Quoted 7 lines
drivers/net/tun.c +3
-1@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz); - hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;
I think you should use tun->set_features instead of tun->vnet_hdr_sz to tell if these features are enabled.
Quoted 107 lines
drivers/net/tun.c +43
-6+ + hdr_len =__tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, + features, from, gso);if (hdr_len <0 )return hdr_len;@@ -1755,7 +1772,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,* (e.g gso or jumbo packet), we will do it at after * skb was created with generic XDP routine. */ - skb =tun_build_skb (tun, tfile, from, &gso, len, &skb_xdp); + skb =tun_build_skb (tun, tfile, from, gso, len, &skb_xdp); err =PTR_ERR_OR_ZERO (skb);if (err) goto drop;@@ -1799,7 +1816,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } } -if (tun_vnet_hdr_to_skb (tun->flags, skb, &gso)) { +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, &hdr)) {atomic_long_inc (&tun->rx_frame_errors); err = -EINVAL ; goto free_skb;@@ -2050,13 +2067,21 @@ static ssize_t tun_put_user(struct tun_struct *tun, }if (vnet_hdr_sz) { -struct virtio_net_hdr gso; +struct virtio_net_hdr_v1_hash_tunnel hdr; +struct virtio_net_hdr *gso; - ret =tun_vnet_hdr_from_skb (tun->flags, tun->dev, skb, &gso); + ret =tun_vnet_hdr_tnl_from_skb (tun->flags, tun->dev, skb, + &hdr);if (ret)return ret; - ret =tun_vnet_hdr_put (vnet_hdr_sz, iter, &gso); +/* +* Drop the packet if the configured header size is too small +* WRT the enabled offloads. +*/ + gso = (struct virtio_net_hdr *)&hdr; + ret =__tun_vnet_hdr_put (vnet_hdr_sz, tun->dev->features, + iter, gso);if (ret)return ret; }@@ -2357,7 +2382,9 @@ static int tun_xdp_one(struct tun_struct *tun, {unsigned int datasize = xdp->data_end - xdp->data;struct tun_xdp_hdr *hdr = xdp->data_hard_start; +struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr;struct virtio_net_hdr *gso = &hdr->gso; +netdev_features_t features =0 ;struct bpf_prog *xdp_prog;struct sk_buff *skb =NULL ;struct sk_buff_head *queue;@@ -2426,7 +2453,17 @@ static int tun_xdp_one(struct tun_struct *tun,if (metasize >0 )skb_metadata_set (skb, metasize); -if (tun_vnet_hdr_to_skb (tun->flags, skb, gso)) { +/* +* Assume tunnel offloads are enabled if the received hdr is large +* enough. +*/ +if (READ_ONCE (tun->vnet_hdr_sz) >=TUN_VNET_TNL_SIZE && + xdp->data - xdp->data_hard_start >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ; + + tnl_hdr = (struct virtio_net_hdr_v1_hash_tunnel *)gso; +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, tnl_hdr)) {atomic_long_inc (&tun->rx_frame_errors);kfree_skb (skb); ret = -EINVAL ;@@ -2812,6 +2849,8 @@ static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) } +#define PLAIN_GSO (NETIF_F_GSO_UDP_L4 | NETIF_F_TSO | NETIF_F_TSO6) +/* This is like a cut-down ethtool ops, except done via tun fd so no * privs required. */ static int set_offload (struct tun_struct *tun,unsigned long arg)@@ -2841,6 +2880,18 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) features |=NETIF_F_GSO_UDP_L4 ; arg &= ~(TUN_F_USO4 |TUN_F_USO6 ); } + +/* +* Tunnel offload is allowed only if some plain offload is +* available, too. +*/ +if (features &PLAIN_GSO && arg &TUN_F_UDP_TUNNEL_GSO ) { + features |=NETIF_F_GSO_UDP_TUNNEL ; +if (arg &TUN_F_UDP_TUNNEL_GSO_CSUM ) + features |=NETIF_F_GSO_UDP_TUNNEL_CSUM ; + arg &= ~(TUN_F_UDP_TUNNEL_GSO | +TUN_F_UDP_TUNNEL_GSO_CSUM ); + } }/* This gives the user a way to test for new features in future by
drivers/net/tun.c static int set_offload(struct tun_struct *tun, unsigned long arg) · line 2855
Quoted 4 lines
drivers/net/tun.c +0
-1@@ -2852,7 +2903,6 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) tun->dev->wanted_features &= ~TUN_USER_FEATURES ; tun->dev->wanted_features |= features;netdev_update_features (tun->dev); -
This line deletion is irrlevant.
Quoted 168 lines
drivers/net/tun.c +0
-0return 0 ; }drivers/net/tun_vnet.h +79
-9@@ -6,6 +6,8 @@#define TUN_VNET_LE 0x80000000#define TUN_VNET_BE 0x40000000 +#define TUN_VNET_TNL_SIZE sizeof(struct virtio_net_hdr_v1_hash_tunnel) +static inline bool tun_vnet_legacy_is_little_endian (unsigned int flags) { bool be = IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE) &&@@ -107,16 +109,26 @@ static inline long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags, } } -static inline int tun_vnet_hdr_get (int sz,unsigned int flags, -struct iov_iter *from, -struct virtio_net_hdr *hdr) +static inline unsigned int tun_vnet_parse_size (netdev_features_t features) +{ +if (!(features &NETIF_F_GSO_UDP_TUNNEL )) +return sizeof (struct virtio_net_hdr ); + +return TUN_VNET_TNL_SIZE ; +} + +static inline int __tun_vnet_hdr_get (int sz,unsigned int flags, +netdev_features_t features, +struct iov_iter *from, +struct virtio_net_hdr *hdr) { +unsigned int parsed_size =tun_vnet_parse_size (features);u16 hdr_len;if (iov_iter_count (from) < sz)return -EINVAL ; -if (!copy_from_iter_full (hdr,sizeof (*hdr), from)) +if (!copy_from_iter_full (hdr, parsed_size, from))return -EFAULT ; hdr_len =tun_vnet16_to_cpu (flags, hdr->hdr_len);@@ -129,32 +141,59 @@ static inline int tun_vnet_hdr_get(int sz, unsigned int flags,if (hdr_len >iov_iter_count (from))return -EINVAL ; -iov_iter_advance (from, sz -sizeof (*hdr)); +iov_iter_advance (from, sz - parsed_size);return hdr_len; } -static inline int tun_vnet_hdr_put (int sz,struct iov_iter *iter, -const struct virtio_net_hdr *hdr) +static inline int tun_vnet_hdr_get (int sz,unsigned int flags, +struct iov_iter *from, +struct virtio_net_hdr *hdr) +{ +return __tun_vnet_hdr_get (sz, flags,0 , from, hdr); +} + +static inline int __tun_vnet_hdr_put (int sz,netdev_features_t features, +struct iov_iter *iter, +const struct virtio_net_hdr *hdr) { +unsigned int parsed_size =tun_vnet_parse_size (features); +if (unlikely (iov_iter_count (iter) < sz))return -EINVAL ; -if (unlikely (copy_to_iter (hdr,sizeof (*hdr), iter) !=sizeof (*hdr))) +if (unlikely (copy_to_iter (hdr, parsed_size, iter) != parsed_size))return -EFAULT ; -if (iov_iter_zero (sz -sizeof (*hdr), iter) != sz -sizeof (*hdr)) +if (iov_iter_zero (sz - parsed_size, iter) != sz - parsed_size)return -EFAULT ;return 0 ; } +static inline int tun_vnet_hdr_put (int sz,struct iov_iter *iter, +const struct virtio_net_hdr *hdr) +{ +return __tun_vnet_hdr_put (sz,0 , iter, hdr); +} +static inline int tun_vnet_hdr_to_skb (unsigned int flags,struct sk_buff *skb,const struct virtio_net_hdr *hdr) {return virtio_net_hdr_to_skb (skb, hdr,tun_vnet_is_little_endian (flags)); } +static inline int +tun_vnet_hdr_tnl_to_skb (unsigned int flags,netdev_features_t features, +struct sk_buff *skb, +const struct virtio_net_hdr_v1_hash_tunnel *hdr) +{ +return virtio_net_hdr_tnl_to_skb (skb, hdr, + !!(features &NETIF_F_GSO_UDP_TUNNEL ), + !!(features &NETIF_F_GSO_UDP_TUNNEL_CSUM ), +tun_vnet_is_little_endian (flags)); +} + static inline int tun_vnet_hdr_from_skb(unsigned int flags, const struct net_device *dev, const struct sk_buff *skb,@@ -183,4 +222,35 @@ static inline int tun_vnet_hdr_from_skb(unsigned int flags,return 0 ; } +static inline int +tun_vnet_hdr_tnl_from_skb (unsigned int flags, +const struct net_device *dev, +const struct sk_buff *skb, +struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr) +{ +bool has_tnl_offload = !!(dev->features &NETIF_F_GSO_UDP_TUNNEL ); +int vlan_hlen =skb_vlan_tag_present (skb) ?VLAN_HLEN :0 ; + +if (virtio_net_hdr_tnl_from_skb (skb, tnl_hdr, has_tnl_offload, +tun_vnet_is_little_endian (flags), + vlan_hlen)) { +struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr; +struct skb_shared_info *sinfo =skb_shinfo (skb); + +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)); +print_hex_dump (KERN_ERR ,"tun: " , +DUMP_PREFIX_NONE , +16 ,1 , skb->head, +min (tun_vnet16_to_cpu (flags, hdr->hdr_len),64 ), true); + } +WARN_ON_ONCE (1 ); +return -EINVAL ; + } + +return 0 ; +} +#endif /* TUN_VNET_H */ include/uapi/linux/if_tun.h +9
-0@@ -93,6 +93,15 @@#define TUN_F_USO4 0x20/* I can handle USO for IPv4 packets */ #define TUN_F_USO6 0x40/* I can handle USO for IPv6 packets */ +/* I can handle TSO/USO for UDP tunneled packets */ +#define TUN_F_UDP_TUNNEL_GSO 0x080 + +/* +* I can handle TSO/USO for UDP tunneled packets requiring csum offload for +* the outer header +*/ +#define TUN_F_UDP_TUNNEL_GSO_CSUM 0x100 +/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ #define TUN_PKT_STRIP 0x0001struct tun_pi {
Paolo Abeni
maintainerRe: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
Quoted 5 lines · Akihiko Odaki
Quoted 4 lines · Paolo Abeni
drivers/net/tun.c +0
-0@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz);
Quoted 7 lines
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1738
Quoted 4 lines
drivers/net/tun.c +3
-1- hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;I think you should use
tun->set_featuresinstead oftun->vnet_hdr_szto tell if these features are enabled.
This is the guest -> host direction. tun->set_features refers to the opposite one. The problem is that tun is not aware of the features negotiated in the guest -> host direction.
The current status (for baremetal/plain offload) is allowing any known feature the other side send - if the virtio header is consistent. This code follows a similar schema.
Note that using 'tun->set_features' instead of 'vnet_hdr_sz' the tun driver will drop all the (legit) GSO over UDP packet sent by the guest when the VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO has been negotiated and VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO has not.
/P
Paolo Abeni
maintainerRe: [PATCH v4 net-next 2/8] virtio_pci_modern: allow configuring extended features
In reply to Jason WangQuoted 22 lines · Jason Wang
Quoted 21 lines · Paolo Abeni
/*
- *vp_modern_get_features- get features from device
+ *vp_modern_get_extended_features- get features from device
* @mdev: the modern virtio-pci device
+ * @features: the features array to be filled
*
- * Returns the features read from the device
+ * Fill the specified features array with the features read from the device */drivers/virtio/virtio_pci_modern_dev.c +6
-2-u64 vp_modern_get_features (struct virtio_pci_modern_device *mdev) +void vp_modern_get_extended_features (struct virtio_pci_modern_device *mdev, +u64 *features) {struct virtio_pci_common_cfg __iomem *cfg = mdev->common; +int i; -u64 features; +virtio_features_zero (features); +for (i =0 ; i <VIRTIO_FEATURES_WORDS ; i++) { +u64 cur;
Quoted 11 lines
drivers/virtio/virtio_pci_modern_dev.c void vp_modern_remove(struct virtio_pci_modern_device *mdev) · line 409
Quoted 9 lines
drivers/virtio/virtio_pci_modern_dev.c +3
-6-vp_iowrite32 (0 , &cfg->device_feature_select); - features =vp_ioread32 (&cfg->device_feature); -vp_iowrite32 (1 , &cfg->device_feature_select); - features |= ((u64 )vp_ioread32 (&cfg->device_feature) <<32 ); - -return features; +vp_iowrite32 (i, &cfg->device_feature_select); + cur =vp_ioread32 (&cfg->device_feature); + features[i >>1 ] |= cur << (32 * (i &1 ));Nit: why not simply cast features to u32 * then everything is simplified.
I can be (and usually I am) low on coffee, but I think that will work only for BE architectures.
/P
Paolo Abeni
maintainerRe: [PATCH v4 net-next 5/8] net: implement virtio helpers to handle UDP GSO tunneling.
In reply to Jason WangQuoted 13 lines · Jason Wang
include/linux/virtio_net.h static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, · line 385
Quoted 10 lines · Paolo Abeni
include/linux/virtio_net.h +10
-0+/* +* vlan_hlen always refers to the outermost MAC header. That also +* means it refers to the only MAC header, if the packet does not carry +* any encapsulation. +*/ +static inline int +virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb, + struct virtio_net_hdr_v1_hash_tunnel *vhdr, + bool tnl_hdr_negotiated, + bool little_endian,Nit: it looks to me we can just accept
netdev_features_there.
That will replace a single bool argument with a netdev_features_t, right?
I guess we can do that when it will allow reducing the arguments number.
Thanks,
Paolo
Re: [PATCH v4 net-next 3/8] vhost-net: allow configuring extended features
In reply to Paolo AbeniQuoted 156 lines · Paolo Abeni
Use the extended feature type for '
acked_features' and implement two new ioctls operation allowing the user-space to set/query an unbounded amount of features.The actual number of processed features is limited by
VIRTIO_FEATURES_MAXand attempts to set features above such limit fail with EOPNOTSUPP.Note that: the legacy ioctls implicitly truncate the negotiated features to the lower 64 bits range and the '
acked_backend_features' field don't need conversion, as the only negotiated feature there is in the low 64 bit range.Acked-by Jason Wang <[email protected]>Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- use a single static (lower case) constant instead of enum and old def
- simplerVHOST_GET_FEATURES_ARRAYimpl
- avoid usingvirtio_features_and_notv2 -> v3:
-virtio_features_t-> u64[2]
- add__counted_byannotation tovhost_features_arrayv1 -> v2:
- change the ioctl to use an extensible API--- drivers/vhost/net.c | 87 ++++++++++++++++++++++++-------- drivers/vhost/vhost.c | 2 +- drivers/vhost/vhost.h | 4 +- include/uapi/linux/vhost.h | 7 +++ include/uapi/linux/vhost_types.h | 5 ++ 5 files changed, 82 insertions(+), 23 deletions(-)drivers/vhost/net.c +41
-20@@ -69,12 +69,12 @@ MODULE_PARM_DESC(experimental_zcopytx, "Enable Zero Copy TX;"#define VHOST_DMA_IS_DONE (len) ((__force u32)(len) >= (__force u32)VHOST_DMA_DONE_LEN) -enum { -VHOST_NET_FEATURES =VHOST_FEATURES | - (1ULL <<VHOST_NET_F_VIRTIO_NET_HDR ) | - (1ULL <<VIRTIO_NET_F_MRG_RXBUF ) | - (1ULL <<VIRTIO_F_ACCESS_PLATFORM ) | - (1ULL <<VIRTIO_F_RING_RESET ) +static const u64 vhost_net_features[VIRTIO_FEATURES_DWORDS ] = { +VHOST_FEATURES | + (1ULL <<VHOST_NET_F_VIRTIO_NET_HDR ) | + (1ULL <<VIRTIO_NET_F_MRG_RXBUF ) | + (1ULL <<VIRTIO_F_ACCESS_PLATFORM ) | + (1ULL <<VIRTIO_F_RING_RESET ), };enum {@@ -1614,16 +1614,17 @@ static long vhost_net_reset_owner(struct vhost_net *n)return err; } -static int vhost_net_set_features (struct vhost_net *n,u64 features) +static int vhost_net_set_features (struct vhost_net *n,const u64 *features) {size_t vhost_hlen, sock_hlen, hdr_len;int i; - hdr_len = (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 & (1 <<VHOST_NET_F_VIRTIO_NET_HDR )) { + hdr_len =virtio_features_test_bit (features,VIRTIO_NET_F_MRG_RXBUF ) || +virtio_features_test_bit (features,VIRTIO_F_VERSION_1 ) ? +sizeof (struct virtio_net_hdr_mrg_rxbuf ) : +sizeof (struct virtio_net_hdr ); + +if (virtio_features_test_bit (features,VHOST_NET_F_VIRTIO_NET_HDR )) {/* vhost provides vnet_hdr */ vhost_hlen = hdr_len; sock_hlen =0 ;@@ -1633,18 +1634,19 @@ static int vhost_net_set_features(struct vhost_net *n, u64 features) sock_hlen = hdr_len; }mutex_lock (&n->dev.mutex); -if ((features & (1 <<VHOST_F_LOG_ALL )) && +if (virtio_features_test_bit (features,VHOST_F_LOG_ALL ) && !vhost_log_access_ok (&n->dev)) goto out_unlock; -if ((features & (1ULL <<VIRTIO_F_ACCESS_PLATFORM ))) { +if (virtio_features_test_bit (features,VIRTIO_F_ACCESS_PLATFORM )) {if (vhost_init_device_iotlb (&n->dev)) goto out_unlock; }for (i =0 ; i <VHOST_NET_VQ_MAX ; ++i) {mutex_lock (&n->vqs[i].vq.mutex); - n->vqs[i].vq.acked_features = features; +virtio_features_copy (n->vqs[i].vq.acked_features_array, + features); n->vqs[i].vhost_hlen = vhost_hlen; n->vqs[i].sock_hlen = sock_hlen;mutex_unlock (&n->vqs[i].vq.mutex);@@ -1681,12 +1683,13 @@ static long vhost_net_set_owner(struct vhost_net *n)static long vhost_net_ioctl (struct file *f,unsigned int ioctl,unsigned long arg) { +u64 all_features[VIRTIO_FEATURES_DWORDS ];struct vhost_net *n = f->private_data;void __user *argp = (void __user *)arg;u64 __user *featurep = argp;struct vhost_vring_file backend; -u64 features; -int r; +u64 features, count, copied; +int r, i;switch (ioctl) {case VHOST_NET_SET_BACKEND :@@ -1694,16 +1697,60 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,return -EFAULT ;return vhost_net_set_backend (n, backend.index, backend.fd);case VHOST_GET_FEATURES : - features =VHOST_NET_FEATURES ; + features = vhost_net_features[0 ];if (copy_to_user (featurep, &features,sizeof features))return -EFAULT ;return 0 ;case VHOST_SET_FEATURES :if (copy_from_user (&features, featurep,sizeof features))return -EFAULT ; -if (features & ~VHOST_NET_FEATURES ) +if (features & ~vhost_net_features[0 ])return -EOPNOTSUPP ; -return vhost_net_set_features (n, features); + +virtio_features_from_u64 (all_features, features); +return vhost_net_set_features (n, all_features); +case VHOST_GET_FEATURES_ARRAY : +if (copy_from_user (&count, argp,sizeof (u64))) +return -EFAULT ; + +/* Copy the net features, up to the user-provided buffer size */ + argp +=sizeof (u64); + copied =min (count,VIRTIO_FEATURES_DWORDS ); +if (copy_to_user (argp, vhost_net_features, + copied *sizeof (u64))) +return -EFAULT ; + +/* Zero the trailing space provided by user-space, if any */ +if (clear_user (argp, (count - copied) *sizeof (u64))) +return -EFAULT ; +return 0 ; +case VHOST_SET_FEATURES_ARRAY :
drivers/vhost/net.c static long vhost_net_ioctl(struct file *f, unsigned int ioctl, · line 1737
Quoted 10 lines
drivers/vhost/net.c +10
-0+if (copy_from_user (&count, argp,sizeof (u64))) +return -EFAULT ; + +virtio_features_zero (all_features); +for (i =0 ; i <min (count,VIRTIO_FEATURES_DWORDS ); ++i) { + argp +=sizeof (u64); +if (copy_from_user (&all_features[i], argp, +sizeof (u64))) +return -EFAULT ; + }
This for loop can be converted into a single copy_from_user() call as done for VHOST_GET_FEATURES_ARRAY/copy_to_user().
drivers/vhost/net.c static long vhost_net_ioctl(struct file *f, unsigned int ioctl, · line 1743
Quoted 6 lines
drivers/vhost/net.c +6
-0+ +/* Any feature specified by user-space above VIRTIO_FEATURES_MAX is +* not supported by definition. +*/ +for (; i < count; ++i) { +if (copy_from_user (&features, argp,sizeof (u64)))
get_user() is a simpler alternative.
drivers/vhost/net.c static long vhost_net_ioctl(struct file *f, unsigned int ioctl, · line 1749
Quoted 6 lines
drivers/vhost/net.c +6
-0+return -EFAULT ; +if (features) +return -EOPNOTSUPP ; + } + +for (i =1 ; i <VIRTIO_FEATURES_DWORDS ; i++)
i should be initialized with 0 to check the first element as done for VHOST_SET_FEATURES.
Quoted 74 lines
drivers/vhost/net.c +4
-0+if (all_features[i] & ~vhost_net_features[i]) +return -EOPNOTSUPP ; + +return vhost_net_set_features (n, all_features);case VHOST_GET_BACKEND_FEATURES : features =VHOST_NET_BACKEND_FEATURES ;if (copy_to_user (featurep, &features,sizeof (features)))drivers/vhost/vhost.c +1
-1@@ -372,7 +372,7 @@ static void vhost_vq_reset(struct vhost_dev *dev, vq->log_used = false; vq->log_addr =-1ull ; vq->private_data =NULL ; - vq->acked_features =0 ; +virtio_features_zero (vq->acked_features_array); vq->acked_backend_features =0 ; vq->log_base =NULL ; vq->error_ctx =NULL ;drivers/vhost/vhost.h +2
-2@@ -133,7 +133,7 @@ struct vhost_virtqueue {struct vhost_iotlb *umem;struct vhost_iotlb *iotlb;void *private_data; -u64 acked_features; +VIRTIO_DECLARE_FEATURES (acked_features);u64 acked_backend_features;/* Log write descriptors */ void __user *log_base;@@ -291,7 +291,7 @@ static inline void *vhost_vq_get_backend(struct vhost_virtqueue *vq)static inline bool vhost_has_feature (struct vhost_virtqueue *vq,int bit) { -return vq->acked_features & (1ULL << bit); +return virtio_features_test_bit (vq->acked_features_array, bit); }static inline bool vhost_backend_has_feature (struct vhost_virtqueue *vq,int bit)include/uapi/linux/vhost.h +7
-0@@ -235,4 +235,11 @@*/ #define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x82, \ struct vhost_vring_state) + +/* Extended features manipulation */ +#define VHOST_GET_FEATURES_ARRAY _IOR(VHOST_VIRTIO, 0x83, \ + struct vhost_features_array) +#define VHOST_SET_FEATURES_ARRAY _IOW(VHOST_VIRTIO, 0x83, \ + struct vhost_features_array) +#endif include/uapi/linux/vhost_types.h +5
-0@@ -110,6 +110,11 @@ struct vhost_msg_v2 { }; }; +struct vhost_features_array { +__u64 count;/* number of entries present in features array */ + __u64 features[] __counted_by(count); +}; +struct vhost_memory_region {__u64 guest_phys_addr;__u64 memory_size;/* bytes */
Jakub Kicinski
maintainerRe: [PATCH v4 net-next 1/8] virtio: introduce extended features
In reply to Paolo AbeniQuoted 11 lines · On Thu, 19 Jun 2025 16:36:57 +0200 Paolo Abeni wrote:
Quoted 7 lines · Jakub Kicinski
include/linux/virtio.h struct virtio_device { · line 163
Quoted 2 lines · On Tue, 17 Jun 2025 18:12:08 +0200 Paolo Abeni wrote:
include/linux/virtio.h +1
-1-u64 features; +VIRTIO_DECLARE_FEATURES (features);FWIW this makes kdoc upset. There is a list of regexps in
scripts/kernel-doc.plwhich make it understand the declaration macros. I thinkVIRTIO_DECLARE_FEATURES()needs to be added to that list.I guess the preferred course of action is sharing a v5 including a
scripts/kernel-doc.plpatch, right?
Not a hard ask, but yes that'd be preferable. Unfortunately kdoc build issues gate passing the patches to selftest stage as they often cause htmldoc build issues as well.
Re: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 18 lines · Paolo Abeni
Quoted 5 lines · Akihiko Odaki
Quoted 4 lines · Paolo Abeni
drivers/net/tun.c +0
-0@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz);Quoted 7 lines
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1738
Quoted 4 lines
drivers/net/tun.c +3
-1- hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;I think you should use
tun->set_featuresinstead oftun->vnet_hdr_szto tell if these features are enabled.This is the guest -> host direction.
tun->set_featuresrefers to the opposite one. The problem is that tun is not aware of the features negotiated in the guest -> host direction.
Quoted 8 lines
The current status (for baremetal/plain offload) is allowing any known feature the other side send - if the virtio header is consistent. This code follows a similar schema.
Note that using '
tun->set_features' instead of 'vnet_hdr_sz' the tun driver will drop all the (legit) GSO over UDP packet sent by the guest when theVIRTIO_NET_F_HOST_UDP_TUNNEL_GSOhas been negotiated andVIRTIO_NET_F_GUEST_UDP_TUNNEL_GSOhas not.
This explanation makes sense. In that case I suggest:
- creating a new function named tun_vnet_hdr_tnl_get() and
- passing vnet_hdr_sz to tun_vnet_hdr_tnl_to_skb()
tun_vnet.h contains the virtio-related logic for better code organization and reuse with tap.c. tap.c can reuse the conditionals on vnet_hdr_sz when tap.c gains the UDP tunneling support.
Re: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 145 lines · Paolo Abeni
Add new tun features to represent the newly introduced virtio GSO over UDP tunnel offload. Allows detection and selection of such features via the existing TUNSETOFFLOAD ioctl and compute the expected virtio header size and tunnel header offset using the current netdev features, so that we can plug almost seamless the newly introduced virtio helpers to serialize the extended virtio header.
Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- virtio tnl-related fields are at fixed offset, cleanup the code accordingly.
- use netdev features instead of flags bit to check for the configured offload
- drop packet in case of enabled features/configured hdr size mismatchv2 -> v3:
- cleaned-up uAPI comments
- use explicit struct layout instead of raw buf.--- drivers/net/tun.c | 70 ++++++++++++++++++++++++----- drivers/net/tun_vnet.h | 88 +++++++++++++++++++++++++++++++++---- include/uapi/linux/if_tun.h | 9 ++++ 3 files changed, 148 insertions(+), 19 deletions(-)drivers/net/tun.c +35
-8@@ -186,7 +186,8 @@ struct tun_struct {struct net_device *dev;netdev_features_t set_features;#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \ - NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4) + NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4 | \ + NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM)int align;int vnet_hdr_sz;@@ -925,6 +926,7 @@ static int tun_net_init(struct net_device *dev) dev->hw_features =NETIF_F_SG |NETIF_F_FRAGLIST |TUN_USER_FEATURES |NETIF_F_HW_VLAN_CTAG_TX |NETIF_F_HW_VLAN_STAG_TX ; + dev->hw_enc_features = dev->hw_features; dev->features = dev->hw_features; dev->vlan_features = dev->features & ~(NETIF_F_HW_VLAN_CTAG_TX |@@ -1698,7 +1700,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,struct sk_buff *skb;size_t total_len =iov_iter_count (from);size_t len = total_len, align = tun->align, linear; -struct virtio_net_hdr gso = {0 }; +struct virtio_net_hdr_v1_hash_tunnel hdr; +struct virtio_net_hdr *gso;int good_linear;int copylen;int hdr_len =0 ;@@ -1708,6 +1711,15 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,int skb_xdp =1 ;bool frags =tun_napi_frags_enabled (tfile);enum skb_drop_reason drop_reason =SKB_DROP_REASON_NOT_SPECIFIED ; +netdev_features_t features =0 ; + +/* +* Keep it easy and always zero the whole buffer, even if the +* tunnel-related field will be touched only when the feature +* is enabled and the hdr size id compatible. +*/ +memset (&hdr,0 ,sizeof (hdr)); + gso = (struct virtio_net_hdr *)&hdr;if (!(tun->flags &IFF_NO_PI )) {if (len <sizeof (pi))@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz); - hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ; + + hdr_len =__tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, + features, from, gso);if (hdr_len <0 )return hdr_len;@@ -1755,7 +1772,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,* (e.g gso or jumbo packet), we will do it at after * skb was created with generic XDP routine. */ - skb =tun_build_skb (tun, tfile, from, &gso, len, &skb_xdp); + skb =tun_build_skb (tun, tfile, from, gso, len, &skb_xdp); err =PTR_ERR_OR_ZERO (skb);if (err) goto drop;@@ -1799,7 +1816,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } } -if (tun_vnet_hdr_to_skb (tun->flags, skb, &gso)) { +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, &hdr)) {atomic_long_inc (&tun->rx_frame_errors); err = -EINVAL ; goto free_skb;@@ -2050,13 +2067,21 @@ static ssize_t tun_put_user(struct tun_struct *tun, }if (vnet_hdr_sz) { -struct virtio_net_hdr gso; +struct virtio_net_hdr_v1_hash_tunnel hdr; +struct virtio_net_hdr *gso; - ret =tun_vnet_hdr_from_skb (tun->flags, tun->dev, skb, &gso); + ret =tun_vnet_hdr_tnl_from_skb (tun->flags, tun->dev, skb, + &hdr);if (ret)return ret; - ret =tun_vnet_hdr_put (vnet_hdr_sz, iter, &gso); +/* +* Drop the packet if the configured header size is too small +* WRT the enabled offloads. +*/ + gso = (struct virtio_net_hdr *)&hdr; + ret =__tun_vnet_hdr_put (vnet_hdr_sz, tun->dev->features, + iter, gso);if (ret)return ret; }@@ -2357,7 +2382,9 @@ static int tun_xdp_one(struct tun_struct *tun, {unsigned int datasize = xdp->data_end - xdp->data;struct tun_xdp_hdr *hdr = xdp->data_hard_start; +struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr;struct virtio_net_hdr *gso = &hdr->gso; +netdev_features_t features =0 ;struct bpf_prog *xdp_prog;struct sk_buff *skb =NULL ;struct sk_buff_head *queue;@@ -2426,7 +2453,17 @@ static int tun_xdp_one(struct tun_struct *tun,if (metasize >0 )skb_metadata_set (skb, metasize);
drivers/net/tun.c static int tun_xdp_one(struct tun_struct *tun, · line 2463
Quoted 10 lines
drivers/net/tun.c +8
-1-if (tun_vnet_hdr_to_skb (tun->flags, skb, gso)) { +/* +* Assume tunnel offloads are enabled if the received hdr is large +* enough. +*/ +if (READ_ONCE (tun->vnet_hdr_sz) >=TUN_VNET_TNL_SIZE && + xdp->data - xdp->data_hard_start >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;
xdp->data - xdp->data_hard_start may not represent the header size.
struct tun_xdp_hdr is filled in vhost_net_build_xdp() in drivers/vhost/net.c. This function sets the two fields with xdp_prepare_buff(), but the arguments passed to xdp_prepare_buff() does not seem to represent the exact size of the header.
Quoted 207 lines
drivers/net/tun.c +17
-1+ + tnl_hdr = (struct virtio_net_hdr_v1_hash_tunnel *)gso; +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, tnl_hdr)) {atomic_long_inc (&tun->rx_frame_errors);kfree_skb (skb); ret = -EINVAL ;@@ -2812,6 +2849,8 @@ static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) } +#define PLAIN_GSO (NETIF_F_GSO_UDP_L4 | NETIF_F_TSO | NETIF_F_TSO6) +/* This is like a cut-down ethtool ops, except done via tun fd so no * privs required. */ static int set_offload (struct tun_struct *tun,unsigned long arg)@@ -2841,6 +2880,18 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) features |=NETIF_F_GSO_UDP_L4 ; arg &= ~(TUN_F_USO4 |TUN_F_USO6 ); } + +/* +* Tunnel offload is allowed only if some plain offload is +* available, too. +*/ +if (features &PLAIN_GSO && arg &TUN_F_UDP_TUNNEL_GSO ) { + features |=NETIF_F_GSO_UDP_TUNNEL ; +if (arg &TUN_F_UDP_TUNNEL_GSO_CSUM ) + features |=NETIF_F_GSO_UDP_TUNNEL_CSUM ; + arg &= ~(TUN_F_UDP_TUNNEL_GSO | +TUN_F_UDP_TUNNEL_GSO_CSUM ); + } }/* This gives the user a way to test for new features in future by @@ -2852,7 +2903,6 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) tun->dev->wanted_features &= ~TUN_USER_FEATURES ; tun->dev->wanted_features |= features;netdev_update_features (tun->dev); -return 0 ; }drivers/net/tun_vnet.h +79
-9@@ -6,6 +6,8 @@#define TUN_VNET_LE 0x80000000#define TUN_VNET_BE 0x40000000 +#define TUN_VNET_TNL_SIZE sizeof(struct virtio_net_hdr_v1_hash_tunnel) +static inline bool tun_vnet_legacy_is_little_endian (unsigned int flags) { bool be = IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE) &&@@ -107,16 +109,26 @@ static inline long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags, } } -static inline int tun_vnet_hdr_get (int sz,unsigned int flags, -struct iov_iter *from, -struct virtio_net_hdr *hdr) +static inline unsigned int tun_vnet_parse_size (netdev_features_t features) +{ +if (!(features &NETIF_F_GSO_UDP_TUNNEL )) +return sizeof (struct virtio_net_hdr ); + +return TUN_VNET_TNL_SIZE ; +} + +static inline int __tun_vnet_hdr_get (int sz,unsigned int flags, +netdev_features_t features, +struct iov_iter *from, +struct virtio_net_hdr *hdr) { +unsigned int parsed_size =tun_vnet_parse_size (features);u16 hdr_len;if (iov_iter_count (from) < sz)return -EINVAL ; -if (!copy_from_iter_full (hdr,sizeof (*hdr), from)) +if (!copy_from_iter_full (hdr, parsed_size, from))return -EFAULT ; hdr_len =tun_vnet16_to_cpu (flags, hdr->hdr_len);@@ -129,32 +141,59 @@ static inline int tun_vnet_hdr_get(int sz, unsigned int flags,if (hdr_len >iov_iter_count (from))return -EINVAL ; -iov_iter_advance (from, sz -sizeof (*hdr)); +iov_iter_advance (from, sz - parsed_size);return hdr_len; } -static inline int tun_vnet_hdr_put (int sz,struct iov_iter *iter, -const struct virtio_net_hdr *hdr) +static inline int tun_vnet_hdr_get (int sz,unsigned int flags, +struct iov_iter *from, +struct virtio_net_hdr *hdr) +{ +return __tun_vnet_hdr_get (sz, flags,0 , from, hdr); +} + +static inline int __tun_vnet_hdr_put (int sz,netdev_features_t features, +struct iov_iter *iter, +const struct virtio_net_hdr *hdr) { +unsigned int parsed_size =tun_vnet_parse_size (features); +if (unlikely (iov_iter_count (iter) < sz))return -EINVAL ; -if (unlikely (copy_to_iter (hdr,sizeof (*hdr), iter) !=sizeof (*hdr))) +if (unlikely (copy_to_iter (hdr, parsed_size, iter) != parsed_size))return -EFAULT ; -if (iov_iter_zero (sz -sizeof (*hdr), iter) != sz -sizeof (*hdr)) +if (iov_iter_zero (sz - parsed_size, iter) != sz - parsed_size)return -EFAULT ;return 0 ; } +static inline int tun_vnet_hdr_put (int sz,struct iov_iter *iter, +const struct virtio_net_hdr *hdr) +{ +return __tun_vnet_hdr_put (sz,0 , iter, hdr); +} +static inline int tun_vnet_hdr_to_skb (unsigned int flags,struct sk_buff *skb,const struct virtio_net_hdr *hdr) {return virtio_net_hdr_to_skb (skb, hdr,tun_vnet_is_little_endian (flags)); } +static inline int +tun_vnet_hdr_tnl_to_skb (unsigned int flags,netdev_features_t features, +struct sk_buff *skb, +const struct virtio_net_hdr_v1_hash_tunnel *hdr) +{ +return virtio_net_hdr_tnl_to_skb (skb, hdr, + !!(features &NETIF_F_GSO_UDP_TUNNEL ), + !!(features &NETIF_F_GSO_UDP_TUNNEL_CSUM ), +tun_vnet_is_little_endian (flags)); +} + static inline int tun_vnet_hdr_from_skb(unsigned int flags, const struct net_device *dev, const struct sk_buff *skb,@@ -183,4 +222,35 @@ static inline int tun_vnet_hdr_from_skb(unsigned int flags,return 0 ; } +static inline int +tun_vnet_hdr_tnl_from_skb (unsigned int flags, +const struct net_device *dev, +const struct sk_buff *skb, +struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr) +{ +bool has_tnl_offload = !!(dev->features &NETIF_F_GSO_UDP_TUNNEL ); +int vlan_hlen =skb_vlan_tag_present (skb) ?VLAN_HLEN :0 ; + +if (virtio_net_hdr_tnl_from_skb (skb, tnl_hdr, has_tnl_offload, +tun_vnet_is_little_endian (flags), + vlan_hlen)) { +struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr; +struct skb_shared_info *sinfo =skb_shinfo (skb); + +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)); +print_hex_dump (KERN_ERR ,"tun: " , +DUMP_PREFIX_NONE , +16 ,1 , skb->head, +min (tun_vnet16_to_cpu (flags, hdr->hdr_len),64 ), true); + } +WARN_ON_ONCE (1 ); +return -EINVAL ; + } + +return 0 ; +} +#endif /* TUN_VNET_H */ include/uapi/linux/if_tun.h +9
-0@@ -93,6 +93,15 @@#define TUN_F_USO4 0x20/* I can handle USO for IPv4 packets */ #define TUN_F_USO6 0x40/* I can handle USO for IPv6 packets */ +/* I can handle TSO/USO for UDP tunneled packets */ +#define TUN_F_UDP_TUNNEL_GSO 0x080 + +/* +* I can handle TSO/USO for UDP tunneled packets requiring csum offload for +* the outer header +*/ +#define TUN_F_UDP_TUNNEL_GSO_CSUM 0x100 +/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ #define TUN_PKT_STRIP 0x0001struct tun_pi {
Paolo Abeni
maintainerRe: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Akihiko OdakiQuoted 28 lines · Akihiko Odaki
Quoted 18 lines · Paolo Abeni
Quoted 5 lines · Akihiko Odaki
Quoted 4 lines · Paolo Abeni
drivers/net/tun.c +0
-0@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz);Quoted 7 lines
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1738
Quoted 4 lines
drivers/net/tun.c +3
-1- hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;I think you should use
tun->set_featuresinstead oftun->vnet_hdr_szto tell if these features are enabled.This is the guest -> host direction.
tun->set_featuresrefers to the opposite one. The problem is that tun is not aware of the features negotiated in the guest -> host direction.Quoted 8 lines
The current status (for baremetal/plain offload) is allowing any known feature the other side send - if the virtio header is consistent. This code follows a similar schema.
Note that using '
tun->set_features' instead of 'vnet_hdr_sz' the tun driver will drop all the (legit) GSO over UDP packet sent by the guest when theVIRTIO_NET_F_HOST_UDP_TUNNEL_GSOhas been negotiated andVIRTIO_NET_F_GUEST_UDP_TUNNEL_GSOhas not.
Quoted 7 lines
This explanation makes sense. In that case I suggest:
- creating a new function namedtun_vnet_hdr_tnl_get()and
- passingvnet_hdr_sztotun_vnet_hdr_tnl_to_skb()
tun_vnet.hcontains the virtio-related logic for better code organization and reuse withtap.c.tap.ccan reuse the conditionals onvnet_hdr_szwhentap.cgains the UDP tunneling support.
Instead of repeating the test twice (in both tun_vnet_hdr_tnl_to_skb() and tun_vnet_hdr_tnl_to_skb(), what about creating a new helper:
tun_vnet_hdr_guest_features(unsigned int vnet_hdr_len)
encapsulating the above logic? That will make also easier to move to the 'correct' solution of having the tun/tap devices aware of the features negotiated in the guest -> host direction.
/P
Paolo Abeni
maintainerRe: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Akihiko OdakiQuoted 17 lines · Akihiko Odaki
drivers/net/tun.c static int tun_xdp_one(struct tun_struct *tun, · line 2463
Quoted 13 lines · Paolo Abeni
drivers/net/tun.c +8
-1@@ -2426,7 +2453,17 @@ static int tun_xdp_one(struct tun_struct *tun,if (metasize >0 )skb_metadata_set (skb, metasize); -if (tun_vnet_hdr_to_skb (tun->flags, skb, gso)) { +/* +* Assume tunnel offloads are enabled if the received hdr is large +* enough. +*/ +if (READ_ONCE (tun->vnet_hdr_sz) >=TUN_VNET_TNL_SIZE && + xdp->data - xdp->data_hard_start >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;
xdp->data-xdp->data_hard_startmay not represent the header size.
Quoted 4 lines
struct
tun_xdp_hdris filled invhost_net_build_xdp()indrivers/vhost/net.c. This function sets the two fields withxdp_prepare_buff(), but the arguments passed toxdp_prepare_buff()does not seem to represent the exact size of the header.
Indeed the xdp->data - xdp->data_hard_start range additionally contains some padding and eventually the xdp specific headroom. The problem is that both info are vhost_net specific and tun can't (or at least shoul) not be aware of them.
The only IMHO feasible refinement could be using xdp->data_meta instead of xdp->data when available. Alternatively I could drop entirely the test.
/P
Paolo Abeni
maintainerRe: [PATCH v4 net-next 3/8] vhost-net: allow configuring extended features
In reply to Akihiko OdakiQuoted 9 lines · Akihiko Odaki
drivers/vhost/net.c static long vhost_net_ioctl(struct file *f, unsigned int ioctl, · line 1743
Quoted 6 lines · Paolo Abeni
drivers/vhost/net.c +6
-0+ +/* Any feature specified by user-space above VIRTIO_FEATURES_MAX is +* not supported by definition. +*/ +for (; i < count; ++i) { +if (copy_from_user (&features, argp,sizeof (u64)))
get_user()is a simpler alternative.
That would require an explicit cast of 'argp' to a suitable pointer type, which is IMHO uglier. I prefer sticking with copy_from_user().
Side note: there is a bug in this loop, as it lacks the needed increment of the src pointer at every iteration.
/P
Paolo Abeni
maintainerRe: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 23 lines · Paolo Abeni
Quoted 17 lines · Akihiko Odaki
drivers/net/tun.c static int tun_xdp_one(struct tun_struct *tun, · line 2463
Quoted 13 lines · Paolo Abeni
drivers/net/tun.c +8
-1@@ -2426,7 +2453,17 @@ static int tun_xdp_one(struct tun_struct *tun,if (metasize >0 )skb_metadata_set (skb, metasize); -if (tun_vnet_hdr_to_skb (tun->flags, skb, gso)) { +/* +* Assume tunnel offloads are enabled if the received hdr is large +* enough. +*/ +if (READ_ONCE (tun->vnet_hdr_sz) >=TUN_VNET_TNL_SIZE && + xdp->data - xdp->data_hard_start >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;
xdp->data-xdp->data_hard_startmay not represent the header size.Quoted 4 lines
struct
tun_xdp_hdris filled invhost_net_build_xdp()indrivers/vhost/net.c. This function sets the two fields withxdp_prepare_buff(), but the arguments passed toxdp_prepare_buff()does not seem to represent the exact size of the header.
Quoted 7 lines
Indeed the
xdp->data-xdp->data_hard_startrange additionally contains some padding and eventually the xdp specific headroom. The problem is that both info arevhost_netspecific and tun can't (or at least shoul) not be aware of them.The only IMHO feasible refinement could be using
xdp->data_metainstead ofxdp->datawhen available. Alternatively I could drop entirely the test.
As the vhost_net padding is considerably larger than the largest possible vnet_hdr_sz, I'll drop entirely the xdp check - so that I can use the newly introduced tun_vnet_hdr_guest_features() helper here, too.
/P
Re: [PATCH v4 net-next 3/8] vhost-net: allow configuring extended features
In reply to Paolo AbeniQuoted 11 lines · Paolo Abeni
Quoted 9 lines · Akihiko Odaki
drivers/vhost/net.c static long vhost_net_ioctl(struct file *f, unsigned int ioctl, · line 1743
Quoted 6 lines · Paolo Abeni
drivers/vhost/net.c +6
-0+ +/* Any feature specified by user-space above VIRTIO_FEATURES_MAX is +* not supported by definition. +*/ +for (; i < count; ++i) { +if (copy_from_user (&features, argp,sizeof (u64)))
get_user()is a simpler alternative.
Quoted 5 lines
That would require an explicit cast of 'argp' to a suitable pointer type, which is IMHO uglier. I prefer sticking with
copy_from_user().Side note: there is a bug in this loop, as it lacks the needed increment of the src pointer at every iteration.
A pointer casted to u64 __user * will make the pointer usage a bit simpler.
For example, initialize featurep as follows:
featurep = (u64 __user *)argp + 1; /* skipping count */
...and you can get the i-th element with:get_user(features, featurep + i)
You don't need sizeof(u64) this way.
Regards,
Akihiko Odaki
Willem de Bruijn
maintainerRe: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 50 lines · Paolo Abeni
Add new tun features to represent the newly introduced virtio GSO over UDP tunnel offload. Allows detection and selection of such features via the existing TUNSETOFFLOAD ioctl and compute the expected virtio header size and tunnel header offset using the current netdev features, so that we can plug almost seamless the newly introduced virtio helpers to serialize the extended virtio header.
Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- virtio tnl-related fields are at fixed offset, cleanup the code accordingly.
- use netdev features instead of flags bit to check for the configured offload
- drop packet in case of enabled features/configured hdr size mismatchv2 -> v3:
- cleaned-up uAPI comments
- use explicit struct layout instead of raw buf.--- drivers/net/tun.c | 70 ++++++++++++++++++++++++----- drivers/net/tun_vnet.h | 88 +++++++++++++++++++++++++++++++++---- include/uapi/linux/if_tun.h | 9 ++++ 3 files changed, 148 insertions(+), 19 deletions(-)drivers/net/tun.c +3
-1@@ -186,7 +186,8 @@ struct tun_struct {struct net_device *dev;netdev_features_t set_features;#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \ - NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4) + NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4 | \ + NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM)int align;int vnet_hdr_sz;@@ -925,6 +926,7 @@ static int tun_net_init(struct net_device *dev) dev->hw_features =NETIF_F_SG |NETIF_F_FRAGLIST |TUN_USER_FEATURES |NETIF_F_HW_VLAN_CTAG_TX |NETIF_F_HW_VLAN_STAG_TX ; + dev->hw_enc_features = dev->hw_features; dev->features = dev->hw_features; dev->vlan_features = dev->features & ~(NETIF_F_HW_VLAN_CTAG_TX |
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1703
Quoted 5 lines
drivers/net/tun.c +1
-1@@ -1698,7 +1700,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,struct sk_buff *skb;size_t total_len =iov_iter_count (from);size_t len = total_len, align = tun->align, linear; -struct virtio_net_hdr gso = {0 }; +struct virtio_net_hdr_v1_hash_tunnel hdr;
Not for this series.
But one day virtio will need a policy on how multiple optional features can be composed, and simple APIs to get to those optional headers. Perhaps something like skb extensions.
Now, each new extention means adding yet another struct and updating all sites that access it.
A minimal rule may be that options can be entirely independent, but if they exist at least their headers are always in a fixed order. Which is already implied by the current extensions, i.e., hash comes before tunnel if present.
Quoted 21 lines
drivers/net/tun.c +10
-0+struct virtio_net_hdr *gso;int good_linear;int copylen;int hdr_len =0 ;@@ -1708,6 +1711,15 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,int skb_xdp =1 ;bool frags =tun_napi_frags_enabled (tfile);enum skb_drop_reason drop_reason =SKB_DROP_REASON_NOT_SPECIFIED ; +netdev_features_t features =0 ; + +/* +* Keep it easy and always zero the whole buffer, even if the +* tunnel-related field will be touched only when the feature +* is enabled and the hdr size id compatible. +*/ +memset (&hdr,0 ,sizeof (hdr)); + gso = (struct virtio_net_hdr *)&hdr;if (!(tun->flags &IFF_NO_PI )) {if (len <sizeof (pi))
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1738
Quoted 7 lines
drivers/net/tun.c +3
-1@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz); - hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;
Maybe a helper virtio_net_has_opt_tunnel(), to encapsulate whatever conditions have to be met. As those conditions are not obvious.
Especially if needed in multiple locations. Not sure if that is the case here, I have not checked that.
Quoted 79 lines
drivers/net/tun.c +29
-6+ + hdr_len =__tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, + features, from, gso);if (hdr_len <0 )return hdr_len;@@ -1755,7 +1772,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,* (e.g gso or jumbo packet), we will do it at after * skb was created with generic XDP routine. */ - skb =tun_build_skb (tun, tfile, from, &gso, len, &skb_xdp); + skb =tun_build_skb (tun, tfile, from, gso, len, &skb_xdp); err =PTR_ERR_OR_ZERO (skb);if (err) goto drop;@@ -1799,7 +1816,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } } -if (tun_vnet_hdr_to_skb (tun->flags, skb, &gso)) { +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, &hdr)) {atomic_long_inc (&tun->rx_frame_errors); err = -EINVAL ; goto free_skb;@@ -2050,13 +2067,21 @@ static ssize_t tun_put_user(struct tun_struct *tun, }if (vnet_hdr_sz) { -struct virtio_net_hdr gso; +struct virtio_net_hdr_v1_hash_tunnel hdr; +struct virtio_net_hdr *gso; - ret =tun_vnet_hdr_from_skb (tun->flags, tun->dev, skb, &gso); + ret =tun_vnet_hdr_tnl_from_skb (tun->flags, tun->dev, skb, + &hdr);if (ret)return ret; - ret =tun_vnet_hdr_put (vnet_hdr_sz, iter, &gso); +/* +* Drop the packet if the configured header size is too small +* WRT the enabled offloads. +*/ + gso = (struct virtio_net_hdr *)&hdr; + ret =__tun_vnet_hdr_put (vnet_hdr_sz, tun->dev->features, + iter, gso);if (ret)return ret; }@@ -2357,7 +2382,9 @@ static int tun_xdp_one(struct tun_struct *tun, {unsigned int datasize = xdp->data_end - xdp->data;struct tun_xdp_hdr *hdr = xdp->data_hard_start; +struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr;struct virtio_net_hdr *gso = &hdr->gso; +netdev_features_t features =0 ;struct bpf_prog *xdp_prog;struct sk_buff *skb =NULL ;struct sk_buff_head *queue;@@ -2426,7 +2453,17 @@ static int tun_xdp_one(struct tun_struct *tun,if (metasize >0 )skb_metadata_set (skb, metasize); -if (tun_vnet_hdr_to_skb (tun->flags, skb, gso)) { +/* +* Assume tunnel offloads are enabled if the received hdr is large +* enough. +*/ +if (READ_ONCE (tun->vnet_hdr_sz) >=TUN_VNET_TNL_SIZE && + xdp->data - xdp->data_hard_start >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ; + + tnl_hdr = (struct virtio_net_hdr_v1_hash_tunnel *)gso; +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, tnl_hdr)) {atomic_long_inc (&tun->rx_frame_errors);kfree_skb (skb); ret = -EINVAL ;
drivers/net/tun.c static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) · line 2853
Quoted 5 lines
drivers/net/tun.c +2
-0@@ -2812,6 +2849,8 @@ static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) } +#define PLAIN_GSO (NETIF_F_GSO_UDP_L4 | NETIF_F_TSO | NETIF_F_TSO6) +
Minor/subjective: prefer const unsigned int at function scope over untyped file scope macros.
Quoted 123 lines
drivers/net/tun.c +12
-1/* This is like a cut-down ethtool ops, except done via tun fd so no * privs required. */ static int set_offload (struct tun_struct *tun,unsigned long arg)@@ -2841,6 +2880,18 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) features |=NETIF_F_GSO_UDP_L4 ; arg &= ~(TUN_F_USO4 |TUN_F_USO6 ); } + +/* +* Tunnel offload is allowed only if some plain offload is +* available, too. +*/ +if (features &PLAIN_GSO && arg &TUN_F_UDP_TUNNEL_GSO ) { + features |=NETIF_F_GSO_UDP_TUNNEL ; +if (arg &TUN_F_UDP_TUNNEL_GSO_CSUM ) + features |=NETIF_F_GSO_UDP_TUNNEL_CSUM ; + arg &= ~(TUN_F_UDP_TUNNEL_GSO | +TUN_F_UDP_TUNNEL_GSO_CSUM ); + } }/* This gives the user a way to test for new features in future by @@ -2852,7 +2903,6 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) tun->dev->wanted_features &= ~TUN_USER_FEATURES ; tun->dev->wanted_features |= features;netdev_update_features (tun->dev); -return 0 ; }drivers/net/tun_vnet.h +37
-9@@ -6,6 +6,8 @@#define TUN_VNET_LE 0x80000000#define TUN_VNET_BE 0x40000000 +#define TUN_VNET_TNL_SIZE sizeof(struct virtio_net_hdr_v1_hash_tunnel) +static inline bool tun_vnet_legacy_is_little_endian (unsigned int flags) { bool be = IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE) &&@@ -107,16 +109,26 @@ static inline long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags, } } -static inline int tun_vnet_hdr_get (int sz,unsigned int flags, -struct iov_iter *from, -struct virtio_net_hdr *hdr) +static inline unsigned int tun_vnet_parse_size (netdev_features_t features) +{ +if (!(features &NETIF_F_GSO_UDP_TUNNEL )) +return sizeof (struct virtio_net_hdr ); + +return TUN_VNET_TNL_SIZE ; +} + +static inline int __tun_vnet_hdr_get (int sz,unsigned int flags, +netdev_features_t features, +struct iov_iter *from, +struct virtio_net_hdr *hdr) { +unsigned int parsed_size =tun_vnet_parse_size (features);u16 hdr_len;if (iov_iter_count (from) < sz)return -EINVAL ; -if (!copy_from_iter_full (hdr,sizeof (*hdr), from)) +if (!copy_from_iter_full (hdr, parsed_size, from))return -EFAULT ; hdr_len =tun_vnet16_to_cpu (flags, hdr->hdr_len);@@ -129,32 +141,59 @@ static inline int tun_vnet_hdr_get(int sz, unsigned int flags,if (hdr_len >iov_iter_count (from))return -EINVAL ; -iov_iter_advance (from, sz -sizeof (*hdr)); +iov_iter_advance (from, sz - parsed_size);return hdr_len; } -static inline int tun_vnet_hdr_put (int sz,struct iov_iter *iter, -const struct virtio_net_hdr *hdr) +static inline int tun_vnet_hdr_get (int sz,unsigned int flags, +struct iov_iter *from, +struct virtio_net_hdr *hdr) +{ +return __tun_vnet_hdr_get (sz, flags,0 , from, hdr); +} + +static inline int __tun_vnet_hdr_put (int sz,netdev_features_t features, +struct iov_iter *iter, +const struct virtio_net_hdr *hdr) { +unsigned int parsed_size =tun_vnet_parse_size (features); +if (unlikely (iov_iter_count (iter) < sz))return -EINVAL ; -if (unlikely (copy_to_iter (hdr,sizeof (*hdr), iter) !=sizeof (*hdr))) +if (unlikely (copy_to_iter (hdr, parsed_size, iter) != parsed_size))return -EFAULT ; -if (iov_iter_zero (sz -sizeof (*hdr), iter) != sz -sizeof (*hdr)) +if (iov_iter_zero (sz - parsed_size, iter) != sz - parsed_size)return -EFAULT ;return 0 ; } +static inline int tun_vnet_hdr_put (int sz,struct iov_iter *iter, +const struct virtio_net_hdr *hdr) +{ +return __tun_vnet_hdr_put (sz,0 , iter, hdr); +} +static inline int tun_vnet_hdr_to_skb (unsigned int flags,struct sk_buff *skb,const struct virtio_net_hdr *hdr) {return virtio_net_hdr_to_skb (skb, hdr,tun_vnet_is_little_endian (flags));
drivers/net/tun_vnet.h static inline int tun_vnet_hdr_get(int sz, unsigned int flags, · line 193
Quoted 10 lines
drivers/net/tun_vnet.h +8
-0} +static inline int +tun_vnet_hdr_tnl_to_skb (unsigned int flags,netdev_features_t features, +struct sk_buff *skb, +const struct virtio_net_hdr_v1_hash_tunnel *hdr) +{ + return virtio_net_hdr_tnl_to_skb(skb, hdr, + !!(features & NETIF_F_GSO_UDP_TUNNEL), + !!(features & NETIF_F_GSO_UDP_TUNNEL_CSUM),
Double exclamation points not needed. Compiler does the right thing when arguments are of type bool.
Quoted 65 lines
drivers/net/tun_vnet.h +34
-0+ tun_vnet_is_little_endian(flags)); +} + static inline int tun_vnet_hdr_from_skb(unsigned int flags, const struct net_device *dev, const struct sk_buff *skb,@@ -183,4 +222,35 @@ static inline int tun_vnet_hdr_from_skb(unsigned int flags,return 0 ; } +static inline int +tun_vnet_hdr_tnl_from_skb (unsigned int flags, +const struct net_device *dev, +const struct sk_buff *skb, +struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr) +{ +bool has_tnl_offload = !!(dev->features &NETIF_F_GSO_UDP_TUNNEL ); +int vlan_hlen =skb_vlan_tag_present (skb) ?VLAN_HLEN :0 ; + +if (virtio_net_hdr_tnl_from_skb (skb, tnl_hdr, has_tnl_offload, +tun_vnet_is_little_endian (flags), + vlan_hlen)) { +struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr; +struct skb_shared_info *sinfo =skb_shinfo (skb); + +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)); +print_hex_dump (KERN_ERR ,"tun: " , +DUMP_PREFIX_NONE , +16 ,1 , skb->head, +min (tun_vnet16_to_cpu (flags, hdr->hdr_len),64 ), true); + } +WARN_ON_ONCE (1 ); +return -EINVAL ; + } + +return 0 ; +} +#endif /* TUN_VNET_H */ include/uapi/linux/if_tun.h +9
-0@@ -93,6 +93,15 @@#define TUN_F_USO4 0x20/* I can handle USO for IPv4 packets */ #define TUN_F_USO6 0x40/* I can handle USO for IPv6 packets */ +/* I can handle TSO/USO for UDP tunneled packets */ +#define TUN_F_UDP_TUNNEL_GSO 0x080 + +/* +* I can handle TSO/USO for UDP tunneled packets requiring csum offload for +* the outer header +*/ +#define TUN_F_UDP_TUNNEL_GSO_CSUM 0x100 +/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ #define TUN_PKT_STRIP 0x0001struct tun_pi {Signature · 1 line
2.49.0
Paolo Abeni
maintainerRe: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
Quoted 14 lines · Willem de Bruijn
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1703
Quoted 6 lines · Paolo Abeni
drivers/net/tun.c +1
-1@@ -1698,7 +1700,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,struct sk_buff *skb;size_t total_len =iov_iter_count (from);size_t len = total_len, align = tun->align, linear; -struct virtio_net_hdr gso = {0 }; +struct virtio_net_hdr_v1_hash_tunnel hdr;Not for this series.
But one day virtio will need a policy on how multiple optional features can be composed, and simple APIs to get to those optional headers. Perhaps something like skb extensions.
Quoted 7 lines
Now, each new extention means adding yet another struct and updating all sites that access it.
A minimal rule may be that options can be entirely independent, but if they exist at least their headers are always in a fixed order. Which is already implied by the current extensions, i.e., hash comes before tunnel if present.
If I read correctly, you are suggesting that negotiating tunnel and not hash would yield this layout:
< basic vnet hdr> <tnl fields>
with no gaps/data between the basic header fields and the tunnel-related one. Am I correct?
This has been discussed in the previous revisions, and a recent specification update explicitly states differently: with tunnel support and without hash report the only possible layout is:
< basic vnet hdr> <hash report field (unused)> <tnl fields>
Since it's in the spec it's too late to change it, unless we add yet another feature for that. I'm gladly leaving that joy and fun to someone else:)
FTR the initial revisions of this series, before I stumbled upon the mentioned spec change, followed the schema you mentioned.
Quoted 12 lines
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1738
Quoted 8 lines
drivers/net/tun.c +3
-1@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz); - hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;Maybe a helper
virtio_net_has_opt_tunnel(), to encapsulate whatever conditions have to be met. As those conditions are not obvious.
Quoted 2 lines
Especially if needed in multiple locations. Not sure if that is the case here, I have not checked that.
Yep, as an outcome of Ajihiko's review I'm encapsulation the above in a new helper - tun_vnet_hdr_guest_features() to be more generic.
Quoted 9 lines
drivers/net/tun.c static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) · line 2853
Quoted 6 lines
drivers/net/tun.c +2
-0@@ -2812,6 +2849,8 @@ static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) } +#define PLAIN_GSO (NETIF_F_GSO_UDP_L4 | NETIF_F_TSO | NETIF_F_TSO6) +Minor/subjective: prefer const unsigned int at function scope over untyped file scope macros.
Unless it's blocking I would keep the current code here.
Quoted 11 lines
drivers/net/tun_vnet.h static inline int tun_vnet_hdr_get(int sz, unsigned int flags, · line 193
Quoted 8 lines
drivers/net/tun_vnet.h +8
-0+static inline int +tun_vnet_hdr_tnl_to_skb (unsigned int flags,netdev_features_t features, +struct sk_buff *skb, +const struct virtio_net_hdr_v1_hash_tunnel *hdr) +{ + return virtio_net_hdr_tnl_to_skb(skb, hdr, + !!(features & NETIF_F_GSO_UDP_TUNNEL), + !!(features & NETIF_F_GSO_UDP_TUNNEL_CSUM),Double exclamation points not needed. Compiler does the right thing when arguments are of type bool.
Will drop in the next revision.
Thanks!
Paolo
Willem de Bruijn
maintainerRe: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
Quoted 28 lines · Paolo Abeni
Quoted 14 lines · Willem de Bruijn
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1703
Quoted 6 lines · Paolo Abeni
drivers/net/tun.c +1
-1@@ -1698,7 +1700,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,struct sk_buff *skb;size_t total_len =iov_iter_count (from);size_t len = total_len, align = tun->align, linear; -struct virtio_net_hdr gso = {0 }; +struct virtio_net_hdr_v1_hash_tunnel hdr;Not for this series.
But one day virtio will need a policy on how multiple optional features can be composed, and simple APIs to get to those optional headers. Perhaps something like skb extensions.
Quoted 7 lines
Now, each new extention means adding yet another struct and updating all sites that access it.
A minimal rule may be that options can be entirely independent, but if they exist at least their headers are always in a fixed order. Which is already implied by the current extensions, i.e., hash comes before tunnel if present.
If I read correctly, you are suggesting that negotiating tunnel and not hash would yield this layout:
< basic vnet hdr> <tnl fields>
Quoted 8 lines
with no gaps/data between the basic header fields and the tunnel-related one. Am I correct?
This has been discussed in the previous revisions, and a recent specification update explicitly states differently: with tunnel support and without hash report the only possible layout is:
< basic vnet hdr> <hash report field (unused)> <tnl fields>
Indeed. Long-term it seems odd to just keep extending the header with every optional feature, even when disabled.
Quoted 3 lines
Since it's in the spec it's too late to change it, unless we add yet another feature for that. I'm gladly leaving that joy and fun to someone else:)
Agreed!
Quoted 15 lines
FTR the initial revisions of this series, before I stumbled upon the mentioned spec change, followed the schema you mentioned.
Quoted 12 lines
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1738
Quoted 8 lines
drivers/net/tun.c +3
-1@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz); - hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;Maybe a helper
virtio_net_has_opt_tunnel(), to encapsulate whatever conditions have to be met. As those conditions are not obvious.
Quoted 5 lines
Quoted 2 lines
Especially if needed in multiple locations. Not sure if that is the case here, I have not checked that.
Yep, as an outcome of Ajihiko's review I'm encapsulation the above in a new helper -
tun_vnet_hdr_guest_features()to be more generic.
Saw that. Awesome.
Quoted 11 lines
Quoted 9 lines
drivers/net/tun.c static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) · line 2853
Quoted 6 lines
drivers/net/tun.c +2
-0@@ -2812,6 +2849,8 @@ static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) } +#define PLAIN_GSO (NETIF_F_GSO_UDP_L4 | NETIF_F_TSO | NETIF_F_TSO6) +Minor/subjective: prefer const unsigned int at function scope over untyped file scope macros.
Unless it's blocking I would keep the current code here.
Ack.
Quoted 18 lines
Quoted 11 lines
drivers/net/tun_vnet.h static inline int tun_vnet_hdr_get(int sz, unsigned int flags, · line 193
Quoted 8 lines
drivers/net/tun_vnet.h +8
-0+static inline int +tun_vnet_hdr_tnl_to_skb (unsigned int flags,netdev_features_t features, +struct sk_buff *skb, +const struct virtio_net_hdr_v1_hash_tunnel *hdr) +{ + return virtio_net_hdr_tnl_to_skb(skb, hdr, + !!(features & NETIF_F_GSO_UDP_TUNNEL), + !!(features & NETIF_F_GSO_UDP_TUNNEL_CSUM),Double exclamation points not needed. Compiler does the right thing when arguments are of type bool.
Will drop in the next revision.
Thanks!
Paolo
Re: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 37 lines · Paolo Abeni
Quoted 28 lines · Akihiko Odaki
Quoted 18 lines · Paolo Abeni
Quoted 5 lines · Akihiko Odaki
Quoted 4 lines · Paolo Abeni
drivers/net/tun.c +0
-0@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz);Quoted 7 lines
drivers/net/tun.c static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, · line 1738
Quoted 4 lines
drivers/net/tun.c +3
-1- hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;I think you should use
tun->set_featuresinstead oftun->vnet_hdr_szto tell if these features are enabled.This is the guest -> host direction.
tun->set_featuresrefers to the opposite one. The problem is that tun is not aware of the features negotiated in the guest -> host direction.Quoted 8 lines
The current status (for baremetal/plain offload) is allowing any known feature the other side send - if the virtio header is consistent. This code follows a similar schema.
Note that using '
tun->set_features' instead of 'vnet_hdr_sz' the tunz driver will drop all the (legit) GSO over UDP packet sent by the guest when theVIRTIO_NET_F_HOST_UDP_TUNNEL_GSOhas been negotiated andVIRTIO_NET_F_GUEST_UDP_TUNNEL_GSOhas not.Quoted 7 lines
This explanation makes sense. In that case I suggest:
- creating a new function namedtun_vnet_hdr_tnl_get()and
- passingvnet_hdr_sztotun_vnet_hdr_tnl_to_skb()
tun_vnet.hcontains the virtio-related logic for better code organization and reuse withtap.c.tap.ccan reuse the conditionals onvnet_hdr_szwhentap.cgains the UDP tunneling support.
Quoted 8 lines
Instead of repeating the test twice (in both
tun_vnet_hdr_tnl_to_skb()andtun_vnet_hdr_tnl_to_skb(), what about creating a new helper:
tun_vnet_hdr_guest_features(unsigned int vnet_hdr_len)encapsulating the above logic? That will make also easier to move to the 'correct' solution of having the tun/tap devices aware of the features negotiated in the guest -> host direction.
netdev_features_t is not used in the other part of the user-to-skb path so we only need the condition "vnet_hdr_sz >= TUN_VNET_TNL_SIZE"; "features = NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM" can be simply skipped.
The condition itself is trivial so probably you don't need a helper for it.
For the skb-to-user path, tun_vnet.h already absorbs the reference to netdev_feature_t in tun_vnet_hdr_tnl_from_skb() and allows reusing it with tap.c, which is a nice example.
Regards,
Akihiko Odaki
Re: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Paolo AbeniQuoted 24 lines · Paolo Abeni
Quoted 23 lines · Paolo Abeni
Quoted 21 lines · Akihiko Odaki
drivers/net/tun.c static int tun_xdp_one(struct tun_struct *tun, · line 2463
Quoted 13 lines · Paolo Abeni
drivers/net/tun.c +8
-1@@ -2426,7 +2453,17 @@ static int tun_xdp_one(struct tun_struct *tun,if (metasize >0 )skb_metadata_set (skb, metasize); -if (tun_vnet_hdr_to_skb (tun->flags, skb, gso)) { +/* +* Assume tunnel offloads are enabled if the received hdr is large +* enough. +*/ +if (READ_ONCE (tun->vnet_hdr_sz) >=TUN_VNET_TNL_SIZE && + xdp->data - xdp->data_hard_start >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;
xdp->data-xdp->data_hard_startmay not represent the header size.struct
tun_xdp_hdris filled invhost_net_build_xdp()indrivers/vhost/net.c. This function sets the two fields withxdp_prepare_buff(), but the arguments passed toxdp_prepare_buff()does not seem to represent the exact size of the header.
Quoted 7 lines
Quoted 7 lines
Indeed the
xdp->data-xdp->data_hard_startrange additionally contains some padding and eventually the xdp specific headroom. The problem is that both info arevhost_netspecific and tun can't (or at least shoul) not be aware of them.The only IMHO feasible refinement could be using
xdp->data_metainstead ofxdp->datawhen available. Alternatively I could drop entirely the test.
struct tun_xdp_hdr can be extended to contain struct virtio_net_hdr_v1_hash_tunnel.
Quoted 4 lines
As the
vhost_netpadding is considerably larger than the largest possiblevnet_hdr_sz, I'll drop entirely the xdp check - so that I can use the newly introducedtun_vnet_hdr_guest_features()helper here, too.
I think dropping the XDP check is the best option (and leaving struct tun_xdp_hdr as is) until vhost_net_build_xdp() gains the UDP tunnel support.
Regards,
Akihiko Odaki
Re: [PATCH v4 net-next 7/8] tun: enable gso over UDP tunnel support.
In reply to Akihiko OdakiQuoted 160 lines · Akihiko Odaki
Quoted 145 lines · Paolo Abeni
Add new tun features to represent the newly introduced virtio GSO over UDP tunnel offload. Allows detection and selection of such features via the existing TUNSETOFFLOAD ioctl and compute the expected virtio header size and tunnel header offset using the current netdev features, so that we can plug almost seamless the newly introduced virtio helpers to serialize the extended virtio header.
Signed-off-by Paolo Abeni <[email protected]>---
v3 -> v4:
- virtio tnl-related fields are at fixed offset, cleanup the code accordingly.
- use netdev features instead of flags bit to check for the configured offload
- drop packet in case of enabled features/configured hdr size mismatchv2 -> v3:
- cleaned-up uAPI comments
- use explicit struct layout instead of raw buf.--- drivers/net/tun.c | 70 ++++++++++++++++++++++++----- drivers/net/tun_vnet.h | 88 +++++++++++++++++++++++++++++++++---- include/uapi/linux/if_tun.h | 9 ++++ 3 files changed, 148 insertions(+), 19 deletions(-)drivers/net/tun.c +35
-8@@ -186,7 +186,8 @@ struct tun_struct {struct net_device *dev;netdev_features_t set_features;#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \ - NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4) + NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4 | \ + NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM)int align;int vnet_hdr_sz;@@ -925,6 +926,7 @@ static int tun_net_init(struct net_device *dev) dev->hw_features =NETIF_F_SG |NETIF_F_FRAGLIST |TUN_USER_FEATURES |NETIF_F_HW_VLAN_CTAG_TX |NETIF_F_HW_VLAN_STAG_TX ; + dev->hw_enc_features = dev->hw_features; dev->features = dev->hw_features; dev->vlan_features = dev->features & ~(NETIF_F_HW_VLAN_CTAG_TX |@@ -1698,7 +1700,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,struct sk_buff *skb;size_t total_len =iov_iter_count (from);size_t len = total_len, align = tun->align, linear; -struct virtio_net_hdr gso = {0 }; +struct virtio_net_hdr_v1_hash_tunnel hdr; +struct virtio_net_hdr *gso;int good_linear;int copylen;int hdr_len =0 ;@@ -1708,6 +1711,15 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,int skb_xdp =1 ;bool frags =tun_napi_frags_enabled (tfile);enum skb_drop_reason drop_reason =SKB_DROP_REASON_NOT_SPECIFIED ; +netdev_features_t features =0 ; + +/* +* Keep it easy and always zero the whole buffer, even if the +* tunnel-related field will be touched only when the feature +* is enabled and the hdr size id compatible. +*/ +memset (&hdr,0 ,sizeof (hdr)); + gso = (struct virtio_net_hdr *)&hdr;if (!(tun->flags &IFF_NO_PI )) {if (len <sizeof (pi))@@ -1721,7 +1733,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,if (tun->flags &IFF_VNET_HDR ) {int vnet_hdr_sz =READ_ONCE (tun->vnet_hdr_sz); - hdr_len =tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, from, &gso); +if (vnet_hdr_sz >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ; + + hdr_len =__tun_vnet_hdr_get (vnet_hdr_sz, tun->flags, + features, from, gso);if (hdr_len <0 )return hdr_len;@@ -1755,7 +1772,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,* (e.g gso or jumbo packet), we will do it at after * skb was created with generic XDP routine. */ - skb =tun_build_skb (tun, tfile, from, &gso, len, &skb_xdp); + skb =tun_build_skb (tun, tfile, from, gso, len, &skb_xdp); err =PTR_ERR_OR_ZERO (skb);if (err) goto drop;@@ -1799,7 +1816,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } } -if (tun_vnet_hdr_to_skb (tun->flags, skb, &gso)) { +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, &hdr)) {atomic_long_inc (&tun->rx_frame_errors); err = -EINVAL ; goto free_skb;@@ -2050,13 +2067,21 @@ static ssize_t tun_put_user(struct tun_struct *tun, }if (vnet_hdr_sz) { -struct virtio_net_hdr gso; +struct virtio_net_hdr_v1_hash_tunnel hdr; +struct virtio_net_hdr *gso; - ret =tun_vnet_hdr_from_skb (tun->flags, tun->dev, skb, &gso); + ret =tun_vnet_hdr_tnl_from_skb (tun->flags, tun->dev, skb, + &hdr);if (ret)return ret; - ret =tun_vnet_hdr_put (vnet_hdr_sz, iter, &gso); +/* +* Drop the packet if the configured header size is too small +* WRT the enabled offloads. +*/ + gso = (struct virtio_net_hdr *)&hdr; + ret =__tun_vnet_hdr_put (vnet_hdr_sz, tun->dev->features, + iter, gso);if (ret)return ret; }@@ -2357,7 +2382,9 @@ static int tun_xdp_one(struct tun_struct *tun, {unsigned int datasize = xdp->data_end - xdp->data;struct tun_xdp_hdr *hdr = xdp->data_hard_start; +struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr;struct virtio_net_hdr *gso = &hdr->gso; +netdev_features_t features =0 ;struct bpf_prog *xdp_prog;struct sk_buff *skb =NULL ;struct sk_buff_head *queue;@@ -2426,7 +2453,17 @@ static int tun_xdp_one(struct tun_struct *tun,if (metasize >0 )skb_metadata_set (skb, metasize);drivers/net/tun.c static int tun_xdp_one(struct tun_struct *tun, · line 2463
Quoted 10 lines
drivers/net/tun.c +8
-1-if (tun_vnet_hdr_to_skb (tun->flags, skb, gso)) { +/* +* Assume tunnel offloads are enabled if the received hdr is large +* enough. +*/ +if (READ_ONCE (tun->vnet_hdr_sz) >=TUN_VNET_TNL_SIZE && + xdp->data - xdp->data_hard_start >=TUN_VNET_TNL_SIZE ) + features =NETIF_F_GSO_UDP_TUNNEL | +NETIF_F_GSO_UDP_TUNNEL_CSUM ;
xdp->data-xdp->data_hard_startmay not represent the header size.
Quoted 4 lines
struct
tun_xdp_hdris filled invhost_net_build_xdp()indrivers/vhost/net.c. This function sets the two fields withxdp_prepare_buff(), but the arguments passed toxdp_prepare_buff()does not seem to represent the exact size of the header.
It is? In vhost_net_build_xdp() it has:
int pad = SKB_DATA_ALIGN(VHOST_NET_RX_PAD + headroom + nvq->sock_hlen);
Btw, I plan to remove tun_xdp_hdr and just use vnet header:
https://www.spinics.net/lists/netdev/msg1098039.html
Thanks
Quoted 209 lines
Quoted 208 lines
drivers/net/tun.c +17
-1+ + tnl_hdr = (struct virtio_net_hdr_v1_hash_tunnel *)gso; +if (tun_vnet_hdr_tnl_to_skb (tun->flags, features, skb, tnl_hdr)) {atomic_long_inc (&tun->rx_frame_errors);kfree_skb (skb); ret = -EINVAL ;@@ -2812,6 +2849,8 @@ static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) } +#define PLAIN_GSO (NETIF_F_GSO_UDP_L4 | NETIF_F_TSO | NETIF_F_TSO6) +/* This is like a cut-down ethtool ops, except done via tun fd so no * privs required. */ static int set_offload (struct tun_struct *tun,unsigned long arg)@@ -2841,6 +2880,18 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) features |=NETIF_F_GSO_UDP_L4 ; arg &= ~(TUN_F_USO4 |TUN_F_USO6 ); } + +/* +* Tunnel offload is allowed only if some plain offload is +* available, too. +*/ +if (features &PLAIN_GSO && arg &TUN_F_UDP_TUNNEL_GSO ) { + features |=NETIF_F_GSO_UDP_TUNNEL ; +if (arg &TUN_F_UDP_TUNNEL_GSO_CSUM ) + features |=NETIF_F_GSO_UDP_TUNNEL_CSUM ; + arg &= ~(TUN_F_UDP_TUNNEL_GSO | +TUN_F_UDP_TUNNEL_GSO_CSUM ); + } }/* This gives the user a way to test for new features in future by @@ -2852,7 +2903,6 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) tun->dev->wanted_features &= ~TUN_USER_FEATURES ; tun->dev->wanted_features |= features;netdev_update_features (tun->dev); -return 0 ; }drivers/net/tun_vnet.h +79
-9@@ -6,6 +6,8 @@#define TUN_VNET_LE 0x80000000#define TUN_VNET_BE 0x40000000 +#define TUN_VNET_TNL_SIZE sizeof(struct virtio_net_hdr_v1_hash_tunnel) +static inline bool tun_vnet_legacy_is_little_endian (unsigned int flags) { bool be = IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE) &&@@ -107,16 +109,26 @@ static inline long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags, } } -static inline int tun_vnet_hdr_get (int sz,unsigned int flags, -struct iov_iter *from, -struct virtio_net_hdr *hdr) +static inline unsigned int tun_vnet_parse_size (netdev_features_t features) +{ +if (!(features &NETIF_F_GSO_UDP_TUNNEL )) +return sizeof (struct virtio_net_hdr ); + +return TUN_VNET_TNL_SIZE ; +} + +static inline int __tun_vnet_hdr_get (int sz,unsigned int flags, +netdev_features_t features, +struct iov_iter *from, +struct virtio_net_hdr *hdr) { +unsigned int parsed_size =tun_vnet_parse_size (features);u16 hdr_len;if (iov_iter_count (from) < sz)return -EINVAL ; -if (!copy_from_iter_full (hdr,sizeof (*hdr), from)) +if (!copy_from_iter_full (hdr, parsed_size, from))return -EFAULT ; hdr_len =tun_vnet16_to_cpu (flags, hdr->hdr_len);@@ -129,32 +141,59 @@ static inline int tun_vnet_hdr_get(int sz, unsigned int flags,if (hdr_len >iov_iter_count (from))return -EINVAL ; -iov_iter_advance (from, sz -sizeof (*hdr)); +iov_iter_advance (from, sz - parsed_size);return hdr_len; } -static inline int tun_vnet_hdr_put (int sz,struct iov_iter *iter, -const struct virtio_net_hdr *hdr) +static inline int tun_vnet_hdr_get (int sz,unsigned int flags, +struct iov_iter *from, +struct virtio_net_hdr *hdr) +{ +return __tun_vnet_hdr_get (sz, flags,0 , from, hdr); +} + +static inline int __tun_vnet_hdr_put (int sz,netdev_features_t features, +struct iov_iter *iter, +const struct virtio_net_hdr *hdr) { +unsigned int parsed_size =tun_vnet_parse_size (features); +if (unlikely (iov_iter_count (iter) < sz))return -EINVAL ; -if (unlikely (copy_to_iter (hdr,sizeof (*hdr), iter) !=sizeof (*hdr))) +if (unlikely (copy_to_iter (hdr, parsed_size, iter) != parsed_size))return -EFAULT ; -if (iov_iter_zero (sz -sizeof (*hdr), iter) != sz -sizeof (*hdr)) +if (iov_iter_zero (sz - parsed_size, iter) != sz - parsed_size)return -EFAULT ;return 0 ; } +static inline int tun_vnet_hdr_put (int sz,struct iov_iter *iter, +const struct virtio_net_hdr *hdr) +{ +return __tun_vnet_hdr_put (sz,0 , iter, hdr); +} +static inline int tun_vnet_hdr_to_skb (unsigned int flags,struct sk_buff *skb,const struct virtio_net_hdr *hdr) {return virtio_net_hdr_to_skb (skb, hdr,tun_vnet_is_little_endian (flags)); } +static inline int +tun_vnet_hdr_tnl_to_skb (unsigned int flags,netdev_features_t features, +struct sk_buff *skb, +const struct virtio_net_hdr_v1_hash_tunnel *hdr) +{ +return virtio_net_hdr_tnl_to_skb (skb, hdr, + !!(features &NETIF_F_GSO_UDP_TUNNEL ), + !!(features &NETIF_F_GSO_UDP_TUNNEL_CSUM ), +tun_vnet_is_little_endian (flags)); +} + static inline int tun_vnet_hdr_from_skb(unsigned int flags, const struct net_device *dev, const struct sk_buff *skb,@@ -183,4 +222,35 @@ static inline int tun_vnet_hdr_from_skb(unsigned int flags,return 0 ; } +static inline int +tun_vnet_hdr_tnl_from_skb (unsigned int flags, +const struct net_device *dev, +const struct sk_buff *skb, +struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr) +{ +bool has_tnl_offload = !!(dev->features &NETIF_F_GSO_UDP_TUNNEL ); +int vlan_hlen =skb_vlan_tag_present (skb) ?VLAN_HLEN :0 ; + +if (virtio_net_hdr_tnl_from_skb (skb, tnl_hdr, has_tnl_offload, +tun_vnet_is_little_endian (flags), + vlan_hlen)) { +struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr; +struct skb_shared_info *sinfo =skb_shinfo (skb); + +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)); +print_hex_dump (KERN_ERR ,"tun: " , +DUMP_PREFIX_NONE , +16 ,1 , skb->head, +min (tun_vnet16_to_cpu (flags, hdr->hdr_len),64 ), true); + } +WARN_ON_ONCE (1 ); +return -EINVAL ; + } + +return 0 ; +} +#endif /* TUN_VNET_H */ include/uapi/linux/if_tun.h +9
-0@@ -93,6 +93,15 @@#define TUN_F_USO4 0x20/* I can handle USO for IPv4 packets */ #define TUN_F_USO6 0x40/* I can handle USO for IPv6 packets */ +/* I can handle TSO/USO for UDP tunneled packets */ +#define TUN_F_UDP_TUNNEL_GSO 0x080 + +/* +* I can handle TSO/USO for UDP tunneled packets requiring csum offload for +* the outer header +*/ +#define TUN_F_UDP_TUNNEL_GSO_CSUM 0x100 +/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ #define TUN_PKT_STRIP 0x0001struct tun_pi {