PCIE设备插入主板上后内核识别流程

2024-01-11 15:59

本文主要是介绍PCIE设备插入主板上后内核识别流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

当PCIe设备插入服务器后,内核会自动进行设备的探测和识别。这个过程通常包括以下几个步骤:

  1. PCIe总线枚举:当PCIe设备插入服务器时,PCIe总线会自动进行枚举,将每个设备的信息存储在PCIe配置空间中。

  2. ACPI设备枚举:内核会通过ACPI(高级配置和电源管理接口)协议来识别PCIe设备。ACPI是一种标准的系统硬件抽象层,它提供了一种与设备无关的方法来控制硬件。

  3. PCIe驱动加载:内核会根据设备的PCIe ID(厂商ID和设备ID)来加载相应的驱动程序。驱动程序通常是以模块的形式加载到内核中的。一旦驱动程序被加载,内核就会调用驱动程序的probe函数来初始化设备。

  4. 系统日志输出:内核会将设备的信息输出到系统日志中,以便管理员进行诊断和调试。

下面是内核调用栈,展示了内核如何识别PCIe设备的过程:

  1. pcie_bus_add_device():PCIe总线枚举函数,用于将PCIe设备添加到总线上。

  2. acpi_scan_add_device():ACPI设备枚举函数,用于根据设备的ACPI路径来识别设备。

  3. pci_device_add():PCIe设备添加函数,用于将设备添加到PCIe总线上。

  4. pci_scan_device():PCIe设备扫描函数,用于扫描设备的PCIe配置空间,获取设备的厂商ID和设备ID。

  5. pci_match_device():PCIe设备匹配函数,用于根据设备的PCIe ID来查找相应的驱动程序。

  6. pci_device_probe():PCIe设备探测函数,用于调用驱动程序的probe函数来初始化设备。

  7. driver_probe_device():驱动程序探测函数,用于初始化驱动程序并调用probe函数来初始化设备。

  8. device_initialize():设备初始化函数,用于初始化设备的属性和状态。

  9. sysfs_create_link():创建sysfs链接函数,用于将设备添加到sysfs文件系统中。

  10. dev_set_name():设置设备名称函数,用于设置设备的名称。

  11. driver_sysfs_add():驱动程序添加函数,用于将驱动程序添加到sysfs文件系统中。

  12. printk():系统日志输出函数,用于将设备的信息输出到系统日志中。

内核识别PCIe设备的过程是一个比较复杂的过程,需要涉及到多个函数和模块的协同工作。不同的内核版本和硬件平台可能会有一些差异,但是整个流程大致是相同的。

函数调用栈:

调用栈
pcie
解释:
subsys_initcall()是Linux内核中用于注册子系统初始化函数的宏定义,pcic_init是一个函数名,表示子系统初始化函数的入口地址。具体来说,subsys_initcall()宏定义会将pcic_init函数注册到内核子系统初始化序列中,当内核启动时,该函数会被自动调用,用于初始化PCI子系统。

PCI子系统是Linux内核中的一个重要子系统,负责管理PCI和PCIe设备。在PCI子系统初始化时,需要进行一系列的初始化操作,例如创建PCI总线、扫描PCI设备、初始化PCI设备和驱动程序、注册PCI中断等等。因此,内核会在启动时自动调用PCI子系统初始化函数,以确保PCI子系统正确地初始化。

subsys_initcall()宏定义是用于将初始化函数注册到内核子系统初始化序列中的,其中subsys表示子系统名称,initcall表示初始化函数类型。在内核启动时,内核会按照子系统初始化序列的顺序依次调用各个子系统的初始化函数,以完成整个内核的初始化。

bar


/** Main entry point from the PCI subsystem.*/
static int __init pcic_init(void)
{struct linux_pcic *pcic;/** PCIC should be initialized at start of the timer.* So, here we report the presence of PCIC and do some magic passes.*/if(!pcic0_up)return 0;pcic = &pcic0;/**      Switch off IOTLB translation.*/writeb(PCI_DVMA_CONTROL_IOTLB_DISABLE, pcic->pcic_regs+PCI_DVMA_CONTROL);/**      Increase mapped size for PCI memory space (DMA access).*      Should be done in that order (size first, address second).*      Why we couldn't set up 4GB and forget about it? XXX*/writel(0xF0000000UL, pcic->pcic_regs+PCI_SIZE_0);writel(0+PCI_BASE_ADDRESS_SPACE_MEMORY, pcic->pcic_regs+PCI_BASE_ADDRESS_0);pcic_pbm_scan_bus(pcic);return 0;
}

这是Linux内核中的一个PCI子系统初始化函数,名为pcic_init()。该函数是PCI子系统的入口函数,用于初始化PCI控制器。具体来说,该函数会进行以下操作:

  1. 检查PCI控制器是否存在,如果不存在则直接返回。
  2. 获取PCI控制器的结构体指针,并关闭PCI控制器的IOTLB转换。
  3. 增加PCI内存空间的映射大小。
  4. 扫描PCI总线,探测PCI设备并加载相应的驱动程序。

