文章目录

  • 1. 问题现象
  • 2. 分析过程
    • Step 1. 分析传统的int_x中断流程
    • Step 2. 排查MSI中断流程
  • 3. 结论

1. 问题现象

在调试 Am5728 + vxworks时,风河反馈两个pcie插槽,只有pcie1能使用msi中断pcie2不能使用使用msi中断,后来只能把两个插槽都换成传统的int_x中断。

为什么pcie2不能使用msi中断?

2. 分析过程

AM5728对硬件中断处理部分可以参考"am5728_int_map_summary.pdf"。

Step 1. 分析传统的int_x中断流程

在初始化时注册中断:

LOCAL STATUS tiAm572xPcieAttach()
{.../* (1) 获取pcie1/pcie2对应dts中的"interrupts"属性pcie1: "interrupts      = <40 0 4>;" pcie2: "interrupts      = <188 0 4>;"*/pDrvCtrl->pMsiIntRes = vxbResourceAlloc (pDev, VXB_RES_IRQ, 0);/* (2) 使用"interrupts"属性来注册pcie中断总入口 */if (ERROR == vxbIntConnect (pDev, pDrvCtrl->pMsiIntRes,(VOIDFUNCPTR)tiAm572xPcieMsiHandler,(void *)pDrvCtrl)){DEBUG_MSG (AM572X_DBG_ERR, "<tiAm572xPcieAttach>: vxbIntConnect ""failed\n");goto errOut;}/* (3) 分配传统INT_X的中断槽位新建一个中断控制器,因为是静态中断,会在vxbIntCtrl[0 - 31] 中寻找一个空闲中断控制器槽位然后在vxbIntrVecs[]中分配4个中断向量槽位*/if (ERROR == vxbIntRegister (pDev, pFdtDev->offset, AM572X_PCI_MAX_INT_LINE,0, &pDrvCtrl->intxVecBase)){DEBUG_MSG (AM572X_DBG_ERR, "<tiAm572xPcieAttach>: Register legacy intx ""vectors failed\n");goto errOut;}/* (4) 分配MSI的中断槽位新建一个中断控制器,因为是动态中断,会在vxbIntCtrl[32 - 39] 中寻找一个空闲中断控制器槽位然后在vxbIntrVecs[]中分配32个中断向量槽位*/if (ERROR == vxbDyncIntRegister (pDev, AM572X_PCIE_MSI_MAX_NUM,&pDrvCtrl->msiVecBase)){DEBUG_MSG (AM572X_DBG_ERR, "<tiAm572xPcieAttach>: Register msi vectors ""failed\n");goto errOut;}...
}

在中断总入口处理函数中,区分是传统INT_X或者MSI中断:

LOCAL void tiAm572xPcieMsiHandler()irqStatus = TI_CFG_READ_4 (pDrvCtrl, TI_CONF_IRQSTATUS_MSI);if (0 == irqStatus){return;}/* (2.1) MSI中断 */if (0 != (irqStatus & TI_CONF_IRQSTATUS_MSI_MSI)){msiStatus = CSR_READ_4 (pDrvCtrl, PL_MSI_CTRL_INT_STATUS_0);if (0 != msiStatus){for (i = 0; i < AM572X_PCIE_MSI_MAX_NUM; i++){if (msiStatus & (1 << i)){/* (2.1.1) 提取出具体的MSI中断编号,并执行中断服务程序 */CSR_WRITE_4 (pDrvCtrl, PL_MSI_CTRL_INT_STATUS_0, (1 << i));vector = pDrvCtrl->msiVecBase + AM572X_PCI_MAX_INT_LINE + i;if (vxbIntrVecs[vector]->flag & VXB_INTR_VEC_ENABLE){VXB_INT_ISR_CALL (vector);}}}}}/* (2.2) 传统INT_X中断 */if (0 != (irqStatus & TI_CONF_IRQSTATUS_MSI_INTX)){for (i = 0; i < AM572X_PCI_MAX_INT_LINE; i++){if (irqStatus & (1 << i)){/* (2.2.1) 提取出具体的INT_X中断编号,并执行中断服务程序 */vector = pDrvCtrl->intxVecBase + i;if (vxbIntrVecs[vector]->flag & VXB_INTR_VEC_ENABLE){VXB_INT_ISR_CALL (vector);}}}}TI_CFG_WRITE_4 (pDrvCtrl, TI_CONF_IRQSTATUS_MSI, irqStatus);}

在pcie bus遍历并添加设备设备时,如果设备的INT_PIN寄存器不为0,会从INT_0/1/2/3中给其分配一个传统INT_X中断:

