PCIe學習筆記之Max payload size

本文基於linux 5.7.0, 平臺是arm64

1. 概述

1.1 什麼是max payload size

我們都知道,PCIe設備是以TLP的形式發送報文的,而max payload size(簡稱mps)決定了pcie設備實際使用的tlp能夠傳輸的最大字節數。mps的大小是由PCIe鏈路兩端的設備協商決定的,PCIe設備發送TLP時,其最大payload不能超過mps的值。
mps定義在Device control register中。
Device Control register:
在這裏插入圖片描述
同樣定義在該寄存器中還有一個Max read request size(簡稱mrrs)配置,它表示每一個讀請求所能夠讀到的最大字節數。當PCIe設備發送memory讀請求TLP時,該TLP所請求的數據的大小不能超過mrrs的值。

1.2 max payload size的作用

pice協議規定有數據的TLP包的傳遞規則是:按照指定DW長度單位傳遞數據,發送端的數據承載量不得超過“Device Control Register”中的“Max_Payload_Size”數值,接收端中,所接收到的數據量也不能超過接收端“Device Control Register”中的“Max_Payload_Size”數值。
所以mps主要的作用是:
(1) mps的大小影響pcie設備的傳輸效率。 對於比較大的數據量,如果mps設置比較小,那麼數據只能被分割成多個TLP進行發送,勢必會影響pcie鏈路帶寬的利用率。但是mps的值也不是越大越好,一方面mps設置的越大,硬件處理數據包所需的內存和邏輯量也隨之增加; 另一方面,mps的值是一個自協商的結果,當前市場上支持較大mps值的ep設備不常見,所以沒有必要設置本端的mps值太大。

(2) 不合理的mps設置會導致數據通信時上報"Malformed TLP"錯誤。 RC設備在與EP設備對接時,對端的EP設備的MPS可能各不相同,需要RC端去適配EP端的mps, 如果EP設備接收的TLP length字段超過了它的mps配置,該設備就會認爲該TLP非法。

1.3 mrrs 對mps的影響

mrrs和mps並沒有直接的聯繫,但是有時候mrrs會對mps有影響。
舉個例子:一個典型的pcie拓撲結構, 中間的紅色數字mps的值。
假設發起一個memory tlp讀請求操作:
在這裏插入圖片描述
根據上文的分析,這種情況下,RC和EP的通信會失敗,因爲他們的mps的值不相等。
但是如果設置各個節點的mrrs的值爲128B,那麼雖然RC設備的mps爲256B,但是實際能夠傳輸的大小卻受到mrrs的影響,變成128B,這種情況下,實際上傳輸的TLP報文最終是128字節,可以滿足各節點mps一致的條件,可以傳輸成功。

總結: mrrs的值不能設置的太小,一方面,如果mrrs設置太小,會影響實際TLP的mps。另一方面,如果我們設定MRRS爲128B,我們讀64KB數據時,就需要發送512(64KB/128B=512)次讀請求,這樣的話,就增加了很多額外的開銷,對數據傳輸速率也是一種傷害,所以,爲了提高特別是大Block Size data的傳輸效率,儘量把mrrs設的大一點,用更少的次數傳遞更多的數據。但是mrrs也不能設置過大,在PCIe鏈路中,我們不能一直髮送TLP,而是需要接受端有足夠的接受空間之後,才能繼續發送TLP,buffer空間太大的話對於EP的設備而言是比較苛刻的,所以在實際的設計中,也會對mrrs的大小進行限制。

2. 設備MPS的查詢和配置

2.1 使用lspci 查詢mps


