|
PCIe Driver |
|
|
|
Jul 29 2015, 07:43
|
Профессионал
    
Группа: Свой
Сообщений: 1 404
Регистрация: 11-03-11
Из: Минск, Беларусь
Пользователь №: 63 539

|
Приветствую. Есть плата с Virtex 7, надо забросить данные в ПК по PCIe. Ранее такого не делал и есь трудности. В FPGA собрана система: GPIO, Timer, PCIe Bridge (регистры GPIO и таймера смапированы на адресное пространство BAR0). Разбираюсь в написании модулей ядра Linux (использую Ubuntu 15.04). Пока реализовал управление GPIO при загрузке/выгрузке модуля в Linux. Хочу добавить прерывание. В FPGA линия interrupt от таймера заводится на PCIe-мост, он должен запихнуть его в систему. Для настройки прерывания в драйвере использую: Код irq_handler_t timer_isr(unsigned int irq, void *dev); struct pci_dev *pdev; char irq; ... pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq ); // для моей платы получаю irq = 11 request_irq(irq_num, timer_isr, IRQF_TRIGGER_RISING, "PCIe INT", (void *)pdev); ... Функция pci_read_config_byte() установит irq = 11.Далее настраиваю таймер, чтобы генерировал прерывание и стартую его. В syslog вижу следующее сообщение Цитата Jul 29 10:57:49 user-pc kernel: [ 797.188154] irq 16: nobody cared (try booting with the "irqpoll" option) Jul 29 10:57:49 user-pc kernel: [ 797.188158] CPU: 7 PID: 0 Comm: swapper/7 Tainted: G OE 3.19.0-25-generic #26-Ubuntu Jul 29 10:57:49 user-pc kernel: [ 797.188159] Hardware name: Gigabyte Technology Co., Ltd. To be filled by O.E.M./Z77P-D3, BIOS F7 08/24/2012 Jul 29 10:57:49 user-pc kernel: [ 797.188160] ffff880408dc92a4 ffff88041edc3e28 ffffffff817c4518 0000000000040400 Jul 29 10:57:49 user-pc kernel: [ 797.188162] ffff880408dc9200 ffff88041edc3e58 ffffffff810d0e86 ffff88041edc3e88 Jul 29 10:57:49 user-pc kernel: [ 797.188164] ffff880408dc9200 0000000000000000 0000000000000010 ffff88041edc3e98 Jul 29 10:57:49 user-pc kernel: [ 797.188165] Call Trace: Jul 29 10:57:49 user-pc kernel: [ 797.188166] <IRQ> [<ffffffff817c4518>] dump_stack+0x45/0x57 Jul 29 10:57:49 user-pc kernel: [ 797.188174] [<ffffffff810d0e86>] __report_bad_irq+0x36/0xd0 Jul 29 10:57:49 user-pc kernel: [ 797.188175] [<ffffffff810d1237>] note_interrupt+0x267/0x2b0 Jul 29 10:57:49 user-pc kernel: [ 797.188177] [<ffffffff810ce8c3>] handle_irq_event_percpu+0x133/0x1a0 Jul 29 10:57:49 user-pc kernel: [ 797.188179] [<ffffffff810ce971>] handle_irq_event+0x41/0x70 Jul 29 10:57:49 user-pc kernel: [ 797.188181] [<ffffffff810d1666>] handle_fasteoi_irq+0x86/0x140 Jul 29 10:57:49 user-pc kernel: [ 797.188182] [<ffffffff81017772>] handle_irq+0x22/0x40 Jul 29 10:57:49 user-pc kernel: [ 797.188184] [<ffffffff817ce55f>] do_IRQ+0x4f/0xf0 Jul 29 10:57:49 user-pc kernel: [ 797.188186] [<ffffffff817cc36d>] common_interrupt+0x6d/0x6d Jul 29 10:57:49 user-pc kernel: [ 797.188186] <EOI> [<ffffffff816663b5>] ? cpuidle_enter_state+0x65/0x160 Jul 29 10:57:49 user-pc kernel: [ 797.188190] [<ffffffff816663a1>] ? cpuidle_enter_state+0x51/0x160 Jul 29 10:57:49 user-pc kernel: [ 797.188192] [<ffffffff81666597>] cpuidle_enter+0x17/0x20 Jul 29 10:57:49 user-pc kernel: [ 797.188195] [<ffffffff810b7ce1>] cpu_startup_entry+0x311/0x3b0 Jul 29 10:57:49 user-pc kernel: [ 797.188198] [<ffffffff81049107>] start_secondary+0x197/0x1c0 Jul 29 10:57:49 user-pc kernel: [ 797.188199] handlers: Jul 29 10:57:49 user-pc kernel: [ 797.188202] [<ffffffff815bb130>] usb_hcd_irq Jul 29 10:57:49 user-pc kernel: [ 797.188204] Disabling IRQ #16 Получается PCIe мост при наличии прерывания от таймера выдаёт его на 16 линию. Для просмотра оборудования в системе установлен System Profiler, для моего устройства он показывает IRQ = 16 (рисунок). Если пытаюсь использовать 16 линию прерывания в функции request_irq, она выдаёт ошибку. Код request_irq(16, timer_isr, IRQF_TRIGGER_RISING, "PCIe INT", (void *)pdev); Вопрос, что я делаю не так? Почему в конфигурационной области моего устройства записано 11 (Interrupt Line), а прерывание срабатывает на 16 линии? Код модуля: CODE /* Necessary includes for device drivers */ #include <linux/init.h> //#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/pci.h> #include <linux/pci_regs.h> #include <linux/interrupt.h> //#include <asm/system.h> /* cli(), *_flags */ #include <asm/uaccess.h> /* copy_from/to_user */
#include "hardware.h" #include "timer.h"
MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Andrei Hres"); MODULE_DESCRIPTION("Driver for /dev/virtex7_pcie"); MODULE_SUPPORTED_DEVICE("virtex7_pcie");
#define __DEBUG_MODE #define DRV_NAME "virtex7_pcie"
#define BAR0 0 #define BAR1 1 #define BAR2 2 #define BAR3 3 #define BAR4 4 #define BAR5 5
int virtex7_pcie_init(void); void virtex7_pcie_exit(void); int virtex7_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent); void virtex7_pcie_remove(struct pci_dev *pdev);
irq_handler_t timer_isr(unsigned int irq, void *dev); int irq_status; char irq_num;
char *bar0_base; unsigned long *pio_base;
static struct pci_device_id virtex7_pcie_ids[] = { {PCI_DEVICE(0x10EE, 0x7014)}, {0,} };
MODULE_DEVICE_TABLE(pci, virtex7_pcie_ids);
static struct pci_driver virtex7_pcie_driver = { .name = DRV_NAME, .probe = virtex7_pcie_probe, .remove = virtex7_pcie_remove, .id_table = virtex7_pcie_ids, //#ifdef CONFIG_PM // .suspend = pcie_suspend, // .resume = pcie_resume, //#endif /* CONFIG_PM */ };
//static int __init virtex7_pcie_init(void) int virtex7_pcie_init(void) { printk(KERN_ALERT "*****************************************\n"); printk(KERN_ALERT "Inserting PCIe module\n"); return pci_register_driver(&virtex7_pcie_driver); }
//static void __exit virtex7_pcie_exit(void) void virtex7_pcie_exit(void) { printk(KERN_ALERT "Removing PCIe module\n"); pci_unregister_driver(&virtex7_pcie_driver); printk(KERN_ALERT "*****************************************\n"); }
int virtex7_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int status; int st; unsigned long data; char byte2; unsigned long bar0_start; unsigned long bar0_end; unsigned long *pio_reg;
printk(KERN_ALERT "Entering PCIe probe() function\n");
status = pci_enable_device(pdev);
if(status == 0) { printk(KERN_ALERT "PCIe device is succesfully enabled\n"); printk(KERN_ALERT "vendor: 0x%.4X\n", pdev->vendor); printk(KERN_ALERT "device: 0x%.4X\n", pdev->device);
pci_read_config_dword(pdev, 0, &data); printk(KERN_ALERT "Data from config space: 0x%.8X\n", data); pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq_num); printk(KERN_ALERT " PCI_INTERRUPT_LINE=%d\n", irq_num);
//irq_num = 16;
pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &byte2); printk(KERN_ALERT " PCI_INTERRUPT_PIN=%d\n", byte2);
bar0_start = pci_resource_start(pdev, BAR0); bar0_end = pci_resource_end(pdev, BAR0); printk(KERN_ALERT "BAR0 start address: 0x%.8X\n", bar0_start); printk(KERN_ALERT "BAR0 end address: 0x%.8X\n", bar0_end); printk(KERN_ALERT "BAR0 size: %u bytes = %u kB\n", bar0_end-bar0_start, (bar0_end-bar0_start+1)/1024);
//pio_reg = (unsigned long *) (bar0_start + 0x8000); //*pio_reg = 0xFFFFFFFF;
//----------------------------------------------------------------
st = pci_request_region(pdev, BAR0, "virtex7_pcie_bar0"); if(st == 0) { printk(KERN_ALERT "Request region success\n"); bar0_base = pci_iomap(pdev, BAR0, 0); printk(KERN_ALERT "bar0_base: 0x%.16X\n", (unsigned int) bar0_base);
pio_base = (unsigned long *) (bar0_base + 32768); printk(KERN_ALERT "pio_base: 0x%.16X\n", (unsigned int) pio_base);
//*(unsigned long *)(bar0_base + 32768) = 0x1; *pio_base = 0x1;
Timer_Init(bar0_base + TIMER_0_OFFSET_BYTES); Timer_IntClear(bar0_base + TIMER_0_OFFSET_BYTES); data = Timer_DbgRdCSR(bar0_base + TIMER_0_OFFSET_BYTES); printk(KERN_ALERT "Timer CSR: 0x%.8X\n", (unsigned int) data);
// free_irq(irq_num, (void *)pdev); irq_status = request_irq(irq_num, timer_isr, IRQF_TRIGGER_RISING/* | IRQF_SHARED*/, "PCIe INT", (void *)pdev); if(irq_status) printk(KERN_ALERT "request_irq() error\n"); else { printk(KERN_ALERT "request_irq() success\n"); Timer_Start(bar0_base + TIMER_0_OFFSET_BYTES); }
Timer_IntClear(bar0_base + TIMER_0_OFFSET_BYTES); data = Timer_DbgRdCSR(bar0_base + TIMER_0_OFFSET_BYTES); printk(KERN_ALERT "Timer CSR: 0x%.8X\n", (unsigned int) data); }
//pio_base = (unsigned char *) (addr_bar0 + 0x8000); //---------------------------------------------------------------- }
return status; }
void virtex7_pcie_remove(struct pci_dev *pdev) { *pio_base = 0;
printk(KERN_ALERT "Disable PCIe device\n"); pci_disable_device(pdev); pci_release_region(pdev, BAR0);
if(irq_status == 0) { free_irq(irq_num, (void *)pdev); Timer_Stop(bar0_base + TIMER_0_OFFSET_BYTES); printk(KERN_ALERT "Free PCI interrupt line #%d\n", irq_num); } }
module_init(virtex7_pcie_init); module_exit(virtex7_pcie_exit);
irq_handler_t timer_isr(unsigned int irq, void *dev) { static int int_counter = 0;
if(Timer_IntStatus(bar0_base + TIMER_0_OFFSET_BYTES)) { Timer_IntClear(bar0_base + TIMER_0_OFFSET_BYTES); }
printk(KERN_ALERT "Interrupt counter %d\n", int_counter); int_counter++;
return IRQ_NONE; }
Эскизы прикрепленных изображений
|
|
|
|
|
Jul 29 2015, 11:28
|
Профессионал
    