tiAm572xPcieAttach() -> vxbPciAutoConfig() -> vxbPciBusAddDev() -> vxbPciResourceInit():LOCAL STATUS vxbPciResourceInit()
{.../* Acquire interrupt pin info *//* (5.1) 获取到pcie设备INT_PIN寄存器的值 */(void) VXB_PCI_CFG_READ(pDev, pIvars, PCI_CFG_DEV_INT_PIN, 1, &intPin);pIvars->pciIntPin = intPin;/** Assign IRQ value and convert to VxBus resource. Note that* this sets up the legacy INTx interrupt only.*//* (5.2) 如果INT_PIN寄存器的值不为0,给其分配一个传统INT_X中断 */if (intPin != 0){pIntr = (VXB_INTR_ENTRY *)vxbMemAlloc (sizeof(VXB_INTR_ENTRY));if (pIntr == NULL)goto fail;/* (5.2.1) 在pcie控制器中分配一个从INT_0/1/2/3中给其分配一个传统INT_X中断VXB_PCI_INT_ASSIGN()最后会调用到tiAm572xPcieIntAssign()函数*/if (VXB_PCI_INT_ASSIGN(pDev, pIvars, intPin, &irq, pIntr) != OK){vxbMemFree (pIntr);pIntr = NULL;goto skip;}/* Save it to the child's intLine register. *//* (5.2.2) 把分配得到的中断号保存到INT_LINE寄存器中 */(void) VXB_PCI_CFG_WRITE(pDev, pIvars, PCI_CFG_DEV_INT_LINE, 1, irq);/* Set up the VxBus resource for this IRQ. *//* (5.2.3) 把中断号包装成VXB_RESOURCE_IRQ,挂到设备的resource链表中 */pResIrq = (VXB_RESOURCE_IRQ *)vxbMemAlloc (sizeof(VXB_RESOURCE_IRQ));if (pResIrq == NULL)goto fail;pRes = (VXB_RESOURCE *)vxbMemAlloc (sizeof(VXB_RESOURCE));if (pRes == NULL)goto fail;pRes->pRes = (void *)pResIrq;pRes->id = VXB_RES_ID_CREATE(VXB_RES_IRQ, 0); /* INTX */pResIrq->hVec = irq;pResIrq->flag |= VXB_INT_FLAG_STATIC;pResIrq->pVxbIntrEntry = (void *)pIntr;pIntr = NULL;if (vxbResourceAdd (&pIvars->vxbResList, pRes) != OK){vxbMemFree ((char *)pIntr);vxbMemFree ((char *)pRes);vxbMemFree ((char *)pResIrq);goto fail;}}}

分配INT_X中断最核心的函数是tiAm572xPcieIntAssign():

tiAm572xPcieIntAssign() -> vxbFdtPciIntrGet():STATUS vxbFdtPciIntrGet()
{/* (5.2.1.1) 根据bus/slot/func/pin来组织查询编号 */addr = (bus << 16) | (slot << 11) | (func << 8);childSpec[0] = addr;childSpec[1] = 0;childSpec[2] = 0;childSpec[3] = pin;mapLen = intrInfo->mapLen;mapPtr = intrInfo->map;/* (5.2.1.2) 使用编号在interrupt-map中查询对应的中断 */while (i < mapLen){pOffset = vxFdtNodeOffsetByPhandle(vxFdt32ToCpu(mapPtr[childSpecCells]));pCell = vxFdtPropGet(pOffset, "#interrupt-cells", &cellSize);if (pCell == NULL)return ERROR;parIntrcells = vxFdt32ToCpu(*(UINT32 *)pCell);rowCells = childSpecCells + 1 +  parIntrcells;/* Apply mask and look up the entry in interrupt map. */for (j = 0; j < childSpecCells; j++){masked[j] = childSpec[j] &vxFdt32ToCpu(intrInfo->mask[j]);if (masked[j] != vxFdt32ToCpu(mapPtr[j]))goto next;}/* Decode interrupt of the parent intr controller. */specIdx = childSpecCells + 1;*interrupt = (UINT8)vxFdt32ToCpu(mapPtr[specIdx]);pIntrEntry->node = pOffset;pIntrEntry->pProp = (UINT32 *)&mapPtr[specIdx + 1];pIntrEntry->numProp = (parIntrcells - 1);return OK ;next:mapPtr += rowCells;i += (UINT32)(rowCells * sizeof(UINT32));}return ERROR;}