[root@localhost linux]# lspci  -s 00:18.0 -vv
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
	Latency: 0, Cache Line Size: 32 bytes
	Interrupt: pin ? routed to IRQ 48
	Bus: primary=00, secondary=1b, subordinate=1b, sec-latency=0
	I/O behind bridge: 00007000-00007fff
	Memory behind bridge: fd100000-fd1fffff
	Prefetchable memory behind bridge: 00000000e7700000-00000000e77fffff
	Secondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- <SERR- <PERR-
	BridgeCtl: Parity- SERR- NoISA+ VGA- MAbort- >Reset- FastB2B-
		PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
	Capabilities: [40] Subsystem: VMware PCI Express Root Port
	Capabilities: [48] Power Management version 3
		Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
		Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
	Capabilities: [50] Express (v2) Root Port (Slot+), MSI 00
		DevCap:	MaxPayload 128 bytes, PhantFunc 0
			ExtTag- RBE-
		DevCtl:	Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
			RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-
			MaxPayload 128 bytes, MaxReadReq 128 bytes

查出來mps的大小爲128 byte, mrrs的大小爲 128byte。

2.2 使用setpci進行設置

在之前的博客 【PCIe學習筆記之pcie結構和配置空間】介紹過怎麼用lspci和setpci得到配置空間的值。

