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の事です。
/* 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の更新と次の送信の制御