我们dts中的interrupt-map定义如下:

        pcie1: pcie@20000000{...#interrupt-cells = <1>;interrupt-map-mask = <0xfff800 0 0 7>;interrupt-map = <0x0000 0 0 1 &pcie1 0       /* INT A */0x0000 0 0 2 &pcie1 1       /* INT B */0x0000 0 0 3 &pcie1 2       /* INT C */0x0000 0 0 4 &pcie1 3>;     /* INT D */};

在pcie设备驱动中,例如i210网卡驱动中通过以下方法来得到这个中断:

geiEndStart() -> geiIvecsAlloc():geiIvecsAlloc()
{...if (ix == 0){ivec->pRes = NULL;
#ifdef _WRS_KERNELif (pDrvCtrl->geiDevType != GEI_DEVTYPE_PCIX){
#ifdef GEI_MSIX_SUPPORTif (pDrvCtrl->geiDevId == INTEL_DEVICEID_82574L){r = vxbPciMsiXAlloc (pDev, 1);if (r == 1){/* add GEI_ICR_OTHER for MSI-X mode */ivec->events |= GEI_ICR_OTHER;}}else
#endif /* GEI_MSIX_SUPPORT *//* (1) 使用MSI分配动态资源,并作为VXB_RESOURCE_IRQ加入到设备的Resource链表当中 */r = vxbPciMsiAlloc (pDev, 1);if (r == 1)/* (2) 如果MSI中断分配成功,获取设备Resource链表中的MSI中断注意使用的是 VXB_RES_IRQ编号1,VXB_RES_IRQ编号1中保存的是新分配的MSI中断VXB_RES_IRQ编号0中保存的是设备创建时分配的INT_X中断*/ivec->pRes = vxbResourceAlloc (pDev, VXB_RES_IRQ, 1);}
#endif /* _WRS_KERNEL *//* Fall back to INTX *//* (3) 如果MSI中断分配失败,获取设备Resource链表中的INT_X中断注意使用的是 VXB_RES_IRQ编号0*/if (ivec->pRes == NULL)ivec->pRes = vxbResourceAlloc (pDev, VXB_RES_IRQ, 0);}elseivec->pRes = NULL;}}...
}

Step 2. 排查MSI中断流程

MSI中断和INT_X中断的流程大致相似,最大的区别还是在中断的分配时机和方法上。

INT_X中断是在pci auto config创建设备时就已经创建好了,设备驱动直接vxbResourceAlloc()编号0的VXB_RES_IRQ就能得到。

MSI中断需要设备驱动自己来调用vxbPciMsiAlloc()来分配,分配完成后vxbResourceAlloc()编号1的VXB_RES_IRQ就能得到。

我们来看看其中关键vxbPciMsiAlloc()的实现:

vxbPciMsiAlloc() -> vxbIntAlloc() -> _func_dyncIntAlloc() -> vxbDyncIntAlloc():LOCAL int vxbDyncIntAlloc(UINT32               count,      /* number of interrupt to request */VXB_DYNC_INT_ENTRY * pVxbDyncIntEntry /* dynamic interrupt entry */){int ix, num;int iy;int base;VXB_DEV_ID pIntCtrl;for (ix = 0; ix < VXB_DYNAMIC_INT_CTRL_MAX; ix++){/* (1.1) 找到中断控制器数组中,第一个能支持动态中断分配的控制器 */if (!(vxbIntCtrl[ix + VXB_INT_CONTRL_MAX].flag & VXB_INT_FLAG_ALLOCATED))continue;base = vxbIntCtrl[ix + VXB_INT_CONTRL_MAX].base;pIntCtrl = (VXB_DEV_ID)vxbIsrPicGet(base);if (pIntCtrl == NULL)continue;/* (1.2) 调用中断控制器的动态中断分配函数这里会调用到tiAm572xPcieIntAlloc()函数 */if ((num = VXB_INT_ALLOC(pIntCtrl, count, pVxbDyncIntEntry)) > 0){for (iy = 0; iy < num; iy++){/* (1.3) 根据分配到的MSI中断编号,加上中断控制器的基地址得到中断的逻辑向量编号 */pVxbDyncIntEntry[iy].lVec = pVxbDyncIntEntry[iy].hVec + \vxbIntCtrl[ix + VXB_INT_CONTRL_MAX].base;}return num;}elsecontinue;}return 0;}

我们看到上述函数有一个明显的漏洞,它只会找第一个空闲的中断控制器,而不是根据设备挂载到哪个pcie控制器下面来找对应的MSI中断控制器。这会导致所有的MSI中断都注册到第一个MSI中断控制器上,即pcie1的MSI中断控制器上。

我们使用isrShow命令来查看实际的中断注册情况:

Pcie1的中断基地址:
<tiAm572xPcieAttach>: Register legacy intx, intxVecBase = 208
<tiAm572xPcieAttach>: Register msi vectors, msiVecBase =212Pcie2的中断基地址:
<tiAm572xPcieAttach>: Register legacy intx, intxVecBase = 244
<tiAm572xPcieAttach>: Register msi vectors, msiVecBase =248-> isrShow
ISR ID     Name                        Tag        HandlerRtn
---------- --------------------------- ---------- ------------------------------
…
0x2028da38 isr14                       40         tiAm572xPcieMsiHandler
0x2034f890 isr15                       216        geiEndLegacyMsiInt            // pcie1网卡msi中断
0x20296040 isr16                       188        tiAm572xPcieMsiHandler
0x2040b250 isr17                       217        geiEndInt                     // pcie2网卡msi中断
…
value = 0 = 0x0

我们可以看到pcie1和pcie2网卡的中断都注册到了pcie1上,符合我们的推测。

3. 结论

综上所述:

该问题的根因是Vxworks的MSI机制有问题,造成pcie1和pcie2网卡的中断都注册到了pcie1上。

我们修改Vxworks的MSI中断分配机制,最核心的修改如下:

diff --git a/vxbDyncIntLib.c b/vxbDyncIntLib.c
index 99be397..ff2c0d9 100644
--- a/vxbDyncIntLib.c
+++ b/vxbDyncIntLib.c
@@ -26,15 +26,19 @@ modification history#include <vxWorks.h>#include <hwif/vxBus.h>
-#include <subsys/int/vxbIntLib.h>
-#include <subsys/int/vxbDyncIntLib.h>
+#include <vxbIntLib.h>
+#include <vxbDyncIntLib.h>#include <hwif/methods/vxbIntMethod.h>+#include <string.h>
+#include <private/kwriteLibP.h>         /* _func_kprintf */
+
+IMPORT VXB_INT_CONTRL vxbIntCtrl[];UINT32 vxbDyncIntCtrlNum;-LOCAL int vxbDyncIntAlloc (UINT32 count, VXB_DYNC_INT_ENTRY * pVxbDyncIntEntry);
-LOCAL void vxbDyncIntFree (UINT32 count, VXB_DYNC_INT_ENTRY * pVxbDyncIntEntry);
+LOCAL int vxbDyncIntAlloc (VXB_DEV_ID pDev, UINT32 count, VXB_DYNC_INT_ENTRY * pVxbDyncIntEntry);
+LOCAL void vxbDyncIntFree (VXB_DEV_ID pDev, UINT32 count, VXB_DYNC_INT_ENTRY * pVxbDyncIntEntry);LOCAL int init = 0;@@ -130,6 +134,7 @@ STATUS vxbDyncIntRegisterLOCAL int vxbDyncIntAlloc(
+       VXB_DEV_ID               pDev,       /* device id */UINT32               count,      /* number of interrupt to request */VXB_DYNC_INT_ENTRY * pVxbDyncIntEntry /* dynamic interrupt entry */)
@@ -137,6 +142,8 @@ LOCAL int vxbDyncIntAllocint ix, num;int iy;int base;
+       char parent_path[100];
+       char child_path[100];VXB_DEV_ID pIntCtrl;@@ -150,6 +157,20 @@ LOCAL int vxbDyncIntAllocif (pIntCtrl == NULL)continue;
+
+               vxbDevPathGet(pIntCtrl, parent_path, 128);
+               vxbDevPathGet(pDev, child_path, 128);
+               //(* _func_kprintf)("parent_path: %s \n", parent_path);
+               //(* _func_kprintf)("child_path: %s \n", child_path);
+
+               //(* _func_kprintf)("parent_path len: %d \n", strlen(parent_path));
+               //(* _func_kprintf)("child_path len: %d \n", strlen(child_path));
+
+               //(* _func_kprintf)("comp 1: %d \n", strcmp(parent_path, child_path));
+               //(* _func_kprintf)("comp 2: %d \n", strcmp(child_path, parent_path));
+
+               if (strcmp(child_path, parent_path) < strlen(parent_path))
+                       continue;if ((num = VXB_INT_ALLOC(pIntCtrl, count, pVxbDyncIntEntry)) > 0){

思想就是让pcie1网卡和pcie2网卡从各自的pcie MSI中断控制器中分配中断。

修改后中断分配效果如下:

-> isrShow
ISR ID     Name                        Tag        HandlerRtn
---------- --------------------------- ---------- ------------------------------
…
0x202873c0 isr12                       40         tiAm572xPcieMsiHandler
0x20342250 isr13                       216        geiEndLegacyMsiInt            // pcie1网卡msi中断
0x20288e90 isr14                       188        tiAm572xPcieMsiHandler
0x203fdb60 isr15                       252        geiEndInt                     // pcie2网卡msi中断
…
value = 0 = 0x0

两路网卡都能使用MSI中断模式工作。

vxworks issue: pcie msi interrupt相关推荐

  1. vxworks issue: PCIE Bar Base Address Unalignment

    文章目录 1. 问题现象 2. 分析过程 Step 1. 排查网卡驱动(vxbGei825xxEnd.c) Step 2. 综合分析(相关知识) Step 3. 排查PCIE物理地址和CPU物理地址转 ...

  2. vxworks issue: dtb overwrite

    文章目录 1. 问题现象 2. 分析过程 Step 1. 排查网卡驱动(vxbFdtTiCpswEnd.c) Step 2. 排查dtb文件内容 Step 3. 排查vxworks内存中dtb内容 S ...

  3. PCIe MSI中断

    detect:检测对端是否在位.前提知识:对端在位,电容变大:发送端发出脉冲,如果脉冲发生变形.幅值降低说明电路总电容大,表明存在对端. 信号名 对ip 来看的方向 含义 cfg_interrupt ...

  4. PCIe MSI 中断相关的启动流程

    文章目录 Linux PCIe 中断相关启动流程 1. PCIe控制器初始化过程 2. PCIe设备初始化过程 驱动详细解读 控制器侧驱动 设备侧驱动 Linux PCIe 中断相关启动流程 1. P ...

  5. js摇奖 转载

    查看全文 http://www.taodudu.cc/news/show-3790499.html 相关文章: 建议初创团队起初也要构建分布式应用 C语言堆栈 西电"智能星"第一届 ...

  6. Linux 禁用msi模式,禁用MSI模式支持

    禁用MSI模式支持 Task Manager might show 100% disk utilization on Windows 10 devices with Message Signaled ...

  7. NXP LS1046A及飞腾新四核 FT2004 PCIE EP端LINUX设备驱动开发

    文章目录 前言 一.PCIE 硬件简介 二.PCIE EP地址映射原理介绍 1. PCI总线的各种域(存储器域.PCI总线域) 2. 开发EP设备驱动要做的事 三.NXP LS1046A PCIE E ...

  8. 2018年买华硕笔记本 安装 Fedora 28,pcieport errors flood the journal

    解决方法(待验证): Adding the pci=noaer boot option solves the problem. ------------------------------------ ...

  9. ixgbe网卡驱动(一)

    注册网卡驱动 和大部分设备驱动一样,网卡驱动是作为一个module注册到kernel的 通过module_init() -> ixgbe_init_module() -> pci_regi ...

最新文章

  1. 一个图片展示效果的站点
  2. Caffe2 Compilation Error gflags.cc' is being linked both statically and dynamically into this execut
  3. 信息系统项目管理师论文历年题目2005-2020
  4. Python模拟随机漫步
  5. 支付业务与技术架构学习总结(4)——对账相关业务知识及对账系统总结
  6. 洛谷P1134阶乘问题(数论,末尾0的个数变形,思维转换)
  7. csdn设置资源下载所需积分
  8. 怎么获取论文所在期刊的电子版封面及目录
  9. cs局域网服务器未响应,电子竞技CS满十开打IP列表
  10. win10中常用快捷键 (包括切换窗口、打开我的电脑等快捷键)
  11. 清明节到来 祭扫各纷然
  12. 【小白向】利用笔记本+网线让台式机上网
  13. 找素数模板:马氏筛法【复杂度nlgnlgn】
  14. tm1650中文资料_TM1650+msp430单片机 调试及遇到问题的总结
  15. 六、HSV颜色空间应用实例——颜色分割提取与替换
  16. thinksns java_ThinkSNS+ 更新播报
  17. SmallUI-小ui 前端ui组件库(好用的组件千篇一律,轻巧的组件万里挑一)
  18. 详解Java NIO,IO与NIO的区别
  19. VScode 删除所有注释+空行(软著格式)
  20. win10笔记本怎么设置双屏显示?win10如何连接外置显示器的教程

热门文章

  1. OpenCV 检测二维码并定位
  2. SqlServer清理缓存方法
  3. 人工智能机器人聊天软件有哪些?推荐三个软件
  4. dropna的subset用法
  5. Golang 安装 Golang
  6. derain survey
  7. C++ fstream流的eof()函数多读一行的问题
  8. 复现开源论文代码总结
  9. python 从日期列表中选出最大的_python – 从日期时间列表中获取最早和最晚时间...
  10. anaconda配置python环境变量_Anaconda的安装及其环境变量的配置详解