Device Control寄存器位於PCI express capability結構裏,對應的capability id 爲0x10(#define PCI_CAP_ID_EXP 0x10 /* PCI Express */)。

[root@localhost linux]# lspci -s 00:18.0 -xxx
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00: ad 15 a0 07 07 00 10 00 01 00 04 06 08 00 81 00
10: 00 00 00 00 00 00 00 00 00 1b 1b 00 70 70 00 00
20: 10 fd 10 fd 71 e7 71 e7 00 00 00 00 00 00 00 00

查看lspci結果, 可以看出偏移0x20的位置對應的capability id爲0x10, 所以device control register的偏移是0x20 + 0x8。
查看device control register的值:

[root@localhost linux]# setpci -s 00:18.0 0x18.W
1b00

或者使用CAP_EXP(對應的是pcie express capability的偏移)

[root@localhost linux]# setpci -s 00:18.0 CAP_EXP+08.W
0000

可以看出device control register的值爲0, 該寄存器【14:12】是mrrs, [7:5]是mps, 所以mps和mrrs都是0, 對應的正好是128B。

bit[2:0] max payload size or max read request size
000 128B
001 256B
010 512B
011 1024B
100 2048B
101 4096B

設置mps和mrrs的大小爲256B,即【14:12】=0x1, [7:5]=0x1

setpci -s 00:18.0 CAP_EXP+08.W=0x2020
[root@localhost linux]# lspci -s 00:18.0 -vvv
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- 
	Capabilities: [50] Express (v2) Root Port (Slot+), MSI 00
		DevCap:	MaxPayload 256 bytes, PhantFunc 0
			ExtTag- RBE-
		DevCtl:	Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
			RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-
			MaxPayload 256 bytes, MaxReadReq 256 bytes

mps和mrrs的值成功設置爲256byte. (有時候設置會不生效,這個也是和硬件支持的能力相關聯)

3. linux內核實現

3.1 mps的配置類型

在linux內核中,當前有四種配置mps的類型,定義在include/linux/pci.h中

enum pcie_bus_config_types {
        PCIE_BUS_TUNE_OFF,      /* Don't touch MPS at all */
        PCIE_BUS_SAFE,          /* Use largest MPS boot-time devices support */
        PCIE_BUS_PERFORMANCE,   /* Use MPS and MRRS for best performance */
        PCIE_BUS_PEER2PEER,     /* Set MPS = 128 for all devices */
};

PCIE_BUS_TUNE_OFF: 不對PCIe MPS(Max Payload Size)進行調整,而是使用BIOS配置好的默認值。
PCIE_BUS_SAFE: 將每個設備的MPS都設爲root complex下所有設備支持的MPS中的最大值。 指的是設置最小的那個設備的mps爲所有設備的mps。
PCIE_BUS_PERFORMANCE: 將設備的MPS設爲其上級總線允許的最大MPS,同時將MRRS(Max Read Request Size)設爲能支持的最大值(但不能大於設備或總線所支持的MPS值)
PCIE_BUS_PEER2PEER: 所有的設備的MPS都設置爲128B,以確保支持所有設備之間的點對點DMA,同時也能保證熱插入(hot-added)設備能夠正常工作,但代價是可能會造成性能損失。

3.2 os更改mps的配置類型

內核在啓動的cmdline中可以配置pci_bus_config,如:

pci=pcie_bus_perf

3.3 代碼實現

結合代碼分析linux中mps的配置, 以及看mps是怎麼生效的:

void pcie_bus_configure_settings(struct pci_bus *bus)
{
        u8 smpss = 0;

        if (!bus->self)
                return;

        if (!pci_is_pcie(bus->self))
                return;

        /*
         * FIXME - Peer to peer DMA is possible, though the endpoint would need
         * to be aware of the MPS of the destination.  To work around this,
         * simply force the MPS of the entire system to the smallest possible.
         */
        if (pcie_bus_config == PCIE_BUS_PEER2PEER)           -------  (1)
                smpss = 0;

        if (pcie_bus_config == PCIE_BUS_SAFE) {
                smpss = bus->self->pcie_mpss;

                pcie_find_smpss(bus->self, &smpss);            ------- (2)
                pci_walk_bus(bus, pcie_find_smpss, &smpss);         
        }

        pcie_bus_configure_set(bus->self, &smpss);              -------- (3)
        pci_walk_bus(bus, pcie_bus_configure_set, &smpss);
}

(1) 當pcie_bus_config是peer2peer時,設置smpss的值爲0, 最終通過pcie_write_mps(dev, 128 << smpss)寫到配置空間裏,所以peer2peer的配置下,mps被固定128B。

(2) 當pcie_bus_config是safe時, pcie_find_smpss()會被調用,該函數的作用是尋找一個最小的mps。

static int pcie_find_smpss(struct pci_dev *dev, void *data)
{
	u8 *smpss = data;
	
	if (*smpss > dev->pcie_mpss)
		*smpss = dev->pcie_mpss;

	return 0;
}

(3) pcie_bus_configure_set會將mps設置寫進配置空間, 會對pci_bus_perf做特殊處理,
在pci_bus_perf配置下: 會對每一個非根節點取本身和父節點的mps的較小值作爲自己的mps

static void pcie_write_mps(struct pci_dev *dev, int mps)
{
	int rc;

	if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {
		mps = 128 << dev->pcie_mpss;

		if (pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT &&
		    dev->bus->self)

			/*
			 * For "Performance", the assumption is made that
			 * downstream communication will never be larger than
			 * the MRRS.  So, the MPS only needs to be configured
			 * for the upstream communication.  This being the case,
			 * walk from the top down and set the MPS of the child
			 * to that of the parent bus.
			 *
			 * Configure the device MPS with the smaller of the
			 * device MPSS or the bridge MPS (which is assumed to be
			 * properly configured at this point to the largest
			 * allowable MPS based on its parent bus).
			 */
			mps = min(mps, pcie_get_mps(dev->bus->self)); 
	}

	rc = pcie_set_mps(dev, mps);
	if (rc)
		pci_err(dev, "Failed attempting to set the MPS\n");
}

同時pci_bus_perf配置下也會對pcie_write_mrrs()進行調用, 將MRRS設爲各個節點能支持的最大值(但不能大於設備或總線所支持的MPS值).

總結

結合Document和代碼分析,最終我們可以得出mps的配置規則如下圖所示:
在這裏插入圖片描述
綠色對應的是pci_bus_no_tune, 什麼都沒改。
橙色的對應的是pci_bus_safe, 所有的設備的mps改爲最小的那個設備的mps值,即所有的設備的mps設置爲256
黃色的對應的pci_bus_perf, 除了根節點以外,比較自己設備的mps和父節點的mps, 選擇較小的那一個設爲自己的mps, 所以除了rc的mps, 其他節點的mps都設置爲256, 此時,還需要更新各個節點mrrs的大小,保證mrrs的值不大於自身的mps的值,否則會出現1.3章節介紹的問題。
藍色的對應的是pci_bus_peer2peer, 保證DMA的通信,所有設備的mps設爲128.

參考資料

PCIe 體系結構導讀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章