PCIe MSI 中断相关的启动流程

文章详细阐述了Linux系统下PCIe控制器和设备的初始化过程,包括内存和中断设置。在控制器侧,主要涉及配置空间设置、设备枚举和pci_dev结构体的构建。设备侧则涵盖内存映射和MSI/MSI-X中断的申请与激活。文章还深入解析了MSI中断的分配和硬件中断号的获取,特别是涉及到GIC和ITS的中断处理机制。
摘要由CSDN通过智能技术生成

Linux PCIe 中断相关启动流程

1. PCIe控制器初始化过程

0. 准备配置空间,做outbound配置
1. 枚举 pcie 设备
    2.1. 为设备准备物理内存
        读写BAR0,获取需求内存大小.
        写 BAR0 为 PCIe地址,做outbound设置(映射物理地址到pcie地址)
    2.2. 为设备准备legacy硬件中断号,但是不会准备MSI/MSI-X硬件中断号
        
2. 构造 pci_dev 结构体
    3.1 用 legacy irq 对应的 软件中断号 填充 pci_dev 结构体变量的 irq 成员
    3.2 填充 pci_dev 结构体变量的 mem 相关成员
    3.3 调用 pci_device_register  注册 pci_dev 结构体变量 到 pci总线 设备链表

2. PCIe设备初始化过程

1. 内存设置
    获取到设备内存并映射物理内存到虚拟地址

2. 中断设置,这些都是在 msi_domain_alloc_irqs 中完成的 , 该函数的消费者有pci_msi_setup_msi_irqs
    2.1. 申请软件中断号
        从bitmap 中获取 软件中断号,然后alloc irq_desc , 并填充 handler 进 irq_action 链表 
    
    2.2. 根据rid((bus << 8) | (dev << 4) | function) 获取硬件中断号
        // 01:00.0应该是 8192+256 开始的
        // 关注 : its_create_device its_lpi_alloc_chunks dev->event_map.lpi_base = lpi_base;
        从 设备配置空间 cap 0x11 中读取到他需要M(Table Size+1)个 msi-x 中断
        解析 pcie 节点的 msi-map,做mask和offset,用rid作为输入,申请硬件中断号.
        在这里会返回硬件中断号的起始number(x)和数量(n)
        假设 x = 8192 , n = 3
        那么会有是三个中断号 , 8192 , 8193 , 8194
        对于ITS来讲,对应的 event id 为 0  1  2 // 这里与ITS设置的表强相关
    2.3. 地址值对设置 // 激活中断1
        做硬件配置,对于ITS,设置它的几个表.
        构造M个 <addr,val> 对
        将 M个 <addr,val> 对 写入 内存.内存基址由 cap msi-x 的 Table offset/Table BIR 指定
        将<addr,val>中的地址做inbound设置,做 Bus Master Enable// 这样 PCIe设备才可能发出中断
        // 对应关系 : 地址对中的 val  等于 hwirq - dev->event_map.lpi_base
        // 在没有pre_ITS补丁的话 , 地址对中的 addr 总是 GITS_TRANSLATER 的地址
        // 每个设备的 地址对中的 addr 都是一样的,但是每个设备有一个deviceid,是唯一的
        // its 根据 deviceid 和 eventid(地址对中的val) 来 转化为 intid 和 distributor
3.MSI-X Enable 及 开启层层开关,都是在request_irq中 完成的 //激活中断2
    索引 irq_domain_ops 中的 activate

驱动详细解读

控制器侧驱动

在配置空间侧只做了一个事情:配置了 BAR0 为 0x68100004

设备侧驱动

设备驱动调用如下函数获取硬件和软件中断号.因为是pci设备,所以不能从设备树中获取硬件中断号.
pci_enable_msix_range// range 指的是 其中的参数 minvec 到 maxvec
	申请软件中断号,申请好了,会填充entry 参数的 vector 成员 为软件中断号

驱动可能会调用如下函数来获取MSI/MSI-X软件中断号,最终都会调用到 __pci_enable_msix_range
	pci_alloc_irq_vectors
	pci_alloc_irq_vectors_affinity
	pci_enable_msix_range
	pci_enable_msi // 已经遗弃了
__pci_enable_msix_range->__pci_enable_msix->msix_capability_init->
pci_msi_setup_msi_irqs
	msi_domain_alloc_irqs
		ops->domain_alloc_irqs/__msi_domain_alloc_irqs 
		// 初始化pci-msi msi domain的时候,
		// 用 msi_domain_ops 变量 来作为 "pci-msi msi domain" 的 结构体(struct msi_domain_ops) 
			
			//1. 计算 gicv3-its irq-domain 中的 硬件中断号的参考值 B , 将B 放入 (struct its_device *)(((msi_alloc_info_t *)arg)->scratchpad[0].ptr)->event_map.lpi_base 变量,备用 // 被 irq_domain_alloc_irqs_hierarchy 用
			msi_domain_prepare_irqs
			
				ops->msi_prepare/its_pci_msi_prepare // irq-gic-v3-its-pci-msi.c
					// 以rid 作为 device id 传递 给 its msi-domain , rid 为 F
					info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev); // get rid // compare with msi-map
					
					msi_info->ops->msi_prepare/its_msi_prepare // irq-gic-v3-its.c
						dev_id = info->scratchpad[0].ul // 消费 F
						its_dev = its_create_device(its, dev_id, nvec, true); // prepare for hwirq
							its_lpi_alloc(nvecs, &lpi_base, &nr_lpis);
					 		dev->event_map.lpi_base = lpi_base; // prepare for hwirq core
				 			dev->event_map.nr_lpis = nr_lpis;
				 			its_send_mapd(dev, 1); // create table ITT
		
