当前位置:网站首页>TCP协议之《数据与控制流程交叉时的延迟处理》

TCP协议之《数据与控制流程交叉时的延迟处理》

2022-08-10 03:26:00 程序员扫地僧

TCP协议的数据流程在遇到控制流程正在进行之时,需要暂停当前的执行流程,延时处理。等到控制流程(例如用户层的相关系统调用退出后)结束,再继续之前的数据流程。如下宏定义TCP_DEFERRED_ALL,内核中存在四种延时处理的情况:TSQ、重传超时、延时ACK和ICMP分片消息处理。

#define TCP_DEFERRED_ALL (TCPF_TSQ_DEFERRED |       \
              TCPF_WRITE_TIMER_DEFERRED |   \
              TCPF_DELACK_TIMER_DEFERRED |  \
              TCPF_MTU_REDUCED_DEFERRED)
enum tsq_flags {
    TSQF_THROTTLED          = (1UL << TSQ_THROTTLED),
    TSQF_QUEUED         = (1UL << TSQ_QUEUED),
    TCPF_TSQ_DEFERRED       = (1UL << TCP_TSQ_DEFERRED),
    TCPF_WRITE_TIMER_DEFERRED   = (1UL << TCP_WRITE_TIMER_DEFERRED),
    TCPF_DELACK_TIMER_DEFERRED  = (1UL << TCP_DELACK_TIMER_DEFERRED),
    TCPF_MTU_REDUCED_DEFERRED   = (1UL << TCP_MTU_REDUCED_DEFERRED),
};

一、TSQ延迟处理
TCP小队列处理tasklet函数tcp_tasklet_func如下,在其处理过程中,如果遇到设置了延时标志TCP_TSQ_DEFERRED的套接口,并且此时用户层系统调用已不再使用该套接口,清除TCP_TSQ_DEFERRED标志,调用TSQ的核心处理函数tcp_tsq_handler。

static void tcp_tasklet_func(unsigned long data)
{
    struct tsq_tasklet *tsq = (struct tsq_tasklet *)data;
 
    list_for_each_safe(q, n, &list) {
        tp = list_entry(q, struct tcp_sock, tsq_node);
        list_del(&tp->tsq_node);
 
        if (!sk->sk_lock.owned && test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags)) {
            bh_lock_sock(sk);
            if (!sock_owned_by_user(sk)) {
                clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags);
                tcp_tsq_handler(sk);
            }
}

内核在pacing超时处理函数和skb缓存释放函数的特定的destructor回调函数中(对于TCP其为tcp_wfree),调度以上的TSQ的tasklet处理流程,在这两个函数中,如果发送队列被压制TSQF_THROTTLED,将其添加到TSQ队列中,由TSQ的tasklet处理。

enum hrtimer_restart tcp_pace_kick(struct hrtimer *timer)
{
    struct tcp_sock *tp = container_of(timer, struct tcp_sock, pacing_timer);
    for (oval = READ_ONCE(sk->sk_tsq_flags);; oval = nval) {
        nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED | TCPF_TSQ_DEFERRED;
        nval = cmpxchg(&sk->sk_tsq_flags, oval, nval);
        if (nval != oval)
            continue;
 
        if (!refcount_inc_not_zero(&sk->sk_wmem_alloc))
            break;
        tsq = this_cpu_ptr(&tsq_tasklet);
        empty = list_empty(&tsq->head);
        list_add(&tp->tsq_node, &tsq->head);
        if (empty)
            tasklet_schedule(&tsq->tasklet);
}
void tcp_wfree(struct sk_buff *skb)
{
    struct sock *sk = skb->sk;
 
    for (oval = READ_ONCE(sk->sk_tsq_flags);; oval = nval) {
        if (!(oval & TSQF_THROTTLED) || (oval & TSQF_QUEUED))
            goto out;
 
        nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED | TCPF_TSQ_DEFERRED;
        nval = cmpxchg(&sk->sk_tsq_flags, oval, nval);
        if (nval != oval)
            continue;
 
        tsq = this_cpu_ptr(&tsq_tasklet);
        empty = list_empty(&tsq->head);
        list_add(&tp->tsq_node, &tsq->head);
        if (empty)
            tasklet_schedule(&tsq->tasklet);
}

二、TCP发送超时延迟处理
如果在发送超时处理时,套接口正在被用户层的系统调用使用,将处理延后到用户层处理完成之后,当用户层释放套接口时,由tcp_release_cb函数进行相应处理。此处,设置标志TCP_WRITE_TIMER_DEFERRED。

static void tcp_write_timer(struct timer_list *t)
{
    struct inet_connection_sock *icsk = from_timer(icsk, t, icsk_retransmit_timer);
    struct sock *sk = &icsk->icsk_inet.sk;
 
    bh_lock_sock(sk);
    if (!sock_owned_by_user(sk)) {
        tcp_write_timer_handler(sk);
    } else {
        /* delegate our work to tcp_release_cb() */
        if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &sk->sk_tsq_flags))
            sock_hold(sk);
    }
    bh_unlock_sock(sk);
    sock_put(sk);
}

