zoukankan      html  css  js  c++  java
  • TCP输出 之 tcp_write_xmit

    概述

    tcp_write_xmit函数完成对待发送数据的分段发送,过程中会遍历发送队列,进行窗口检查,需要TSO分段则分段,然后调用tcp_transmit_skb发送数据段;

    源码分析
      1 static bool 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     bool is_cwnd_limited = false, is_rwnd_limited = false;
     10     u32 max_segs;
     11 
     12     /* 已发送数据段数量 */
     13     sent_pkts = 0;
     14 
     15     /* 发送多个数据段 */
     16     if (!push_one) {
     17         /* Do MTU probing. */
     18         /* 发送路径mtu探测 */
     19         result = tcp_mtu_probe(sk);
     20         /* 失败 */
     21         if (!result) {
     22             return false;
     23         }
     24         /* 成功,设置已发送数据段数为1 */
     25         else if (result > 0) {
     26             sent_pkts = 1;
     27         }
     28     }
     29 
     30     /* 获取最大tso分段 */
     31     max_segs = tcp_tso_segs(sk, mss_now);
     32 
     33     /* 有数据段要发送 */
     34     while ((skb = tcp_send_head(sk))) {
     35         unsigned int limit;
     36 
     37         /* 初始化tso分段相关 */
     38         tso_segs = tcp_init_tso_segs(skb, mss_now);
     39         BUG_ON(!tso_segs);
     40 
     41         if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) {
     42             /* "skb_mstamp" is used as a start point for the retransmit timer */
     43             skb_mstamp_get(&skb->skb_mstamp);
     44             goto repair; /* Skip network transmission */
     45         }
     46 
     47         /* 检测拥塞窗口大小 */
     48         cwnd_quota = tcp_cwnd_test(tp, skb);
     49         /* 为0 */
     50         if (!cwnd_quota) {
     51             /* 尾部丢失探测段,设置为1 */
     52             if (push_one == 2)
     53                 /* Force out a loss probe pkt. */
     54                 cwnd_quota = 1;
     55             /* 其他情况,跳出 */
     56             else
     57                 break;
     58         }
     59 
     60         /* 检查tcp的数据段是否在发送窗口之内 */
     61         if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) {
     62             /* 不在,标记,跳出 */
     63             is_rwnd_limited = true;
     64             break;
     65         }
     66 
     67         /* 不需要tso分段 */
     68         if (tso_segs == 1) {
     69             /* 检查nagle算法是否允许发送数据段 */
     70             if (unlikely(!tcp_nagle_test(tp, skb, mss_now,
     71                              (tcp_skb_is_last(sk, skb) ?
     72                               nonagle : TCP_NAGLE_PUSH))))
     73                 break;
     74         } 
     75         /* 需要tso分段 */
     76         else {
     77             /* 检查是否可以延迟发送 */
     78             if (!push_one &&
     79                 tcp_tso_should_defer(sk, skb, &is_cwnd_limited,
     80                          max_segs))
     81                 break;
     82         }
     83 
     84         /* 设置分段长度限制为mss */
     85         limit = mss_now;
     86 
     87         /* 需要分段 && 非紧急模式,重新确定分段长度限制 */
     88         if (tso_segs > 1 && !tcp_urg_mode(tp))
     89             limit = tcp_mss_split_point(sk, skb, mss_now,
     90                             min_t(unsigned int,
     91                               cwnd_quota,
     92                               max_segs),
     93                             nonagle);
     94 
     95         /* skb中数据段长度>分段长度限制,则进行分段,会申请新的skb */
     96         if (skb->len > limit &&
     97             unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
     98             break;
     99 
    100         if (test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags))
    101             clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags);
    102         if (tcp_small_queue_check(sk, skb, 0))
    103             break;
    104 
    105         /* 发送分段数据 */
    106         if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
    107             break;
    108 
    109 repair:
    110         /* Advance the send_head.  This one is sent out.
    111          * This call will increment packets_out.
    112          */
    113         /* 进行发送之后的数据更新,包括统计计数和定时器等 */
    114         tcp_event_new_data_sent(sk, skb);
    115 
    116         /* 更新最新发送小包的结束序号 */
    117         tcp_minshall_update(tp, mss_now, skb);
    118 
    119         /* 更新发送数据段数量 */
    120         sent_pkts += tcp_skb_pcount(skb);
    121 
    122         /* 只发送一个段,则跳出 */
    123         if (push_one)
    124             break;
    125     }
    126 
    127     if (is_rwnd_limited)
    128         tcp_chrono_start(sk, TCP_CHRONO_RWND_LIMITED);
    129     else
    130         tcp_chrono_stop(sk, TCP_CHRONO_RWND_LIMITED);
    131 
    132     /* 本次有数据发送,拥塞相关数据更新 */
    133     if (likely(sent_pkts)) {
    134         if (tcp_in_cwnd_reduction(sk))
    135             tp->prr_out += sent_pkts;
    136 
    137         /* Send one loss probe per tail loss episode. */
    138         /* 每次发送一个尾部丢失探测 */
    139         if (push_one != 2)
    140             tcp_schedule_loss_probe(sk);
    141 
    142         /* 拥塞窗口校验 */
    143         is_cwnd_limited |= (tcp_packets_in_flight(tp) >= tp->snd_cwnd);
    144         tcp_cwnd_validate(sk, is_cwnd_limited);
    145         return false;
    146     }
    147 
    148     
    149     /*  本次无数据发送,已发出未确认的数据段不为0或者发送队列为空,认为成功 */
    150     return !tp->packets_out && tcp_send_head(sk);
    151 }
  • 相关阅读:
    SAP 用户权限解剖
    效率极低人群的七大习惯你占了几项? 迎客
    数据库到底用不用外键 迎客
    办公室生存——与人相处的30个原则 迎客
    虚拟机 VirtualBox 迎客
    fancybox 迎客
    遥志代理服务器软件CCProxy 迎客
    JRE和JDK的区别 迎客
    小众软件 迎客
    网络推广方法汇集 迎客
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11752189.html
Copyright © 2011-2022 走看看