PCI,PCIe扫盲

PCI和PCIe

PCI即Peripheral Component Interconnect,中文意思是“外围器件互联”,是由PCISIG (PCI Special Interest Group)推出的一种局部并行总线标准。 它曾经是个人电脑中使用最为广泛的接口,几乎所有的主板产品上都带有这种插槽。目前该总线已经逐渐被PCI Express总线所取代。

PCI-Express是继ISA和PCI总线之后的第三代I/O总线,即3GIO。 由Intel在2001年的IDF上提出,由PCI-SIG(PCI特殊兴趣组织)认证发布后才改名为“PCI-Express”。它的主要优势就是数据传输速率高,另外还有抗干扰能力强,传输距离远,功耗低等优点。

PCI做的工作

0、Driver通过pci_register_driver()向PCI subsystem注册
1、PCI subsystem扫描可用设备,通过设备ID找到对应的driver,调用driver的probe()函数初始化设备
2、PCI subsystem是driver和HW之间的桥梁,driver通过调用PCI subsystem提供的函数控制HW
3、PCI设备有唯一的总线地址BDF,内核通过BDF(Bus Device Function)对设备进行寻址
4、PCI设备通过PCI总线访问内存,PCI设备之间也可以通过PCI总线传输数据

PCI总线结构

PCI总线是一种树型结构,并且独立于CPU总线,可以和CPU总线并行操作。PCI总线上可以挂接PCI设备和PCI桥,PCI总线上只允许有一个PCI主设备(同一时刻), 其他的均为PCI 从设备,而且读写操作只能在主从设备之间进行,从设备之间的数据交换需要通过主设备中转。 但是这并不意味着所有的读写操作都需要通过北桥中转,因为PCI总线上的主设备和从设备属性是可以变化的。 下面是一个典型的总线结构。 一个典型的总线结构

PCI总线是一种共享总线,所以需要特定的仲裁器(Arbiter)来决定当前时刻的总线的控制权。 一般该仲裁器位于北桥中,而仲裁器(主机)则通过一对引脚,REQ#(request) 和GNT# (grant)来与各个从机连接。

三种数据传输模型

三种数据传输模型的示意图如下图所示

Programmed I/O(PIO):
比如说某一个PCI设备需要向内存(SDRAM)中写入一些数据,该PCI设备会向CPU请求一个中断,然后CPU首先先通过PCI总线把该PCI设备的数据读取到CPU内部的寄存器中,然后再把数据从内部寄存器写入到内存(SDRAM)中。

DMA:
DMA是一种在传输过程中,几乎不需要CPU进行干预的数据传输方式。如上面的图片所示,以太网可以直接向内存(SDRAM)中写入数据,而几乎不需要CPU的干预。

Peer-To-Peer:
前面的文章中,我们介绍过PCI总线系统中的主机身份并不是固定不变的,而是可以切换的(借助仲裁器),但是同一时刻只能存在一个主机。完成Peer-to-Peer这一传输方式的前提是,PCI总线系统中至少存在一个有能力成为主机的设备。在仲裁器的控制下,完成主机身份的切换,进而获得PCI总线的控制权,然后与总线上的其他PCI设备进行通信。不过,需要注意的是,在实际的系统中,Peer-to-Peer这一传输方式却很少被使用,这是因为获得主机身份的PCI设备(Initiator)和另一个PCI设备(Target)通常采用不同的数据格式,除非他们是同一个厂家的设备。

PCI地址空间

PCI总线具有32位数据/地址复用总线,所以其存储地址空间为2的32次方=4GB。也就是PCI上的所有设备共同映射到这4GB上,每个PCI设备占用唯一的一段PCI地址,以便于PCI总线统一寻址。每个PCI设备通过PCI寄存器中的基地址寄存器来指定映射的首地址。如下图所示:

PCI体系结构中,一共支持三种地址空间:Memory Address Space、I/O Address Space和Configuration Address Space。其中x86处理器可以直接访问的只有Memory Address Space和I/O Address Space。而访问Configuration Address Space则需要通过索引IO寄存器来完成。

注: 在PCIe中,则引入了一种新的Configuration Address Space访问方式:将其直接映射到了Memory Address Space当中。
CPU使用不同的指令进行Memory和I/O地址空间的访问,Memory地址空间的访问效率更高。因为I/O访问效率低,现在建议使用Memory地址空间。

PCIe总线结构

PCI是并行总线,PCIe是串行总线。
PCI是共享总线,PCIe是点对点的。
典型的PCIe总线结构图如下:

图中的Root Complex经常被称为RC或者Root。在PCIe的Spec中,并没有特别详细的关于Root Complex的定义,从实际的角度来讲,可以把Root Complex理解为CPU与PCIe总线系统通信的媒介。