Группа: Свой
Сообщений: 1 404
Регистрация: 11-03-11
Из: Минск, Беларусь
Пользователь №: 63 539

|
Прерывание стало работать со следующими изменениями: Код request_irq(pdev->irq, timer_isr, IRQF_SHARED, "PCIe INT", (void *)pdev); // pdev->irq = 16 , ранее использовался неверный флаг IRQF_TRIGGER_RISING и значение pdev->irq = 16. Остался вопрос по номеру линии прерывания. В конфигурационной области PCIe моего устройства записано 11 (Interrupt Line), а прерывание срабатывает для линии №16. Нашёл, что при старте системы плате назначена линия прерывания №11, через некоторое время линию прерывания №11 занимает другое устройство (SMBus Controller). Плате назначается линия прерывания №16, которую она делит с USB Controller-ом, но номер линии прерывания (Interrupt Line) в конфигурационной области PCIe не меняется (может и меняется но функция pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq )устанавливает переменную irq = 11)? В struct pci_dev *pdev значение pdev->irq = 16, а если использовать pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq) получим irq = 11???
|
|
|
|
|
Jul 31 2015, 14:37
|
Профессионал
    
Группа: Свой
Сообщений: 1 404
Регистрация: 11-03-11
Из: Минск, Беларусь
Пользователь №: 63 539

