一尖坤、錯誤分類
如上圖,pci傳輸過程中夯接,以及pcie設(shè)備自身發(fā)現(xiàn)的錯誤焕济,可以兩大類
- 可恢復(fù)錯誤: 表示硬件會自動恢復(fù)的錯誤,無需軟件參與
- 不可恢復(fù)錯誤盔几,不可恢復(fù)錯誤分為2小類:
- Non-Fatal錯誤: 表示傳輸事務(wù)出錯不可靠晴弃,但是物理層link還OK
- Fatal錯誤: 表示物理層link已經(jīng)出問題
從錯誤出現(xiàn)在PCIe所屬層級來分,可以分為4類:
- 物理層
- 數(shù)據(jù)鏈路層
- 傳輸層
- pcie設(shè)備內(nèi)部錯誤
二逊拍、錯誤檢測機制
Completion Status
傳輸過程中上鞠,如果接收端發(fā)現(xiàn)有問題,會在回復(fù)的TLP包中的completion status位置相應(yīng)的錯誤值芯丧,如下圖
cpl.status占了3bit芍阎,不同取值含義如下:
其中最主要的有2個:
- cpl.status=0b001,表示UR
- cpl.status=0b100注整,表示CA
不在表中的取值當(dāng)前都是保留狀態(tài)
下圖是對tlp的一種判斷邏輯:
Error轉(zhuǎn)發(fā)
在規(guī)范中能曾,這種錯誤還被稱為:data poisoning度硝,還是很形象的肿轨。這種錯誤只針對發(fā)送TLP或者回復(fù)的TLP中攜帶有數(shù)據(jù)的情況。并且是中間接收節(jié)點(像switch的UP/DP口)檢測到TLP包中攜帶的數(shù)據(jù)有問題時蕊程,會置TLP中“EP”位為1椒袍,表示此TLP包攜帶的數(shù)據(jù)有問題
Error Message
device發(fā)現(xiàn)錯誤,通過特殊的Message類型的TLP發(fā)送給RC進(jìn)行處理藻茂,TLP如下圖所示
其中type類型低3bit必須是0驹暑,表示發(fā)給RC,Message Code表示錯誤級別辨赐,取值如下圖所示:
這里有幾個需要注意的點:
- 錯誤消息是需要按錯誤級別發(fā)送給RC進(jìn)行統(tǒng)一處理
- 錯誤消息來自PCIe設(shè)備优俘、傳統(tǒng)PCI設(shè)備、RC自己
- 一個Request ID檢測到多條同級別的錯誤消息掀序,多條消息可能合并帆焕,同一級別的錯- 誤消息至少發(fā)送一條
- 對于錯誤消息,RC根據(jù)Request ID來標(biāo)識消息來源不恭,并且將消息轉(zhuǎn)換成平臺級別的事件叶雹,像中斷
三财饥、錯誤列表
General PCIe error list
內(nèi)部錯誤是與PCIe接口相關(guān)的錯誤,它發(fā)生在組件內(nèi)折晦,可能不是由于PCIe接口本身的數(shù)據(jù)包或事件钥星,也不是由于在PCI上發(fā)起的事務(wù)表達(dá)。
- 舉例1:數(shù)據(jù)包緩沖區(qū)錯誤被ECC糾正满着,上報corrected Internal Errror
- 舉例2:數(shù)據(jù)包緩沖區(qū)錯誤無法被ECC糾正, 上報un corrected Internal Errror
Physical Layer Error List
規(guī)范中只有Receiver Error一種
在網(wǎng)上還看到屯掖,Training Error也是一種物理層錯誤,參見:這篇文章
Data Link Layer Error List
Transaction Layer Error List
四镐作、錯誤記錄
不支持AER
不支持AER的設(shè)備暮顺,有兩個地方會記錄部分錯誤信息:
-
PCI兼容的配置空間中的status寄存器,由command寄存器控制
對于Bridge响驴,像UP/DP/RP透且,還有Secondary Status/Bridge Control兩個寄存器控制,與Status/Command寄存器類似豁鲤,是記錄和控制下層bus下設(shè)備相關(guān)錯誤
-
PCIe CAP下的Device Status寄存器秽誊,由device control寄存器控制
支持AER
支持AER的設(shè)備,會將之前列的Error list記錄在AER CAP的相應(yīng)bit位
部分錯誤攜帶TLP header相關(guān)信息琳骡,存放在AER CAP的Hear Log/TLP Prefix Log寄存器
Multiple Error處理
在AER CAP中有一個Advanced Error Capabilites and Control Register寄存器:
- First Error Pointer指向最先發(fā)生的Uncorrectable錯誤所在bit位的值锅论;Header Log寄存器記錄對應(yīng)錯誤的報頭信息
- 當(dāng)?shù)谝粋€Uncorrectable錯誤被處理并清除之后, First Error Pointer/Header Log會指向下一個Uncorrectable錯誤的相關(guān)信息
- 當(dāng)所有Uncorrectable錯誤被處理并清除之后楣号, First Error Pointer/Header Log指向無效值
- 當(dāng)錯誤報頭記錄數(shù)量超過實現(xiàn)所支持的最大數(shù)量時最易,會報Header Log Overflow錯誤
RC
RC除了記錄上面描述的Device相關(guān)的AER信息以外,還需要記錄接收到從下層或RC自己上報的AER信息
通過source寄存器標(biāo)識錯誤來源炫狱,如下圖
Root Error Status寄存器中的位表示收到了什么類型的錯誤藻懒,如下圖
注意:Bit5/bit6只是為了區(qū)分收到的uncorrectable錯誤是Fatal的還是Non-Fatal的
RC收到AER信息之后,可以:
-
配置觸發(fā)一個中斷(AER CAP中的Root Error Command Register來控制)视译,軟件來進(jìn)行處理AER
-
或者產(chǎn)生一個System Error(PCIe CAP里的Root Control Register來控制)
Advisory Non-Fatal Error
在某些情況下嬉荆,非致命錯誤的檢測器不是確定錯誤是否可恢復(fù)的最合適的代理,或者它甚至不需要任何恢復(fù)操作酷含。主要由檢測代理(請求者鄙早、完成者、接收者)的角色和特定錯誤確定椅亚。
基于此限番,提出了Advisory Non-Fatal Error場景,在此場景下收到的Non-Fatal錯誤換成Correctable錯誤(如果使能了AER)呀舔,或者不上報錯誤消息(沒有使能AER)
- Completer Sending a Completion with UR/CA Status
- Intermediate Receiver
- Ultimate PCI Express Receiver of a Poisoned TLP
- Requester with Completion Timeout
- Receiver of an Unexpected Completion
五弥虐、設(shè)備錯誤記錄以及上報的流程
六、錯誤消息上報控制
七、linux內(nèi)核對AER的處理
RC的AER功能初始化
/**
* aer_enable_rootport - enable Root Port's interrupts when receiving messages
* @rpc: pointer to a Root Port data structure
*
* Invoked when PCIe bus loads AER service driver.
*/
static void aer_enable_rootport(struct aer_rpc *rpc)
{
struct pci_dev *pdev = rpc->rpd;
int aer = pdev->aer_cap;
u16 reg16;
u32 reg32;
/* Clear PCIe Capability's Device Status */
pcie_capability_read_word(pdev, PCI_EXP_DEVSTA, ®16);
pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, reg16);
/* Disable system error generation in response to error messages */
pcie_capability_clear_word(pdev, PCI_EXP_RTCTL,
SYSTEM_ERROR_INTR_ON_MESG_MASK);
/* Clear error status */
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, ®32);
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, reg32);
pci_read_config_dword(pdev, aer + PCI_ERR_COR_STATUS, ®32);
pci_write_config_dword(pdev, aer + PCI_ERR_COR_STATUS, reg32);
pci_read_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, ®32);
pci_write_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, reg32);
aer_enable_irq(pdev);
}
static void aer_enable_irq(struct pci_dev *pdev)
{
int aer = pdev->aer_cap;
u32 reg32;
/* Enable Root Port's interrupt in response to error messages */
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, ®32);
reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32);
}
設(shè)備的AER初始化
void pci_aer_init(struct pci_dev *dev)
{
int n;
dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (!dev->aer_cap)
return;
dev->aer_stats = kzalloc(sizeof(struct aer_stats), GFP_KERNEL);
/*
* We save/restore PCI_ERR_UNCOR_MASK, PCI_ERR_UNCOR_SEVER,
* PCI_ERR_COR_MASK, and PCI_ERR_CAP. Root and Root Complex Event
* Collectors also implement PCI_ERR_ROOT_COMMAND (PCIe r5.0, sec
* 7.8.4).
*/
n = pcie_cap_has_rtctl(dev) ? 5 : 4;
pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ERR, sizeof(u32) * n);
pci_aer_clear_status(dev);
if (pci_aer_available())
pci_enable_pcie_error_reporting(dev);
pcie_set_ecrc_checking(dev);
}
-
pci_aer_clear_status(dev);
對應(yīng)的定義如下
int pci_aer_clear_status(struct pci_dev *dev)
{
if (!pcie_aer_is_native(dev))
return -EIO;
return pci_aer_raw_clear_status(dev);
}
/**
* pci_aer_raw_clear_status - Clear AER error registers.
* @dev: the PCI device
*
* Clearing AER error status registers unconditionally, regardless of
* whether they're owned by firmware or the OS.
*
* Returns 0 on success, or negative on failure.
*/
int pci_aer_raw_clear_status(struct pci_dev *dev)
{
int aer = dev->aer_cap;
u32 status;
int port_type;
if (!aer)
return -EIO;
port_type = pci_pcie_type(dev);
if (port_type == PCI_EXP_TYPE_ROOT_PORT ||
port_type == PCI_EXP_TYPE_RC_EC) {
pci_read_config_dword(dev, aer + PCI_ERR_ROOT_STATUS, &status);
pci_write_config_dword(dev, aer + PCI_ERR_ROOT_STATUS, status);
}
pci_read_config_dword(dev, aer + PCI_ERR_COR_STATUS, &status);
pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS, status);
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, &status);
pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, status);
return 0;
}
-
pci_enable_pcie_error_reporting(dev);
對應(yīng)的定義如下
static int pci_enable_pcie_error_reporting(struct pci_dev *dev)
{
int rc;
if (!pcie_aer_is_native(dev))
return -EIO;
rc = pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS);
return pcibios_err_to_errno(rc);
}
- 對于Bridge來說躯舔,還需要配置Bridge Control寄存器的SEER驴剔,這一位是控制是否轉(zhuǎn)發(fā)從下面設(shè)備接收到的錯誤消息到上層
static void pci_configure_serr(struct pci_dev *dev)
{
u16 control;
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
/*
* A bridge will not forward ERR_ messages coming from an
* endpoint unless SERR# forwarding is enabled.
*/
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &control);
if (!(control & PCI_BRIDGE_CTL_SERR)) {
control |= PCI_BRIDGE_CTL_SERR;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, control);
}
}
}
設(shè)備AER初始化在設(shè)備枚舉中的調(diào)用層次如下圖
RC對AER的處理
- 接收到AER中斷之后,會進(jìn)入AER的中斷處理函數(shù)粥庄,讀取status/source等信息
/**
* aer_irq - Root Port's ISR
* @irq: IRQ assigned to Root Port
* @context: pointer to Root Port data structure
*
* Invoked when Root Port detects AER messages.
*/
static irqreturn_t aer_irq(int irq, void *context)
{
struct pcie_device *pdev = (struct pcie_device *)context;
struct aer_rpc *rpc = get_service_data(pdev);
struct pci_dev *rp = rpc->rpd;
int aer = rp->aer_cap;
struct aer_err_source e_src = {};
pci_read_config_dword(rp, aer + PCI_ERR_ROOT_STATUS, &e_src.status);
if (!(e_src.status & AER_ERR_STATUS_MASK))
return IRQ_NONE;
pci_read_config_dword(rp, aer + PCI_ERR_ROOT_ERR_SRC, &e_src.id);
pci_write_config_dword(rp, aer + PCI_ERR_ROOT_STATUS, e_src.status);
if (!kfifo_put(&rpc->aer_fifo, e_src))
return IRQ_HANDLED;
return IRQ_WAKE_THREAD;
}
- 中斷處理函數(shù)會將AER相關(guān)信息通過FIFO隊列傳送給后端處理線程isr丧失,isr出隊一個一個處理AER
static irqreturn_t aer_isr(int irq, void *context)
{
struct pcie_device *dev = (struct pcie_device *)context;
struct aer_rpc *rpc = get_service_data(dev);
struct aer_err_source e_src;
if (kfifo_is_empty(&rpc->aer_fifo))
return IRQ_NONE;
while (kfifo_get(&rpc->aer_fifo, &e_src))
aer_isr_one_error(rpc, &e_src);
return IRQ_HANDLED;
}
- 處理AER的主要邏輯在
aer_isr_one_error(rpc, &e_src)
中,定義如下:
/**
* aer_isr_one_error - consume an error detected by root port
* @rpc: pointer to the root port which holds an error
* @e_src: pointer to an error source
*/
static void aer_isr_one_error(struct aer_rpc *rpc,
struct aer_err_source *e_src)
{
struct pci_dev *pdev = rpc->rpd;
struct aer_err_info e_info;
pci_rootport_aer_stats_incr(pdev, e_src);
/*
* There is a possibility that both correctable error and
* uncorrectable error being logged. Report correctable error first.
*/
if (e_src->status & PCI_ERR_ROOT_COR_RCV) {
e_info.id = ERR_COR_ID(e_src->id);
e_info.severity = AER_CORRECTABLE;
if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV)
e_info.multi_error_valid = 1;
else
e_info.multi_error_valid = 0;
aer_print_port_info(pdev, &e_info);
if (find_source_device(pdev, &e_info))
aer_process_err_devices(&e_info);
}
if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
e_info.id = ERR_UNCOR_ID(e_src->id);
if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
e_info.severity = AER_FATAL;
else
e_info.severity = AER_NONFATAL;
if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)
e_info.multi_error_valid = 1;
else
e_info.multi_error_valid = 0;
aer_print_port_info(pdev, &e_info);
if (find_source_device(pdev, &e_info))
aer_process_err_devices(&e_info);
}
}
主要分三塊內(nèi)容:
- 統(tǒng)計RC收到各類型錯誤的計數(shù)
- 處理此AER中的可恢復(fù)中斷
- 處理此AER中的不可恢復(fù)中斷
而后兩塊內(nèi)容處理邏輯類似惜互,都包含三方面:
- 找到AER來源布讹,即此AER是從哪個設(shè)備發(fā)上來的
- 統(tǒng)計此設(shè)備上出現(xiàn)的各類錯誤的計數(shù)
- 對此錯誤進(jìn)行處理