• linux设备模型:bus概念及pci_bus分析


    上一篇<>中分析了kset容器、class设备驱动抽象框架、class_kset用于类(class)的热插拔事件转到类子系统等内容,本篇继续分析设备模型bus、设备、驱动之间的联系。

    bus(总线)是一种特殊的抽象框架,与class有着本质上的不同,class感觉上只是把一些核心组件聚集在一起,它主要为访问组件提供便利(如提供组件地址),而bus则是实实在在的功能性框架,它即可负责管理、维护驱动与设备之间的关系,也可作为主桥设备与硬件之间的访问通道等等。

    pci总线,当pci硬件设备插入卡槽后,首先通过pci_bus_type(pci总线操作结构) 中的 pm(电源操作结构)操作相关函数唤醒设备,然后通过pci_bus_match函数查找硬件设备是否被注册到驱动(通过pci_id表, id_table 指向驱动程序设备id表的指针),如果找到匹配的驱动将会调用probe函数(pci_driver文章中分析),执行驱动的探测函数,完成驱动的注册工作,届时这个pci硬件即可进入正常工作状态,它们之间的关系为:

                               主桥							       
                                 ^					                			    
    							 |													
    							 v			                         
                    		   父bus                           
    	 						 ^					                		    
    							 |									设备
    							 v			                         ^	
    pci硬件相关     <->     		bus...            <->           	 |
    																 v	
    																驱动
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    然后执行到pci_bus的pci_device_probe函数,通过主桥设备对象找到pin值(引脚号),计算出卡槽位置(槽号),然后通过槽号找到对应的中断号,写入到中断线(行)并关联到pci设备(由pci驱动使用),最后在连接设备的cpu上执行驱动程序初始化,执行驱动的探测函数(用户注册的驱动) 。

    pci设备通过klist_devices列表(链表)记录pci驱动knode_driver(节点),可以理解为pci设备记录了一组动态pci驱动节点。


    目录


    1. 函数分析

    1.1 buses_init

    1.2 pci_driver_init

    2. 源码结构

    3. 部分结构定义

    4. 扩展函数/变量

    目录预览


    1. 函数分析

    1.1 buses_init

      bus初始化函数这里首先创建bus_kset,关联了uevent操作,然后创建system_kset(表示子系统设备)

    int __init buses_init(void)
    {
            bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); // 创建kset容器结构对象,设置根kobj名称,关联kobjs的uevent操作等...
            // 根kobj -> /sys/bus/
            if (!bus_kset)
                    return -ENOMEM;
    
            system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj); // 创建kset容器,父kobj -> /sys/devices/ 的 子节点 kobj -> /sys/devices/system/
            if (!system_kset)
                    return -ENOMEM;
    
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    bus_uevent_ops
    devices_kset

    1.2 pci_driver_init

      注册总线类型,pci_bus_type、pcie_port_bus_type,注册dma通知链

    static int __init pci_driver_init(void)
    {
            int ret;
    
            ret = bus_register(&pci_bus_type); // 注册总线,通过pci_bus_type操作中的函数做到pci <-> 驱动 <-> 设备直接的互通关系
            // 注册总线,创建总线uevent,创建subsys_private私有结构(通过subsys(子系统)关联bustype/class结构的驱动程序,驱动绑定到设备列表中(knode_driver 加入到 klist_devices列表(链表)中))等等
    
    #ifdef CONFIG_PCIEPORTBUS
            ret = bus_register(&pcie_port_bus_type); // 注册总线,pcie_port_bus_type
            if (ret)
                    return ret;
    #endif
            dma_debug_add_bus(&pci_bus_type); // 注册bus通知链
            // 当驱动注册失败并且在dma的哈希列表中时(已分配dma地址), 调用dma_debug_device_change函数输出信息
            return 0;
    }
    postcore_initcall(pci_driver_init);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    pci_bus_type
    bus_register
    pcie_port_bus_type


    2. 源码结构

      bus_uevent_ops kset与它相关的kobjects的uevent操作

    static const struct kset_uevent_ops bus_uevent_ops = {
            .filter = bus_uevent_filter, // bus 用户空间事件过滤器函数,如果等于&bus_ktype,返回1
            // bus_ktype做了bus_sysfs_ops(.sysfs_ops,sysfs操作)和bus_release(.release,释放函数)赋值
    };
    
    • 1
    • 2
    • 3
    • 4

      pci_bus_type 设备的总线类型

    struct bus_type pci_bus_type = {
            .name           = "pci", // 名称
            .match          = pci_bus_match, // 每当为此总线添加新设备或驱动程序时,可能会调用多次
            // 如果给定的设备可以由给定的驱动程序处理,它应该返回一个正值,否则返回零
            // 如果无法确定驱动程序支持该设备,它也可能返回错误代码
            // 在-EPROBE_DEFER的情况下,它将对设备排队等待延迟探测
            
            .uevent         = pci_uevent, // pci设备信息记录到环境缓冲区
            .probe          = pci_device_probe, // 探测函数
            .remove         = pci_device_remove,  // 移除设备
            .shutdown       = pci_device_shutdown, // 设备关闭
            .dev_groups     = pci_dev_groups, // 设备属性组
            .bus_groups     = pci_bus_groups, // bus属性组
            .drv_groups     = pci_drv_groups,  // 驱动属性组
            .pm             = PCI_PM_OPS_PTR, // 子系统级别和设备驱动程序级别的设备电源管理操作
            .num_vf         = pci_bus_num_vf, 
            .dma_configure  = pci_dma_configure, // 使用来自主桥父节点(如果有)的OF节点或ACPI节点的相同信息更新PCI设备的DMA配置
            .dma_cleanup    = pci_dma_cleanup, // 设备驱动程序停止通过内核DMA API处理DMA设备
            // 它必须在iommu_device_use_default_domain()之后调用
    };
    EXPORT_SYMBOL(pci_bus_type);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    pci_bus_match
    pci_device_probe
    PCI_PM_OPS_PTR

      pcie_port_bus_type 设备的总线类型

    struct bus_type pcie_port_bus_type = {
            .name           = "pci_express",
            .match          = pcie_port_bus_match,  // 每当为此总线添加新设备或驱动程序时,可能会调用多次
    };
    EXPORT_SYMBOL_GPL(pcie_port_bus_type);
    
    • 1
    • 2
    • 3
    • 4
    • 5

      PCI_PM_OPS_PTR 子系统级别和设备驱动程序级别的设备电源管理操作

    static const struct dev_pm_ops pci_dev_pm_ops = {
            .prepare = pci_pm_prepare, // 防止在设备返回后注册新的子设备
            // 驱动程序的子系统和内核的其他部分通常应该在prepare()成功后防止对探测方法的新调用
            
            .complete = pci_pm_complete, // 设备从系统睡眠中恢复,刷新设备的电源状态数据
            .suspend = pci_pm_suspend, // 禁用PTM允许一些系统,进入低功率PM状态
            // 禁用PTM,但保留dev->ptm_enabled,以便在必要时在恢复时默默重新启用它
            
            .suspend_late = pci_pm_suspend_late, // 设备挂起
            .resume = pci_pm_resume, // 恢复设备
            .resume_early = pci_pm_resume_early, // 恢复设备的早期准备
            .freeze = pci_pm_freeze, // 冻结(创建快照),在创建系统内存的快照映像之前恢复所有运行时挂起的设备
            .thaw = pci_pm_thaw, // 特定于休眠,在创建休眠映像或映像创建失败后执行
            // 也在尝试从这样的图像恢复主存储器的内容失败后执行
            
            .poweroff = pci_pm_poweroff, // 特定于休眠,在保存休眠映像后执行。类似于suspend(),但它不需要将设备的设置保存到内存中
            .poweroff_late = pci_pm_poweroff_late, // 继续做poweroff()开始的操作。类似于suspend_late(),但它不需要在内存中保存设备的设置
            .restore = pci_pm_restore,  // 特定于休眠,在从休眠映像恢复主内存内容之后执行,类似于resume()
            .suspend_noirq = pci_pm_suspend_noirq, // 完成由suspend()启动的操作
            // 执行挂起设备所需的任何附加操作,该设备可能正在与其驱动程序的中断处理程序竞争,在执行suspend_noirq()时,该程序保证不会运行
            
            .resume_noirq = pci_pm_resume_noirq, // 通过执行any为resume()的执行做准备
            .freeze_noirq = pci_pm_freeze_noirq, // 完成由freeze()启动的操作
            .thaw_noirq = pci_pm_thaw_noirq,
            .poweroff_noirq = pci_pm_poweroff_noirq,
            .restore_noirq = pci_pm_restore_noirq,
            .runtime_suspend = pci_pm_runtime_suspend, // 为设备做好准备,在这种情况下,由于电源管理,它将无法与CPU(s)和RAM通信
            .runtime_resume = pci_pm_runtime_resume, // 将设备置于完全活动状态,以响应硬件生成的唤醒事件或应软件的请求
            .runtime_idle = pci_pm_runtime_idle, // 设备似乎是不活动的,如果所有必要条件都得到满足,它可能被置于低功率状态
    };
    
    #define PCI_PM_OPS_PTR  (&pci_dev_pm_ops)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3. 部分结构定义

      device_driver 基本的设备驱动结构

    struct device_driver {
    	const char		*name; // 设备驱动程序的名称
    	struct bus_type		*bus; // 驱动的设备所属的总线
    
    	struct module		*owner; // 模块所有者
    	const char		*mod_name;	/* 用于内置模块 */
    
    	bool suppress_bind_attrs;	/* 禁用通过sysfs绑定/解除绑定 */
    	enum probe_type probe_type; // 要使用的探测类型(同步或异步)
    
    	const struct of_device_id	*of_match_table; // 打开的固件表
    	const struct acpi_device_id	*acpi_match_table; // ACPI(高级配置与电源接口)匹配表
    
    	int (*probe) (struct device *dev); // 查询特定设备是否存在,该驱动程序是否可以使用该设备,并将驱动程序绑定到特定设备
    	void (*sync_state)(struct device *dev); // 在连接到该设备的所有状态跟踪消费者(在late_initcall时存在)成功绑定到驱动程序后,调用该函数将设备状态同步到软件状态
    	// 如果设备没有消费者,该函数将在late_initcall_sync级别调用
    	// 如果设备有从未绑定到驱动程序的消费者,则此函数将永远不会被调用,直到他们绑定到驱动程序
    	
    	int (*remove) (struct device *dev); // 当设备从系统中移除时调用,以解除设备与该驱动程序的绑定
    	void (*shutdown) (struct device *dev); // 在关机时调用,使设备暂停
    	int (*suspend) (struct device *dev, pm_message_t state); // 调用设备进入休眠模式
    	// 通常是低功率状态
    	
    	int (*resume) (struct device *dev); // 调用将设备从睡眠模式中唤醒
    	const struct attribute_group **groups; // 驱动核心自动创建的默认属性
    	const struct attribute_group **dev_groups; // 设备实例绑定到驱动程序后附加的附加属性
    
    	const struct dev_pm_ops *pm; // 匹配此驱动程序的设备的电源管理操作
    	void (*coredump) (struct device *dev); // 当sysfs条目被写入时调用
    	// 设备驱动程序需要调用dev_coredump API,从而产生一个uevent
    
    	struct driver_private *p; // 驱动核心的私有数据,除了驱动核心之外没有人可以碰它
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

      pci_dev 结构描述了PCI设备

    /*  */
    struct pci_dev {
    	struct list_head bus_list;	/* 每总线列表中的节点 */
    	struct pci_bus	*bus;		/* 该设备所在的总线 */
    	struct pci_bus	*subordinate;	/* 此设备桥接的总线 */
    
    	void		*sysdata;	/* 用于特定于系统的扩展的钩子 */
    	struct proc_dir_entry *procent;	/* /proc/bus/pci中的设备项 */
    	struct pci_slot	*slot;		/* 该设备所在的物理槽位 */
    
    	unsigned int	devfn;		/* 编码的设备和功能(func)索引 */
    	unsigned short	vendor; // 厂商ID
    	unsigned short	device; // 设备ID 
    	unsigned short	subsystem_vendor; // 子系统厂商ID
    	unsigned short	subsystem_device; // 子系统设备ID
    	unsigned int	class;		/* 3 bytes: (base,sub,prog-if) */
    	u8		revision;	/* PCI修订版本,类字的低字节 */
    	u8		hdr_type;	/* PCI头类型(' multi'标志被屏蔽) */
    #ifdef CONFIG_PCIEAER
    	u16		aer_cap;	/* AER功能偏移 */
    	struct aer_stats *aer_stats;	/* 该设备的AER统计 */
    #endif
    #ifdef CONFIG_PCIEPORTBUS
    	struct rcec_ea	*rcec_ea;	/* RCEC缓存端点关联 */
    	struct pci_dev  *rcec;          /* RCEC相关设备 */
    #endif
    	u32		devcap;		/* 作为PCIe设备功能 */
    	u8		pcie_cap;	/* 作为PCIe功能偏移 */
    	u8		msi_cap;	/* MSI功能偏移 */
    	u8		msix_cap;	/* MSI-X功能偏移 */
    	u8		pcie_mpss:3;	/* 支持的PCIe最大负载大小 */
    	u8		rom_base_reg;	/* 配置寄存器控制ROM */
    	u8		pin;		/* 此设备使用的中断引脚 */
    	u16		pcie_flags_reg;	/* 缓存PCIe功能寄存器 */
    	unsigned long	*dma_alias_mask;/* 启用devfn别名的掩码 */
    
    	struct pci_driver *driver;	/* 与此设备绑定的驱动程序 */
    	u64		dma_mask;	// 本设备实现的总线地址位的掩码
    	// 通常这是0xffffffff
    	// 只有当设备破坏了DMA或支持64位传输时,才需要更改此设置.
    
    	struct device_dma_parameters dma_parms; // dma设备参数
    
    	pci_power_t	current_state;	// 当前操作状态
    	// 在ACPI中,这是D0-D3, D0是完全功能的,D3是关闭的
    	
    	unsigned int	imm_ready:1;	/* 支持立即准备 */
    	u8		pm_cap;		/* 电源功能偏移 */
    	unsigned int	pme_support:5;	/* 可生成pme状态的位掩码 */
    	unsigned int	pme_poll:1;	/* 轮询设备的PME状态位 */
    	unsigned int	d1_support:1;	/* 支持低功耗D1 */
    	unsigned int	d2_support:1;	/* 支持低功耗状态为D2 */
    	unsigned int	no_d1d2:1;	/* D1和D2禁止使用 */
    	unsigned int	no_d3cold:1;	/* D3cold是被禁止的 */
    	unsigned int	bridge_d3:1;	/* 允许D3用于桥 */
    	unsigned int	d3cold_allowed:1;	/* D3cold是用户允许的 */
    	unsigned int	mmio_always_on:1;	/* 禁止在BAR调整时关闭io/mem解码 */
    	unsigned int	wakeup_prepared:1; // 唤醒准备
    	unsigned int	skip_bus_pm:1;	/* 内部:跳过总线级PM */
    	unsigned int	ignore_hotplug:1;	/* 忽略热插拔事件 */
    	unsigned int	hotplug_user_indicators:1; /* 由sysfs用户单独控制的SlotCtl指示灯 */
    	unsigned int	clear_retrain_link:1;	/* 需要手动清除再重新链接位 */
    	unsigned int	d3hot_delay;	/* D3hot->D0 的转换时间(ms) */
    	unsigned int	d3cold_delay;	/* D3cold->D0 的转换时间(ms) */
    
    #ifdef CONFIG_PCIEASPM
    	struct pcie_link_state	*link_state;	/* ASPM链接状态 */
    	unsigned int	ltr_path:1;	/* 从根到这里支持延迟容忍报告 */
    	u16		l1ss;		/* L1SS能力指针 */
    #endif
    	unsigned int	pasid_no_tlp:1;		/* PASID在没有TLP前缀的情况下工作 */
    	unsigned int	eetlp_prefix_path:1;	/* 端到端TLP前缀 */
    
    	pci_channel_state_t error_state;	/* 当前连接状态 */
    	struct device	dev;			/* 通用设备接口 */
    
    	int		cfg_size;		/* 配置空间的大小 */
    
    	/*
    	 * 不要直接接触中断线和基址寄存器,而是使用存储在这里的值。他们可能是不同的
    	 */
    	unsigned int	irq;
    	struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O和内存区域+扩展rom */
    
    	bool		match_driver;		/* 跳过附加驱动程序 */
    
    	unsigned int	transparent:1;		/* 减去解码桥 */
    	unsigned int	io_window:1;		/* 网桥有I/O窗口 */
    	unsigned int	pref_window:1;		/* 桥有优先窗口 */
    	unsigned int	pref_64_window:1;	/* Pref mem窗口为64位 */
    	unsigned int	multifunction:1;	/* 多功能设备 */
    
    	unsigned int	is_busmaster:1;		/* 是总线主控装置 */
    	unsigned int	no_msi:1;		/* 不可以使用MSI */
    	unsigned int	no_64bit_msi:1;		/* 只能使用32位的msi */
    	unsigned int	block_cfg_access:1;	/* 配置空间访问被阻止 */
    	unsigned int	broken_parity_status:1;	/* 生成假正奇偶校验 */
    	unsigned int	irq_reroute_variant:2;	/* 需要IRQ重新路由变体 */
    	unsigned int	msi_enabled:1;
    	unsigned int	msix_enabled:1;
    	unsigned int	ari_enabled:1;		/* ARI转发 */
    	unsigned int	ats_enabled:1;		/* 地址转换Svc */
    	unsigned int	pasid_enabled:1;	/* 进程地址空间ID */
    	unsigned int	pri_enabled:1;		/* 页请求接口 */
    	unsigned int	is_managed:1;		/* 通过devres管理 */
    	unsigned int	is_msi_managed:1;	/* MSI发布通过安装的devres */
    	unsigned int	needs_freset:1;		/* 需要基本复位 */
    	unsigned int	state_saved:1;
    	unsigned int	is_physfn:1;
    	unsigned int	is_virtfn:1;
    	unsigned int	is_hotplug_bridge:1;
    	unsigned int	shpc_managed:1;		/* shpchp拥有的SHPC */
    	unsigned int	is_thunderbolt:1;	/* Thunderbolt控制器 */
    	/*
    	 * 标记为不受信任的设备可能执行DMA攻击或类似攻击
    	 * 它们通常通过Thunderbolt等外部端口连接,但不限于此
    	 * 当启用IOMMU时,它们应该获得完整的映射,以确保它们不能访问任意内存
    	 */
    	unsigned int	untrusted:1;
    	/*
    	 * 来自平台的信息,例如ACPI或设备树,可能会将设备标记为“面向外部”
    	 * 面向外部的设备本身是内部的,但它的下游设备是外部的
    	 */
    	unsigned int	external_facing:1;
    	unsigned int	broken_intx_masking:1;	/* 不使用INTx屏蔽 */
    	unsigned int	io_window_1k:1;		/* 英特尔网桥1K I/O窗口 */
    	unsigned int	irq_managed:1;
    	unsigned int	non_compliant_bars:1;	/* 损坏的bar(可映射访问的硬件物理空间),忽略它们 */
    	unsigned int	is_probed:1;		/* 正在进行设备探测 */
    	unsigned int	link_active_reporting:1;/* 能够报告链路活动的设备 */
    	unsigned int	no_vf_scan:1;		/* IOV启用后不扫描VF */
    	unsigned int	no_command_memory:1;	/* 没有PCI_COMMAND_MEMORY */
    	unsigned int	rom_bar_overlap:1;	/* ROM BAR损坏(或未使用) */
    	pci_dev_flags_t dev_flags;
    	atomic_t	enable_cnt;	/* 已调用pci_enable_device */
    
    	u32		saved_config_space[16]; /* 挂起时保存的配置空间 */
    	struct hlist_head saved_cap_space;
    	int		rom_attr_enabled;	/* ROM属性显示 */
    	struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* 资源的sysfs文件 */
    	struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* 用于资源WC映射的sysfs文件 */
    
    #ifdef CONFIG_HOTPLUG_PCI_PCIE
    	unsigned int	broken_cmd_compl:1;	/* 某些命令没有完成 */
    #endif
    #ifdef CONFIG_PCIE_PTM
    	u16		ptm_cap;		/* PTM能力 */
    	unsigned int	ptm_root:1;
    	unsigned int	ptm_enabled:1;
    	u8		ptm_granularity;
    #endif
    #ifdef CONFIG_PCI_MSI
    	void __iomem	*msix_base;
    	raw_spinlock_t	msi_lock;
    #endif
    	struct pci_vpd	vpd;
    #ifdef CONFIG_PCIE_DPC
    	u16		dpc_cap;
    	unsigned int	dpc_rp_extensions:1;
    	u8		dpc_rp_log_size;
    #endif
    #ifdef CONFIG_PCI_ATS
    	union {
    		struct pci_sriov	*sriov;		/* PF: SR-IOV信息 */
    		struct pci_dev		*physfn;	/* VF: 相关PF */
    	};
    	u16		ats_cap;	/* ATS能力偏移 */
    	u8		ats_stu;	/* ATS最小翻译单元 */
    #endif
    #ifdef CONFIG_PCI_PRI
    	u16		pri_cap;	/* PRI能力偏移 */
    	u32		pri_reqs_alloc; /* 分配的PRI请求数 */
    	unsigned int	pasid_required:1; /* 需要PRG响应PASID */
    #endif
    #ifdef CONFIG_PCI_PASID
    	u16		pasid_cap;	/* PASID能力偏移 */
    	u16		pasid_features;
    #endif
    #ifdef CONFIG_PCI_P2PDMA
    	struct pci_p2pdma __rcu *p2pdma;
    #endif
    	u16		acs_cap;	/* ACS能力偏移 */
    	phys_addr_t	rom;		/* 物理地址(如果不是来自BAR) */
    	size_t		romlen;		/* 长度(如果不是来自BAR) */
    	/*
    	 * 驱动程序名称来强制匹配
    	 * 不要直接设置,因为core会释放它
    	 * 使用driver_set_override()来设置或清除它
    	 */
    	const char	*driver_override;
    
    	unsigned long	priv_flags;	/* PCI驱动程序的私有标志 */
    
    	/* 这些方法索引pci_reset_fn_methods[] */
    	u8 reset_methods[PCI_NUM_RESET_METHODS]; /* 按优先顺序 */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196

      pci_device_id PCI设备ID结构

    struct pci_device_id {
    	__u32 vendor, device;		/* 厂商ID和设备ID or PCI_ANY_ID */
    	// #define PCI_ANY_ID (~0) 如果设置这个标志,应该是一个通用的设备,或者某厂商的产品使用通用的设备驱动
    	
    	__u32 subvendor, subdevice;	/* 子系统的厂商ID和设备ID or PCI_ANY_ID */
    	__u32 class, class_mask;	/* (class,subclass,prog-if) triplet */
    	// class: 要匹配的设备类、子类和“接口”
    	// class_mask: 限制比较class字段的哪些子字段
    	
    	kernel_ulong_t driver_data;	/* 驱动程序的私有数据 */
    	__u32 override_only; // 为true时,强制dev->driver_override为该驱动时匹配
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

      pci_dynid 保存PCI设备动态ID的结构

    struct pci_dynid {
    	struct list_head node; // 设备节点列表
    	struct pci_device_id id;
    };
    
    • 1
    • 2
    • 3
    • 4

      pci_bus PCI总线结构

    struct pci_bus {
    	struct list_head node;		/* 总线列表中的节点 */
    	struct pci_bus	*parent;	/* 所在的桥(bridge)上的父总线 */
    	struct list_head children;	/* 子总线列表 */
    	struct list_head devices;	/* 此总线上的设备列表 */
    	struct pci_dev	*self;		/* 父级看到的桥接设备 */
    	struct list_head slots;		/* 总线上的槽的列表;  pci_slot_mutex互斥锁保护 */
    	struct resource *resource[PCI_BRIDGE_RESOURCE_NUM];
    	struct list_head resources;	/* 路由到此总线的地址空间 */
    	struct resource busn_res;	/* 路由到此总线的总线编号 */
    
    	struct pci_ops	*ops;		/* 配置访问函数,操作 */
    	void		*sysdata;	/* 用于特定于系统的扩展的钩子 */
    	struct proc_dir_entry *procdir;	/* /proc/bus/pci中的目录条目 */
    
    	unsigned char	number;		/* Bus号 */
    	unsigned char	primary;	/* 主桥数 */
    	unsigned char	max_bus_speed;	/* enum pci_bus_speed */
    	unsigned char	cur_bus_speed;	/* enum pci_bus_speed */
    #ifdef CONFIG_PCI_DOMAINS_GENERIC
    	int		domain_nr;
    #endif
    
    	char		name[48];
    
    	unsigned short	bridge_ctl;	/* 管理NO_ISA/FBB/等行为 */
    	pci_bus_flags_t bus_flags;	/* 由子总线继承 */
    	struct device		*bridge;
    	struct device		dev;
    	struct bin_attribute	*legacy_io;	/* 此总线的遗留I/O */
    	struct bin_attribute	*legacy_mem;	/* 遗留内存 */
    	unsigned int		is_added:1;
    	unsigned int		unsafe_warn:1;	/* 警告RW1C配置写入 */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

      pci_host_bridge pci主桥设备(连接CPU和PCI bus)结构

    struct pci_host_bridge {
    	struct device	dev;
    	struct pci_bus	*bus;		/* Root bus */
    	struct pci_ops	*ops;
    	struct pci_ops	*child_ops;
    	void		*sysdata;
    	int		busnr;
    	int		domain_nr;
    	struct list_head windows;	/* resource_entry */
    	struct list_head dma_ranges;	/* dma范围资源列表 */
    	u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* 平台IRQ源口 */
    	int (*map_irq)(const struct pci_dev *, u8, u8);
    	void (*release_fn)(struct pci_host_bridge *);
    	void		*release_data;
    	unsigned int	ignore_reset_delay:1;	/* 对于整个层次结构 */
    	unsigned int	no_ext_tags:1;		/* 没有扩展标签 */
    	unsigned int	native_aer:1;		/* 操作系统可能使用PCIe AER */
    	unsigned int	native_pcie_hotplug:1;	/* 操作系统可能使用PCIe热插拔 */
    	unsigned int	native_shpc_hotplug:1;	/* 操作系统可能使用SHPC热插拔 */
    	unsigned int	native_pme:1;		/* 操作系统可能使用PCIe PME */
    	unsigned int	native_ltr:1;		/* 操作系统可以使用PCIe LTR */
    	unsigned int	native_dpc:1;		/* 操作系统可以使用PCIe DPC */
    	unsigned int	preserve_config:1;	/* 保留固件资源设置 */
    	unsigned int	size_windows:1;		/* 启用根总线大小调整 */
    	unsigned int	msi_domain:1;		/* 桥需要MSI域 */
    
    	/* 资源调整要求 */
    	resource_size_t (*align_resource)(struct pci_dev *dev,
    			const struct resource *res,
    			resource_size_t start,
    			resource_size_t size,
    			resource_size_t align);
    	unsigned long	private[] ____cacheline_aligned;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

      pci_ops pci操作

    struct pci_ops {
    	int (*add_bus)(struct pci_bus *bus); // 添加总线
    	void (*remove_bus)(struct pci_bus *bus); // 释放总线
    	void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where); // 映射
    	int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); // 读
    	int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); // 写
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

      device_link 设备链接表示结构

    struct device_link {
    	struct device *supplier; // 链接厂商的设备
    	struct list_head s_node; // 链接到厂商设备的消费者链接列表
    	struct device *consumer; // 链接消费者端的设备
    	struct list_head c_node; // 链接到消费者设备的厂商链接列表
    	struct device link_dev; // 用于在sysfs中公开链接详细信息的设备
    	enum device_link_state status; // 链接的状态(相对于驱动程序的存在)
    	u32 flags; // Link标志
    	refcount_t rpm_active; // 消费者设备是否处于运行时PM活动状态
    	struct kref kref; // 计算重复添加相同链接的次数
    	struct work_struct rm_work; // 用于删除链接的工作结构
    	bool supplier_preactivated; /* 厂商在消费者探测前已激活 */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4. 扩展函数/变量

      devices_kset 设备kset容器,根kobj -> /sys/devices/

    /* /sys/devices/ */
    struct kset *devices_kset;
    
    • 1
    • 2

      pci_bus_match 判断一个PCI设备结构是否具有匹配的PCI设备id结构

    /*
     * dev 要匹配的PCI设备结构
     * drv 用于搜索匹配PCI设备id结构的设备驱动程序
     * 
     * 驱动程序用来检查系统中的PCI设备是否在其支持的设备列表中
     * 返回匹配的pci_device_id结构,如果不匹配则返回%NULL
     * /
    static int pci_bus_match(struct device *dev, struct device_driver *drv)
    {
            struct pci_dev *pci_dev = to_pci_dev(dev);
            // #define to_pci_dev(n) container_of(n, struct pci_dev, dev)
            
            struct pci_driver *pci_drv; // /* 与此设备绑定的驱动程序 */
            const struct pci_device_id *found_id;
    
            if (!pci_dev->match_driver) // 跳过附加驱动程序
                    return 0;
    
            pci_drv = to_pci_driver(drv);
            found_id = pci_match_device(pci_drv, pci_dev);
            if (found_id)
                    return 1;
    
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    device_driver
    pci_dev
    pci_match_device

      pci_match_device 查看pci设备是否匹配pci驱动程序的id列表

    static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
                                                        struct pci_dev *dev)
    {
            struct pci_dynid *dynid;
            const struct pci_device_id *found_id = NULL, *ids;
    
            /* 
             * 当设置driver_override时,只绑定匹配的驱动
             * 某厂家的设备不支持通用的驱动情况下,应该设置这个标志
             */
            if (dev->driver_override && strcmp(dev->driver_override, drv->name))
                    return NULL;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    pci_device_id
    pci_dynid

    /* 在静态id之前,先看看动态id */
            spin_lock(&drv->dynids.lock);
            list_for_each_entry(dynid, &drv->dynids.list, node) {
                    if (pci_match_one_device(&dynid->id, dev)) { // 告诉一个PCI设备结构是否有匹配
                            found_id = &dynid->id;
                            break;
                    }
            }
            spin_unlock(&drv->dynids.lock);
    	
    		if (found_id)
                    return found_id;
    ||
    \/
    static inline const struct pci_device_id *
    pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
    {
            if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
                (id->device == PCI_ANY_ID || id->device == dev->device) &&
                (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
                (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
                !((id->class ^ dev->class) & id->class_mask)) // 检查pci设备是否有匹配的驱动
                // 如果部分ID指定PCI_ANY_ID,表示这一部分属于通用的
                // 如id->vendor  == PCI_ANY_ID,表示这个pci设备可以是任意厂商的产品
                // 如id->vendor == dev->vendor,id->device == PCI_ANY_ID 表示这个厂商的多类pci设备产品可以使用自家的同一套驱动
                    return id;
            return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    for (ids = drv->id_table; (found_id = pci_match_id(ids, dev)); // id_table 指向驱动程序设备id表的指针
                 ids = found_id + 1) { // 查看PCI设备是否匹配给定的pci_id表
                    /*
                     * 匹配表基于driver_override进行拆分
                     * 如果设置了override_only,则强制driver_override匹配
                     */
                    if (found_id->override_only) {
                            if (dev->driver_override)
                                    return found_id;
                    } else {
                            return found_id;
                    }
            }
    
            /* Driver_override将始终匹配,发送一个虚拟id */
            if (dev->driver_override)
                    return &pci_device_id_any;
            return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

      pci_device_probe 通过主桥设备对象找到pin值(引脚号),计算出卡槽位置(槽号),然后通过槽号找到对应的中断号,写入到中断线(行)并关联到pci设备(由pci驱动使用),最后在连接设备的cpu上执行驱动程序初始化,执行驱动的探测函数(用户注册的驱动)

    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);
    
            if (!pci_device_can_probe(pci_dev)) // 不是虚拟设备 或者 驱动自动探测VF 或者 驱动始终匹配,继续执行
                    return -ENODEV;
    
    		pci_assign_irq(pci_dev); // 通过主桥设备对象找到pin值(引脚号),计算出卡槽位置(槽号),然后通过槽号找到对应的中断号,写入到中断线(行)并关联到pci设备(由pci驱动使用)
    		// pci驱动 <-> 中断号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    pci_assign_irq

    		error = pcibios_alloc_irq(pci_dev); // arm64架构默认注册、生效中断(irq),x86等平台不做任何处理
    		// 龙芯架构另做了msi判断,如果msi生效(独立的中断),则不生效中断(irq)
    
    		...
    		error = __pci_device_probe(drv, pci_dev);
    		...
    		return error;
    }
    ||
    \/
    static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
    {
            const struct pci_device_id *id;
            int error = 0;
    
            if (drv->probe) {
                    error = -ENODEV;
    
                    id = pci_match_device(drv, pci_dev); // 查看pci设备是否匹配pci驱动程序的id列表
                    if (id)
                            error = pci_call_probe(drv, pci_dev, id); // 在连接设备的cpu上执行驱动程序初始化,执行驱动的探测函数(用户注册的驱动)
            }
            return error;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    pci_call_probe

      pci_assign_irq 通过主桥设备对象找到pin值(引脚号),计算出卡槽位置(槽号),然后通过槽号找到对应的中断号,写入到中断线(行)并关联到pci设备(由pci驱动使用)

    void pci_assign_irq(struct pci_dev *dev)
    {
            u8 pin;
            u8 slot = -1;
            int irq = 0;
            struct pci_host_bridge *hbrg = pci_find_host_bridge(dev->bus); // 找到根bus总线(所在的桥(bridge)上的父总线),返回主桥设备对象
    
            if (!(hbrg->map_irq)) {
                    pci_dbg(dev, "runtime IRQ mapping not provided by arch\n");
                    return;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    pci_find_host_bridge

    		/*
             * 如果这个设备不在主总线上,我们需要弄清楚它将进入哪个中断引脚
             * 我们知道它将进入哪一个插槽,因为该插槽是桥的所在位置
             * 每次中断线通过PCI-PCI桥时,我们都必须应用swizze函数
             */
            pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); // 通过PCI_OP_READ扩展的pci总线读函数,读一个字节
            // #define PCI_INTERRUPT_LINE      0x3c    /* 8 bits */
            // #define PCI_INTERRUPT_PIN       0x3d    /* 8 bits */
            // #define PCI_MIN_GNT             0x3e    /* 8 bits */
            // #define PCI_MAX_LAT             0x3f    /* 8 bits */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    pci_read_config_byte

    		/* 处理错误的pin值 */
            if (pin > 4)
                    pin = 1;
    
    		if (pin) {
                    /* Follow the chain of bridges, swizzling as we go. */
                    if (hbrg->swizzle_irq) // 平台IRQ源口(数据流)
                            slot = (*(hbrg->swizzle_irq))(dev, &pin); // 得到槽号
    
                    /*
                     * 如果没有使用swizzling函数,map_irq()必须忽略slot
                     */
                    irq = (*(hbrg->map_irq))(dev, slot, pin); // 中断号
                    if (irq == -1)
                            irq = 0;
            }
            dev->irq = irq;
    
    		/*
             * 总是告诉设备,这样驱动程序就知道真正要使用的IRQ是什么
             * 设备不使用它
             */
            pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); // 通过PCI_OP_READ扩展的pci总线写函数,写一个字节
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    pci_write_config_byte

      pci_find_host_bridge 找到根bus总线(所在的桥(bridge)上的父总线),返回主桥设备对象

    pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus)
    {       
            struct pci_bus *root_bus = find_pci_root_bus(bus);
    ||
    \/
    static struct pci_bus *find_pci_root_bus(struct pci_bus *bus) // 找到根bus总线(所在的桥(bridge)上的父总线)
    {
            while (bus->parent)
                    bus = bus->parent;
    
            return bus;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    pci_bus

    return to_pci_host_bridge(root_bus->bridge); // 返回主桥设备对象
    }       
    ||
    \/
    #define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    pci_host_bridge

      pci_read_config_byte 通过PCI_OP_READ扩展的pci总线读函数,读一个字节

    EXPORT_SYMBOL(pci_bus_read_config_byte); // 读1个字节
    EXPORT_SYMBOL(pci_bus_read_config_word); // 读一个字,2个字节
    EXPORT_SYMBOL(pci_bus_read_config_dword); // 读两个字,4个字节
    ||
    \/
    PCI_OP_READ(byte, u8, 1)
    PCI_OP_READ(word, u16, 2)
    PCI_OP_READ(dword, u32, 4)
    ||
    \/
    #define PCI_OP_READ(byte, u8, 1) \
    int noinline pci_bus_read_config_byte  
            (struct pci_bus *bus, unsigned int devfn, int pos, type *value)  // pci总线读函数扩展
    {                                                                       
            int res;                                                        
            unsigned long flags;                                            
            u32 data = 0;                                                   
            if (PCI_byte_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;     
            // #define PCI_byte_BAD 0
            // #define PCI_word_BAD (pos & 1)
            // #define PCI_dword_BAD (pos & 3)
            
            /* PCI函数可能返回的错误值 */
            // #define PCIBIOS_SUCCESSFUL              0x00
            // #define PCIBIOS_FUNC_NOT_SUPPORTED      0x81
            // #define PCIBIOS_BAD_VENDOR_ID           0x83
            // #define PCIBIOS_DEVICE_NOT_FOUND        0x86
            // #define PCIBIOS_BAD_REGISTER_NUMBER     0x87
            // #define PCIBIOS_SET_FAILED              0x88
            // #define PCIBIOS_BUFFER_TOO_SMALL        0x89
            
            pci_lock_config(flags);                                         
            res = bus->ops->read(bus, devfn, pos, len, &data); // 主桥设备->pci_ops->读函数          
            if (res)                                                       
                    PCI_SET_ERROR_RESPONSE(value);                         
            else                                                          
                    *value = (u8)data;  // 数据通过value返回                                 
            pci_unlock_config(flags);                                      
            return res;                                                  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    pci_ops

      pci_write_config_byte 通过PCI_OP_READ扩展的pci总线写函数,写一个字节

    EXPORT_SYMBOL(pci_bus_write_config_byte); // 写1个字节
    EXPORT_SYMBOL(pci_bus_write_config_word); // 写一个字,2个字节
    EXPORT_SYMBOL(pci_bus_write_config_dword); // 写两个字,4个字节
    ||
    \/
    PCI_OP_WRITE(byte, u8, 1)
    PCI_OP_WRITE(word, u16, 2)
    PCI_OP_WRITE(dword, u32, 4)
    ||
    \/
    #define PCI_OP_WRITE(size, type, len) \
    int noinline pci_bus_write_config_byte \
            (struct pci_bus *bus, unsigned int devfn, int pos, type value)  // pci总线写函数扩展  
    {                                                                       
            int res;                                                        
            unsigned long flags;                                            
            if (PCI_byte_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;      
                     
            pci_lock_config(flags);                                         
            res = bus->ops->write(bus, devfn, pos, len, value); // 主桥设备->pci_ops->写函数            
            pci_unlock_config(flags);                                       
            return res;                                                     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

      pci_call_probe 在连接设备的cpu上执行驱动程序初始化,执行驱动的探测函数(用户注册的驱动)

    static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
                              const struct pci_device_id *id)
    {
            int error, node, cpu;
            struct drv_dev_and_id ddi = { drv, dev, id };
    
            /*
             * 在连接设备的节点上执行驱动程序初始化
             * 这样,驱动程序可能会在右侧节点上分配其本地内存.
             */
            node = dev_to_node(&dev->dev); // dev->numa_node
            dev->is_probed = 1; // 设备需要探测
    
            cpu_hotplug_disable(); // cpu_hotplug_disabled++
            // 等待当前正在运行的CPU热插拔操作完成(如果有),并禁用将来的CPU热插拔(从sysfs)
            // “cpu_add_remove_lock”保护“cpu_hotplug_disabled”标志
            // 在执行热插拔操作之前,热插拔路径也会获取相同的锁
            // 因此,获取该锁可以确保当前正在运行的任何热插拔操作相互排斥
    
    		/*
    		 * 防止从物理设备的work_on_cpu() 探测Virtual Function设备时嵌套work_on_cpu()
    		 */
    		if (node < 0 || node >= MAX_NUMNODES || !node_online(node) ||
                pci_physfn_is_probed(dev)) { // numa节点不满足使用情况,或者是虚拟设备
                    cpu = nr_cpu_ids; // cpu = 1
            } else {
                    cpumask_var_t wq_domain_mask;
    
                    if (!zalloc_cpumask_var(&wq_domain_mask, GFP_KERNEL)) { // 为&wq_domain_mask(指针)分配内存
                            error = -ENOMEM;
                            goto out;
                    }
                    cpumask_and(wq_domain_mask,
                                housekeeping_cpumask(HK_TYPE_WQ),
                                housekeeping_cpumask(HK_TYPE_DOMAIN));
    
                    cpu = cpumask_any_and(cpumask_of_node(node),
                                          wq_domain_mask);
                    free_cpumask_var(wq_domain_mask);
            }
    
    		if (cpu < nr_cpu_ids)
                    error = work_on_cpu(cpu, local_pci_probe, &ddi); // 在指定cpu上的线程上下文中运行local_pci_probe
            else
                    error = local_pci_probe(&ddi); // 执行驱动的探测函数(用户注册的驱动)
    out:
            dev->is_probed = 0;
            cpu_hotplug_enable(); // cpu热插拔恢复,cpu_hotplug_disabled--
            return error;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

      bus_register 注册总线,创建总线uevent,创建subsys_private私有结构(通过subsys(子系统)关联bustype/class结构的驱动程序,驱动绑定到设备列表中(knode_driver 加入到 klist_devices列表(链表)中))等等

    int bus_register(struct bus_type *bus)
    {
            int retval;
            struct subsys_private *priv; // 用于将private保存到bustype/class结构的驱动程序核心结构
            struct lock_class_key *key = &bus->lock_key;
    
            priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
    
    		priv->bus = bus; // 关联bus
            bus->p = priv; // bus私有字段赋值
    
            BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); // 初始化总线通知程序列表,用于显示任何与此总线上的事情有关的内容
    
    		retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); // 设置与bus相同的名称
    
    		priv->subsys.kobj.kset = bus_kset; // 关联bus_kset
            priv->subsys.kobj.ktype = &bus_ktype; // 关联bus_ktype
            priv->drivers_autoprobe = 1; // 自动探测
    
    		retval = kset_register(&priv->subsys); // 注册kset(子系统)
    
    		retval = bus_create_file(bus, &bus_attr_uevent); // 创建bus文件,从kernfs_node_cache缓存中分配kn节点及初始化,将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    bus_attr_uevent

    retval = bus_add_groups(bus, bus->bus_groups); // 获取kobj对象的sysfs所有权数据,创建kernfs_node节点及命名空间 (父级kn,如目录),然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点(属性列表中的节点)
    ...
    }
    EXPORT_SYMBOL_GPL(bus_register);
    
    • 1
    • 2
    • 3
    • 4
    priv->devices_kset = kset_create_and_add("devices", NULL,
                                                     &priv->subsys.kobj); // 创建devices_kset容器
    
    
    priv->drivers_kset = kset_create_and_add("drivers", NULL,
                                                     &priv->subsys.kobj); // 创建drivers_kset容器
    
    INIT_LIST_HEAD(&priv->interfaces);
    __mutex_init(&priv->mutex, "subsys mutex", key);
    klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); // 初始化klist 设备结构,get/put函数用于初始化获取和释放klist_node结构中记录的设备
    klist_init(&priv->klist_drivers, NULL, NULL); // 初始化klist 驱动结构
    
    retval = add_probe_files(bus); // bus关联属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    bus_attr_drivers_probe

      bus_attr_uevent 定义bus属性结构

    static struct bus_attribute bus_attr_uevent = __ATTR(uevent, 0200, NULL,
                                                         bus_uevent_store); // 定义bus属性结构
    // name为"uevent",bus_uevent_store函数发送带参数的合成uevent事件
    
    • 1
    • 2
    • 3

      bus_attr_drivers_probe 用于驱动绑定到设备列表等功能的函数

    bus_attr_drivers_probe
    ||
    \/
    static BUS_ATTR_WO(drivers_probe);
    ||
    \/
    #define BUS_ATTR_WO(drivers_probe) \
            struct bus_attribute bus_attr_drivers_probe = __ATTR_WO(drivers_probe)
    ||
    \/
    struct bus_attribute bus_attr_drivers_probe = {                                             
            .attr   = { .name = "drivers_probe", .mode = 0200 },        
            .store  = drivers_probe_store,                                       
    };
    ||
    \/
    static ssize_t drivers_probe_store(struct bus_type *bus,
    				   const char *buf, size_t count)
    {
    	struct device *dev;
    	int err = -EINVAL;
    
    	dev = bus_find_device_by_name(bus, NULL, buf); // 设备迭代器中找到对应的设备
    	//bus的私有结构中匹配klist_devices
    	
    	if (!dev)
    		return -ENODEV;
    	if (bus_rescan_devices_helper(dev, NULL) == 0) // 驱动绑定到设备列表等功能的函数
    		err = count;
    	put_device(dev);
    	return err;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    device_bind_driver

      device_bind_driver 驱动加入到设备列表中(knode_driver 加入到 klist_devices列表(链表)中),deferred_probe列表用于重试一次,这是安全的

    int device_bind_driver(struct device *dev)
    {
    	int ret;
    
    	ret = driver_sysfs_add(dev); // 驱动私有结构的kobj与设备kobj之间,互相创建不同名称的链接文件,创建coredump属性(coredump_store函数)
    	if (!ret) {
    		device_links_force_bind(dev); // 为强制绑定设备做准备,检查厂商是否存在(硬件)
    		driver_bound(dev); // 驱动加入到设备列表中(knode_driver 加入到 klist_devices列表(链表)中),deferred_probe列表用于重试一次,这是安全的
    	}
    	else if (dev->bus)
    		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
    					     BUS_NOTIFY_DRIVER_NOT_BOUND, dev); // 调用阻塞通知器链中的函数
    	    // 无法绑定驱动程序通知
    	return ret;
    }
    EXPORT_SYMBOL_GPL(device_bind_driver);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    driver_sysfs_add
    device_link

      driver_sysfs_add 发送准备驱动绑定通知,驱动私有结构的kobj与设备kobj之间,互相创建不同名称的链接文件,创建coredump属性(coredump_store函数)

    static int driver_sysfs_add(struct device *dev)
    {
            int ret;
    
            if (dev->bus)
                    blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                                 BUS_NOTIFY_BIND_DRIVER, dev); // 调用阻塞通知器链中的函数
                    // #define BUS_NOTIFY_BIND_DRIVER          0x00000004 /* 准备驱动绑定通知 */
    
    		ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
                                    kobject_name(&dev->kobj)); // 在两个对象之间创建符号链接
             // dev->driver->p->kobj 链接到 dev->kobj
    
    		ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
                                    "driver"); 
    
    		if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump)
                    return 0;
    
            ret = device_create_file(dev, &dev_attr_coredump); // static DEVICE_ATTR_WO(coredump);
            // coredump_store
            if (!ret)
                    return 0;
    		...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    目录预览


    <>
    <>

  • 相关阅读:
    Multisim14.0仿真(十)同相放大器
    【死磕slam14】slambook2第五讲 详解joinMap.cpp
    TiDB 锁冲突问题处理
    华天OA任意文件上传漏洞 复现
    [数据可视化] 折线图(Line Chart)
    基于深度学习的车辆检测技术
    什么是 XML?使用IntelliJ IDEA 创建一个简单的 xml 文件
    Java开发三年四面字节跳动复习一个月斩获offer,寒冬并不可怕啊,所以现在酷暑也要坚持哦
    GPT接入飞书
    Hadoop入门介绍
  • 原文地址:https://blog.csdn.net/a29562268/article/details/127700155