在Linux中,有两种方法来处理传入TCP数据段:快速路径(Fast Path)和慢速路径(Slow Path)。使用快速路径只进行最少的处理,如处理数据段、发生ACK、存储时间戳等。使用慢速路径可以处理乱序数据段、PAWS、socket内存管理和紧急数据等。Linux通过预测标志来区分这两种处理模式,预测标志存储在tp->pred_flags,生成这个标志的函数是__tcp_fast_path_on和tcp_fast_path_on,TCP会直接使用这两个函数来生成预测标记,也可以调用tcp_fast_path_check来完成这一任务:
613 static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
614 {
615 tp->pred_flags = htonl((tp->tcp_header_len << 26) |
616 ntohl(TCP_FLAG_ACK) |
617 snd_wnd);
618 }
619
620 static inline void tcp_fast_path_on(struct tcp_sock *tp)
621 {
622 __tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale);
623 }
624
625 static inline void tcp_fast_path_check(struct sock *sk)
626 {
627 struct tcp_sock *tp = tcp_sk(sk);
628
629 if (skb_queue_empty(&tp->out_of_order_queue) &&
630 tp->rcv_wnd &&
631 atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
632 !tp->urg_data)
633 tcp_fast_path_on(tp);
634 }
613-617:__tcp_fast_path_on函数做的工作实际上是在构建TCP首部的第4个字节,即首部长度、标记位、窗口(格式见1.2节)。其中tp->tcp_header_len是首部长度的字节数,TCP首部中记录首部长度的数值位于第4个字节的高4bit,即第28-31bit,而且这个数值乘以4才是首部长度的字节数。故tp->tcp_header_len需要左移28位,再右移2位(除以4),即左移26位。
620-622:tcp_fast_path_on封装了__tcp_fast_path_on函数,它在设置预测标记时会考虑窗口扩大因子的影响
625-633:tcp_fast_path_check会先检查条件是否满足,如果满足再设置预测标记。条件是:
(1)没有乱序数据(629)
(2)接收窗口不为0(630)
(3)接收缓存未耗尽(631)
(4)没有紧急数据(632)
TCP直接调用__tcp_fast_path_on的时机是connect系统调用即将结束时:
5291 void tcp_finish_connect(struct sock *sk, struct sk_buff *skb)
5292 {
...
5320 if (!tp->rx_opt.snd_wscale)
5321 __tcp_fast_path_on(tp, tp->snd_wnd);
5322 else
5323 tp->pred_flags = 0;
...
TCP直接调用tcp_fast_path_on的时机是收到三次握手中的ACK报文时:
5600 int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
5601 const struct tcphdr *th, unsigned int len)
5602 {
...
5678 int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH |
5679 FLAG_UPDATE_TS_RECENT) > 0;
5680
5681 switch (sk->sk_state) {
5682 case TCP_SYN_RECV:
5683 if (acceptable) {
...
5745 tcp_fast_path_on(tp);
5746 } else {
5747 return 1;
5748 }
5749 break;
...
TCP直接调用tcp_fast_path_check的时机有3处:
(1)当读过紧急数据时;紧急数据是由慢速路径处理,需要保持在慢速路径模式直到收完紧急数据,然后就可以开启快速路径模式了。
1545 int tcp_recvmsg(struct kiocb *ioc