技术之美

暂无签名

  • 博客访问: 1069172
  • 博文数量: 133
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4832
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-23 18:56
个人简介

将晦涩难懂的技术讲的通俗易懂

文章分类

全部博文(133)

文章存档

2017年(7)

2016年(26)

2015年(18)

2014年(60)

2013年(22)

微信关注

IT168企业级官微



微信号:IT168qiye



系统架构师大会



微信号:SACC2013

订阅
热词专题
linux tcp GSO和TSO实现 2017-05-06 20:48:53

分类: LINUX

linux tcp GSOTSO实现

——lvyilong316

(注:kernel版本:linux 2.6.32)

概念

TSO(TCP Segmentation Offload): 是一种利用网卡来对大数据包进行自动分段,降低CPU负载的技术。 其主要是延迟分段。

GSO(Generic Segmentation Offload): GSO是协议栈是否推迟分段,在发送到网卡之前判断网卡是否支持TSO,如果网卡支持TSO则让网卡分段,否则协议栈分完段再交给驱动。 如果TSO开启,GSO会自动开启。

以下是TSOGSO的组合关系:

l  GSO开启, TSO开启协议栈推迟分段,并直接传递大数据包到网卡,让网卡自动分段

l  GSO开启, TSO关闭协议栈推迟分段,在最后发送到网卡前才执行分段

l  GSO关闭, TSO开启GSO开启, TSO开启

l  GSO关闭, TSO关闭不推迟分段,在tcp_sendmsg中直接发送MSS大小的数据包

开启GSO/TSO

驱动程序在注册网卡设备的时候默认开启GSO: NETIF_F_GSO

驱动程序会根据网卡硬件是否支持来设置TSO: NETIF_F_TSO

可以通过ethtool -K来开关GSO/TSO


点击(此处)折叠或打开

  1. #define NETIF_F_SOFT_FEATURES (NETIF_F_GSO | NETIF_F_GRO)
  2. int register_netdevice(struct net_device *dev)
  3. {
  4.               ...
  5.               /* Transfer changeable features to wanted_features and enable
  6.                * software offloads (GSO and GRO).
  7.                */
  8.               dev->hw_features |= NETIF_F_SOFT_FEATURES;
  9.               dev->features |= NETIF_F_SOFT_FEATURES; //默认开启GRO/GSO
  10.               dev->wanted_features = dev->features & dev->hw_features;
  11.               ...
  12. }
  13. static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  14. {
  15.               ...
  16.               netdev->features = NETIF_F_SG |
  17.                                               NETIF_F_TSO |
  18.                                               NETIF_F_TSO6 |
  19.                                               NETIF_F_RXHASH |
  20.                                               NETIF_F_RXCSUM |
  21.                                               NETIF_F_HW_CSUM;
  22.               register_netdev(netdev);
  23.               ...
  24. }


是否推迟分段

从上面我们知道GSO/TSO是否开启是保存在dev->features中,而设备和路由关联,当我们查询到路由后就可以把配置保存在sock中。

比如在tcp_v4_connecttcp_v4_syn_recv_sock都会调用sk_setup_caps来设置GSO/TSO配置。

需要注意的是,只要开启了GSO,即使硬件不支持TSO,也会设置NETIF_F_TSO,使得sk_can_gso(sk)GSO开启或者TSO开启的时候都返回true

 l  sk_setup_caps


点击(此处)折叠或打开

  1. #define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6)
  2. #define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT)

  3. void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
  4. {
  5.               __sk_dst_set(sk, dst);
  6.               sk->sk_route_caps = dst->dev->features;
  7.               if (sk->sk_route_caps & NETIF_F_GSO) /*GSO默认都会开启*/
  8.                              sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE; /*打开TSO*/
  9.               if (sk_can_gso(sk)) { /*对于tcp这里会成立*/
  10.                              if (dst->header_len) {
  11.                                            sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
  12.                              } else {
  13.                                            sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;
  14.                                            sk->sk_gso_max_size = dst->dev->gso_max_size; /*GSO_MAX_SIZE=65536*/
  15.                              }
  16.               }
  17. }


