Linux tcp tsq

1、背景

TCP发送的数据经过IP层添加IP包头后,会被发送到IP数据栈和网卡之间的队列中,当网卡能够发送数据时会到这个队列中去取skb。
当TCP发送数据过快时,或一个带宽很大或者包速率很大的非TCP数据流会把队列里的所有空间占满,造成数据包丢失和延时的问题。
更糟的是,这样很可能会使另一个缓存产生,进而产生一个静止队列(standing queue),造成更严重的延时并使TCP的RTT和拥塞窗口的计算出现问题。
Linux3.6.0出现后,Linux内核增加了TCP小队列(TCP Small Queue)的机制,用于解决该问题。TCP小队列对每个TCP数据流中,能够同时参与排队的字节数做出了限制,
这个限制是通过net.ipv4.tcp_limit_output_bytes内核选项实现的。当TCP发送的数据超过这个限制时,多余的数据会被放入另外一个队列中,再通过tastlet机制择机发送。


2、tcp_write_xmit

	//判断sk_wmem_alloc是否大于limit,如果是,则置上TSQ_THROTTLED标志,然后退出发送流程
	if (atomic_read(&sk->sk_wmem_alloc) > limit) {
		set_bit(TSQ_THROTTLED, &tp->tsq_flags);
		/* It is possible TX completion already happened
		 * before we set TSQ_THROTTLED, so we must
		 * test again the condition.
		 * We abuse smp_mb__after_clear_bit() because
		 * there is no smp_mb__after_set_bit() yet
		 */
		smp_mb__after_clear_bit();
		if (atomic_read(&sk->sk_wmem_alloc) > limit)
			break;
	}

3、网卡发送完,触发软中断

ixgbe_xmit_frame
	__ixgbe_xmit_frame
		ixgbe_xmit_frame_ring
			dev_kfree_skb_any
				__dev_kfree_skb_any
					void __dev_kfree_skb_irq(struct sk_buff *skb, enum skb_free_reason reason)
					{
						unsigned long flags;

						if (likely(atomic_read(&skb->users) == 1)) {
							smp_rmb();
							atomic_set(&skb->users, 0);
						} else if (likely(!atomic_dec_and_test(&skb->users))) {
							return;
						}
						get_kfree_skb_cb(skb)->reason = reason;
						local_irq_save(flags);
						skb->next = __this_cpu_read(softnet_data.completion_queue);
						__this_cpu_write(softnet_data.completion_queue, skb);
						raise_softirq_irqoff(NET_TX_SOFTIRQ);
						local_irq_restore(flags);
					}
					
net_tx_action
	skb_release_all
		skb_release_head_state
			skb->destructor
				tcp_wfree
					void tcp_wfree(struct sk_buff *skb)
					{
						struct sock *sk = skb->sk;
						struct tcp_sock *tp = tcp_sk(sk);
						//判断TSQ_THROTTLED是否置位
						if (test_and_clear_bit(TSQ_THROTTLED, &tp->tsq_flags) &&
							!test_and_set_bit(TSQ_QUEUED, &tp->tsq_flags)) {
							unsigned long flags;
							struct tsq_tasklet *tsq;

							/* Keep a ref on socket.
							 * This last ref will be released in tcp_tasklet_func()
							 */
							atomic_sub(skb->truesize - 1, &sk->sk_wmem_alloc);

							/* queue this socket to tasklet queue */
							local_irq_save(flags);
							tsq = &__get_cpu_var(tsq_tasklet);
							//将tcp_socket添加到tsq链表
							list_add(&tp->tsq_node, &tsq->head);
							//触发tasklet软中断
							tasklet_schedule(&tsq->tasklet);
							local_irq_restore(flags);
						} else {
							sock_wfree(skb);
					}

4、进入tasklet软中断

void __init tcp_tasklet_init(void)
{
	int i;

	for_each_possible_cpu(i) {
		struct tsq_tasklet *tsq = &per_cpu(tsq_tasklet, i);

		INIT_LIST_HEAD(&tsq->head);
		tasklet_init(&tsq->tasklet,
			     tcp_tasklet_func,
			     (unsigned long)tsq);
	}
}
tcp_tasklet_func

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值