|
Цитата(Tarbal @ Jul 31 2015, 14:45)  Насколько я понимаю вопрос. PCI имеет механизмы обработки прерываний даже если несколько устройств пользуется одним и тем же проводом для вызова прерывания. Много устройств прекрасно ладят используя общий провод для вызова прерываний. Да, так и есть - моё PCIe устройство делит Interrupt Line #16 c USB контроллером (при определении обработчика прерывания используется флаг IRQF_SHARED). Но в системе куча свободных линий и хотелось бы повесить его на отдельную. Цитата(Tarbal @ Jul 31 2015, 14:45)  Далее пойдут общие соображения и я не поручусь, что они точны, поскольку с PCI мало работал. Прерывание физически присоединено к проводу и задется при изготовлении устройства. Можно изготовить устройство так, что есть возможность выбора. Надо найти документацию на устройство и узнать если есть такая возможность. А как тогда объяснить что при старте система назначает моему устройству Interrupt Line #11, а потом (загружаю свой модуль в ядро) перебрасывает его на Interrupt Line #16? Как я пока понял прерывания для PCIe работают на уровне Transaction Layer, т.е. устройство PCIe передаёт сигнал прерывания посредством сообщений. Не совсем понимаю разницу между Legacy Interrupt, MSI Interrupt и MSI-X Interrupt, но вроде как речь везде идёт о транзакциях. Посмотрю, что там может быть ещё железного. Спасибо. Раньше драйвер немного неправильно конфигурировал таймер - прерывание срабатывало очень часто. Мышь, клава и всё остальное, что висело на USB контроллере с Interrupt Line #16, жутко тормозили. Счас подправил функции настройки таймера и он отлично делит 16 линию прерывания с USB контроллером. Вопрос, как происходит назначение устройству линии прерывания, остаётся актуальным. Буду благодарен, если кто подскажет.
|
|
|
|
|
Aug 6 2015, 06:12
|
Профессионал
    
