背景
參考 dpdk 官方 bonding pmd 文檔旋讹,mode 0 是輪循砾跃,mode 1 是鏡像矫户,我司線上使用的 mode 4 是 802.3AD 協(xié)義,使用特定層面 hash 算法來(lái)分配流量磅叛。
使用 mode 4 比較特別屑咳,要求
- It needs to call rte_eth_tx_burst and rte_eth_rx_burst with intervals period of less than 100ms.
- Calls to rte_eth_tx_burst must have a buffer size of at least 2xN, where N is the number of slaves. This is a space required for LACP frames. Additionally LACP packets are included in the statistics, but they are not returned to the application.
dpvs bond 問(wèn)題
最初搭建 fullnat 時(shí)想用網(wǎng)卡 bonding, 由于線上都是 mode 4, 不給網(wǎng)絡(luò)組同學(xué)添麻煩,最好保持一致弊琴。但是 dpvs 程序啟動(dòng)后兆龙,初始化報(bào) warning,并且交換機(jī)也看到 bonding 失敗敲董。具體報(bào)錯(cuò)消息如下:
PMD: Slave 1: failed to enqueue LACP packet into RX ring.
Receive and transmit functions must be invoked on bonded
interface at least 10 times per second or LACP will not
work correctly
將 DPDK 編繹成 DEBUG 模式紫皇,打開 bond debug 開關(guān)。dpvs 也全部打開 DEBUG, 查看日志輸出:
PMD: 625 [Port 0: rx_machine] LACP -> CURRENT
PMD: LACP: {
subtype= 01
ver_num=01
actor={ tlv=01, len=14
pri=0080, system=B4:43:26:57:15:01, key=410E, p_pri=0080 p_num=5E00
state={ ACT AGG DEF }
}
partner={ tlv=02, len=14
pri=0000, system=00:00:00:00:00:00, key=0000, p_pri=0000 p_num=0000
state={ ACT AGG DEF EXP }
}
collector={info=03, length=10, max_delay=0000
, type_term=00, terminator_length = 00}
PMD: 625 [Port 0: tx_machine] Sending LACP frame
PMD: LACP: {
subtype= 01
ver_num=01
actor={ tlv=01, len=14
pri=FFFF, system=6C:92:BF:47:B2:66, key=2100, p_pri=FF00 p_num=0100
state={ ACT AGG }
}
partner={ tlv=02, len=14
pri=0080, system=B4:43:26:57:15:01, key=410E, p_pri=0080 p_num=5E00
state={ ACT AGG DEF }
}
collector={info=03, length=10, max_delay=0000
, type_term=00, terminator_length = 00}
結(jié)合 dpdk 源代碼解讀腋寨,rx_machine 是收取交換機(jī)發(fā)來(lái)的協(xié)義消息聪铺,成功接收,看到交換機(jī)的 mac 地址 B4:43:26:57:15:01
萄窜,dpdk 根據(jù) mode 4 協(xié)義發(fā)送 lacp 回包计寇,內(nèi)容和接收的相反,bond 網(wǎng)卡 mac 是 6C:92:BF:47:B2:66
脂倦,結(jié)合最后報(bào)錯(cuò) failed to enqueue LACP packet into RX ring
, 可以定性是 tx_ring 隊(duì)列一直沒(méi)消費(fèi)番宁,dpvs 沒(méi)有發(fā)送 lacp 回包給交換機(jī)。
dpvs 發(fā)包邏輯
dpvs發(fā)包都在大 loop 里赖阻,是個(gè)死循環(huán)蝶押,發(fā)送函數(shù)是 lcore_job_xmit
static void lcore_job_xmit(void *args)
{
int i, j;
lcoreid_t cid;
portid_t pid;
struct netif_queue_conf *qconf;
cid = rte_lcore_id();
for (i = 0; i < lcore_conf[lcore2index[cid]].nports; i++) {
pid = lcore_conf[lcore2index[cid]].pqs[i].id;
for (j = 0; j < lcore_conf[lcore2index[cid]].pqs[i].ntxq; j++) {
qconf = &lcore_conf[lcore2index[cid]].pqs[i].txqs[j];
if (qconf->len <= 0)
continue;
netif_tx_burst(cid, pid, j);
qconf->len = 0;
}
}
}
static inline void netif_tx_burst(lcoreid_t cid, portid_t pid, queueid_t qindex)
{
int ntx, ii;
struct netif_queue_conf *txq;
unsigned i = 0;
struct rte_mbuf *mbuf_copied = NULL;
struct netif_port *dev = NULL;
assert(LCORE_ID_ANY != cid);
txq = &lcore_conf[lcore2index[cid]].pqs[port2index[cid][pid]].txqs[qindex];
if (0 == txq->len)
return;
dev = netif_port_get(pid);
......
ntx = rte_eth_tx_burst(pid, txq->id, txq->mbufs, txq->len);
lcore_stats[cid].opackets += ntx;
/* do not calculate obytes here in consideration of efficency */
if (unlikely(ntx < txq->len)) {
RTE_LOG(DEBUG, NETIF, "Fail to send %d packets on dpdk%d tx%d\n", ntx,pid, txq->id);
lcore_stats[cid].dropped += txq->len - ntx;
for (ii = ntx; ii < txq->len; ii++)
rte_pktmbuf_free(txq->mbufs[ii]);
}
}
基本邏輯,就是判斷 qconf->len 發(fā)包隊(duì)列長(zhǎng)度火欧,如果大于 0棋电,表示有數(shù)據(jù)需要發(fā)送。最終調(diào)用 dpdk 庫(kù)函數(shù) rte_eth_tx_burst
完成最終發(fā)送邏輯苇侵。
但是這個(gè) qconf 隊(duì)列和 lacp tx_ring 有什么關(guān)系呢赶盔?完全沒(méi)有關(guān)系...
dpdk lacp 發(fā)包邏輯
首先,每個(gè)物理網(wǎng)卡都是一個(gè) rte_eth_dev
, 而 bonding 的網(wǎng)卡是 rte_vdev_device
, 需要調(diào)用 bond_alloc
初始化榆浓,最重要有兩個(gè)函數(shù) bond_mode_8023ad_setup
和 bond_ethdev_mode_set
bond_mode_8023ad_setup
會(huì)設(shè)置一個(gè)定時(shí)器于未,每 100ms 調(diào)用一次 bond_mode_8023ad_periodic_cb
函數(shù),這個(gè)就是用來(lái)協(xié)商 lacp 的陡鹃。rx_machine_update
接收 rx_ring 數(shù)據(jù)烘浦,tx_machine
封裝好本機(jī) lacp 后發(fā)送到 tx_ring. bond_ethdev_mode_set
最重要的是調(diào)置 rx_burst 和 tx_burst 回調(diào)函數(shù)
eth_dev->rx_pkt_burst = bond_ethdev_rx_burst_8023ad;
eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_8023ad;
非 bonding 的普通網(wǎng)卡,tx_pkt_burst萍鲸,rx_pkt_burst 都是 ixgbe 的闷叉。
bond_ethdev_tx_burst_8023ad
代碼比較長(zhǎng)不貼了,原理就是在業(yè)務(wù)正常調(diào)用 rte_eth_tx_burst
時(shí)脊阴,順便把 lacp tx_ring 的數(shù)據(jù)發(fā)送到網(wǎng)卡握侧。
問(wèn)題所在
結(jié)合兩個(gè)發(fā)包邏輯可以明確,dpvs 屬于業(yè)務(wù)層嘿期,qconf 的緩存里有數(shù)據(jù)才發(fā)送品擎,而 lcap 的數(shù)據(jù)是存儲(chǔ)在 dpdk 底層的 tx_ring 中,當(dāng) bond 網(wǎng)卡剛被 dpvs up 起來(lái)時(shí)秽五,業(yè)務(wù)層是沒(méi)有數(shù)據(jù)的孽查,也就不會(huì)調(diào)用 rte_eth_tx_burst
順便把 lacp 發(fā)送給交換機(jī)。
解決方案坦喘?
最簡(jiǎn)單的就是修改 lcore_job_xmit
代碼盲再,去掉所有隊(duì)列長(zhǎng)度判斷的邏輯,這樣就會(huì)一直調(diào)用底層 rte_eth_tx_burst
. 測(cè)試一下瓣铣,bond 成功答朋。但是性能肯定會(huì)受影響,希望 dpvs 開發(fā)團(tuán)隊(duì)能 fix 一下棠笑。
想到的另外一個(gè)方案梦碗,就是注冊(cè)一個(gè) slow 類型的 job,專職用于刷新 lacp, 我去掉個(gè) pr 吧。