Appearance
PCIe驱动测试
本章介绍PCIe驱动的编译、模块加载、功能测试方法以及常见问题的解决方案。
编译PCIe驱动
1. 内核源码树编译
如果PCIe驱动是内核源码的一部分,可以通过以下步骤编译:
bash
# 进入内核源码目录
cd /usr/src/linux
# 配置内核,确保PCI支持和相关驱动选项被启用
make menuconfig
# 启用PCI支持
Device Drivers --->
[*] PCI support --->
<*> PCI Express support
[*] PCI Express ASPM control
# 启用PCI设备驱动
Device Drivers --->
<*> Network device support --->
<*> Ethernet driver support --->
<*> <Your PCIe Ethernet Driver>
# 编译内核和模块
make -j$(nproc)
# 安装内核和模块
make modules_install
make install2. 独立模块编译
如果PCIe驱动是独立开发的,可以使用Makefile进行编译:
makefile
obj-m += pcie_driver.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean编译命令:
bash
make加载PCIe驱动
1. 加载驱动模块
bash
# 加载PCI核心模块
modprobe pci
# 加载PCIe ASPM模块(可选)
modprobe pcie_aspm
# 加载PCIe设备驱动模块
insmod pcie_driver.ko
# 查看模块是否加载成功
lsmod | grep pcie_driver2. 自动加载驱动
可以通过以下方式实现驱动的自动加载:
- 添加到模块依赖文件:bash
echo "pcie_driver" >> /etc/modules
2. **使用udev规则**:
```bash
sudo cat > /etc/udev/rules.d/99-pcie-driver.rules << EOF
SUBSYSTEM=="pci", ATTR{vendor}=="0x1234", ATTR{device}=="0x5678", RUN+="/sbin/modprobe pcie_driver"
EOFPCIe设备检测
1. 查看PCI设备
bash
# 查看所有PCI设备
lspci
# 查看PCI设备详细信息
lspci -v
# 查看PCI设备的配置空间
lspci -x
# 查看特定PCI设备的信息
lspci -d <vendor>:<device>2. 查看PCIe设备信息
bash
# 查看PCIe设备的链路状态
lspci -vv | grep -A 10 "LnkSta"
# 查看PCIe设备的扩展能力
lspci -vv | grep -A 20 "Capabilities: [.*] Express"3. 查看PCI设备驱动绑定情况
bash
# 查看PCI设备与驱动的绑定情况
lspci -k
# 查看特定PCI设备的驱动信息
lspci -s 01:00.0 -k功能测试
1. 设备绑定测试
bash
# 绑定设备到驱动
echo "0000:01:00.0" > /sys/bus/pci/drivers/pcie_driver/bind
# 解绑设备
echo "0000:01:00.0" > /sys/bus/pci/drivers/pcie_driver/unbind2. 配置空间访问测试
bash
# 查看PCI设备的配置空间
sudo setpci -s 01:00.0 -dump
# 读取特定配置寄存器
sudo setpci -s 01:00.0 0x00.l
# 写入配置寄存器
sudo setpci -s 01:00.0 0x40.w=0x12343. 内存映射测试
可以使用以下程序测试PCIe设备的内存映射功能:
c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
int main(void)
{
int fd;
void *bar0;
unsigned long bar0_size = 0x10000; // 假设BAR0大小为64KB
/* 打开PCI设备文件 */
fd = open("/sys/bus/pci/devices/0000:01:00.0/resource0", O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
/* 映射BAR0到用户空间 */
bar0 = mmap(NULL, bar0_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (bar0 == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
/* 读取BAR0的前4个字节 */
printf("BAR0[0x0000]: 0x%08X\n", *((unsigned int *)bar0));
/* 写入BAR0 */
*((unsigned int *)bar0) = 0x12345678;
printf("BAR0[0x0000] after write: 0x%08X\n", *((unsigned int *)bar0));
/* 取消映射 */
if (munmap(bar0, bar0_size) < 0) {
perror("munmap");
close(fd);
return 1;
}
/* 关闭设备文件 */
close(fd);
return 0;
}编译和运行:
bash
gcc pcie_mmap_test.c -o pcie_mmap_test
sudo ./pcie_mmap_test4. 中断测试
可以使用以下命令测试PCIe设备的中断:
bash
# 查看设备的中断信息
cat /proc/interrupts | grep 01:00.0
# 查看中断统计信息
cat /proc/stat | grep intr性能测试
1. PCIe带宽测试
可以使用PCITree或PCIeBench等工具测试PCIe带宽:
bash
# 使用PCITree测试PCIe带宽
sudo ./pcitree -b -d 01:00.0
# 使用lspci测试PCIe链路速度
lspci -vv | grep -A 1 "LnkSta:"2. 延迟测试
可以使用以下程序测试PCIe设备的延迟:
c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <time.h>
int main(void)
{
int fd;
void *bar0;
unsigned long bar0_size = 0x10000;
struct timespec start, end;
double elapsed;
int i;
fd = open("/sys/bus/pci/devices/0000:01:00.0/resource0", O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
bar0 = mmap(NULL, bar0_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (bar0 == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
/* 预热 */
for (i = 0; i < 1000; i++) {
*((unsigned int *)bar0) = i;
__sync_synchronize();
}
/* 测试延迟 */
clock_gettime(CLOCK_MONOTONIC, &start);
for (i = 0; i < 1000000; i++) {
*((unsigned int *)bar0) = i;
__sync_synchronize();
unsigned int val = *((unsigned int *)bar0);
__sync_synchronize();
}
clock_gettime(CLOCK_MONOTONIC, &end);
elapsed = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
printf("Average latency: %.2f ns\n", (elapsed * 1e9) / 2000000);
munmap(bar0, bar0_size);
close(fd);
return 0;
}编译和运行:
bash
gcc pcie_latency_test.c -o pcie_latency_test
sudo ./pcie_latency_test调试PCIe驱动
1. 内核日志查看
bash
# 查看PCI相关的内核日志
dmesg | grep -i pci
# 实时查看内核日志
journalctl -k -f | grep -i pci2. 启用PCI调试
bash
# 启用PCI核心调试
echo 1 > /sys/module/pci/parameters/debug
# 启用PCI设备驱动调试
echo 1 > /sys/module/pcie_driver/parameters/debug3. PCI调试工具
- lspci:列出PCI设备和详细信息
- setpci:读取和写入PCI配置空间
- pcitree:PCI设备树查看和带宽测试工具
- pcieutils:PCIe设备调试和测试工具集
- devmem2:读写物理内存的工具
4. 调试PCIe驱动的常见问题
设备无法被识别
- 检查PCIe插槽是否正确插入
- 检查设备电源是否正常
- 检查BIOS设置是否启用了该PCIe插槽
驱动加载失败
- 检查内核版本是否匹配
- 检查模块依赖是否满足
- 查看内核日志获取详细错误信息
中断不触发
- 检查中断线是否正确配置
- 检查中断屏蔽寄存器
- 确保设备中断使能
内存访问错误
- 检查BAR映射是否正确
- 检查内存访问权限
- 确保内存访问对齐
卸载PCIe驱动
bash
# 卸载PCIe设备驱动模块
rmmod pcie_driver
# 卸载PCIe ASPM模块(可选)
rmmod pcie_aspm常见问题解决方案
1. PCIe设备无法枚举
问题:系统启动后无法识别PCIe设备
解决方案:
- 检查设备是否正确插入PCIe插槽
- 检查设备电源是否正常
- 尝试更换PCIe插槽
- 更新BIOS到最新版本
- 检查内核是否支持该设备
2. PCIe链路速度协商失败
问题:PCIe设备链路速度低于预期
解决方案:
- 查看PCIe链路状态:
lspci -vv | grep -A 1 "LnkSta:" - 禁用ASPM:
echo performance > /sys/module/pcie_aspm/parameters/policy - 检查PCIe插槽和设备是否支持相同的速率
- 尝试降低PCIe总线的传输速率
3. PCIe设备中断冲突
问题:PCIe设备中断与其他设备冲突
解决方案:
- 查看中断分配情况:
cat /proc/interrupts - 在BIOS中重新分配PCI中断
- 使用MSI/MSI-X中断代替传统中断
- 禁用其他设备的中断(临时测试)
4. PCIe设备DMA错误
问题:PCIe设备DMA传输失败
解决方案:
- 检查DMA地址映射是否正确
- 确保DMA缓冲区对齐
- 检查设备DMA能力
- 启用IOMMU:
intel_iommu=on iommu=pt
5. PCIe设备性能问题
问题:PCIe设备性能低于预期
解决方案:
- 检查PCIe链路宽度和速率
- 优化DMA缓冲区大小
- 使用分散-聚集(scatter-gather)DMA
- 减少PCIe事务的数量
- 优化内存访问模式
总结
PCIe驱动测试是确保驱动正常工作的重要步骤。通过本章介绍的编译、加载、测试和调试方法,可以有效地开发和调试PCIe驱动。在测试过程中,应该注意观察内核日志,使用适当的调试工具,及时发现和解决问题。
此外,还应该根据具体PCIe设备的特点,设计专门的测试用例,确保驱动在各种情况下都能正常工作。