Группа: Свой
Сообщений: 1 404
Регистрация: 11-03-11
Из: Минск, Беларусь
Пользователь №: 63 539

|
Как правильно со стороны ядра выделить память, чтобы моё устройство могло писать в неё? Пока сделал так: Код pc_buffer = kmalloc(pc_buffer_size, GFP_KERNEL); // выделяю буфер pc_buffer_phys = virt_to_phys(pc_buffer); // получаю физический адрес выделенной памяти Полученный физический адрес памяти (32 бита) записываю в регистр PCIe моста в Virtex 7 (старшие 32 бита записываю 0 т.к. возвращаемый адрес 32 бита). Пытаюсь из Xilinx SDK получить доступ к выделенному адресному пространству (через мост). Иногда всё проходит нормально, вижу память выделенную драйвером Linux (предварительно установил все байты буфера в определённое значение). Могу изменить из Xilinx SDK данные в выделенном буфере (в прерывании драйвер мониторит содержимое участка памяти буфера, и могу видеть, что данные меняются). Иногда всё работает как-то криво, либо вообще не могу видеть буфер памяти со стороны FPGA, либо оно подключилось к какому-то другому участку памяти (через мост пишу/читаю данные, т.е. какую-то память он видит, но даннае в буфере драйвера не меняются). Правильно ли я определяю физический адрес выделенной памяти?
|
|
|
|
|
Aug 6 2015, 07:20
|
Профессионал
    
Группа: Свой
Сообщений: 1 404
Регистрация: 11-03-11
Из: Минск, Беларусь
Пользователь №: 63 539