该函数主要是针对Sun Sparc体系结构的PCI控制器进行初始化的,其中包括关闭IOTLB转换和增加PCI内存空间的映射大小等操作。在最后,该函数会调用pcic_pbm_scan_bus()函数来扫描PCI总线并探测PCI设备。在探测到PCI设备后,PCI子系统会自动加载相应的驱动程序,从而完成PCI设备的初始化和驱动加载操作。

总之,pcic_init()函数是Linux内核中一个非常重要的PCI子系统初始化函数,用于初始化PCI控制器并扫描PCI总线,是PCI子系统的入口函数。

static void __init pcic_pbm_scan_bus(struct linux_pcic *pcic)
{struct linux_pbm_info *pbm = &pcic->pbm;pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, &pcic_ops, pbm);if (!pbm->pci_bus)return;#if 0 /* deadwood transplanted from sparc64 */pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);pci_record_assignments(pbm, pbm->pci_bus);pci_assign_unassigned(pbm, pbm->pci_bus);pci_fixup_irq(pbm, pbm->pci_bus);
#endifpci_bus_add_devices(pbm->pci_bus);
}

这是Linux内核中用于PCI总线扫描的函数,名为pcic_pbm_scan_bus()。该函数的作用是扫描PCI总线,探测PCI设备并加载相应的驱动程序。具体来说,该函数会进行以下操作:

  1. 获取PCI总线信息结构体指针,并调用pci_scan_bus()函数扫描PCI总线,探测PCI设备并加载相应的驱动程序。
  2. 如果扫描失败,则直接返回。
  3. 调用pci_bus_add_devices()函数将PCI设备添加到总线上。

在PCI总线扫描过程中,PCI子系统会自动探测PCI设备并加载相应的驱动程序。在驱动程序中,可以使用pci_register_driver()函数将驱动程序注册到PCI子系统中,从而使得驱动程序能够正确地探测和管理PCI设备。在探测到PCI设备后,PCI子系统会自动加载相应的驱动程序,从而完成PCI设备的初始化和驱动加载操作。

pcic_pbm_scan_bus()函数是Linux内核中一个用于PCI总线扫描的函数,用于探测PCI设备并加载相应的驱动程序,是PCI子系统的重要组成部分。


/*** pci_scan_child_bus_extend() - Scan devices below a bus* @bus: Bus to scan for devices* @available_buses: Total number of buses available (%0 does not try to*		     extend beyond the minimal)** Scans devices below @bus including subordinate buses. Returns new* subordinate number including all the found devices. Passing* @available_buses causes the remaining bus space to be distributed* equally between hotplug-capable bridges to allow future extension of the* hierarchy.*/
static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,unsigned int available_buses)
{unsigned int used_buses, normal_bridges = 0, hotplug_bridges = 0;unsigned int start = bus->busn_res.start;unsigned int devfn, fn, cmax, max = start;struct pci_dev *dev;int nr_devs;dev_dbg(&bus->dev, "scanning bus\n");/* Go find them, Rover! */for (devfn = 0; devfn < 256; devfn += 8) {nr_devs = pci_scan_slot(bus, devfn);/** The Jailhouse hypervisor may pass individual functions of a* multi-function device to a guest without passing function 0.* Look for them as well.*/if (jailhouse_paravirt() && nr_devs == 0) {for (fn = 1; fn < 8; fn++) {dev = pci_scan_single_device(bus, devfn + fn);if (dev)dev->multifunction = 1;}}}/* Reserve buses for SR-IOV capability */used_buses = pci_iov_bus_range(bus);max += used_buses;/** After performing arch-dependent fixup of the bus, look behind* all PCI-to-PCI bridges on this bus.*/if (!bus->is_added) {dev_dbg(&bus->dev, "fixups for bus\n");pcibios_fixup_bus(bus);bus->is_added = 1;}/** Calculate how many hotplug bridges and normal bridges there* are on this bus. We will distribute the additional available* buses between hotplug bridges.*/for_each_pci_bridge(dev, bus) {if (dev->is_hotplug_bridge)hotplug_bridges++;elsenormal_bridges++;}/** Scan bridges that are already configured. We don't touch them* unless they are misconfigured (which will be done in the second* scan below).*/for_each_pci_bridge(dev, bus) {cmax = max;max = pci_scan_bridge_extend(bus, dev, max, 0, 0);/** Reserve one bus for each bridge now to avoid extending* hotplug bridges too much during the second scan below.*/used_buses++;if (cmax - max > 1)used_buses += cmax - max - 1;}/* Scan bridges that need to be reconfigured */for_each_pci_bridge(dev, bus) {unsigned int buses = 0;if (!hotplug_bridges && normal_bridges == 1) {/** There is only one bridge on the bus (upstream* port) so it gets all available buses which it* can then distribute to the possible hotplug* bridges below.*/buses = available_buses;} else if (dev->is_hotplug_bridge) {/** Distribute the extra buses between hotplug* bridges if any.*/buses = available_buses / hotplug_bridges;buses = min(buses, available_buses - used_buses + 1);}cmax = max;max = pci_scan_bridge_extend(bus, dev, cmax, buses, 1);/* One bus is already accounted so don't add it again */if (max - cmax > 1)used_buses += max - cmax - 1;}/** Make sure a hotplug bridge has at least the minimum requested* number of buses but allow it to grow up to the maximum available* bus number of there is room.*/if (bus->self && bus->self->is_hotplug_bridge) {used_buses = max_t(unsigned int, available_buses,pci_hotplug_bus_size - 1);if (max - start < used_buses) {max = start + used_buses;/* Do not allocate more buses than we have room left */if (max > bus->busn_res.end)max = bus->busn_res.end;dev_dbg(&bus->dev, "%pR extended by %#02x\n",&bus->busn_res, max - start);}}/** We've scanned the bus and so we know all about what's on* the other side of any bridges that may be on this bus plus* any devices.** Return how far we've got finding sub-buses.*/dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max);return max;
}

