.. SPDX-License-Identifier: GPL-2.0 .. include:: <isonum.txt> .. include:: ../disclaimer-zh_CN.rst :Original: Documentation/PCI/msi-howto.rst :翻译: å¸å»¶è…¾ Yanteng Si <siyanteng@loongson.cn> :æ ¡è¯‘: =========== MSIé©±åŠ¨æŒ‡å— =========== :作者: Tom L Nguyen; Martine Silbermann; Matthew Wilcox :版æƒ: 2003, 2008 Intel Corporation å…³äºŽæœ¬æŒ‡å— ========== 本指å—介ç»äº†æ¶ˆæ¯æ ‡è®°ä¸æ–(MSI)的基本知识,使用MSIç›¸å¯¹äºŽä¼ ç»Ÿä¸æ–机制的优势,如何 改å˜ä½ 的驱动程åºä»¥ä½¿ç”¨MSI或MSI-X,以åŠåœ¨è®¾å¤‡ä¸æ”¯æŒMSIæ—¶å¯ä»¥å°è¯•çš„一些基本诊æ–方法。 什么是MSI? ========== ä¿¡æ¯ä¿¡å·ä¸æ–是指从设备写到一个特殊的地å€ï¼Œå¯¼è‡´CPU接收到一个ä¸æ–。 MSI能力首次在PCI 2.2ä¸è§„定,åŽæ¥åœ¨PCI 3.0ä¸å¾—到增强,å…许对æ¯ä¸ªä¸æ–进行å•ç‹¬å±è”½ã€‚ MSI-X功能也éšç€PCI 3.0被引入。它比MSI支æŒæ¯ä¸ªè®¾å¤‡æ›´å¤šçš„ä¸æ–,并å…许独立é…ç½®ä¸æ–。 设备å¯ä»¥åŒæ—¶æ”¯æŒMSIå’ŒMSI-X,但一次åªèƒ½å¯ç”¨ä¸€ä¸ªã€‚ 为什么用MSI? ============ æœ‰ä¸‰ä¸ªåŽŸå› å¯ä»¥è¯´æ˜Žä¸ºä»€ä¹ˆä½¿ç”¨MSIæ¯”ä¼ ç»Ÿçš„åŸºäºŽé’ˆè„šçš„ä¸æ–有优势。 基于针脚的PCIä¸æ–é€šå¸¸åœ¨å‡ ä¸ªè®¾å¤‡ä¹‹é—´å…±äº«ã€‚ä¸ºäº†æ”¯æŒè¿™ä¸€ç‚¹ï¼Œå†…æ ¸å¿…é¡»è°ƒç”¨æ¯ä¸ªä¸Žä¸æ–相 关的ä¸æ–处ç†ç¨‹åºï¼Œè¿™å¯¼è‡´äº†æ•´ä¸ªç³»ç»Ÿæ€§èƒ½çš„é™ä½Žã€‚MSI从ä¸å…±äº«ï¼Œæ‰€ä»¥è¿™ä¸ªé—®é¢˜ä¸ä¼šå‡ºçŽ°ã€‚ 当一个设备将数æ®å†™å…¥å†…å˜ï¼Œç„¶åŽå¼•å‘一个基于引脚的ä¸æ–时,有å¯èƒ½åœ¨æ‰€æœ‰çš„æ•°æ®åˆ°è¾¾å†…å˜ ä¹‹å‰ï¼Œä¸æ–就已ç»åˆ°è¾¾äº†ï¼ˆè¿™åœ¨PCI-PCIæ¡¥åŽé¢çš„设备ä¸å˜å¾—更有å¯èƒ½ï¼‰ã€‚为了确ä¿æ‰€æœ‰çš„æ•° æ®å·²ç»åˆ°è¾¾å†…å˜ä¸ï¼Œä¸æ–处ç†ç¨‹åºå¿…须在引å‘ä¸æ–的设备上读å–一个寄å˜å™¨ã€‚PCI事务排åºè§„ 则è¦æ±‚所有的数æ®åœ¨è¿”回寄å˜å™¨çš„值之å‰åˆ°è¾¾å†…å˜ã€‚使用MSIå¯ä»¥é¿å…è¿™ä¸ªé—®é¢˜ï¼Œå› ä¸ºä¸æ–产 生的写入ä¸èƒ½é€šè¿‡æ•°æ®å†™å…¥ï¼Œæ‰€ä»¥å½“ä¸æ–å‘生时,驱动程åºçŸ¥é“所有的数æ®å·²ç»åˆ°è¾¾å†…å˜ä¸ã€‚ PCI设备æ¯ä¸ªåŠŸèƒ½åªèƒ½æ”¯æŒä¸€ä¸ªåŸºäºŽå¼•è„šçš„ä¸æ–。通常情况下,驱动程åºå¿…须查询设备以找出 å‘生了什么事件,这就å‡æ…¢äº†å¯¹å¸¸è§æƒ…况的ä¸æ–处ç†ã€‚有了MSI,设备å¯ä»¥æ”¯æŒæ›´å¤šçš„ä¸æ–, å…许æ¯ä¸ªä¸æ–被专门用于ä¸åŒçš„目的。一ç§å¯èƒ½çš„设计是给ä¸ç»å¸¸å‘生的情况(如错误)æä¾› 自己的ä¸æ–,这使得驱动程åºå¯ä»¥æ›´æœ‰æ•ˆåœ°å¤„ç†æ£å¸¸çš„ä¸æ–处ç†è·¯å¾„。其他å¯èƒ½çš„设计包括给 网å¡çš„æ¯ä¸ªæ•°æ®åŒ…队列或å˜å‚¨æŽ§åˆ¶å™¨çš„æ¯ä¸ªç«¯å£æ供一个ä¸æ–。 如何使用MSI =========== PCI设备被åˆå§‹åŒ–为使用基于引脚的ä¸æ–。设备驱动程åºå¿…须将设备设置为使用MSI或MSI-X。 并éžæ‰€æœ‰çš„机器都能æ£ç¡®åœ°æ”¯æŒMSI,对于这些机器,下é¢æè¿°çš„API将简å•åœ°å¤±è´¥ï¼Œè®¾å¤‡å°† 继ç»ä½¿ç”¨åŸºäºŽå¼•è„šçš„ä¸æ–。 åŠ å…¥å†…æ ¸å¯¹MSIçš„æ”¯æŒ ------------------- 为了支æŒMSI或MSI-Xï¼Œå†…æ ¸åœ¨æž„å»ºæ—¶å¿…é¡»å¯ç”¨CONFIG_PCI_MSI选项。这个选项åªåœ¨æŸäº›æž¶ 构上å¯ç”¨ï¼Œè€Œä¸”它å¯èƒ½å–决于其他一些选项的设置。例如,在x86ä¸Šï¼Œä½ å¿…é¡»åŒæ—¶å¯ç”¨X86_UP_APIC 或SMP,æ‰èƒ½çœ‹åˆ°CONFIG_PCI_MSI选项。 使用MSI ------- 大部分沉é‡çš„工作是在PCI层为驱动程åºå®Œæˆçš„。驱动程åºåªéœ€è¦è¯·æ±‚PCI层为这个设备设置 MSI功能。 è¦è‡ªåŠ¨ä½¿ç”¨MSI或MSI-Xä¸æ–å‘é‡ï¼Œè¯·ä½¿ç”¨ä»¥ä¸‹å‡½æ•°:: int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, unsigned int max_vecs, unsigned int flags); 它为一个PCI设备分é…最多至max_vecsçš„ä¸æ–å‘é‡ã€‚它返回分é…çš„å‘é‡æ•°é‡æˆ–一个负的错误。 如果设备对最å°æ•°é‡çš„å‘é‡æœ‰è¦æ±‚,驱动程åºå¯ä»¥ä¼ 递一个min_vecså‚数,设置为这个é™åˆ¶ï¼Œ 如果PCIæ ¸ä¸èƒ½æ»¡è¶³æœ€å°æ•°é‡çš„å‘é‡ï¼Œå°†è¿”回-ENOSPC。 flagså‚数用æ¥æŒ‡å®šè®¾å¤‡å’Œé©±åŠ¨ç¨‹åºå¯ä»¥ä½¿ç”¨å“ªç§ç±»åž‹çš„ä¸æ–(PCI_IRQ_LEGACY, PCI_IRQ_MSI, PCI_IRQ_MSIX)。一个方便的çŸè¯ï¼ˆPCI_IRQ_ALL_TYPES)也å¯ä»¥ç”¨æ¥è¦æ±‚任何å¯èƒ½çš„ä¸æ–类型。 如果PCI_IRQ_AFFINITYæ ‡å¿—è¢«è®¾ç½®ï¼Œpci_alloc_irq_vectors()将把ä¸æ–分散到å¯ç”¨çš„CPU上。 è¦èŽ·å¾—ä¼ é€’ç»™require_irq()å’Œfree_irq()çš„Linux IRQå·ç å’Œå‘é‡ï¼Œè¯·ä½¿ç”¨ä»¥ä¸‹å‡½æ•°:: int pci_irq_vector(struct pci_dev *dev, unsigned int nr); åœ¨åˆ é™¤è®¾å¤‡ä¹‹å‰ï¼Œåº”使用以下功能释放任何已分é…的资æº:: void pci_free_irq_vectors(struct pci_dev *dev); 如果一个设备åŒæ—¶æ”¯æŒMSI-Xå’ŒMSI功能,这个API将优先使用MSI-X,而ä¸æ˜¯MSI。MSI-X支 æŒ1到2048之间的任何数é‡çš„ä¸æ–。相比之下,MSI被é™åˆ¶ä¸ºæœ€å¤š32个ä¸æ–(而且必须是2的幂)。 æ¤å¤–,MSIä¸æ–å‘é‡å¿…须连ç»åˆ†é…,所以系统å¯èƒ½æ— 法为MSI分é…åƒMSI-Xé‚£æ ·å¤šçš„å‘é‡ã€‚在一 些平å°ä¸Šï¼ŒMSIä¸æ–必须全部针对åŒä¸€ç»„CPU,而MSI-Xä¸æ–å¯ä»¥å…¨éƒ¨é’ˆå¯¹ä¸åŒçš„CPU。 如果一个设备既ä¸æ”¯æŒMSI-X,也ä¸æ”¯æŒMSIï¼Œå®ƒå°±ä¼šé€€å›žåˆ°ä¸€ä¸ªä¼ ç»Ÿçš„IRQå‘é‡ã€‚ MSI或MSI-Xä¸æ–的典型用法是分é…å°½å¯èƒ½å¤šçš„å‘é‡ï¼Œå¯èƒ½è¾¾åˆ°è®¾å¤‡æ”¯æŒçš„æžé™ã€‚如果nvec大于 设备支æŒçš„æ•°é‡ï¼Œå®ƒå°†è‡ªåŠ¨è¢«é™åˆ¶åœ¨æ”¯æŒçš„é™åº¦å†…,所以没有必è¦äº‹å…ˆæŸ¥è¯¢æ”¯æŒçš„å‘é‡çš„æ•°é‡ã€‚:: nvec = pci_alloc_irq_vectors(pdev, 1, nvec, PCI_IRQ_ALL_TYPES) if (nvec < 0) goto out_err; 如果一个驱动程åºä¸èƒ½æˆ–ä¸æ„¿æ„处ç†å¯å˜æ•°é‡çš„MSIä¸æ–,它å¯ä»¥è¦æ±‚一个特定数é‡çš„ä¸æ–,将该 æ•°é‡ä½œä¸ºâ€œmin_vecs“和“max_vecs“å‚æ•°ä¼ é€’ç»™pci_alloc_irq_vectors()函数。:: ret = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_ALL_TYPES); if (ret < 0) goto out_err; 上述请求类型的最è‡åæ˜è‘—的例å是为一个设备å¯ç”¨å•ä¸€çš„MSI模å¼ã€‚它å¯ä»¥é€šè¿‡ä¼ 递两个1作为 'min_vecs'å’Œ'max_vecs'æ¥å®žçŽ°:: ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (ret < 0) goto out_err; 一些设备å¯èƒ½ä¸æ”¯æŒä½¿ç”¨ä¼ 统的线路ä¸æ–,在这ç§æƒ…况下,驱动程åºå¯ä»¥æŒ‡å®šåªæŽ¥å—MSI或MSI-X。:: nvec = pci_alloc_irq_vectors(pdev, 1, nvec, PCI_IRQ_MSI | PCI_IRQ_MSIX); if (nvec < 0) goto out_err; ä¼ ç»ŸAPI ----------- 以下用于å¯ç”¨å’Œç¦ç”¨MSI或MSI-Xä¸æ–çš„æ—§APIä¸åº”该在新代ç ä¸ä½¿ç”¨:: pci_enable_msi() /* deprecated */ pci_disable_msi() /* deprecated */ pci_enable_msix_range() /* deprecated */ pci_enable_msix_exact() /* deprecated */ pci_disable_msix() /* deprecated */ æ¤å¤–,还有一些APIæ¥æ供支æŒçš„MSI或MSI-Xå‘é‡çš„æ•°é‡ï¼špci_msi_vec_count()å’Œ pci_msix_vec_count()。一般æ¥è¯´ï¼Œåº”该é¿å…使用这些方法,而是让pci_alloc_irq_vectors() æ¥é™åˆ¶å‘é‡çš„æ•°é‡ã€‚å¦‚æžœä½ å¯¹å‘é‡çš„æ•°é‡æœ‰åˆæ³•çš„特殊用例,我们å¯èƒ½è¦é‡æ–°å®¡è§†è¿™ä¸ªå†³å®šï¼Œ å¹¶å¢žåŠ ä¸€ä¸ªpci_nr_irq_vectors()助手,é€æ˜Žåœ°å¤„ç†MSIå’ŒMSI-X。 使用MSI时需è¦è€ƒè™‘çš„å› ç´ ----------------------- è‡ªæ—‹é” ~~~~~~ 大多数设备驱动程åºéƒ½æœ‰ä¸€ä¸ªæ¯çš„自旋é”,在ä¸æ–处ç†ç¨‹åºä¸è¢«å 用。对于基于引脚的ä¸æ– 或å•ä¸€çš„MSI,没有必è¦ç¦ç”¨ä¸æ–(Linuxä¿è¯åŒä¸€ä¸æ–ä¸ä¼šè¢«é‡æ–°è¾“入)。如果一个设备 使用多个ä¸æ–,驱动程åºå¿…须在é”被æŒæœ‰çš„时候ç¦ç”¨ä¸æ–。如果设备å‘出一个ä¸åŒçš„ä¸æ–, 驱动程åºå°†æ»é”,试图递归地获å–自旋é”。这ç§æ»é”å¯ä»¥é€šè¿‡ä½¿ç”¨spin_lock_irqsave() 或spin_lock_irq()æ¥é¿å…,它们å¯ä»¥ç¦ç”¨æœ¬åœ°ä¸æ–并获å–é”(è§ã€Šä¸å¯é çš„é”定指å—》)。 如何判æ–一个设备上是å¦å¯ç”¨äº†MSI/MSI-X ------------------------------------- 使用“lspci -v“(以root身份)å¯èƒ½ä¼šæ˜¾ç¤ºä¸€äº›å…·æœ‰â€œMSI“ã€â€œMessage Signalled Interrupts“ 或“MSI-X“功能的设备。这些功能ä¸çš„æ¯ä¸€ä¸ªéƒ½æœ‰ä¸€ä¸ªâ€œå¯ç”¨â€œæ ‡å¿—,åŽé¢æ˜¯â€œ+“(å¯ç”¨ï¼‰ 或“-“(ç¦ç”¨ï¼‰ã€‚ MSI特性 ======= 众所周知,一些PCI芯片组或设备ä¸æ”¯æŒMSI。PCIåè®®æ ˆæ供了三ç§ç¦ç”¨MSI的方法: 1. 全局的 2. 在一个特定的桥åŽé¢çš„所有设备上 3. 在å•ä¸€è®¾å¤‡ä¸Š 全局ç¦ç”¨MSI ----------- ä¸€äº›ä¸»æŽ§èŠ¯ç‰‡ç»„æ ¹æœ¬æ— æ³•æ£ç¡®æ”¯æŒMSI。如果我们幸è¿çš„è¯ï¼Œåˆ¶é€ 商知é“这一点,并在 ACPI FADT表ä¸æŒ‡æ˜Žäº†å®ƒã€‚在这ç§æƒ…况下,Linux会自动ç¦ç”¨MSI。有些æ¿å¡åœ¨è¡¨ä¸æ²¡ 有包括这一信æ¯ï¼Œå› æ¤æˆ‘们必须自己检测它们。完整的列表å¯ä»¥åœ¨drivers/pci/quirks.c ä¸çš„quirk_disable_all_msi()函数附近找到。 å¦‚æžœä½ æœ‰ä¸€å—有MSI问题的æ¿åï¼Œä½ å¯ä»¥åœ¨å†…æ ¸å‘½ä»¤è¡Œä¸ä¼ 递pci=nomsiæ¥ç¦ç”¨æ‰€æœ‰è®¾ 备上的MSIã€‚ä½ æœ€å¥½æŠŠé—®é¢˜æŠ¥å‘Šç»™linux-pci@vger.kernel.org,包括完整的 “lspci -vâ€œï¼Œè¿™æ ·æˆ‘ä»¬å°±å¯ä»¥æŠŠè¿™äº›æ€ªç™–æ·»åŠ åˆ°å†…æ ¸ä¸ã€‚ ç¦ç”¨æ¡¥ä¸‹çš„MSI ------------- 一些PCI桥接器ä¸èƒ½åœ¨æ€»çº¿ä¹‹é—´æ£ç¡®åœ°è·¯ç”±MSI。在这ç§æƒ…况下,必须在桥接器åŽé¢çš„所 有设备上ç¦ç”¨MSI。 一些桥接器å…è®¸ä½ é€šè¿‡æ”¹å˜PCIé…置空间的一些ä½æ¥å¯ç”¨MSI(特别是Hypertransport 芯片组,如nVidia nForceå’ŒServerworks HT2000ï¼‰ã€‚ä¸Žä¸»æœºèŠ¯ç‰‡ç»„ä¸€æ ·ï¼ŒLinux大 多知é“它们,如果å¯ä»¥çš„è¯ï¼Œä¼šè‡ªåŠ¨å¯ç”¨MSIã€‚å¦‚æžœä½ æœ‰ä¸€ä¸ªLinuxä¸çŸ¥é“çš„ç½‘æ¡¥ï¼Œä½ å¯ä»¥ ç”¨ä½ çŸ¥é“的任何方法在é…置空间ä¸å¯ç”¨MSI,然åŽé€šè¿‡ä»¥ä¸‹æ–¹å¼åœ¨è¯¥ç½‘桥上å¯ç”¨MSI:: echo 1 > /sys/bus/pci/devices/$bridge/msi_bus å…¶ä¸$bridgeæ˜¯ä½ æ‰€å¯ç”¨çš„桥的PCI地å€ï¼ˆä¾‹å¦‚0000:00:0e.0)。 è¦ç¦ç”¨MSI,请回显0而ä¸æ˜¯1。改å˜è¿™ä¸ªå€¼åº”è¯¥è°¨æ…Žè¿›è¡Œï¼Œå› ä¸ºå®ƒå¯èƒ½ä¼šç ´å这个桥下é¢æ‰€ 有设备的ä¸æ–处ç†ã€‚ åŒæ ·ï¼Œè¯·é€šçŸ¥ linux-pci@vger.kernel.org 任何需è¦ç‰¹æ®Šå¤„ç†çš„桥。 在å•ä¸€è®¾å¤‡ä¸Šå…³é—MSIs -------------------- 众所周知,有些设备的MSI实现是有问题的。通常情况下,这是在å•ä¸ªè®¾å¤‡é©±åŠ¨ç¨‹åºä¸å¤„ç†çš„, 但å¶å°”也有必è¦ç”¨ä¸€ä¸ªå¤æ€ªçš„方法æ¥å¤„ç†ã€‚一些驱动程åºæœ‰ä¸€ä¸ªé€‰é¡¹å¯ä»¥ç¦ç”¨MSI的使用。虽然 这对驱动程åºçš„作者æ¥è¯´æ˜¯ä¸€ä¸ªæ–¹ä¾¿çš„å˜é€šåŠžæ³•ï¼Œä½†è¿™ä¸æ˜¯ä¸€ä¸ªå¥½çš„åšæ³•ï¼Œä¸åº”该被模仿。 寻找设备上MSI被ç¦ç”¨çš„åŽŸå› ------------------------- ä»Žä»¥ä¸Šä¸‰ä¸ªéƒ¨åˆ†ï¼Œä½ å¯ä»¥çœ‹åˆ°æœ‰è®¸å¤šåŽŸå› 导致MSI没有在æŸä¸ªè®¾å¤‡ä¸Šè¢«å¯ç”¨ã€‚ä½ çš„ç¬¬ä¸€æ¥åº”该是 ä»”ç»†æ£€æŸ¥ä½ çš„dmesgä»¥ç¡®å®šä½ çš„æœºå™¨æ˜¯å¦å¯ç”¨äº†MSIã€‚ä½ è¿˜åº”è¯¥æ£€æŸ¥ä½ çš„.configä»¥ç¡®å®šä½ å·²ç» å¯ç”¨äº†CONFIG_PCI_MSI。 然åŽï¼Œâ€œlspci -t“给出一个设备上é¢çš„ç½‘åˆ—è¡¨ã€‚è¯»å– ``/sys/bus/pci/devices/*/msi_bus`` å°†å‘Šè¯‰ä½ MSI是å¦è¢«å¯ç”¨ï¼ˆ1)或ç¦ç”¨ï¼ˆ0)。如果在任何属于PCIæ ¹å’Œè®¾å¤‡ä¹‹é—´çš„æ¡¥çš„msi_bus 文件ä¸å‘现0,说明MSI被ç¦ç”¨ã€‚ 也需è¦æ£€æŸ¥è®¾å¤‡é©±åŠ¨ç¨‹åºï¼Œçœ‹å®ƒæ˜¯å¦æ”¯æŒMSI。例如,它å¯èƒ½åŒ…å«å¯¹å¸¦æœ‰PCI_IRQ_MSI或 PCI_IRQ_MSIXæ ‡å¿—çš„pci_alloc_irq_vectors()的调用。