现在的位置: 首页 > 综合 > 正文

TCP拥塞控制算法内核实现剖析(一)

2014年03月10日 ⁄ 综合 ⁄ 共 6256字 ⁄ 字号 评论关闭

内核版本:2.6.37

主要源文件:linux-2.6.37/ net/ ipv4/ Tcp_cong.c 

本文主要分析RENO及TCP拥塞控制基础的实现

======================================================================================================

struct sock *sk 和 struct tcp_sock *tp 的转换

在include/ linux/ Tcp.h中,
static inline struct tcp_sock *tcp_sk(const struct sock *sk) 
{
        return (struct tcp_sock *)sk ;
}

给出struct sock *sk,
struct tcp_sock *tp = tcp_sk(sk) ;

 

tcp_sock结构

struct tcp_sock
{
       ...
 u32 window_clamp ; /* Maximal window to advertise */
 u32 rcv_ssthresh ; /* Current window clamp */
 u32 rcv_wnd ; /* Current receiver window */
       ...
 /* snd_wll 记录发送窗口更新时,造成窗口更新的那个数据报的第一个序号。
  * 它主要用于在下一次判断是否需要更新发送窗口。
  */
 u32 snd_wll ; /* Sequence for window update */ 
 u32 snd_wnd ; /* 发送窗口的大小,直接取值于来自对方的数据报的TCP首部 */
 /* Maximal window ever seen from peer 记录来自对方通告的窗口的最大值 */
 /* First byte we want an ack for 发送窗口的左边沿 */
 u32 max_window ;  u32 snd_una ; 
        ...
 /*
  * Slow start and congestion control
  */
 u32 snd_ssthresh ; /* Slow start size threshold */
 u32 snd_cwnd ; /* Sending congestion window */
 /*表示在当前的拥塞控制窗口中已经发送的数据段的个数*/
 u32 snd_cwnd_cnt ; /* Linear increase counter */ 
 u32 snd_cwnd_clamp ; /* Do not allow snd_cwnd to grow above this */
        ...
 u32 mss_cache ; /* cached effective mss , not including SACKS */
 u32 bytes_acked ; /* Appropriate Byte Counting - RFC3465 */
       ...
}

拥塞避免算法关键部分

/* In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd ( or alternative w ) */
void tcp_cong_avoid_ai(struct tcp_sock *tp , u32 w)
{
        if ( tp->snd_cwnd_cnt >= w) {
                   if ( tp->snd_cwnd < tp->snd_cwnd_clamp)
                        tp->snd_cwnd++ ;
                   tp->snd_cwnd_cnt = 0 ;
        } else {
                   tp->snd_cwnd_cnt ++ ;
        }                
}
EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai) ;

慢启动算法

void tcp_slow_start( struct tcp_sock *tp )
{
 int cnt ; /* increase in packets */

/* RFC3465 : ABC slow start
 * Increase only after a full MSS of bytes is acked
 *
 * TCP sender SHOULD increase cwnd by the number of
 * previously unacknowledged bytes ACKed by each incoming 
 * acknowledgment , provided the increase is not more than L
 */
 /* ack的数据少于MSS */
 if ( sysctl_tcp_abc && tp->bytes_acked < tp->mss_cached )  
        return ;

/* 此时不是应该进入拥塞避免?*/
 if ( sysctl_tcp_max_ssthresh >0 && tcp->snd_cwnd >sysctl_tcp_max_ssthresh)
        cnt = sysctl_tcp_max_ssthresh >> 1 ; /* limited slow start */
 else 
        cnt = tp->snd_cwnd ; /* exponential increase */

/* RFC3465 : ABC
 * We MAY increase by 2 if discovered delayed ack  
 */
/* 如果接收方启用了延时确认,此时收到的确认代表两个MSS数据报*/
if ( sysctl_tcp_abc >1 && tp->bytes_acked >= 2*tp->mss_cache ) 
        cnt <<= 1 ;

tp->bytes_acked = 0 ; 
tp->snd_cwnd_cnt += cnt ; /* 此时snd_cwnd_cnt等于snd_cwnd或2*snd_cwnd */

 while( tp->snd_cwnd_cnt >= tp->snd_cwnd ) { 
        tp->snd_cwnd_cnt -= tp->snd_cwnd ;
        if( tp->snd_cwnd < tp->snd_cwnd_clamp )
                tp->snd_cwnd++ ;
 }
}
EXPORT_SYMBOL_GPL( tcp_slow_start ) ;

代表拥塞算法的结构体

#define TCP_CA_NAME_MAX 16struct tcp_congestion_ops {
        struct list_head list ;
        unsigned long flags ;
        /* initialize private data (optional) */
        void (*init) (struct sock *sk) ;
        /* cleanup private data (optional) */ 
        void (*release) (struct sock *sk) ;
        /* return slow start threshold (required) */
        u32 (*ssthresh) (struct sock *sk) ;
        /* lower bound for congestion window (optional) */
        u32 (*min_cwnd) (const struct sock *sk) ;
        /* do new cwnd calculation (required) */
        void (*cong_avoid) (struct sock *sk , u32 ack , u32 in_flight ) ;
        /* call before changing ca_state (optional) */
        void (*set_state) (struct sock *sk , u8 new_state) ;
        /* call when cwnd event occurs (optional) */
        void (*cwnd_event) (struct sock *sk , enum tcp_ca_event ev) ;
        /* new value of cwnd after loss (optional) */
        u32 (*undo_cwnd) (struct sock *sk) ;
        /* hook for packet ack accounting (optional) */
        void (*pkts_acked) (struct sock *sk , u32 num_acked , s32 rtt_us) ;
        /* get info for inet_diag (optional) */
        void (*get_info) (struct sock *sk , u32 ext , struct sk_buff *skb) ;
        char name[TCP_CA_NAME_MAX] ;
        struct module *owner ;
}

 