三、延时ACK定时器
TCP协议的延时ACK超时处理函数tcp_delack_timer,如果在其超时调用时,检查用户层的控制流程在处理此套接口sock_owned_by_user,设置ACK被阻止blocked标志,并且设置TCP_DELACK_TIMER_DEFERRED标志,表明延时ACK需要在用户调用退出时继续执行。

static void tcp_delack_timer(struct timer_list *t)
{
    struct inet_connection_sock *icsk = from_timer(icsk, t, icsk_delack_timer);
    struct sock *sk = &icsk->icsk_inet.sk;
 
    bh_lock_sock(sk);
    if (!sock_owned_by_user(sk)) {
        tcp_delack_timer_handler(sk);
    } else {
        icsk->icsk_ack.blocked = 1;
        /* deleguate our work to tcp_release_cb() */
        if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &sk->sk_tsq_flags))
            sock_hold(sk);
    }
    bh_unlock_sock(sk);
    sock_put(sk);
}

四、ICMP分片消息延迟处理
在接收到ICMP错误信息后,对于IPv4而言,如果其错误码为ICMP_FRAG_NEEDED,对于IPv6而言,如果其类型为ICMPV6_PKT_TOOBIG,都表明发送的数据包太大,需要进行分片。此时,如果套接口被用户层系统调用使用,设置TCP_MTU_REDUCED_DEFERRED标志,等到用户系统调用退出时继续处理。

void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
{
    switch (type) {
    case ICMP_DEST_UNREACH:
 
        if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
            tp->mtu_info = info;
            if (!sock_owned_by_user(sk)) {
                tcp_v4_mtu_reduced(sk);
            } else {
                if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &sk->sk_tsq_flags))
                    sock_hold(sk);
            }
            goto out;
        }
}
static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info)
{
    if (type == ICMPV6_PKT_TOOBIG) {
        if (!ip6_sk_accept_pmtu(sk))
            goto out;
 
        tp->mtu_info = ntohl(info);
        if (!sock_owned_by_user(sk))
            tcp_v6_mtu_reduced(sk);
        else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &sk->sk_tsq_flags))
            sock_hold(sk);
        goto out;
    }
}

五、继续延迟的处理流程
用户层的处理开始之前需要使用lock_sock上锁套接口,在执行完处理之后由函数release_sock释放套接口锁。在此函数中,内核将调用特定于协议的释放回调函数release_cb,对于TCP协议而言,其为tcp_release_cb函数。

void release_sock(struct sock *sk)
{
    spin_lock_bh(&sk->sk_lock.slock);
 
    if (sk->sk_prot->release_cb)
        sk->sk_prot->release_cb(sk);
}
如下tcp_release_cb函数所示,如果没有任何需要延迟处理(TCP_DEFERRED_ALL)的操作时,直接返回。之后,根据延时标志分别处理被延时的处理流程,注意在设置延时标志之时,内核使用函数sock_hold增加了套接口的引用计数,此处在处理过后,递减套接口计数__sock_put函数。

void tcp_release_cb(struct sock *sk)
{
    unsigned long flags, nflags;
 
    /* perform an atomic operation only if at least one flag is set */
    do {
        flags = sk->sk_tsq_flags;
        if (!(flags & TCP_DEFERRED_ALL))
            return;
        nflags = flags & ~TCP_DEFERRED_ALL;
    } while (cmpxchg(&sk->sk_tsq_flags, flags, nflags) != flags);
 
    if (flags & TCPF_TSQ_DEFERRED)
        tcp_tsq_handler(sk);
 
    sock_release_ownership(sk);
 
    if (flags & TCPF_WRITE_TIMER_DEFERRED) {
        tcp_write_timer_handler(sk);
        __sock_put(sk);
    }
    if (flags & TCPF_DELACK_TIMER_DEFERRED) {
        tcp_delack_timer_handler(sk);
        __sock_put(sk);
    }
    if (flags & TCPF_MTU_REDUCED_DEFERRED) {
        inet_csk(sk)->icsk_af_ops->mtu_reduced(sk);
        __sock_put(sk);
    }
}

原网站

版权声明
本文为[程序员扫地僧]所创,转载请带上原文链接,感谢
https://blog.csdn.net/wuyongmao/article/details/126247027