mac80211解析之发送速率控制

mac80211的模块初始化的时候,也初始化了minstrel,minstrel是调用速率控制函数模快,通过tx.c中的ieee80211_tx_h_rate_ctrl句柄填充结构体ieee80211_tx_info的速率变量ieee80211_tx_rate。

模块初始化后调用了ieee80211_rate_control_register进行速率控制注册,注册了定义好的速率控制操作结构体mac80211_minstrel_ht,内容如下:

 


static const struct rate_control_ops mac80211_minstrel_ht = {
    .name = "minstrel_ht",
    .tx_status = minstrel_ht_tx_status,
    .get_rate = minstrel_ht_get_rate,
    .rate_init = minstrel_ht_rate_init,
    .rate_update = minstrel_ht_rate_update,
    .alloc_sta = minstrel_ht_alloc_sta,
    .free_sta = minstrel_ht_free_sta,
    .alloc = minstrel_ht_alloc,
    .free = minstrel_ht_free,
#ifdef CPTCFG_MAC80211_DEBUGFS
    .add_sta_debugfs = minstrel_ht_add_sta_debugfs,
    .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs,
#endif
    .get_expected_throughput = minstrel_ht_get_expected_throughput,
};


其中minstrel_ht_get_rate函数,这个函数在高速率控制函数rc80211_minstrel_ht.c文件中,内容如下:


static void
minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
                     struct ieee80211_tx_rate_control *txrc)
{
    const struct mcs_group *sample_group;
    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
    struct ieee80211_tx_rate *rate = &info->status.rates[0];
    struct minstrel_ht_sta_priv *msp = priv_sta;
    struct minstrel_ht_sta *mi = &msp->ht;
    struct minstrel_priv *mp = priv;
    int sample_idx;

    if (rate_control_send_low(sta, priv_sta, txrc))
        return;

    if (!msp->is_ht)
        return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc);

    info->flags |= mi->tx_flags;
    minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);

#ifdef CPTCFG_MAC80211_DEBUGFS
    if (mp->fixed_rate_idx != -1)
        return;
#endif

    /* Don't use EAPOL frames for sampling on non-mrr hw */
    if (mp->hw->max_rates == 1 &&
        (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
        sample_idx = -1;
    else
        sample_idx = minstrel_get_sample_rate(mp, mi);

    mi->total_packets++;

    /* wraparound */
    if (mi->total_packets == ~0) {
        mi->total_packets = 0;
        mi->sample_packets = 0;
    }

    if (sample_idx < 0)
        return;

    sample_group = &minstrel_mcs_groups[sample_idx / MCS_GROUP_RATES];
    info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
    rate->count = 1;

    if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
        int idx = sample_idx % ARRAY_SIZE(mp->cck_rates);
        rate->idx = mp->cck_rates[idx];
        rate->flags = 0;
        return;
    }

    rate->idx = sample_idx % MCS_GROUP_RATES +
            (sample_group->streams - 1) * 8;
    rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags;
}


表示站点信息的结构体ieee80211_sta,定义如下:

/**
 * struct ieee80211_sta - station table entry
 *
 * A station table entry represents a station we are possibly
 * communicating with. Since stations are RCU-managed in
 * mac80211, any ieee80211_sta pointer you get access to must
 * either be protected by rcu_read_lock() explicitly or implicitly,
 * or you must take good care to not use such a pointer after a
 * call to your sta_remove callback that removed it.
 */
struct ieee80211_sta {
    u32 supp_rates[IEEE80211_NUM_BANDS];
    u8 addr[ETH_ALEN];
    u16 aid;
    struct ieee80211_sta_ht_cap ht_cap;
    struct ieee80211_sta_vht_cap vht_cap;
    bool wme;
    u8 uapsd_queues;
    u8 max_sp;
    u8 rx_nss;
    enum ieee80211_sta_rx_bandwidth bandwidth;
    enum ieee80211_smps_mode smps_mode;
    struct ieee80211_sta_rates __rcu *rates;
    bool tdls;

    /* must be last */
    u8 drv_priv[0] __aligned(sizeof(void *));
};


minstrel_ht_get_rate函数中rate_control_send_low函数是进行低速发送,内容如下:



bool rate_control_send_low(struct ieee80211_sta *pubsta,
               void *priv_sta,
               struct ieee80211_tx_rate_control *txrc)
{
    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
    struct ieee80211_supported_band *sband = txrc->sband;
    struct sta_info *sta;
    int mcast_rate;
    bool use_basicrate = false;

    if (!pubsta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
        __rate_control_send_low(txrc->hw, sband, pubsta, info,
                    txrc->rate_idx_mask);

        if (!pubsta && txrc->bss) {
            mcast_rate = txrc->bss_conf->mcast_rate[sband->band];
            if (mcast_rate > 0) {
                info->control.rates[0].idx = mcast_rate - 1;
                return true;
            }
            use_basicrate = true;
        } else if (pubsta) {
            sta = container_of(pubsta, struct sta_info, sta);
            if (ieee80211_vif_is_mesh(&sta->sdata->vif))
                use_basicrate = true;
        }

        if (use_basicrate)
            rc_send_low_basicrate(&info->control.rates[0].idx,
                          txrc->bss_conf->basic_rates,
                          sband);

        return true;
    }
    return false;
}


如果不是数据帧或没有ACK,则调用__rate_control_send_low函数,该函数内容如下:


static void __rate_control_send_low(struct ieee80211_hw *hw,
                    struct ieee80211_supported_band *sband,
                    struct ieee80211_sta *sta,
                    struct ieee80211_tx_info *info,
                    u32 rate_mask)
{
    int i;
    u32 rate_flags =
        ieee80211_chandef_rate_flags(&hw->conf.chandef);

    if ((sband->band == IEEE80211_BAND_2GHZ) &&
        (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
        rate_flags |= IEEE80211_RATE_ERP_G;

    info->control.rates[0].idx = 0;
    for (i = 0; i < sband->n_bitrates; i++) {
        if (!(rate_mask & BIT(i)))
            continue;

        if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
            continue;

        if (!rate_supported(sta, sband->band, i))
            continue;

        info->control.rates[0].idx = i;
        break;
    }
    WARN_ON_ONCE(i == sband->n_bitrates);

    info->control.rates[0].count =
        (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
        1 : hw->max_rate_tries;

    info->control.skip_table = 1;
}


其中用到发送tx速率控制结构体ieee80211_tx_rate_control ,定义如下:

/**
 * struct ieee80211_tx_rate_control - rate control information for/from RC algo
 */
struct ieee80211_tx_rate_control {
    struct ieee80211_hw *hw;
    struct ieee80211_supported_band *sband;
    struct ieee80211_bss_conf *bss_conf;
    struct sk_buff *skb;
    struct ieee80211_tx_rate reported_rate;
    bool rts, short_preamble;
    u8 max_rate_idx;
    u32 rate_idx_mask;
    u8 *rate_idx_mcs_mask;
    bool bss;
};


发送tx速率结构体ieee80211_tx_rate,定义如下:

 


/**
 * struct ieee80211_tx_rate - rate selection/status
 * 
 * A value of -1 for @idx indicates an invalid rate and, if used
 * in an array of retry rates, that no more rates should be tried.
 *
 * When used for transmit status reporting, the driver should
 * always report the rate along with the flags it used.
 *
 * &struct ieee80211_tx_info contains an array of these structs
 * in the control information, and it will be filled by the rate
 * control algorithm according to what should be sent. For example,
 * if this array contains, in the format { <idx>, <count> } the
 * information
 *    { 3, 2 }, { 2, 2 }, { 1, 4 }, { -1, 0 }, { -1, 0 }
 * then this means that the frame should be transmitted
 * up to twice at rate 3, up to twice at rate 2, and up to four
 * times at rate 1 if it doesn't get acknowledged. Say it gets
 * acknowledged by the peer after the fifth attempt, the status
 * information should then contain
 *   { 3, 2 }, { 2, 2 }, { 1, 1 }, { -1, 0 } ...
 * since it was transmitted twice at rate 3, twice at rate 2
 * and once at rate 1 after which we received an acknowledgement.
 */
struct ieee80211_tx_rate {
    s8 idx;
    u16 count:5,
        flags:11;
} __packed;


发送速率tx的数据信息结构体ieee80211_tx_info ,定义如下:

/**
 * struct ieee80211_tx_info - skb transmit information
 *
 * This structure is placed in skb->cb for three uses:
 *  (1) mac80211 TX control - mac80211 tells the driver what to do
 *  (2) driver internal use (if applicable)
 *  (3) TX status information - driver tells mac80211 what happened
 *
 * @flags: transmit info flags, defined above
 * @band: the band to transmit on (use for checking for races)
 * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC
 * @ack_frame_id: internal frame ID for TX status, used internally
 * @control: union for control data
 * @status: union for status data
 * @driver_data: array of driver_data pointers
 * @ampdu_ack_len: number of acked aggregated frames.
 *  relevant only if IEEE80211_TX_STAT_AMPDU was set.
 * @ampdu_len: number of aggregated frames.
 *  relevant only if IEEE80211_TX_STAT_AMPDU was set.
 * @ack_signal: signal strength of the ACK frame
 */
struct ieee80211_tx_info {
    /* common information */
    u32 flags;
    u8 band;

    u8 hw_queue;

    u16 ack_frame_id;

    union {
        struct {
            union {
                /* rate control */
                struct {
                    struct ieee80211_tx_rate rates[
                        IEEE80211_TX_MAX_RATES];
                    s8 rts_cts_rate_idx;
                    u8 use_rts:1;
                    u8 use_cts_prot:1;
                    u8 short_preamble:1;
                    u8 skip_table:1;
                    /* 2 bytes free */
                };
                /* 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;
            u32 flags;
            /* 4 bytes free */
        } control;
        struct {
            struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
            s32 ack_signal;
            u8 ampdu_ack_len;
            u8 ampdu_len;
            u8 antenna;
            void *status_driver_data[21 / sizeof(void *)];
        } status;
        struct {
            struct ieee80211_tx_rate driver_rates[
                IEEE80211_TX_MAX_RATES];
            u8 pad[4];

            void *rate_driver_data[
                IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)];
        };
        void *driver_data[
            IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)];
    };
};


使用简单速率控制则调用minstrel_get_sample_rate函数来实现,内容如下:


static int
minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
{
    struct minstrel_rate_stats *mr;
    struct minstrel_mcs_group_data *mg;
    unsigned int sample_dur, sample_group;
    int sample_idx = 0;

    if (mi->sample_wait > 0) {
        mi->sample_wait--;
        return -1;
    }

    if (!mi->sample_tries)
        return -1;

    sample_group = mi->sample_group;
    mg = &mi->groups[sample_group];
    sample_idx = sample_table[mg->column][mg->index];
    minstrel_next_sample_idx(mi);

    if (!(mg->supported & BIT(sample_idx)))
        return -1;

    mr = &mg->rates[sample_idx];
    sample_idx += sample_group * MCS_GROUP_RATES;

    /*
     * Sampling might add some overhead (RTS, no aggregation)
     * to the frame. Hence, don't use sampling for the currently
     * used rates.
     */
    if (sample_idx == mi->max_tp_rate ||
        sample_idx == mi->max_tp_rate2 ||
        sample_idx == mi->max_prob_rate)
        return -1;

    /*
     * Do not sample if the probability is already higher than 95%
     * to avoid wasting airtime.
     */
    if (mr->probability > MINSTREL_FRAC(95, 100))
        return -1;

    /*
     * Make sure that lower rates get sampled only occasionally,
     * if the link is working perfectly.
     */
    sample_dur = minstrel_get_duration(sample_idx);
    if (sample_dur >= minstrel_get_duration(mi->max_tp_rate2) &&
        (mi->max_prob_streams <
         minstrel_mcs_groups[sample_group].streams ||
         sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {
        if (mr->sample_skipped < 20)
            return -1;

        if (mi->sample_slow++ > 2)
            return -1;
    }
    mi->sample_tries--;

    return sample_idx;
}


经过速率算法的计算后得到结果,其实最后修改速率的结果传递了minstrel_ht_get_rate函数最后的rate->idx和rate->flags:

rate->idx = sample_idx % MCS_GROUP_RATES +
        (sample_group->streams - 1) * 8;
rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags;


可以根据rate结构体ieee80211_tx_rate,自定义符合要求的idx和flags来实现速率的控制和修改。
————————————————
版权声明:本文为CSDN博主「viewsky11」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/viewsky11/article/details/56843450

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值