|
Последний вопрос снят. Посмотрел старую инфу, где Код unsigned long virt_to_phys( volatile void *address ); В новых исходниках Код #ifdef CONFIG_PHYS_ADDR_T_64BIT typedef u64 phys_addr_t; #else typedef u32 phys_addr_t; #endif
static inline phys_addr_t virt_to_phys(volatile void *address) { return __pa(address); } , как итог загонял в мост неверный адрес. Вопрос про прерывания остаётся актуальным, пока так и не понял, как система раздаёт линии прерываний. Добавил только разрешение MSI и система при загрузке модуля стала выделять отдельную линию для моего устройства.
|
|
|
|
|
Aug 6 2015, 09:14
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Система не может взять и выделить PCI-устройству любую линию прерывания, какую посчитает нужным, так как эта линия может схемотехнически тупо не проложена до процессора и аппаратных средств довести эту линию от PCI-устройства до процессора у данной модели материнской платы нет. Как разложены линии прерывания по плате знает только разработчик платы и сообщает эту информацию разработчику BIOS, который в свою очередь строит в BIOS таблицы роутинга для всех слотов PCI. В процессе старта и энумерации всех подключенных PCI-устройств BIOS раздаёт прерывания и прокладывает их до процессора известным ему аппаратным способом, после чего номер выделенного прерывания записывается в соответствующий конфиг-регистр. Этот регистр просто ячейка памяти для драйвера, который будет работать с устройством, то бишь произвольная перезапись номера прерывания не меняет выделенной линии и просто ведет к сбою логики работы драйвера. Операционная система может оставить роутинг PCI-прерываний, как его создал BIOS при загрузке, а может и перестроить по-своему, но опираясь на таблицы роутинга, размещённые в BIOS-e. По практике, поскольку PCI-прерывания работают по уровню и легко разделяются между устройствами, разработчики плат выделяют на PCI 2-3 линии IRQ и всё. Поэтому на уровне физики номер прерывания тот, который записан в регистре устройства и брать для подключения обработчика нужно именно его. Всё остальное, что показывает система - это виртуальные вектора, созданные самой системой в процессе старта драйверов, чтобы как-то разобраться в кипе устройств и отправить пришедшее прерывание IRQ 11 на нужный из 5 подключенных обработчиков.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Aug 6 2015, 11:01
|
Профессионал
    
Группа: Свой
Сообщений: 1 404
Регистрация: 11-03-11
Из: Минск, Беларусь
Пользователь №: 63 539

