gpt4 book ai didi

c - 如何处理来自 Linux 内核模块中已经具有不可共享处理程序的 PCI 设备的中断?

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:19:59 25 4
gpt4 key购买 nike

我正在使用 educational edu PCI device 通过 QEMU 学习 Linux 内核 PCI 开发。

如果我将设备用于:

-device edu

它是在启动时插入的,我的中断编号为 11 并且运行良好。

但是,我开始尝试在使用 monitor 命令启动后插入设备:

device_add edu

其次是:

echo 1 > /sys/bus/pci/rescan

如果我这样做,IRQ 将被分配给一个中断 0,它上面已经有一个不可共享的中断(计时器),并且我的:

 request_irq(pci_irq, irq_handler, IRQF_SHARED, "pci_irq_handler0", &major)

失败并显示消息:

 genirq: Flags mismatch irq 0. 00000080 (pci_irq_handler0) vs. 00015a00 (timer)

从内核源代码中,我们看到 0x80 是可共享标志,它不存在于计时器中。

这是 edu 设备中的错误,还是我可以在我的内核模块中做些什么?

启动时带有 -device 的状态可以:

device      BDF      IRQ
========== ======= ===
edu 00:04.0 10
virtio-pci 00:05.0 11

device_add 给出:

device      BDF      IRQ
========== ======= ===
virtio-pci 00:04.0 10
edu 00:05.0 0

所以我们看到 eduvirtio-pci 在探测器上交换了位置,但不幸的是没有 IRQ。

full device code with all boilerplate is on GitHub,这是一个最小化的复制版本:

#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>

#define BAR 0
#define CDEV_NAME "lkmc_pci"
#define EDU_DEVICE_ID 0x11e8
#define IO_IRQ_ACK 0x64
#define IO_IRQ_STATUS 0x24
#define QEMU_VENDOR_ID 0x1234

MODULE_LICENSE("GPL");

static struct pci_device_id pci_ids[] = {
{ PCI_DEVICE(QEMU_VENDOR_ID, EDU_DEVICE_ID), },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, pci_ids);

static int major;
static int pci_irq;
static struct pci_dev *pdev;
static void __iomem *mmio;

static struct file_operations fops = {
.owner = THIS_MODULE,
};

static irqreturn_t irq_handler(int irq, void *dev)
{
int devi;
irqreturn_t ret;
u32 irq_status;

devi = *(int *)dev;
irq_status = ioread32(mmio + IO_IRQ_STATUS);
pr_info("irq_handler irq = %d dev = %d irq_status = %llx\n",
irq, devi, (unsigned long long)irq_status);
iowrite32(irq_status, mmio + IO_IRQ_ACK);
ret = IRQ_HANDLED;
return ret;
}

static int pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
u8 val;

pr_info("pci_probe\n");
major = register_chrdev(0, CDEV_NAME, &fops);
pdev = dev;
if (pci_enable_device(dev) < 0) {
dev_err(&(pdev->dev), "pci_enable_device\n");
goto error;
}
if (pci_request_region(dev, BAR, "myregion0")) {
dev_err(&(pdev->dev), "pci_request_region\n");
goto error;
}
mmio = pci_iomap(pdev, BAR, pci_resource_len(pdev, BAR));
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &val);
pci_irq = val;
if (request_irq(pci_irq, irq_handler, IRQF_SHARED, "pci_irq_handler0", &major) < 0) {
dev_err(&(dev->dev), "request_irq\n");
goto error;
}
/* This makes the device generate an interrupt. */
iowrite32(0x12345678, mmio + 0x60);
return 0;
error:
return 1;
}

static void pci_remove(struct pci_dev *dev)
{
pr_info("pci_remove\n");
free_irq(pci_irq, &major);
pci_release_region(dev, BAR);
unregister_chrdev(major, CDEV_NAME);
}

static struct pci_driver pci_driver = {
.name = "lkmc_pci",
.id_table = pci_ids,
.probe = pci_probe,
.remove = pci_remove,
};

static int myinit(void)
{
if (pci_register_driver(&pci_driver) < 0) {
return 1;
}
return 0;
}

static void myexit(void)
{
pci_unregister_driver(&pci_driver);
}

module_init(myinit);
module_exit(myexit);

可能相关:https://serverfault.com/questions/70585/manually-assign-a-pci-card-to-an-interrupt

最佳答案

正如 0andriy 所提到的,我们需要使用 dev->pci 而不是使用 pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &val); 读取配置;
。 TODO 遵循内核代码并准确理解原因。这有效:

#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>

#define BAR 0
#define CDEV_NAME "lkmc_hw_pci_min"
#define EDU_DEVICE_ID 0x11e9
#define QEMU_VENDOR_ID 0x1234

MODULE_LICENSE("GPL");

static struct pci_device_id id_table[] = {
{ PCI_DEVICE(QEMU_VENDOR_ID, EDU_DEVICE_ID), },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, id_table);
static int major;
static struct pci_dev *pdev;
static void __iomem *mmio;
static struct file_operations fops = {
.owner = THIS_MODULE,
};

static irqreturn_t irq_handler(int irq, void *dev)
{
pr_info("irq_handler irq = %d dev = %d\n", irq, *(int *)dev);
iowrite32(0, mmio + 4);
return IRQ_HANDLED;
}

static int probe(struct pci_dev *dev, const struct pci_device_id *id)
{
pr_info("probe\n");
major = register_chrdev(0, CDEV_NAME, &fops);
pdev = dev;
if (pci_enable_device(dev) < 0) {
dev_err(&(pdev->dev), "pci_enable_device\n");
goto error;
}
if (pci_request_region(dev, BAR, "myregion0")) {
dev_err(&(pdev->dev), "pci_request_region\n");
goto error;
}
mmio = pci_iomap(pdev, BAR, pci_resource_len(pdev, BAR));
pr_info("dev->irq = %u\n", dev->irq);
if (request_irq(dev->irq, irq_handler, IRQF_SHARED, "pci_irq_handler0", &major) < 0) {
dev_err(&(dev->dev), "request_irq\n");
goto error;
}
iowrite32(0x12345678, mmio);
return 0;
error:
return 1;
}

static void remove(struct pci_dev *dev)
{
pr_info("remove\n");
free_irq(dev->irq, &major);
pci_release_region(dev, BAR);
unregister_chrdev(major, CDEV_NAME);
}

static struct pci_driver pci_driver = {
.name = CDEV_NAME,
.id_table = id_table,
.probe = probe,
.remove = remove,
};

static int myinit(void)
{
if (pci_register_driver(&pci_driver) < 0) {
return 1;
}
return 0;
}

static void myexit(void)
{
pci_unregister_driver(&pci_driver);
}

module_init(myinit);
module_exit(myexit);

关于c - 如何处理来自 Linux 内核模块中已经具有不可共享处理程序的 PCI 设备的中断?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44740254/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com