在Tcp_cong.c中,有全局变量:

int sysctl_tcp_max_ssthresh = 0 ;

/* define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x) */

static DEFINE_SPINLOCK( tcp_cong_list_lock ) ;

static LIST_HEAD( tcp_cong_list ) ; // tcp拥塞控制算法链表,其元素为tcp_congestion_ops

/*

 BUG_ON( ) ; 如果BUG_ON中的条件为真就调用BUG,它输出一些信息,然后调用panic函数挂起系统。

 char *strncpy( char * dest , char *src , size_t n ) ;

 它与strcpy不同之处在于复制n个字符,而不是把所有的字符拷贝(包括结尾'\0')。

  当src的长度小于n时,dst内的未复制空间用'\0'填充。否则,复制n个字符到dst,没有加'\0'。这里就要注意在字符串dst结尾处理加'\0'的情况了。

 rcu_read_lock() // 读者在读取由RCU保护的共享数据时使用该函数标记它进入读端临界区。

 rcu_read_unlock() // 该函数与rcu_read_lock配对使用,用以标记读者退出读端临界区。

*/

 

对拥塞控制算法的一些操作(读写增减注册等)

/* Get current default congestion control */
void tcp_get_default_congestion_control( char *name )
{
        struct tcp_congestion_ops *ca ;
        /* We will always have reno */
        BUG_ON( list_empty( &tcp_cong_list) ) ;

        rcu_read_lock( ) ;
        ca = list_entry( tcp_cong_list . next , struct tcp_congestion_ops , list ) ;
        strncpy( name , ca->name , TCP_CA_NAME_MAX ) ;
        rcu_read_unlock( ) ;
}

 

struct sock——representation of sockets

 struct inet_sock——representation of INET sockets

 struct inet_connection_sock——INET connection oriented sockets

 struct tcp_sock——tcp sockets

 以上几种socket越分越细,比如inet_connection_sock是在inet_sock上的扩展,具有自己特有的属性。

 tcp_sock是TCP协议专用的一个socket表示,它是在struct inet_connection_sock基础进行扩展,主要是增加了滑动窗口协议,避免拥塞算法等一些TCP专有属性。

 

struct inet_connection_sock {
        ...
        // Pluggable congestion control hook
        const struct tcp_congestion_ops *icsk_ca_ops ; 
        ...

        u32 icsk_ca_priv[16] ;
#define ICSK_CA_PRIV_SIZE (16*sizeof(u32))
}

 

举例://有一个初始化了得struct sock *sk

struct inet_connection_sock *icsk = inet_csk( sk ) ;

printk(KERN_INFO "%s" , icsk->icsk_ca_ops->name) ; //当前连接拥塞控制算法名称

 

struct inet_sock {
        ...
        /* Socket demultiplex comparisons on incoming packets */
        __be32 inet_daddr ;
        __be16 inet_dport ;
        __be32 inet_saddr ;
        __be16 inet_sport ;
        __be16 inet_num ; // local port 
        __be32 inet_rcv_saddr ; // Bound local IPv4 addr
        ...
}

 

/* Built list of non-restricted congestion control values*/
void tcp_get_allowed_congestion_control( char *buf , size_t maxlen)
{
        struct tcp_congestion_ops *ca ;
        size_t offs = 0 ;
        *buf = '\0' ; //有必要?
         rcu_read_lock() ;
        list_for_each_entry( ca , &tcp_cong_list , list ) {
                if( !( ca->flags & TCP_CONG_NON_RESTRICTED)) //排除有限制的。限制和非限制区别?
                         continue;
                offs += snprintf( buf+offs , maxlen-offs , "%s%s" , offs == 0?"" : " " , ca->name) ;
        }
        rcu_read_unlock() ;
}

 

/* Simple linear search , don't expect many entries! */
static struct tcp_congestion_ops*tcp_ca_find( const char *name)
{
        struct tcp_congestion_ops *e ;
        list_for_each_entry_rcu( e , &tcp_cong_list , list ) {
                if( strcmp(e->name , name)==0)
                        return e ;
         }
         return NULL ;
}

 

/*
 * Attach new congestion control algorithm to the list 
 * of available options.
 */
int tcp_register_congestion_control( struct tcp_congestion_ops *ca )
{
        int ret = 0 ;
        /* all algorithms must implement ssthresh and cong_avoid ops */
        if ( !ca->ssthresh || !ca->cong_avoid ) {
                printk(KERN_ERR "TCP %s does not implement required ops\n",
                            ca->name) ;
                return -EINVAL ;
        }

        spin_lock(&tcp_cong_list_lock) ;
        if( tcp_ca_find (ca->name)) {
                printk(KERN_NOTICE "TCP %s already registered\n", ca->name) ;
                ret = -EEXIST; //不能直接return,不然会造成死锁
         } else {
                list_add_tail_rcu( &ca->list , &tcp_cong_list) ;
                printk(KERN_INFO "TCP %s registered\n", ca->name) ;
        }
        spin_unlock(&tcp_cong_list_lock) ;

        return ret ;
}

======================================================================================================

 

抱歉!评论已关闭.