zoukankan      html  css  js  c++  java
  • 【Atheros】网卡驱动速率调整算法概述

    我做网卡驱动,最主要的内容就是设计和改进速率调整算法,随着802.11协议簇的新标准越来越多,速率越来越高,调制编码方式也越来越多,一般来说,速率越高越可能丢包,速率越低越稳定,这是整体状况,但不是必然的规律,所以,只用固定的速率来发送显然是不合适的,这就需要速率调整算法来自己调节,信号比较好的时候,就用高速率来发送,信道状况不好了,就换用低速率来发,atheros驱动中提供了两种可选的速率调整算法,ath9k和minstrel,其中minstrel要好一些,后面我会分别根据源码解读minstrel和ath9k这两种算法,这一篇文章,只介绍一个重要的结构体,从而引出速率调整算法的任务。

    接触过网络编程的朋友都不会对socket感到陌生,在数据链路层,有一个与之对应的结构体,叫做sk_buff,一般记作skb,skb中有一个域叫做control_buffer,也就是skb->cb,是一个48字节长度的内存区域,这个区域的设计,是为了协议栈中各个层存储一些私有的数据,数据包在不同层之间传递时,比如从网络层传输到链路层之后,这个域的数据就没有用了,可以放心地被链路层写入新的数据,链路层在把这个数据包交给物理层去发送的时候,需要指定一些参数,比如这个数据包要用20MHz还是40MHz去发送,用哪个速率去发送,如果发送失败了需要重传的话,最多重传多少次等等的信息,这些信息就存储在skb->cb中,存储的格式,是按照下面这个结构体来存的:

    struct ieee80211_tx_info {
        /* common information */
        u32 flags;
        u8 band;
    
        u8 antenna_sel_tx;
    
        u16 ack_frame_id;
    
        union {
            struct {
                union {
                    /* rate control */
                    struct {
                        struct ieee80211_tx_rate rates[
                            IEEE80211_TX_MAX_RATES];
                        s8 rts_cts_rate_idx;
                    };
                    /* only needed before rate control */
                    unsigned long jiffies;
                };
                /* NB: vif can be NULL for injected frames */
                struct ieee80211_vif *vif;
                struct ieee80211_key_conf *hw_key;
                struct ieee80211_sta *sta;
            } control;
            struct {
                struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
                u8 ampdu_ack_len;
                int ack_signal;
                u8 ampdu_len;
                /* 15 bytes free */
            } status;
            struct {
                struct ieee80211_tx_rate driver_rates[
                    IEEE80211_TX_MAX_RATES];
                void *rate_driver_data[
                    IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)];
            };
            void *driver_data[
                IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)];
        };
    };

    其中,很多字段的意义不需要操心,只需要关注其中union的一部分(标红的union),这段内存区域可以通过control和status等方式访问,这个control和status就是我们关注的重点!

    他们的核心又是同样的一个字段:我标红的ieee80211_tx_rate:

    struct ieee80211_tx_rate {
        s8 idx;
        u8 count;
        u8 flags;
    } __packed;

    这个结构体代表一个速率,看字段名就很明显了:速率号、发送次数和一个标志位(标识带宽、SGI/LGI等),网卡可能会支持多速率重传,就是先用某个速率发送,如果几次都失败了就换第二个速率发,因此,底层在发包过程中,需要一个ieee80211_tx_rate的数组,速率调整算法的任务,就是把前面结构体中的这个字段填充好交给底层:

    struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];

    回到前面那个结构体tx_info,它拥有一个union,主要包括control和status两个部分,control部分是在发送过程中被使用的,速率调整算法会:

    struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
    struct ieee80211_tx_rate *rates = tx_info->control.rates;
    
    rates[0].idx=0; rates[0].count=2; rates[0].flags=zzz0;
    rates[1].idx=1; rates[1].count=4; rates[1].flags=zzz1;
    rates[2].idx=2; rates[2].count=8; rates[2].flags=zzz2;

    这样,我就指定了,发送时数据包依次使用0、1、2这三个速率最多发送2、4、8次,直到发送成功为止。那么发送完成之后,我们需要知道发送到底成功了没有,最后是哪个速率发送成功的等信息。所以这个结构体又会被底层返回给我们,此时,我们可以通过status访问之前设置的这些数据,只是每一项的count域被底层重写了,之前是表示每个速率的最大发送次数是多少,现在变成了每个速率的实际发送次数是多少。

    struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
    struct ieee80211_tx_rate *rates = tx_info->status.rates;
    
    // rates[0].count == 4
    // rates[1].count == 1
    // rates[2].count == 0

    这就代表速率0发了4次,最终都失败了,速率1发了1次就成功了,当然速率2没有用到,发了0次。

    所以速率调整算法的任务就是在发送过程中把tx_info->control.rates填充好,等发送结束后根据tx_info->status.rates来做速率的调整。

    下面就依次介绍这两种算法:MinstrelAth9k

  • 相关阅读:
    列表第一篇文档与其他文档不同样式
    当前栏目判断有无子栏目
    当前栏目文章数
    有关当前焦点的标签,只有我能理解
    给推荐一个标识
    附件下载次数
    收藏代码
    关联会员头像信息
    当前三级折叠菜单导航
    centos7 安装配置apache
  • 原文地址:https://www.cnblogs.com/smarterplanet/p/4081996.html
Copyright © 2011-2022 走看看