|
На рисунке Configuration Space для устройства PCIe есть поле Interrupt Line. Оно, как понимаю, доступно только для чтения. Можете пояснить, что это такое и за что оно отвечает? Если повесить обработчик прерывания на 11 линию (записана в поле Interrupt Line), то работать не будет. При загрузке модуля указатель на структуру struct pci_dev *pdev содержит номер линии прерывания pdev->irq отличный от того, что хранится в конфигурационной области PCIe (всегда = 11). Это будет либо 16 (MSI запрещены), либо 31 (разрешены MSI). Если обработчик привязан к этому номеру - работает. Откуда взялся номер 11 в конфигурационной области? Если его пишет система (или BIOS), то почему после переопределения номера линии прерывания он не переписывается на правильный? Драйвер virtex7. MSI запрещены cat /proc/interrupts CODE CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 0: 17 0 0 0 0 0 0 0 IO-APIC-edge timer 1: 2 0 0 0 0 0 0 0 IO-APIC-edge i8042 8: 1 0 0 0 0 0 0 0 IO-APIC-edge rtc0 9: 0 0 0 0 0 0 0 0 IO-APIC-fasteoi acpi 12: 2 0 0 0 0 1 0 1 IO-APIC-edge i8042 16: 11080 968 13363 494207 41182 4940 73084 4109705 IO-APIC 16-fasteoi ehci_hcd:usb3, virtex7 17: 66 176 1 2 11 897 3 17 IO-APIC 17-fasteoi snd_hda_intel 19: 10129 11616 4327 9923 15263 31670 14741 30747 IO-APIC 19-fasteoi ata_piix, ata_piix 23: 27 0 0 7 1 0 0 0 IO-APIC 23-fasteoi ehci_hcd:usb4 25: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 26: 17 87949 1 470 4 0 0 9 PCI-MSI-edge eth0 27: 28196 0 1 42 2 2 0 1 PCI-MSI-edge eth1 28: 12 0 0 0 3 0 0 0 PCI-MSI-edge mei_me 29: 27 103 1 1 11 313 20 7 PCI-MSI-edge snd_hda_intel 30: 1954797 699670 527638 508968 858144 423732 314299 277036 PCI-MSI-edge nouveau NMI: 68 57 54 49 66 51 38 69 Non-maskable interrupts LOC: 1477693 1742544 1765876 1910964 513009 525984 531958 2214784 Local timer interrupts SPU: 0 0 0 0 0 0 0 0 Spurious interrupts PMI: 68 57 54 49 66 51 38 69 Performance monitoring interrupts IWI: 4371 467 310 533 505 357 275 360 IRQ work interrupts RTR: 6 0 0 0 0 0 0 0 APIC ICR read retries RES: 134654 144649 116848 92117 45669 39876 36313 47002 Rescheduling interrupts CAL: 52294 87270 93843 89098 75381 80948 91265 91551 Function call interrupts TLB: 26135 26093 25448 23842 20976 23850 25294 17155 TLB shootdowns TRM: 0 0 0 0 0 0 0 0 Thermal event interrupts THR: 0 0 0 0 0 0 0 0 Threshold APIC interrupts MCE: 0 0 0 0 0 0 0 0 Machine check exceptions MCP: 50 50 50 50 50 50 50 50 Machine check polls HYP: 0 0 0 0 0 0 0 0 Hypervisor callback interrupts ERR: 0 MIS: 0
MSI разрешены cat /proc/interrupts CODE CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 0: 17 0 0 0 0 0 0 0 IO-APIC-edge timer 1: 2 0 0 0 0 0 0 0 IO-APIC-edge i8042 8: 1 0 0 0 0 0 0 0 IO-APIC-edge rtc0 9: 0 0 0 0 0 0 0 0 IO-APIC-fasteoi acpi 12: 2 0 0 0 0 1 0 1 IO-APIC-edge i8042 16: 11080 968 13363 535687 41182 4940 73084 4587454 IO-APIC 16-fasteoi ehci_hcd:usb3 17: 66 176 1 2 11 897 3 17 IO-APIC 17-fasteoi snd_hda_intel 19: 10321 11791 4942 10065 16048 32307 16633 31262 IO-APIC 19-fasteoi ata_piix, ata_piix 23: 27 0 0 7 1 0 0 0 IO-APIC 23-fasteoi ehci_hcd:usb4 25: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 26: 17 88916 1 470 4 0 0 9 PCI-MSI-edge eth0 27: 31025 0 1 42 2 2 0 1 PCI-MSI-edge eth1 28: 12 0 0 0 3 0 0 0 PCI-MSI-edge mei_me 29: 27 103 1 1 11 313 20 7 PCI-MSI-edge snd_hda_intel 30: 2145752 747852 565542 547596 947147 459822 339967 308636 PCI-MSI-edge nouveau 31: 10 0 0 0 0 1 0 0 PCI-MSI-edge virtex7 NMI: 74 61 58 53 68 53 40 73 Non-maskable interrupts LOC: 1630591 1926949 1962790 2112890 550816 568671 579103 2443575 Local timer interrupts SPU: 0 0 0 0 0 0 0 0 Spurious interrupts PMI: 74 61 58 53 68 53 40 73 Performance monitoring interrupts IWI: 4389 469 314 604 508 361 279 1490 IRQ work interrupts RTR: 6 0 0 0 0 0 0 0 APIC ICR read retries RES: 140695 148620 119993 95453 48181 41309 37573 50718 Rescheduling interrupts CAL: 54810 89545 96134 90298 77653 83242 93564 92749 Function call interrupts TLB: 27526 27963 26448 26049 22528 24252 25863 18098 TLB shootdowns TRM: 0 0 0 0 0 0 0 0 Thermal event interrupts THR: 0 0 0 0 0 0 0 0 Threshold APIC interrupts MCE: 0 0 0 0 0 0 0 0 Machine check exceptions MCP: 54 54 54 54 54 54 54 54 Machine check polls HYP: 0 0 0 0 0 0 0 0 Hypervisor callback interrupts ERR: 0 MIS: 0
Эскизы прикрепленных изображений
|
|
|
|
|
Aug 8 2015, 15:10
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Цитата(doom13 @ Aug 6 2015, 15:01)  На рисунке Configuration Space для устройства PCIe есть поле Interrupt Line. Оно, как понимаю, доступно только для чтения. Можете пояснить, что это такое и за что оно отвечает? Насколько я помню, только для чтения поле Interrupt Pin, оно просто определяет, на какую из 4 линий INTA#-INTD# выведена линия прерывания данной платы. Исторически сложилось так, что при конструировании материнских плат линии запросов прерываний от слота к слоту идут со сдвигом, то есть в 1-м слоте PCI INTA подключается к INTA, INTB->INTB, INTC->INTC, INTD->INTD, во 2-м слоте уже INTA->INTB, INTB->INTC, INTC->INTD, INTD->INTA и т. д. Поэтому, если все слоты будут заполнены платами PCI, у которых прерывание выведено на INTA - они равномерно распределятся по всем линиям INTA-INTD. Если какая-то плата окажется с линией INTB - ничего страшного не произойдет, она разделит с другой платой одну линию прерывания. Поэтому конструкторы периферийных плат PCI тоже не мудруствуют лукаво и в 99% случаев используют INTA. Все 4 линии идут в мост, и уже BIOS (или ОС) решает, выделить каждой линии INTA#-INTD# по своему IRQ или всех повесить на одно IRQ. Задача BIOS - изолировать прерывания PCI-устройств от других (ISA, Legacy), которые не могут быть разделены между несколькими устройствами. Interrupt Line регистр - это просто байтовая ячейка памяти, в которую после энумерации энумератор (BIOS или ОС) записывает выделенную для устройства и проложенную до процессора линию IRQ, чтобы драйвер мог разобраться, к чему цеплять обработчик прерывания. Никакого физического смысла, кроме ячейки памяти, поле Interrupt Line не несет. С MSI история примерно та же, только вместо линий INTA#-INTD# используются сообщения с соответствующим номером 1-4 для совместимости, ну и с другими номерами для доп. возможностей.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Aug 10 2015, 09:43
|
Профессионал
    