从上面可以看出,如果设备开启了GSOsock都会将TSO标志打开,但是注意这和硬件是否开启TSO无关,硬件的TSO取决于硬件自身特性的支持。下面看下sk_can_gso的逻辑。

l  sk_can_gso


点击(此处)折叠或打开

  1. static inline int sk_can_gso(const struct sock *sk)
  2. {
  3.     /*对于tcp,在tcp_v4_connect中被设置:sk->sk_gso_type = SKB_GSO_TCPV4*/
  4.               return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type);
  5. }


l  net_gso_ok


点击(此处)折叠或打开

  1. static inline int net_gso_ok(int features, int gso_type)
  2. {
  3.               int feature = gso_type << NETIF_F_GSO_SHIFT;
  4.               return (features & feature) == feature;
  5. }


由于对于tcp sk_setup_capssk->sk_route_caps也被设置有SKB_GSO_TCPV4,所以整个sk_can_gso成立。

GSO的数据包长度

对紧急数据包或GSO/TSO都不开启的情况,才不会推迟发送, 默认使用当前MSS。开启GSO后,tcp_send_mss返回mss和单个skbGSO大小,为mss的整数倍。

l  tcp_send_mss


点击(此处)折叠或打开

  1. static int tcp_send_mss(struct sock *sk, int *size_goal, int flags)
  2. {
  3.               int mss_now;
  4.  
  5.               mss_now = tcp_current_mss(sk);/*通过ip option,SACKs及pmtu确定当前的mss*/
  6.               *size_goal = tcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB));
  7.  
  8.               return mss_now;
  9. }


l  tcp_xmit_size_goal


点击(此处)折叠或打开

  1. static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
  2.                                                                 int large_allowed)
  3. {
  4.               struct tcp_sock *tp = tcp_sk(sk);
  5.               u32 xmit_size_goal, old_size_goal;
  6.  
  7.               xmit_size_goal = mss_now;
  8.     /*这里large_allowed表示是否是紧急数据*/
  9.               if (large_allowed && sk_can_gso(sk)) { /*如果不是紧急数据且支持GSO*/
  10.                              xmit_size_goal = ((sk->sk_gso_max_size - 1) -
  11.                                                            inet_csk(sk)->icsk_af_ops->net_header_len -
  12.                                                            inet_csk(sk)->icsk_ext_hdr_len -
  13.                                                            tp->tcp_header_len);/*xmit_size_goal为gso最大分段大小减去tcp和ip头部长度*/
  14.  
  15.                              xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal);/*最多达到收到的最大rwnd窗口通告的一半*/
  16.  
  17.                              /* We try hard to avoid divides here */
  18.                              old_size_goal = tp->xmit_size_goal_segs * mss_now;
  19.  
  20.                              if (likely(old_size_goal <= xmit_size_goal &&
  21.                                               old_size_goal + mss_now > xmit_size_goal)) {
  22.                                            xmit_size_goal = old_size_goal; /*使用老的xmit_size*/
  23.                              } else {
  24.                                            tp->xmit_size_goal_segs = xmit_size_goal / mss_now;
  25.                                            xmit_size_goal = tp->xmit_size_goal_segs * mss_now; /*使用新的xmit_size*/
  26.                              }
  27.               }
  28.  
  29.               return max(xmit_size_goal, mss_now);
  30. }


l  tcp_sendmsg

应用程序send()数据后,会在tcp_sendmsg中尝试在同一个skb,保存size_goal大小的数据,然后再通过tcp_push把这些包通过tcp_write_xmit发出去


