From: Chia-Yu Chang <[email protected]>

virtio_net currently negotiates ECN-related capabilities through
VIRTIO_NET_F_HOST_ECN and VIRTIO_NET_F_GUEST_ECN. This is not sufficient
for flows using AccECN (RFC9768), because AccECN requires preserving the
ACE signal (CWR flag is part of it) across GSO operations. Without
explicit AccECN capability bits, the device and driver may treat AccECN
traffic using the RFC3168 ECN offload logic, causing the CWR flag to be
cleared. As a result, AccECN segments may lose their ACE signal integrity.

Fix this by adding new AccECN capability bits for negotiation between
host and guest: VIRTIO_NET_F_HOST_ACCECN and VIRTIO_NET_F_GUEST_ACCECN.
In addition, translate the AccECN GSO flag correctly between the virtio
header (VIRTIO_NET_HDR_GSO_ACCECN) and skb metadata (SKB_GSO_TCP_ACCECN)
to ensure correct ACE signal preservation bwtwen virtio_net and the
socket stacki.

This corresponds to discussions in virtio mailing list:
https://lore.kernel.org/all/[email protected]/
And it was suggested to clarify documents of SKB_GSO_TCP_ECN and
SKB_GSO_TCP_ACCECN first.

Signed-off-by: Chia-Yu Chang <[email protected]>

---
v3:
- Update commit message and title for clarity

v2:
- Replace VIRTIO_NET_HDR_GSO_ECN with VIRTIO_NET_HDR_GSO_ECN_FLAGS
---
 drivers/net/virtio_net.c        | 14 +++++++++++---
 drivers/vdpa/pds/debugfs.c      |  6 ++++++
 include/linux/virtio_net.h      | 18 +++++++++++-------
 include/uapi/linux/virtio_net.h |  5 +++++
 4 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index db88dcaefb20..103fb87c690e 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -75,6 +75,7 @@ static const unsigned long guest_offloads[] = {
        VIRTIO_NET_F_GUEST_TSO4,
        VIRTIO_NET_F_GUEST_TSO6,
        VIRTIO_NET_F_GUEST_ECN,
+       VIRTIO_NET_F_GUEST_ACCECN,
        VIRTIO_NET_F_GUEST_UFO,
        VIRTIO_NET_F_GUEST_CSUM,
        VIRTIO_NET_F_GUEST_USO4,
@@ -87,6 +88,7 @@ static const unsigned long guest_offloads[] = {
 #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_ACCECN) | \
                        (1ULL << VIRTIO_NET_F_GUEST_UFO)  | \
                        (1ULL << VIRTIO_NET_F_GUEST_USO4) | \
                        (1ULL << VIRTIO_NET_F_GUEST_USO6) | \
@@ -5976,6 +5978,7 @@ static int virtnet_xdp_set(struct net_device *dev, struct 
bpf_prog *prog,
            && (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) ||
+               virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ACCECN) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO4) ||
@@ -6635,6 +6638,7 @@ static bool virtnet_check_guest_gso(const struct 
virtnet_info *vi)
        return virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) ||
+               virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ACCECN) ||
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO) ||
                (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO4) &&
                virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO6));
@@ -6749,6 +6753,8 @@ static int virtnet_probe(struct virtio_device *vdev)
                        dev->hw_features |= NETIF_F_TSO6;
                if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN))
                        dev->hw_features |= NETIF_F_TSO_ECN;
+               if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ACCECN))
+                       dev->hw_features |= NETIF_F_GSO_ACCECN;
                if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_USO))
                        dev->hw_features |= NETIF_F_GSO_UDP_L4;
 
