e1000e驱动的MSI中断测试

本文主要讲述一下e1000e网卡驱动的MSI中断测试,以及对通过PROC文件(/proc/irq/目录)设置的中断参数所造成的影响。

intel网卡e1000e驱动的open函数e1000_open,会测试MSI中断。所以每次在使能网卡时,都会进行中断测试。intel的其它网卡如e1000等驱动没有此项测试功能。由代码中的注释可知,是由于PCIe的一些问题导致芯片忽略掉e1000e的MSI消息,才必须进行的测试。

static int e1000_open(struct net_device *netdev)
{/* Work around PCIe errata with MSI interrupts causing some chipsets to* ignore e1000e MSI messages, which means we need to test our MSI* interrupt now*/if (adapter->int_mode != E1000E_INT_MODE_LEGACY) {err = e1000_test_msi(adapter);}
}

核心测试函数为e1000_test_msi_interrupt。首先清除原有申请的中断(e1000_free_irq),并且重置MSI中断设置,其次重新申请一个专门用于测试的中断(pci_enable_msi),并且注册一个测试用中断处理函数(e1000_intr_msi_test);最后在测试完成之后,申请一个正常工作所用中断(e1000_request_irq)。

static int e1000_test_msi_interrupt(struct e1000_adapter *adapter)
{struct net_device *netdev = adapter->netdev;struct e1000_hw *hw = &adapter->hw;e1000_free_irq(adapter);e1000e_reset_interrupt_capability(adapter);adapter->flags |= FLAG_MSI_TEST_FAILED;err = pci_enable_msi(adapter->pdev);err = request_irq(adapter->pdev->irq, e1000_intr_msi_test, 0, netdev->name, netdev);/* fire an unusual interrupt on the test handler */ew32(ICS, E1000_ICS_RXSEQ);e1e_flush();msleep(100);if (adapter->flags & FLAG_MSI_TEST_FAILED) {adapter->int_mode = E1000E_INT_MODE_LEGACY;e_info("MSI interrupt test failed, using legacy interrupt.\n");} else {e_dbg("MSI interrupt test succeeded!\n");}e1000e_set_interrupt_capability(adapter);return e1000_request_irq(adapter);
}

MSI中断测试用的标志为FLAG_MSI_TEST_FAILED。在测试开始前置位FLAG_MSI_TEST_FAILED,紧接着主动出发一个E1000_ICR_RXSEQ的接收序列错误的中断,等待100毫秒之后,判断FLAG_MSI_TEST_FAILED标志是否清除。其标志在MSI测试中断处理函数e1000_intr_msi_test中清除,所以在正常触发了中断处理函数后,此标志就会被清除。

static irqreturn_t e1000_intr_msi_test(int __always_unused irq, void *data)
{u32 icr = er32(ICR);if (icr & E1000_ICR_RXSEQ) {adapter->flags &= ~FLAG_MSI_TEST_FAILED;wmb();}
}

接下来看一下在此过程中,PROC文件中的中断配置发生的变化。首先在中断关闭过程中,e1000_free_irq函数最终调用到__free_irq函数,其将删除PROC文件中的中断配置目录,例如中断号为36,其proc文件目录/proc/irq/36/将被删除,中断36相关的配置将全部消失,如中断的CPU核亲和性配置smp_affinity等将消失。

static void e1000_free_irq(struct e1000_adapter *adapter)
{struct net_device *netdev = adapter->netdev;free_irq(adapter->pdev->irq, netdev);
}
static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{unregister_handler_proc(irq, action);
}

另外中断重置函数e1000e_reset_interrupt_capability,将会释放MSI中断号,关闭MSI中断功能,恢复原传统的中断线的中断号,default_irq变量中保存着MSI开启之前的中断号。

void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter)
{if (adapter->flags & FLAG_MSI_ENABLED) {pci_disable_msi(adapter->pdev);adapter->flags &= ~FLAG_MSI_ENABLED;}
}
void pci_disable_msi(struct pci_dev *dev)
{pci_msi_shutdown(dev);free_msi_irqs(dev);
}
void pci_msi_shutdown(struct pci_dev *dev)
{struct msi_desc *desc;desc = list_first_entry(&dev->msi_list, struct msi_desc, list);dev->irq = desc->msi_attrib.default_irq;
} 

重新注册中断处理函数request_irq将会创建全新的中断proc文件,具体为/proc/irq/目录下创建以中断号为名称的新目录,例如中断号为36,新目录为/proc/irq/36/。

int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, ...)
{struct irqaction *action;retval = __setup_irq(irq, desc, action);
}
static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{register_irq_proc(irq, desc);register_handler_proc(irq, new);
}

通过在__setup_irq函数中调用register_irq_proc函数实现。此时创建的中断配置将是系统默认的配置参数。例如smp_affinity参数,默认使用全局变量irq_default_affinity,其在系统初始化时被设置为所有可用的CPU核。

void register_irq_proc(unsigned int irq, struct irq_desc *desc)
{sprintf(name, "%d", irq);/* create /proc/irq/1234 */desc->dir = proc_mkdir(name, root_irq_dir);/* create /proc/irq//smp_affinity */proc_create_data("smp_affinity", 0600, desc->dir, &irq_affinity_proc_fops, (void *)(long)irq);
}
static void __init init_irq_default_affinity(void)
{alloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT);cpumask_setall(irq_default_affinity);
}

至此结论是,对于e1000e驱动的网卡,由于MSI中断测试的存在,如果通过PROC文件改变了中断的smp_affinity参数,在关闭/重新使能(ifconfig eth0 down/up)网络接口操作后,smp_affinity的配置将丢失。


内核版本:Linux-4.15

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部