Группа: Свой
Сообщений: 1 404
Регистрация: 11-03-11
Из: Минск, Беларусь
Пользователь №: 63 539

|
Цитата(gerber @ Aug 8 2015, 18:10)  Interrupt Line регистр - это просто байтовая ячейка памяти, в которую после энумерации энумератор (BIOS или ОС) записывает выделенную для устройства и проложенную до процессора линию IRQ, чтобы драйвер мог разобраться, к чему цеплять обработчик прерывания. Никакого физического смысла, кроме ячейки памяти, поле Interrupt Line не несет. Каким тогда образом, если в Interrupt Line записано 11 (при выполнении функции pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq_num) получаю irq_num = 11), прерывание работает на линии № 16 (если MSI запрещены), либо № 31 (если MSI разрешены)?
|
|
|
|
|
Aug 10 2015, 16:22
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Цитата(doom13 @ Aug 10 2015, 13:43)  Каким тогда образом, если в Interrupt Line записано 11 (при выполнении функции pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq_num) получаю irq_num = 11), прерывание работает на линии № 16 (если MSI запрещены), либо № 31 (если MSI разрешены)? Возможно, это "происки" операционной системы, то есть номера 16 и 31 являются виртуальными номерами прерываний. Так как на одном физическом номере IRQ может висеть несколько PCI-устройств - ОС должна как-то различать, какой драйвер к какому устройству подключается. Для этого ОС "придумывает" свои номера устройствам, а сама "втихаря" подвешивает свой обработчик на IRQ 11. Как только произойдёт IRQ 11 - система передает управление всем подключенным обработчикам (возможно, с приоритетом), пока кто-то из обработчиков не сообщит, что, мол, да, это моё устройство сгенерировало прерывание, и я его обработал. Так система поймет, какое именно устройство сгенерировало прерывание IRQ 11.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Aug 13 2015, 11:51
|
Профессионал
    
Группа: Свой
Сообщений: 1 404
Регистрация: 11-03-11
Из: Минск, Беларусь
Пользователь №: 63 539

|
В драйвере есть операции работы с файлом драйвера ( virtex7board_ioctl добавил недавно): Код static const struct file_operations virtex7board_fops= { .owner = THIS_MODULE, .open = virtex7board_open, .release = virtex7board_release, .read = virtex7board_read, .write = virtex7board_write, .unlocked_ioctl = virtex7board_ioctl, }; Для работы с файлом драйвера из user space использовал fopen, fwrite, fread, fclose и всё было гуд. Теперь добавил в модуль драйвера поддержку ioctl. Тут появились какие-то ошибки. Функция fread как-то неправильно работает, она вызывает virtex7board_ioctl и virtex7board_read и передаёт неправильный размер данных для чтения в virtex7board_read (4096 байт вместо 16). Попробовал использовать open, read, write, close - тут всё нормально. ???
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|