点击(此处)折叠或打开

  1. int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
  2.                              size_t size)
  3. {
  4.     struct sock *sk = sock->sk;
  5.     struct iovec *iov;
  6.     struct tcp_sock *tp = tcp_sk(sk);
  7.     struct sk_buff *skb;
  8.     int iovlen, flags;
  9.     int mss_now, size_goal;
  10.     int err, copied;
  11.     long timeo;
  12.  
  13.     lock_sock(sk);
  14.     TCP_CHECK_TIMER(sk);
  15.  
  16.     flags = msg->msg_flags;
  17.     timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
  18.  
  19.     /* Wait for a connection to finish. */
  20.     if ((<< sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
  21.         if ((err = sk_stream_wait_connect(sk, &timeo)) != 0)
  22.              goto out_err;
  23.  
  24.     /* This should be in poll */
  25.     clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
  26.     /* size_goal表示GSO支持的大小,为mss的整数倍,不支持GSO时则和mss相等 */
  27.     mss_now = tcp_send_mss(sk, &size_goal, flags);/*返回值mss_now为真实mss*/
  28.  
  29.     /* Ok commence sending. */
  30.     iovlen = msg->msg_iovlen;
  31.     iov = msg->msg_iov;
  32.     copied = 0;
  33.  
  34.     err = -EPIPE;
  35.     if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
  36.         goto out_err;
  37.  
  38.     while (--iovlen >= 0) {
  39.         size_t seglen = iov->iov_len;
  40.         unsigned char __user *from = iov->iov_base;
  41.  
  42.         iov++;
  43.  
  44.         while (seglen > 0) {
  45.             int copy = 0;
  46.             int max = size_goal; /*每个skb中填充的数据长度初始化为size_goal*/
  47.             /* 从sk->sk_write_queue中取出队尾的skb,因为这个skb可能还没有被填满 */
  48.             skb = tcp_write_queue_tail(sk);
  49.             if (tcp_send_head(sk)) { /*如果之前还有未发送的数据*/
  50.                 if (skb->ip_summed == CHECKSUM_NONE) /*比如路由变更,之前的不支持TSO,现在的支持了*/
  51.                     max = mss_now; /*上一个不支持GSO的skb,继续不支持*/
  52.                 copy = max - skb->len; /*copy为每次想skb中拷贝的数据长度*/
  53.             }
  54.            /*copy<=0表示不能合并到之前skb做GSO*/
  55.            if (copy <= 0) {
  56. new_segment:
  57.                /* Allocate new segment. If the interface is SG,
  58.                 * allocate skb fitting to single page.
  59.                 */
  60.                /* 内存不足,需要等待 */
  61.                if (!sk_stream_memory_free(sk))
  62.                    goto wait_for_sndbuf;
  63.                /* 分配新的skb */
  64.                skb = sk_stream_alloc_skb(sk, select_size(sk)sk->sk_allocation);
  65.                if (!skb)
  66.                    goto wait_for_memory;
  67.  
  68.                /*
  69.                 * Check whether we can use HW checksum.
  70.                 */
  71.                /*如果硬件支持checksum,则将skb->ip_summed设置为CHECKSUM_PARTIAL,表示由硬件计算校验和*/
  72.                if (sk->sk_route_caps & NETIF_F_ALL_CSUM)
  73.                    skb->ip_summed = CHECKSUM_PARTIAL;
  74.                /*将skb加入sk->sk_write_queue队尾, 同时去掉skb的TCP_NAGLE_PUSH标记*/
  75.                skb_entail(sk, skb);
  76.                copy = size_goal; /*这里将每次copy的大小设置为size_goal,即GSO支持的大小*/
  77.                max = size_goal;
  78.            }
  79.  
  80.            /* Try to append data to the end of skb. */
  81.            if (copy > seglen)
  82.                copy = seglen;
  83.  
  84.            /* Where to copy to? */
  85.            if (skb_tailroom(skb) > 0) { /*如果skb的线性区还有空间,则先填充skb的线性区*/
  86.                /* We have some space in skb head. */
  87.                if (copy > skb_tailroom(skb))
  88.                    copy = skb_tailroom(skb);
  89.                if ((err = skb_add_data(skb, from, copy)) != 0) /*copy用户态数据到skb线性区*/
  90.                    goto do_fault;
  91.            } else { /*否则尝试向SG的frags中拷贝*/
  92.                int merge = 0;
  93.                int i = skb_shinfo(skb)->nr_frags;
  94.                struct page *page = TCP_PAGE(sk);
  95.                int off = TCP_OFF(sk);
  96.  
  97.                if (skb_can_coalesce(skb, i, page, off) &off != PAGE_SIZE) {/*pfrag->page和frags[i-1]是否使用相同页,并且page_offset相同*/
  98.                    /* We can extend the last page
  99.                    * fragment. */
  100.                    merge = 1; /*说明和之前frags中是同一个page,需要merge*/
  101.                } else if (== MAX_SKB_FRAGS |(!&& !(sk->sk_route_caps & NETIF_F_SG))) {
  102.                    /* Need to add new fragment and cannot
  103.                     * do this because interface is non-SG,
  104.                     * or because all the page slots are
  105.                     * busy. */
  106.                    /*如果设备不支持SG,或者非线性区frags已经达到最大,则创建新的skb分段*/
  107.                    tcp_mark_push(tp, skb); /*标记push flag*/
  108.                        goto new_segment;
  109.                } else if (page) {
  110.                    if (off == PAGE_SIZE) {
  111.                        put_page(page); /*增加page引用计数*/
  112.                        TCP_PAGE(sk) = page = NULL;
  113.                        off = 0;
  114.                    }
  115.               } else
  116.                   off = 0;
  117.                   if (copy > PAGE_SIZE - off)
  118.                   copy = PAGE_SIZE - off;
  119.                   if (!sk_wmem_schedule(sk, copy))
  120.                       goto wait_for_memory;
  121.  
  122.                  if (!page) {
  123.                      /* Allocate new cache page. */
  124.                      if (!(page = sk_stream_alloc_page(sk)))
  125.                      goto wait_for_memory;
  126.                  }
  127.  
  128.                  err = skb_copy_to_page(sk, from, skb, page, off, copy); /*拷贝数据到page中*/
  129.                  if (err) {
  130.                      /* If this page was new, give it to the
  131.                       * socket so it does not get leaked.
  132.                       */
  133.                      if (!TCP_PAGE(sk)) {
  134.                          TCP_PAGE(sk) = page;
  135.                          TCP_OFF(sk) = 0;
  136.                      }
  137.                      goto do_error;
  138.                 }
  139.  
  140.                 /* Update the skb. */
  141.                 if (merge) { /*pfrag和frags[- 1]是相同的*/
  142.                      skb_shinfo(skb)->frags[- 1].size += copy;
  143.                 } else {
  144.                     skb_fill_page_desc(skb, i, page, off, copy);
  145.                     if (TCP_PAGE(sk)) {
  146.                         get_page(page);
  147.                     } else if (off + copy < PAGE_SIZE) {
  148.                         get_page(page);
  149.                         TCP_PAGE(sk) = page;
  150.                     }
  151.                 }
  152.                 TCP_OFF(sk) = off + copy;
  153.             }
  154.             if (!copied)
  155.                 TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_PSH;
  156.  
  157.             tp->write_seq += copy;
  158.             TCP_SKB_CB(skb)->end_seq += copy;
  159.             skb_shinfo(skb)->gso_segs = 0; /*清零tso分段数,让tcp_write_xmit去计算*/
  160.             from += copy;
  161.             copied += copy;
  162.             if ((seglen -= copy) == 0 && iovlen == 0)
  163.                 goto out;
  164.             /* 还有数据没copy,并且没有达到最大可拷贝的大小(注意这里max之前被赋值为size_goal,即GSO支持的大小), 尝试往该skb继续添加数据*/
  165.             if (skb->len < max || (flags & MSG_OOB))
  166.                 continue;
  167.             /*下面的逻辑就是:还有数据没copy,但是当前skb已经满了,所以可以发送了(但不是一定要发送)*/
  168.             if (forced_push(tp)) { /*超过最大窗口的一半没有设置push了*/
  169.                 tcp_mark_push(tp, skb); /*设置push标记,更新pushed_seq*/
  170.                 __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH); /*调用tcp_write_xmit马上发送*/
  171.             } else if (skb == tcp_send_head(sk)) /*第一个包,直接发送*/
  172.                 tcp_push_one(sk, mss_now);
  173.                 continue; /*说明发送队列前面还有skb等待发送,且距离之前push的包还不是非常久*/
  174. wait_for_sndbuf:
  175.                 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
  176. wait_for_memory:
  177.                 if (copied)/*先把copied的发出去再等内存*/
  178.                     tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
  179.                /*阻塞等待内存*/
  180.                if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
  181.                    goto do_error;
  182.                mss_now = tcp_send_mss(sk, &size_goal, flags);
  183.           }
  184.       }
  185.  
  186. out:
  187.       if (copied) /*所有数据都放到发送队列中了,调用tcp_push发送*/
  188.           tcp_push(sk, flags, mss_now, tp->nonagle);
  189.       TCP_CHECK_TIMER(sk);
  190.       release_sock(sk);
  191.       return copied;
  192.  
  193. do_fault:
  194.       if (!skb->len) {
  195.           tcp_unlink_write_queue(skb, sk);
  196.           /* It is the one place in all of TCP, except connection
  197.            * reset, where we can be unlinking the send_head.
  198.            */
  199.           tcp_check_send_head(sk, skb);
  200.           sk_wmem_free_skb(sk, skb);
  201.       }
  202.  
  203. do_error:
  204.       if (copied)
  205.           goto out;
  206. out_err:
  207.       err = sk_stream_error(sk, flags, err);
  208.       TCP_CHECK_TIMER(sk);
  209.       release_sock(sk);
  210.       return err;
  211. }

     最终会调用tcp_push发送skb,而tcp_push又会调用tcp_write_xmittcp_sendmsg已经把数据按照GSO最大的size,放到一个个的skb中, 最终调用tcp_write_xmit发送这些GSOtcp_write_xmit会检查当前的拥塞窗口,还有nagle测试,tsq检查来决定是否能发送整个或者部分的skb 如果只能发送一部分,则需要调用tso_fragment做切分。最后通过tcp_transmit_skb发送, 如果发送窗口没有达到限制,skb中存放的数据将达到GSO最大值

l  tcp_write_xmit


点击(此处)折叠或打开

  1. static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
  2.                                              int push_one, gfp_t gfp)
  3. {
  4.     struct tcp_sock *tp = tcp_sk(sk);
  5.     struct sk_buff *skb;
  6.     unsigned int tso_segs, sent_pkts;
  7.     int cwnd_quota;
  8.     int result;
  9.  
  10.     sent_pkts = 0;
  11.  
  12.     if (!push_one) {
  13.         /* Do MTU probing. */
  14.         result = tcp_mtu_probe(sk);
  15.         if (!result) {
  16.             return 0;
  17.         } else if (result > 0) {
  18.             sent_pkts = 1;
  19.         }
  20.     }
  21.     /*遍历发送队列*/
  22.     while ((skb = tcp_send_head(sk))) {
  23.         unsigned int limit;
  24.  
  25.         tso_segs = tcp_init_tso_segs(sk, skb, mss_now); /*skb->len/mss,重新设置tcp_gso_segs,因为在tcp_sendmsg中被清零了*/
  26.         BUG_ON(!tso_segs);
  27.  
  28.         cwnd_quota = tcp_cwnd_test(tp, skb);
  29.         if (!cwnd_quota)
  30.             break;
  31.  
  32.         if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now)))
  33.             break;
  34.  
  35.         if (tso_segs == 1) { /*tso_segs=1表示无需tso分段*/
  36.             /* 根据nagle算法,计算是否需要推迟发送数据 */
  37.             if (unlikely(!tcp_nagle_test(tp, skb, mss_now(tcp_skb_is_last(sk, skb) ? /*last skb就直接发送*/
  38.                            nonagle : TCP_NAGLE_PUSH))))
  39.                 break;
  40.         } else {/*有多个tso分段*/
  41.             if (!push_one /*push所有skb*/
  42.                  && tcp_tso_should_defer(sk, skb))/*/如果发送窗口剩余不多,并且预计下一个ack将很快到来(意味着可用窗口会增加),则推迟发送*/
  43.                break;
  44.         }
  45.         /*下面的逻辑是:不用推迟发送,马上发送的情况*/
  46.         limit = mss_now;
  47.         /*由于tso_segs被设置为skb->len/mss_now,所以开启gso时一定大于1*/
  48.         if (tso_segs > 1 && !tcp_urg_mode(tp)) /*tso分段大于1且非urg模式*/
  49.             limit = tcp_mss_split_point(sk, skb, mss_now, cwnd_quota);/*返回当前skb中可以发送的数据大小,通过mss和cwnd*/
  50.         /* 当skb的长度大于限制时,需要调用tso_fragment分片,如果分段失败则暂不发送 */
  51.         if (skb->len > limit &&
  52.              unlikely(tso_fragment(sk, skb, limit, mss_now))) /*/按limit切割成多个skb*/
  53.             break;
  54.  
  55.         TCP_SKB_CB(skb)->when = tcp_time_stamp;
  56.         /*发送,如果包被qdisc丢了,则退出循环,不继续发送了*/
  57.         if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
  58.             break;
  59.  
  60.         /* Advance the send_head. This one is sent out.
  61.          * This call will increment packets_out.
  62.          */
  63.         /*更新sk_send_head和packets_out*/
  64.         tcp_event_new_data_sent(sk, skb);
  65.         tcp_minshall_update(tp, mss_now, skb);
  66.         sent_pkts++;
  67.  
  68.         if (push_one)
  69.             break;
  70.     }
  71.  
  72.     if (likely(sent_pkts)) {
  73.         tcp_cwnd_validate(sk);
  74.         return 0;
  75.     }
  76.     return !tp->packets_out && tcp_send_head(sk);
  77. }

    其中tcp_init_tso_segs会设置skbgso信息后文分析。我们看到tcp_write_xmit 会调用tso_fragment进行“tcp分段”。而分段的条件是skb->len > limit。这里的关键就是limit的值,我们看到在tso_segs > 1时,也就是开启gso的时候,limit的值是由tcp_mss_split_point得到的,也就是min(skb->len, window),即发送窗口允许的最大值。在没有开启gsolimit就是当前的mss