需要特别说明的是,Root Complex(RC or Root)和Switch都是全新的PCIe中的概念,它们结构中的每一个端口(Port)都可以对应于PCI总线中的PCI-to-PCI桥的概念。也就是说,每一个RC和Switch中一般都有多个类似于PCI-to-PCI桥的东西。分别如下两张图所示:

PCIe层次结构

和很多的串行传输协议一样,一个完整的PCIe体系结构包括应用层、事务层(Transaction Layer)、数据链路层(Data Link Layer)和物理层(Physical Layer)。其中,应用层并不是PCIe Spec所规定的内容,完全由用户根据自己的需求进行设计,另外三层都是PCIe Spec明确规范的,并要求设计者严格遵循的。

PCIe带宽

# lspci -s 02:00.0 -vvvv | grep Lnk 
                LnkCap: Port #0, Speed 8GT/s, Width x16, ASPM not supported, Exit Latency L0s unlimited, L1 unlimited 
                LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk+ 
                LnkSta: Speed 8GT/s, Width x16, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt- 
                LnkCtl2: Target Link Speed: 8GT/s, EnterCompliance- SpeedDis- 
                LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete+, EqualizationPhase1+

LnkCap means -> cababilities of host PCIe port
  2.5GT -> PCIe1
  5.0GT -> PCIe2
  8.0GT -> PCIe3
  x16 -> number of PCIe links
LnkSta -> status of PCIe port -> what is inserted
LnkCtl2 -> Negotiated speed -> PCIe version

1 PCIe link is able to transfer:
 for PCIe 1.0 250 MB/s
 for PCIe 2.0 500 MB/s
 for PCIe 3.0 985 MB/s

with 16 PCIe links you can achieve:
with PCIe 1.0: 250 * 16 = 4000 MB/s -> 32000 Mbps -> 32 Gbps
with PCIe 2.0: 500 * 16 = 8000 MB/s -> 64000 Mbps -> 64 Gbps
with PCIe 3.0: 985 * 16 = 15760 MB/s -> 126080 Mbps -> ~126 Gbps

Domain

domain应该不是socket序号。

gigabyte-r120-01.rhts.eng.pek2.xxx.com

这这台机器上,只有一个socket:
[root@gigabyte-r120-01 ~]# lscpu
Architecture:        aarch64
Byte Order:          Little Endian
CPU(s):              32
On-line CPU(s) list: 0-31
Thread(s) per core:  1
Core(s) per cluster: 16
Socket(s):           1

但却有下面这些domain:
[root@gigabyte-r120-01 ~]# lspci -D | awk -F ":" '{print $1}'| sort|uniq
0000
0001
0002
0004
0006

https://stackoverflow.com/questions/49050847/how-is-pci-segmentdomain-related-to-multiple-host-bridgesor-root-bridges

下面是我基于这篇文章的一些理解:

linux中,domain号的意思是:
  对于PCI,代表的是Host Bridge号,
  对于PCIe,代表的是PCI complex root (PCR)号,
  Host Bridge和PRC的功能类似,都是链接CPU和PCI/PCIe system的桥梁。

所以linux系统中有几个domain取决于硬件有几个Host Bridge/PCR。

另外,
linux中的domain号和PCI规范里面的domain号,指的算是一个意思。
linux中的domain号和PCIe规范里面的domain号,指的不是一个意思,
  PCIe规范中每一个root port都有一个domain(它这个domain是用来控制流量路由的,所以和linux中domain的意思不一样)
  PCIe规范中的PCI segment group和linux中的domain是一个意思。

code in linux

pci_bus_max_busnr(struct pci_bus *bus)                            # returns maximum PCI bus number of given bus' children
pci_status_get_and_clear_errors(struct pci_dev *pdev)             # return and clear error bits in PCI_STATUS
__iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar)           # iomap
pci_find_capability(struct pci_dev *dev, int cap)                 # query for devices' capabilities
pci_get_dsn(struct pci_dev *dev)                                  # Read and return the 8-byte Device Serial Number
platform_pci_power_manageable(struct pci_dev *dev)                # power management
platform_pci_set_power_state(struct pci_dev *dev,pci_power_t t)   # power management
pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)   # Use PCI PM registers to set the power state of  given PCI device
pci_wakeup(struct pci_dev *pci_dev, void *ign)                    # Wake up a PCI device
pci_power_up(struct pci_dev *dev)                                 # Put the given device into D0 (http://blog.chinaaet.com/justlxy/p/5100061872)
pci_save_state(struct pci_dev *dev)                               # save the PCI configuration space of a device before suspending
pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) # set device flag
pci_enable_device_io(struct pci_dev *dev)                         # Initialize a device for use with IO space
pci_enable_device_mem(struct pci_dev *dev)                        # Initialize a device for use with Memory space
pci_enable_device(struct pci_dev *dev)                            # Initialize device before it's used by a driver.
pci_configure_ari(struct pci_dev *dev)                            # enable or disable ARI forwarding
pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) # Reserve PCI I/O and memory resource

参考文档