0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Marvell Wi-Fi driverのmulti port aggregation を読む

Posted at

Marvell Wi-Fi driverのmulti port aggregation についてLinuxカーネルに含まれているソースコードを調べてみました。

この機能は、11n の frame aggregation を SDIOの書き込み手順を工夫して効率的に行う方法です。

まとめ

  • multi port aggregation は SDIOのCMD53の1回の書き込み時に複数のパケットをまとめて書き込むことで、チップにまとめて複数のパケットを渡し、チップで11nのMPDUフレームアグリゲーションを効率的に行う機能です。
  • 複数のパケットを書き込む際に、CMD53の書き込みアドレスに、パケット数などの情報をエンコードして渡して、チップ内のHWアシスト機能を有効に使います。
  • アグリゲートは最大8個または16個をまとめて書き込む。
  • ドライバでは、なるだけアグリゲーションするパケット数を増やしたい。そうするとレイテンシは犠牲になるがスループットは増える。
  • sdio.cのmwifiex_host_to_card_mp_aggr()にアグリゲートのアルゴリズムが記述してある。
  • 送信キューの残りデータの有無で、アルゴリズムを変える。、
  • 送信キューに残りデータが無いなら、データをすぐ送信する。
  • 送信キューに残りデータがあるなら、なるべく、アグリゲートバッファにデータをコピーするだけにする。 バッファが足りなくなった等の事象が発生したら送信を行う。

準備

  • Linux kernel 4.6.2
  • drivers/net/wireless/marvell/mwiflex
  • 送信のみを対象とする。

mpa の構造体

  • 送信のmpa構造体です。 mpaはmulti port aggregationの事です。
sdio.h
/* data structure for SDIO MPA TX */
struct mwifiex_sdio_mpa_tx {
        /* multiport tx aggregation buffer pointer */
        u8 *buf;     //バッファ  
        u32 buf_len; //バッファの使用量
        u32 pkt_cnt; //パケット数
        u32 ports;   // mpaのports 使用予定のbitmap
        u16 start_port; //mpaのports 開始位置
        u8 enabled;  // 1なら multiport aggregation 有効、0なら無効
        u32 buf_size; // バッファbufのサイズ チップによって異なる 87xx は16KB
        u32 pkt_aggr_limit; // アグリゲートする最大パケット数 チップによって異なる 87xxは8個
};
  • この構造体が sdio_cardのメンバにあり card->mpa_tx のように参照できます。

mpaのステータス

  • mpaのステータスを見るマクロなどを見てみます。

  • 送信のmpaを使用中かどうか。pkt_cntが正なら使用中です。

/* SDIO Tx aggregation in progress ? */
#define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0)
  • mpat_tx.bufに長さlenの空きがあるかどうか確認
/* SDIO Tx aggregation buffer room for next packet ? */
#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ((a->mpa_tx.buf_len+len)    \
                        <= a->mpa_tx.buf_size)
  • payloadからpky_lenだけ mpat_tx.bufにデータをコピー。 pkt_cnt, start_port, portsをアップデート
/* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */
#define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do {      \
    memmove(&a->mpa_tx.buf[a->mpa_tx.buf_len],          \
            payload, pkt_len);              \
    a->mpa_tx.buf_len += pkt_len;                   \
    if (!a->mpa_tx.pkt_cnt)                     \
        a->mpa_tx.start_port = port;                \
    if (a->mpa_tx.start_port <= port)               \
        a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt));        \
    else                                \
        a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+        \
                        (a->max_ports - \
                        a->mp_end_port)));  \
    a->mpa_tx.pkt_cnt++;                        \
} while (0)
  • アグリゲーション最大個数に達したかどうか確認
/* SDIO Tx aggregation limit ? */
#define MP_TX_AGGR_PKT_LIMIT_REACHED(a)                 \
            (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit)
  • アグリゲーションのカウンタと使用量をリセット
/* Reset SDIO Tx aggregation buffer parameters */
#define MP_TX_AGGR_BUF_RESET(a) do {                    \
    a->mpa_tx.pkt_cnt = 0;                      \
    a->mpa_tx.buf_len = 0;                      \
    a->mpa_tx.ports = 0;                        \
    a->mpa_tx.start_port = 0;                   \
} while (0)

#送信

mwifiex_host_to_card

  • sdio.cで定義
  • DATAかCMDかでportを選択
  • 上位から来るデータをSDIOブロックサイズ単位になるように長さを調整、(CMD53のマルチブロック転送のため)
  • mwifiex_host_to_card_mp_aggr()を呼ぶ
  • エラーで実際の送信はなかったら、portsの情報からcard->curr_wr_portとcard->mp_wr_bitmapをアップデート

