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

时间:2017-06-24 19:35:38

标签: c linux linux-kernel qemu pci

我正在使用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

1 个答案:

答案 0 :(得分:0)

正如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);