这段代码是用于扫描PCI子总线的函数。它接受两个参数:PCI总线对象和可用总线数。它首先迭代PCI总线下的所有设备,并扫描它们。然后,它为SR-IOV能力保留总线。接下来,它计算总线上的热插拔桥和普通桥的数量,并在第一次扫描中扫描已配置的桥。它还分配了额外的可用总线,以便将其分配给热插拔桥。在第二次扫描中,它扫描需要重新配置的桥,并将额外的总线分配给热插拔桥。最后,它确保热插拔桥至少拥有最小数量的总线,并返回扫描到的最大总线号。

这篇关于PCIE设备插入主板上后内核识别流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/594908

相关文章

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

MySQL INSERT语句实现当记录不存在时插入的几种方法

《MySQLINSERT语句实现当记录不存在时插入的几种方法》MySQL的INSERT语句是用于向数据库表中插入新记录的关键命令,下面:本文主要介绍MySQLINSERT语句实现当记录不存在时... 目录使用 INSERT IGNORE使用 ON DUPLICATE KEY UPDATE使用 REPLACE

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

使用PyTorch实现手写数字识别功能

《使用PyTorch实现手写数字识别功能》在人工智能的世界里,计算机视觉是最具魅力的领域之一,通过PyTorch这一强大的深度学习框架,我们将在经典的MNIST数据集上,见证一个神经网络从零开始学会识... 目录当计算机学会“看”数字搭建开发环境MNIST数据集解析1. 认识手写数字数据库2. 数据预处理的

Pytorch微调BERT实现命名实体识别

《Pytorch微调BERT实现命名实体识别》命名实体识别(NER)是自然语言处理(NLP)中的一项关键任务,它涉及识别和分类文本中的关键实体,BERT是一种强大的语言表示模型,在各种NLP任务中显著... 目录环境准备加载预训练BERT模型准备数据集标记与对齐微调 BERT最后总结环境准备在继续之前,确

Spring AI ectorStore的使用流程

《SpringAIectorStore的使用流程》SpringAI中的VectorStore是一种用于存储和检索高维向量数据的数据库或存储解决方案,它在AI应用中发挥着至关重要的作用,本文给大家介... 目录一、VectorStore的基本概念二、VectorStore的核心接口三、VectorStore的

python之流程控制语句match-case详解

《python之流程控制语句match-case详解》:本文主要介绍python之流程控制语句match-case使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录match-case 语法详解与实战一、基础值匹配(类似 switch-case)二、数据结构解构匹

讯飞webapi语音识别接口调用示例代码(python)

《讯飞webapi语音识别接口调用示例代码(python)》:本文主要介绍如何使用Python3调用讯飞WebAPI语音识别接口,重点解决了在处理语音识别结果时判断是否为最后一帧的问题,通过运行代... 目录前言一、环境二、引入库三、代码实例四、运行结果五、总结前言基于python3 讯飞webAPI语音

Jmeter如何向数据库批量插入数据

《Jmeter如何向数据库批量插入数据》:本文主要介绍Jmeter如何向数据库批量插入数据方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Jmeter向数据库批量插入数据Jmeter向mysql数据库中插入数据的入门操作接下来做一下各个元件的配置总结Jmete

在VSCode中本地运行DeepSeek的流程步骤

《在VSCode中本地运行DeepSeek的流程步骤》本文详细介绍了如何在本地VSCode中安装和配置Ollama和CodeGPT,以使用DeepSeek进行AI编码辅助,无需依赖云服务,需要的朋友可... 目录步骤 1:在 VSCode 中安装 Ollama 和 CodeGPT安装Ollama下载Olla