TIPC GSO is implemented in the skb frag_list way as SCTP does.

We don't need to change much in the tx path, but only create
a head skb and append the skbs when there are more than one
skb ready to send. In the lower-layer gso_segment(), it does
fragmentation by copy eth header or ip/udp header to each
skb in the head_skb's frag_list and send them one by one.

This supports with both eth media and udp media.

Signed-off-by: Xin Long <lucien....@gmail.com>
---
 net/tipc/bearer.c    | 23 +++++++++++++++++++++--
 net/tipc/msg.h       |  1 +
 net/tipc/offload.c   | 41 +++++++++++++++++++++++++++++++++++++++++
 net/tipc/udp_media.c |  7 +++++++
 4 files changed, 70 insertions(+), 2 deletions(-)

diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 443f8e5b9477..b0321b21bfdc 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -570,8 +570,9 @@ void tipc_bearer_xmit(struct net *net, u32 bearer_id,
                      struct tipc_media_addr *dst,
                      struct tipc_node *__dnode)
 {
+       struct sk_buff *head = NULL, *skb, *tmp;
        struct tipc_bearer *b;
-       struct sk_buff *skb, *tmp;
+       u16 segs = 0;
 
        if (skb_queue_empty(xmitq))
                return;
@@ -585,13 +586,31 @@ void tipc_bearer_xmit(struct net *net, u32 bearer_id,
                if (likely(test_bit(0, &b->up) || msg_is_reset(buf_msg(skb)))) {
 #ifdef CONFIG_TIPC_CRYPTO
                        tipc_crypto_xmit(net, &skb, b, dst, __dnode);
-                       if (skb)
+                       if (!skb)
+                               continue;
 #endif
+                       if (!skb->ignore_df) { /* PLPMTUD probe packet*/
                                b->media->send_msg(net, skb, b, dst);
+                               continue;
+                       }
+                       if (!head) {
+                               segs = 1;
+                               head = skb;
+                               continue;
+                       }
+                       if (tipc_msg_gso_append(&head, skb, segs)) {
+                               segs++;
+                               continue;
+                       }
+                       b->media->send_msg(net, head, b, dst);
+                       segs = 1;
+                       head = skb;
                } else {
                        kfree_skb(skb);
                }
        }
+       if (head)
+               b->media->send_msg(net, head, b, dst);
        rcu_read_unlock();
 }
 
diff --git a/net/tipc/msg.h b/net/tipc/msg.h
index d6c6231b8208..4d1ff666790c 100644
--- a/net/tipc/msg.h
+++ b/net/tipc/msg.h
@@ -1205,6 +1205,7 @@ bool __tipc_skb_queue_sorted(struct sk_buff_head *list, 
u16 seqno,
 bool tipc_msg_skb_clone(struct sk_buff_head *msg, struct sk_buff_head *cpy);
 void tipc_offload_init(void);
 void tipc_offload_exit(void);
+bool tipc_msg_gso_append(struct sk_buff **p, struct sk_buff *skb, u16 segs);
 
 static inline u16 buf_seqno(struct sk_buff *skb)
 {
diff --git a/net/tipc/offload.c b/net/tipc/offload.c
index f8a81c8886f0..d137679f4db0 100644
--- a/net/tipc/offload.c
+++ b/net/tipc/offload.c
@@ -18,6 +18,47 @@ static struct packet_offload tipc_packet_offload 
__read_mostly = {
        },
 };
 
+bool tipc_msg_gso_append(struct sk_buff **p, struct sk_buff *skb, u16 segs)
+{
+       struct sk_buff *head = *p;
+       struct sk_buff *nskb;
+
+       if (head->len + skb->len >= 65535)
+               return false;
+
+       if (segs == 1) {
+               nskb = tipc_buf_acquire(0, GFP_ATOMIC);
+               if (!nskb)
+                       return false;
+
+               nskb->ip_summed = CHECKSUM_UNNECESSARY;
+               nskb->truesize += head->truesize;
+               nskb->data_len += head->len;
+               nskb->len += head->len;
+               TIPC_SKB_CB(nskb)->tail = head;
+
+               skb_shinfo(nskb)->frag_list = head;
+               skb_shinfo(nskb)->gso_segs = 1;
+               skb_shinfo(nskb)->gso_type = SKB_GSO_TIPC;
+               skb_shinfo(nskb)->gso_size = GSO_BY_FRAGS;
+               skb_reset_network_header(head);
+
+               head = nskb;
+               *p = head;
+       }
+
+       head->truesize += skb->truesize;
+       head->data_len += skb->len;
+       head->len += skb->len;
+       TIPC_SKB_CB(head)->tail->next = skb;
+       TIPC_SKB_CB(head)->tail = skb;
+
+       skb_shinfo(head)->gso_segs++;
+       skb_reset_network_header(skb);
+
+       return true;
+}
+
 void tipc_offload_init(void)
 {
        dev_add_offload(&tipc_packet_offload);
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index 5078c5b19e81..7da02db6a50e 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -245,6 +245,13 @@ static int tipc_udp_send_msg(struct net *net, struct 
sk_buff *skb,
                        goto out;
        }
 
+       if (skb_is_gso(skb))
+               skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
+
+       skb->encapsulation = 1;
+       skb_reset_inner_mac_header(skb);
+       skb_reset_inner_network_header(skb);
+       skb_reset_inner_transport_header(skb);
        skb_set_inner_protocol(skb, htons(ETH_P_TIPC));
        ub = rcu_dereference(b->media_ptr);
        if (!ub) {
-- 
2.27.0



_______________________________________________
tipc-discussion mailing list
tipc-discussion@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tipc-discussion

Reply via email to