首页
关于
Search
1
PacketForge 博客部署文档
16 阅读
2
深入 SONiC 与 SAI:从命令行到芯片的完整数据之旅
14 阅读
3
DPU 与 CPO:AI 时代的网络基础设施革命
11 阅读
4
Broadcom芯片
10 阅读
5
DPDK高性能网络
10 阅读
默认分类
交换芯片
高性能网络
AI算力网络
WebSocket高性能项目
DPDK 成长路线
①性能工程基础
②DPDK 源码深读
③用户态协议栈
④软件交换与 SONiC
⑤PCIe、虚拟化与 DPU
⑥面试与复盘
登录
Search
标签搜索
DPDK→DPU 学习路线
P0
P1
P2
P3
P5
PCIe
P4
P6
libmtcp
NUMA
mTCP
OVS
SONiC
SAI
DPU
perf
gdb
EAL
mempool
PacketForge
累计撰写
53
篇文章
累计收到
3
条评论
首页
栏目
页面
关于
最新文章
热门文章
评论最多
点赞最多
2026-08-18
P4-03 · 从 0 写一个最小 PCIe 内核驱动:24 行 probe 一切,然后逐行升级
对应路线图:Phase 4 (Day 120-140) | 内核 v6.6 | 实验代码:code/dpu_dev/01_pci/,引用均标 文件:行号上一篇:P4-02 MSI-X | 下一篇:P5-01 DMA/IOMMU一句话定位PCIe 驱动的最小骨架不是操作寄存器,而是参与内核的"设备-驱动匹配协议":填一张 pci_device_id 表 + 一个 probe 回调,driver core 就会在设备出现时替你调它。code/dpu_dev/01_pci/pci_scan.c 全文件只有 24 行,却能 probe 整条总线上的所有设备——这是每个网卡驱动(ixgbe、mlx5)的同款起点。真实源码精读① 全文件带读:code/dpu_dev/01_pci/pci_scan.c:1-24#include <linux/module.h> #include <linux/pci.h> static int probe(struct pci_dev *pdev, const struct pci_device_id *id) { printk("pci_scan: %04x:%04x (rev %02x) BAR0=%llx IRQ=%d\n", pdev->vendor, pdev->device, pdev->revision, pci_resource_start(pdev, 0), pdev->irq); return 0; } static struct pci_device_id ids[] = { { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0, } }; MODULE_DEVICE_TABLE(pci, ids); static struct pci_driver drv = { .name = "pci_scan", .id_table = ids, .probe = probe, }; module_pci_driver(drv); MODULE_LICENSE("GPL");逐行拆:probe 回调(:4-10):签名 (struct pci_dev *, const struct pci_device_id *) 是 PCI 子系统的硬约定。这里读的 vendor/device/revision 正是 P4-01 里 pci_scan_device 从 Config Space 填进 pci_dev 的(probe.c:2507-2508);pci_resource_start(pdev, 0) 是 BAR0 物理地址(宏定义 pci.h:2107);pdev->irq 是内核分配的 IRQ 号(pci.h:420)。返回 0 = "这个设备我接管了"。ids 表(:12-16):全 PCI_ANY_ID 表示匹配所有设备——所以本模块会 probe 系统里每一个 PCI 设备,只能在学习机上玩。真正的驱动用 PCI_DEVICE(0x8086, 0x10fb) 这类精确匹配(宏在 pci.h:962)。表项结构在 mod_devicetable.h:44-50:vendor/device/subvendor/subdevice/class/class_mask 六维过滤 + driver_data 私有数据。module_pci_driver(drv)(:23):展开成 module_driver(drv, pci_register_driver, pci_unregister_driver)(pci.h:1598-1599),自动生成 init/exit,替换手写 module_init/module_exit 的样板代码。② 匹配与 probe 触发:struct pci_driver 与 driver core(pci.h:928-943)struct pci_driver { struct list_head node; const char *name; const struct pci_device_id *id_table; /* Must be non-NULL for probe to be called */ int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */ void (*remove)(struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */ // ...省略 suspend/resume/shutdown/sriov/err_handler ... struct device_driver driver; // ... };id_table 注释说得很直白:没有它 probe 就不会被调用。注册入口 __pci_register_driver(pci-driver.c:1451-1467)只做一件事:填 drv->driver.bus = &pci_bus_type 然后 driver_register()——后面就是通用设备模型的 match → probe 机器了。设备侧由 pci_device_probe(pci-driver.c:444-467)接棒:static int pci_device_probe(struct device *dev) { int error; struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = to_pci_driver(dev->driver); // ... pci_assign_irq(pci_dev); error = pcibios_alloc_irq(pci_dev); // ... pci_dev_get(pci_dev); error = __pci_device_probe(drv, pci_dev); // ... }注意顺序:先 pci_assign_irq 分配 IRQ,再进驱动的 probe——所以 probe 里 pdev->irq 一定已经有值。③ remove 与卸载:内核兜底(pci-driver.c:469-510 节选)static void pci_device_remove(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = pci_dev->driver; if (drv->remove) { // ... runtime PM barrier ... drv->remove(pci_dev); // ... } pcibios_free_irq(pci_dev); pci_dev->driver = NULL; // ... }drv->remove 是驱动自己的清理窗口(放走 IRQ、unmap、还资源);窗口之外的事内核兜底:pcibios_free_irq 收尾 IRQ(:486)、解绑 driver = NULL(:487)。rmmod 会经 pci_unregister_driver(pci-driver.c:1480)对每个被该驱动接管的设备各调一次 remove。④ 升级路线:把"只读"变成"拥有"pci_scan.c 只读不占,等于借设备看了一眼。要真正"拥有"一个 BAR(比如网卡的 MMIO 寄存器区),probe 里按序做四件事(API 均已核实存在):步骤API位置作用1pci_enable_device(pdev)pci.c:2191打开 IO/MEM decode,唤醒设备2pci_request_regions(pdev, name)pci.c:4273声明 resource 所有权(/proc/iomem 可见)3pci_ioremap_bar(pdev, bar)pci.c:252映射 BAR 到内核虚拟地址4request_irq(pci_irq_vector(pdev, 0), handler, ...)interrupt.h:165挂中断(配合 P4-02 的 MSI-X)remove 里严格逆序:free_irq(interrupt.h:196)→ iounmap → pci_release_regions(pci.c:4254)→ pci_disable_device(pci.c:2379)。🐧 内核机制对照:驱动协议 vs 用户态"野路子"环节内核驱动(本模块)DPDK 用户态(bus/pci)设备发现driver core 匹配 id_table → probe自己扫 /sys/bus/pci/devices/"接管"动作probe 返回 0,设备归属本驱动bind 到 vfio-pci/uio 驱动后拿 fdBAR 访问ioremap → 内核虚拟地址mmap,pci_common_uio.c:178-179中断request_irq + 上半部/NAPI轮询或 epoll 中断线程资源冲突防护pci_request_regions 互斥依赖内核驱动让位(bound to VFIO)DPDK 不是"没有驱动协议",而是把协议放进了用户态:内核里那个 vfio-pci 驱动替它完成了 probe/接管,用户态只负责 mmap 和轮询。面试 3 问probe 什么时候被调用? → 设备注册或驱动注册后 driver core 完成 match 即调;前提是 id_table 非 NULL(pci.h:931 注释);入口 pci_device_probe(pci-driver.c:444)先 pci_assign_irq 再调驱动的 probe(pci-driver.c:453-460)。PCI_ANY_ID 表项的风险? → 匹配所有设备(pci_scan.c:12-13),probe 返回 0 会接管每一个设备(pci_scan.c:9),真实驱动必须用 PCI_DEVICE 精确匹配(pci.h:962-964)。remove 里不释放资源会怎样? → IRQ 方面内核会兜底 pcibios_free_irq(pci-driver.c:486),但 ioremap 的地址和 request_regions 占用的 resource 泄漏,再加载驱动会 EBUSY——所以 remove 必须逆序归还(见上表)。🔧 动手实验cd code/dpu_dev/01_pci make # Makefile:1-2,obj-m += pci_scan.o sudo insmod pci_scan.ko dmesg | tail -30 # 每个 PCI 设备一行:vendor:device rev BAR0 IRQ sudo rmmod pci_scan升级练习(在实验机上,不要在主力机上):给 probe 加 pci_enable_device + pci_request_regions,验证 /proc/iomem 里出现 pci_scan 名字段;再试 pci_ioremap_bar 读 4 字节网卡寄存器与 lspci -xxxx 对拍。数据落点 perf-data/P4/03-pci-scan-dmesg.txt(dmesg 原文 + iomem 段 + 升级版对比)。下一篇:P5-01 · DMA/IOVA/IOMMU:设备怎么看到内存
2026年08月18日
0 阅读
0 评论
0 点赞
第 1/1 页 · 本页 1 条 · 共 1 条