/*
irq_domain_alloc_irqs_hierarchy 的 步骤
	1.调用 ops->alloc,参数中有virq
		1.1 获取该域的中断号 hwirq
		1.2 为father 准备中断号,并调用 irq_domain_alloc_irqs_hierarchy call father
		1.3 将 hwirq 和 virq 设置入 irq_data	
*/
			//2. 填充硬件中断号, 申请软件中断号,desc
			for_each_msi_entry(desc, dev)
				
				// 2.1 计算 gicv3-its-pci irq-domain 中的 硬件中断号A,将A放入 arg->hwirq 备用
				ops->set_desc(&arg, desc);/pci_msi_domain_set_desc
					//将 gicv3-its-pci irq-domain 中的 硬件中断号 放入 arg 中的 hwirq ,备用
					// 至此, gicv3-its irq-domain 中的 硬件中断号的参考值 和 gicv3-its-pci irq-domain 的硬件中断号都放入了arg变量
				
				__irq_domain_alloc_irqs
					virq = irq_domain_alloc_descs // 申请软件中断号
					// 注意 : 虽然过程中有多个domain的硬件中断号,但是软件中断号只有一个(一处)
					// 是因为 只需要最底层的domain 来申请软件中断号即可
					// 高层的 硬件中断号 只负责使用
					// 以下的 virq 的值 都是同一个值
					// 三个层次中,共有多个少 irq_descs 结构体
					
					// irq_domain_alloc_irqs_hierarchy 1
					irq_domain_alloc_irqs_hierarchy
IRQ_domain:GICV3-ITS-PCI:domain->ops->alloc/msi_domain_alloc 			 // irq-domain : gicv3-its-pci
							irq_hw_number_t hwirq = ops->get_hwirq(info, arg); // 获取域中的硬件中断号 : arg->hwirq // 消费了A
							
							// irq_domain_alloc_irqs_hierarchy 2
							irq_domain_alloc_irqs_parent/irq_domain_alloc_irqs_hierarchy // call father
IRQ_domain:GICV3-ITS 			domain->ops->alloc/its_irq_domain_alloc  // irq-domain : gicv3-its 
									its_alloc_device_irq(its_dev, nr_irqs, &hwirq)	// 获取域中的硬件中断号 // 消费了B
										idx = bitmap_find_free_region(...);
										*hwirq = dev->event_map.lpi_base + idx;
									its_irq_gic_domain_alloc(domain, virq + i, hwirq + i); // call father
										fwspec.param[1] = hwirq; // parepare hwirq for father // 准备 IRQ_domain:GICV3 的 硬件中号 C
										
										// irq_domain_alloc_irqs_hierarchy 3
										irq_domain_alloc_irqs_parent/irq_domain_alloc_irqs_hierarchy
IRQ_domain:GICV3 					 			domain->ops->alloc/gic_irq_domain_alloc  // irq-domain : gicv3
													gic_irq_domain_translate	// 获取域中的硬件中断号 // 消费了 C
													// 没有 call father . 这里没有这一步,是因为GICV3 是 顶层 irq domain
													gic_irq_domain_map/irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_fasteoi_irq
														irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, chip_data);  // set irq_data
														__irq_set_handler(virq, handler, 0, handler_name);
														irq_set_handler_data(virq, handler_data);


IRQ_domain:GICV3-ITS				irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, &its_irq_chip, its_dev); // // set irq_data
										irq_data->hwirq = hwirq; // 填充硬件中断号
										
IRQ_domain:GICV3-ITS-PCI:ops->msi_init(domain, info, virq + i, hwirq + i, arg);/msi_domain_ops_init // set irq_data
							irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip, info->chip_data);
							__irq_set_handler(virq, info->handler, 0, info->handler_name);
							irq_set_handler_data(virq, info->handler_data);
							

			//3. 将 一个个中断 对应的  <addr,value> 填充并 写入 内存
			for_each_msi_vector(desc, i, dev) 
				irq_domain_activate_irq
					__irq_domain_activate_irq
						domain->ops->activate/msi_domain_activate
							irq_chip_compose_msi_msg
								pos->chip->irq_compose_msi_msg /its_irq_compose_msi_msg // irq-gic-v3-its.c
							irq_chip_write_msi_msg(irq_data, msg)
								data->chip->irq_write_msi_msg /pci_msi_domain_write_msg // irq-gic-v3-its-pci-msi.c

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值