mwifiex_host_to_card_mp_aggr

  • sdio.cで定義
  • cardのportがcontrol,cmd だったり、 mpaが無効な場合は1個単位で送信
  • パケットが続いているなら
  • MPA実施中なら
  • MPAバッファに空きがあるなら f_precopy_cur_buf を1にする
  • 次のパケット分の空きが無いなら、f_send_aggr_buf = 1;にする
  • 実施中でないなら
  • MPAバッファに空きがあってかつ??なら f_precopy_cur_buf = 1;
  • そうでないなら f_send_cur_buf = 1;
    if (next_pkt_len) {
        /* More pkt in TX queue */
        mwifiex_dbg(adapter, INFO,
                "info: %s: more packets in queue.\n",
                __func__);

        if (MP_TX_AGGR_IN_PROGRESS(card)) {
            if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) {
                f_precopy_cur_buf = 1;

                if (!(card->mp_wr_bitmap &
                      (1 << card->curr_wr_port)) ||
                    !MP_TX_AGGR_BUF_HAS_ROOM(
                        card, pkt_len + next_pkt_len))
                    f_send_aggr_buf = 1;
            } else {
                /* No room in Aggr buf, send it */
                f_send_aggr_buf = 1;

                if (!(card->mp_wr_bitmap &
                      (1 << card->curr_wr_port)))
                    f_send_cur_buf = 1;
                else
                    f_postcopy_cur_buf = 1;
            }
        } else {
            if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) &&
                (card->mp_wr_bitmap & (1 << card->curr_wr_port)))
                f_precopy_cur_buf = 1;
            else
                f_send_cur_buf = 1;
        }
    } else {
  • パケットが続いてなく、最後のパケットなら
  • MPA実施中なら
  • f_send_aggr_buf = 1;
  • MPAバッファに空きがあるなら f_precopy_cur_buf を1にする
  • そうでないなら、f_send_cur_buf = 1;
    } else {
        /* Last pkt in TX queue */
        mwifiex_dbg(adapter, INFO,
                "info: %s: Last packet in Tx Queue.\n",
                __func__);

        if (MP_TX_AGGR_IN_PROGRESS(card)) {
            /* some packs in Aggr buf already */
            f_send_aggr_buf = 1;

            if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len))
                f_precopy_cur_buf = 1;
            else
                /* No room in Aggr buf, send it */
                f_send_cur_buf = 1;
        } else {
            f_send_cur_buf = 1;
        }
    }
  • f_precopy_cur_buf なら MP_TX_AGGR_BUF_PUT()でコピー
  • f_send_aggr_buf なら
  • mport を設定
  • mportにmwifiex_write_data_to_card()で書き込み
  • MP_TX_AGGR_BUF_RESET(card);でmpaのカウンタをリセット
  • f_send_cur_bufなら mwifiex_write_data_to_card()でシングル送信
  • f_postcopy_cur_buf なら MP_TX_AGGR_BUF_PUT()でコピー

mpa の port

  • SDIO CMD52のポートのアドレスに、mpaのstart portとmpaのportsビットマップをエンコードしてチップに伝える。 アドレスにエンコードするのはチップのHWアシストを使うためだと考えられる。
  • mpa の portのHW仕様は card->supports_sdio_new_mode のtrue/falseで異なる。
  • これは 87xxはfalseで 88xx, 89xxでtrueとなる
  • falseの場合 mportの32bitの割り当て
  • 32 - 25 : adapter->ioport
  • 24 - 17 : SDIO_MPA_ADDR_BASE
  • 16 - 5 : mpa ports (1からのビットマップ)
  • 4 - 1 : mpa start_port
  • 設定例
            mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
                 (card->mpa_tx.ports << 4)) +
                 card->mpa_tx.start_port;
  • trueの場合 mportの32bitの割り当て
  • 32 - 25 : adapter->ioport
  • 24 - 17 : SDIO_MPA_ADDR_BASE
  • 16 - 5 : mpa ports (portsを2進数にして含まれる1の個数 -1)
  • 4 - 1 : mpa start_port
  • 設定例
            u32 port_count;
            int i;

            for (i = 0, port_count = 0; i < card->max_ports; i++)
                if (card->mpa_tx.ports & BIT(i))
                    port_count++;

            /* Writing data from "start_port + 0" to "start_port +
             * port_count -1", so decrease the count by 1
             */
            port_count--;
            mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
                 (port_count << 8)) + card->mpa_tx.start_port;
  • ports の内容の遷移

  • 00000000 : 最初

  • 00000001 : 1パケット追加

  • 00000011 : 1パケット追加

  • 00000111 : 1パケット追加

  • 00000000 : アグリゲート送信

  • 00001000 : 1パケット追加

  • 00011000 : 1パケット追加

  • 00111000 : 1パケット追加

  • 01111000 : 1パケット追加

  • 11111000 : 1パケット追加

  • 11111001 : 1パケット追加

  • 11111011 : 1パケット追加

  • 00000000 : アグリゲート送信

  • 00000100 : 1パケット追加

  • mpaのports使用状況は割り込み毎にmwifiex_process_int_status()でcard->mp_wr_bitmapにアップデートされる。

  • card->max_ports はチップによって異なる 16か32か64。

  • データ送信のportはmparのportと通常の1個のデータ通信のポートの2個ある

分からないところ

  • 送信完了の処理
  • 割り込みの処理
  • portsの更新と次の送信の制御
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?