l  tcp_init_tso_segs

 点击(此处)折叠或打开


  1. static int tcp_init_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now)
  2. {
  3.     int tso_segs = tcp_skb_pcount(skb); /*skb_shinfo(skb)->gso_seg之前被初始化为0*/
  4.  
  5.     if (!tso_segs || (tso_segs > 1 && tcp_skb_mss(skb) != mss_now)) {
  6.         tcp_set_skb_tso_segs(sk, skb, mss_now);
  7.         tso_segs = tcp_skb_pcount(skb);
  8.     }
  9.     return tso_segs;
  10. }

点击(此处)折叠或打开

  1. static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now)
  2. {
  3.     /* Make sure we own this skb before messing gso_size/gso_segs */
  4.     WARN_ON_ONCE(skb_cloned(skb));
  5.     if (skb->len <= mss_now || !sk_can_gso(sk) ||
  6.         skb->ip_summed == CHECKSUM_NONE) {/*不支持gso的情况*/
  7.        /* Avoid the costly divide in the normal
  8.         * non-TSO case.
  9.         */
  10.        skb_shinfo(skb)->gso_segs = 1;
  11.        skb_shinfo(skb)->gso_size = 0;
  12.        skb_shinfo(skb)->gso_type = 0;
  13.     } else {
  14.         skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now); /*被设置为skb->len/mss_now*/
  15.         skb_shinfo(skb)->gso_size = mss_now; /*注意mss_now为真实的mss,这里保存以供gso分段使用*/
  16.         skb_shinfo(skb)->gso_type = sk->sk_gso_type;
  17.     }
  18. }

tcp_write_xmit最后会调用ip_queue_xmit发送skb,进入ip层。

ip分片,tcp分段,GSOTSO

之后的逻辑就是之前另一篇文章中分析的GSO逻辑了。下面我们看下整个协议栈中ip分片,tcp分段,GSOTSO的关系。我将这个流程由下图表示。

 

阅读(4988) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册