@@ -7169,9 +7175,11 @@ static struct virtio_device_id id_table[] = {
        VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM, \
        VIRTIO_NET_F_MAC, \
        VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, \
-       VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, 
VIRTIO_NET_F_GUEST_TSO6, \
-       VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, \
-       VIRTIO_NET_F_HOST_USO, VIRTIO_NET_F_GUEST_USO4, 
VIRTIO_NET_F_GUEST_USO6, \
+       VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_HOST_ACCECN, \
+       VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, \
+       VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_ACCECN, \
+       VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_USO, \
+       VIRTIO_NET_F_GUEST_USO4, VIRTIO_NET_F_GUEST_USO6, \
        VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, \
        VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, \
        VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ, \
diff --git a/drivers/vdpa/pds/debugfs.c b/drivers/vdpa/pds/debugfs.c
index c328e694f6e7..90bd95db0245 100644
--- a/drivers/vdpa/pds/debugfs.c
+++ b/drivers/vdpa/pds/debugfs.c
@@ -78,6 +78,9 @@ static void print_feature_bits_all(struct seq_file *seq, u64 
features)
                case BIT_ULL(VIRTIO_NET_F_GUEST_ECN):
                        seq_puts(seq, " VIRTIO_NET_F_GUEST_ECN");
                        break;
+               case BIT_ULL(VIRTIO_NET_F_GUEST_ACCECN):
+                       seq_puts(seq, " VIRTIO_NET_F_GUEST_ACCECN");
+                       break;
                case BIT_ULL(VIRTIO_NET_F_GUEST_UFO):
                        seq_puts(seq, " VIRTIO_NET_F_GUEST_UFO");
                        break;
@@ -90,6 +93,9 @@ static void print_feature_bits_all(struct seq_file *seq, u64 
features)
                case BIT_ULL(VIRTIO_NET_F_HOST_ECN):
                        seq_puts(seq, " VIRTIO_NET_F_HOST_ECN");
                        break;
+               case BIT_ULL(VIRTIO_NET_F_HOST_ACCECN):
+                       seq_puts(seq, " VIRTIO_NET_F_HOST_ACCECN");
+                       break;
                case BIT_ULL(VIRTIO_NET_F_HOST_UFO):
                        seq_puts(seq, " VIRTIO_NET_F_HOST_UFO");
                        break;
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 75dabb763c65..0cf86b026828 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -11,7 +11,7 @@
 
 static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
 {
-       switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
+       switch (gso_type & ~VIRTIO_NET_HDR_GSO_ECN_FLAGS) {
        case VIRTIO_NET_HDR_GSO_TCPV4:
                return protocol == cpu_to_be16(ETH_P_IP);
        case VIRTIO_NET_HDR_GSO_TCPV6:
@@ -31,7 +31,7 @@ static inline int virtio_net_hdr_set_proto(struct sk_buff 
*skb,
        if (skb->protocol)
                return 0;
 
-       switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
+       switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN_FLAGS) {
        case VIRTIO_NET_HDR_GSO_TCPV4:
        case VIRTIO_NET_HDR_GSO_UDP:
        case VIRTIO_NET_HDR_GSO_UDP_L4:
@@ -58,7 +58,7 @@ static inline int __virtio_net_hdr_to_skb(struct sk_buff *skb,
        unsigned int ip_proto;
 
        if (hdr_gso_type != VIRTIO_NET_HDR_GSO_NONE) {
-               switch (hdr_gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
+               switch (hdr_gso_type & ~VIRTIO_NET_HDR_GSO_ECN_FLAGS) {
                case VIRTIO_NET_HDR_GSO_TCPV4:
                        gso_type = SKB_GSO_TCPV4;
                        ip_proto = IPPROTO_TCP;
@@ -84,7 +84,9 @@ 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_ACCECN)
+                       gso_type |= SKB_GSO_TCP_ACCECN;
+               else if (hdr_gso_type & VIRTIO_NET_HDR_GSO_ECN)
                        gso_type |= SKB_GSO_TCP_ECN;
 
                if (hdr->gso_size == 0)
@@ -159,7 +161,7 @@ static inline int __virtio_net_hdr_to_skb(struct sk_buff 
*skb,
                unsigned int nh_off = p_off;
                struct skb_shared_info *shinfo = skb_shinfo(skb);
 
-               switch (gso_type & ~SKB_GSO_TCP_ECN) {
+               switch (gso_type & ~(SKB_GSO_TCP_ECN | SKB_GSO_TCP_ACCECN)) {
                case SKB_GSO_UDP:
                        /* UFO may not include transport header in gso_size. */
                        nh_off -= thlen;
@@ -231,7 +233,9 @@ static inline int virtio_net_hdr_from_skb(const struct 
sk_buff *skb,
                        hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4;
                else
                        return -EINVAL;
-               if (sinfo->gso_type & SKB_GSO_TCP_ECN)
+               if (sinfo->gso_type & SKB_GSO_TCP_ACCECN)
+                       hdr->gso_type |= VIRTIO_NET_HDR_GSO_ACCECN;
+               else if (sinfo->gso_type & SKB_GSO_TCP_ECN)
                        hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
        } else
                hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
@@ -282,7 +286,7 @@ virtio_net_hdr_tnl_to_skb(struct sk_buff *skb,
                return -EINVAL;
 
        /* The UDP tunnel must carry a GSO packet, but no UFO. */
-       gso_inner_type = hdr->gso_type & ~(VIRTIO_NET_HDR_GSO_ECN |
+       gso_inner_type = hdr->gso_type & ~(VIRTIO_NET_HDR_GSO_ECN_FLAGS |
                                           VIRTIO_NET_HDR_GSO_UDP_TUNNEL);
        if (!gso_inner_type || gso_inner_type == VIRTIO_NET_HDR_GSO_UDP)
                return -EINVAL;
diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
index 1db45b01532b..af5bfe45aa1f 100644
--- a/include/uapi/linux/virtio_net.h
+++ b/include/uapi/linux/virtio_net.h
@@ -56,6 +56,8 @@
 #define VIRTIO_NET_F_MQ        22      /* Device supports Receive Flow
                                         * Steering */
 #define VIRTIO_NET_F_CTRL_MAC_ADDR 23  /* Set MAC address */
+#define VIRTIO_NET_F_HOST_ACCECN 25    /* Host can handle GSO of AccECN */
+#define VIRTIO_NET_F_GUEST_ACCECN 26   /* Guest can handle GSO of AccECN */
 #define VIRTIO_NET_F_DEVICE_STATS 50   /* Device can provide device-level 
statistics. */
 #define VIRTIO_NET_F_VQ_NOTF_COAL 52   /* Device supports virtqueue 
notification coalescing */
 #define VIRTIO_NET_F_NOTF_COAL 53      /* Device supports notifications 
coalescing */
@@ -165,6 +167,9 @@ struct virtio_net_hdr_v1 {
 #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 */
+#define VIRTIO_NET_HDR_GSO_ACCECN      0x10    /* TCP AccECN segmentation */
+#define VIRTIO_NET_HDR_GSO_ECN_FLAGS   (VIRTIO_NET_HDR_GSO_ECN | \
+                                        VIRTIO_NET_HDR_GSO_ACCECN)
        __u8 gso_type;
        __virtio16 hdr_len;     /* Ethernet + IP + tcp/udp hdrs */
        __virtio16 gso_size;    /* Bytes to append to hdr_len per frame */
-- 
2.34.1


Reply via email to