Обработка аппаратных прерываний внешнего таймера

Вопросы программного кода и архитектуры Linux

Модератор: Olej

Vladmir
Интересующийся
Сообщения: 3
Зарегистрирован: 28 янв 2013, 17:30
Контактная информация:

Обработка аппаратных прерываний внешнего таймера

Непрочитанное сообщение Vladmir » 28 янв 2013, 19:10

Здравствуйте уважаемые форумчане.
Есть железка Helios (HLV800-256AV) шина PC/104 (ISA) на которой имеется аппаратный таймер. Пишу драйвер, в котором мне необходимо обработать прерывания по таймеру с максимально возможной скоростью.
Версия ядра 2.6.23. Собственно для таймера можно задать частоту с которой он будет генерировать прерывания по линии IRQ=5 до 100 КГц. Ранее мне не приходилось писать драйвера под линукс, посему извините если что не так пишу.

Код: Выделить всё


//Вот обработчик
static irqreturn_t my_interrupt( int irq, void *dev_id ) 
{
    irq_counter++;           // Счетчик событий таймера
    outb(0x04,BAHelios); // Сброс состояния прерывания таймера для платы Helios
    return IRQ_HANDLED; 
}
...
//При инициализации модуля назначаю обработчик
result = request_irq(irq, &my_interrupt, IRQF_TIMER, "my_interrupt", &my_dev_id);

Состояние обработчика прерываний смотрю через cat /proc/interrupts. На линии 5 кроме моего обработчика никто не висит. Собственно модуль ядра по обработке данного прерывания работает на частотах до 20КГц (те обработчик вызывается через каждые 50 микросекунд). Обработчик собственно ничего не делает кроме увеличения счетчика и сброса прерывания на плате. При увеличении частоты таймера скажем до 40КГц (25 микросекунд), обработчик прерываний выполняется какое-то время и после чего перестает вызываться (cat /proc/interrupts счетчик событий останавливается), при этом другие функции модуля ядра работают (IOCTL READ итп). После этого обработчик прерываний как-будто бы вообще перестает отрабатывать. Посему возникают вопросы:

1. При каких условиях ядро(а может что то еще) может прервать обработку прерываний в модуле ядра? При этом никаких ошибок не выдается.
2. Как оценить максимально возможную частоту обработки прерываний?
3. Можно ли гарантировать что все поступающие прерывания от аппаратуры(внешнего таймера) будут обработаны? Может есть какие программные способы которые позволят избежать подобного "зависания" (пусть лучше будут необработанные прерывания чем полная остановка их обслуживания)?
4. Что произойдет если нагрузка на обработчик будет возрастать и по времени будет приближаться к частоте вызова обработчика?

Буду рад любым ответам по данным вопросам.
С уважением Владимир.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Обработка аппаратных прерываний внешнего таймера

Непрочитанное сообщение Olej » 29 янв 2013, 03:14

Vladmir писал(а):Собственно модуль ядра по обработке данного прерывания работает на частотах до 20КГц (те обработчик вызывается через каждые 50 микросекунд). Обработчик собственно ничего не делает кроме увеличения счетчика и сброса прерывания на плате.
Идея каким-то образом использовать прерывания в микросекундном диапазоне - это сама по себе идея порочная, для Linux... это в порядке предварительных соображений (ничего путнего из этого не получится).
Vladmir писал(а): При увеличении частоты таймера скажем до 40КГц (25 микросекунд), обработчик прерываний выполняется какое-то время и после чего перестает вызываться (cat /proc/interrupts счетчик событий останавливается), при этом другие функции модуля ядра работают (IOCTL READ итп). После этого обработчик прерываний как-будто бы вообще перестает отрабатывать. Посему возникают вопросы:
Такого быть не может. Поэтому ищите какое-то другое объяснение наблюдаемому.
Vladmir писал(а): 1. При каких условиях ядро(а может что то еще) может прервать обработку прерываний в модуле ядра? При этом никаких ошибок не выдается.
Ни при каких условиях.
(Разве что на время обработки "нижней половины" прерывания, когда прерывания по этой линии IRQ запрещены)
Vladmir писал(а): 2. Как оценить максимально возможную частоту обработки прерываний?
Нет такого понятия "максимально возможная частота обработки прерываний"

Кроме того, учитывайте, что ваш IRQ 5 - один из самых низкоприоритетных в системе. Если у вас есть что-то, например, сеть, или HDD, то они будут вытеснять ваш обработчик IRQ 5, чем значительно ухудшать его соотношение производительность/частота ... ну, и ещё у вас есть системный таймер, который выше приоритетом, возбуждается не часто (1мс.), но выполняет очень много работы, чем отодвигает ваш IRQ 5.
Vladmir писал(а): 3. Можно ли гарантировать что все поступающие прерывания от аппаратуры(внешнего таймера) будут обработаны? Может есть какие программные способы которые позволят избежать подобного "зависания" (пусть лучше будут необработанные прерывания чем полная остановка их обслуживания)?
Обработаны будут только все те прерывания, для которых прерывания на линии будут разрешены.
Разрешаться прерывания с этой линии будут только после return IRQ_HANDLED;
И только для текущего процессора в SMP. У вам SMP, или нет?

Картина будет существенным образом зависеть от того, как запрограммирована реакция у вас контроллера прерываний: а). по уровню, б). по фронту.
Vladmir писал(а): 4. Что произойдет если нагрузка на обработчик будет возрастать и по времени будет приближаться к частоте вызова обработчика?
Как минимум, прерывания начнут пропускаться. Но может быть и что похуже. Нужно смотреть детальнее - это тоже может радикально зависеть от того, прерывания у вас реагируют по уровню или по фронту (как запрограммирован контроллер).

P.S. Если указываете свою железку (HLV800-256AV) - указывайте и URL производителя для этой железки.
Посмотрите внимательнее что там за контроллер прерываний стоит.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Обработка аппаратных прерываний внешнего таймера

Непрочитанное сообщение Olej » 29 янв 2013, 03:22

Vladmir писал(а): Буду рад любым ответам по данным вопросам.
Вы смотрели вот это: проект книги: "Модули ядра Linux".
А ещё лучше вот это: Драйверы и модули ядра Linux.

Спрашиваю для того, чтобы представлять: какие способы вы уже перепробовали?

Vladmir
Интересующийся
Сообщения: 3
Зарегистрирован: 28 янв 2013, 17:30
Контактная информация:

Re: Обработка аппаратных прерываний внешнего таймера

Непрочитанное сообщение Vladmir » 29 янв 2013, 09:48

P.S. Если указываете свою железку (HLV800-256AV) - указывайте и URL производителя для этой железки.
Посмотрите внимательнее что там за контроллер прерываний стоит.
К сожалению в мануале на железку не описано какой контроллер используется. Хотя предполагаю что он - I8259. Об этом говорит proc/interrupts.

Код: Выделить всё

[root@DSC_DKLinux> cat /proc/interrupts
           CPU0
  0:     342679    XT-PIC-XT        timer
  1:          8    XT-PIC-XT        i8042
  2:          0    XT-PIC-XT        cascade
  5:     901625    XT-PIC-XT        HeliosTimer
  6:          0    XT-PIC-XT        ehci_hcd:usb1
  9:      18581    XT-PIC-XT        ehci_hcd:usb2, ohci_hcd:usb3, eth0
 12:        121    XT-PIC-XT        i8042
 14:      68533    XT-PIC-XT        ide0
 15:          0    XT-PIC-XT        ohci_hcd:usb4
NMI:          0
ERR:          0
Процессор Vortex86DX архитектура x86. Те система не многопроцессорная, если я правильно понимаю аббревиатуру SMP.
http://docs2.diamondsystems.com/files/b ... Manual.pdf
http://docs2.diamondsystems.com/products/helios
Картина будет существенным образом зависеть от того, как запрограммирована реакция у вас контроллера прерываний: а). по уровню, б). по фронту.
Честно говоря не заморачивался как запрограммирован контроллер прерываний в линуксе по уровню или по фронту. Ведь прерывания доходят до моего обработчика :

Код: Выделить всё

5:     901625    XT-PIC-XT        HeliosTimer
В заголовочном файле interrupt.h конечно есть флаги по заданию реакции, однако я их не использовал и при этом все работает.

Код: Выделить всё

#define IRQF_TRIGGER_NONE	0x00000000
#define IRQF_TRIGGER_RISING	0x00000001
#define IRQF_TRIGGER_FALLING	0x00000002
#define IRQF_TRIGGER_HIGH	0x00000004
#define IRQF_TRIGGER_LOW	0x00000008
#define IRQF_TRIGGER_MASK	(IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
				 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE	0x00000010
Задавал только флаги IRQF_TIMER и IRQF_DISABLED. И чесно говоря разницы не заметил.
result = request_irq(irq, &my_interrupt, IRQF_DISABLED /*IRQF_TIMER*/, "HeliosTimer", &my_dev_id);
1. Ни при каких условиях.
(Разве что на время обработки "нижней половины" прерывания, когда прерывания по этой линии IRQ запрещены)
С обработкой "нижней половины" пока не разобрался поэтому и такой простой обработчик прерывания.
Vladmir писал(а):При увеличении частоты таймера скажем до 40КГц (25 микросекунд), обработчик прерываний выполняется какое-то время и после чего перестает вызываться (cat /proc/interrupts счетчик событий останавливается), при этом другие функции модуля ядра работают (IOCTL READ итп). После этого обработчик прерываний как-будто бы вообще перестает отрабатывать.
Такого быть не может. Поэтому ищите какое-то другое объяснение наблюдаемому.
Однако есть. Объяснение найти пока не получается.

Выкладываю полный текст модуля и тестовой программы

Код: Выделить всё

/*
 * HKM - Helios Kernel Module
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/fs.h>                   /* file_operations */
#include <asm/io.h>
#include <asm/uaccess.h>                /* copy_to_user */

#include "hkm.h"

static int irq = 5;                     /* Interrupt number IRQ */
static int my_dev_id =0;                /* Unique ID when registering interrupts */
static unsigned int irq_counter = 0;    /* Event counter interrupts */
static int hkm_major = 251;
#define BAHelios 0x280                  /* Base address of the board Helios in the system */

/* Interrupt handler */
static irqreturn_t my_interrupt( int irq, void *dev_id ) 
{
    irq_counter++;
    outb(0x04,BAHelios);                //Clear the interrupt on the board Helios
    return IRQ_HANDLED;
}

/*
 * hkm_open() - Handle open(/dev/hkm)
 */
static int hkm_open(struct inode *node, struct file *filep)
{
//	printk("hkm: hkm_open() \n");
    return 0;		
}

/*
 * hkm_ioctl() - Handle ioctl(/dev/hkm) 
 */
static int hkm_ioctl(struct inode *node, struct file *filep, unsigned int cmd, 
					unsigned long arg)
{
    int rc;
	if ( cmd == IOCTL_DSCUDKP_CONF ) 
        {
        rc = copy_to_user((void *)arg, (void *)&irq_counter, sizeof(unsigned int/*irq_counter*/));
		if ( rc ) {
			printk("hkm: hkm_ioctl(): IOCTL_DSCUDKP_CONF copy_to_user() failed with rc=%d\n", rc);
			return -EFAULT;
		}
        }
	return 0;		
}

/* 
 * Initialize file_operations structure used to attach to events on /dev/hkm 
 * such as open/close.
 */
static struct file_operations hkm_fops = {
	open:  hkm_open,
//	release:  hkm_release,
	ioctl:  hkm_ioctl,
//	read:  hkm_read,
};

/* Initialize (load) the module */
static int __init my_init( void ) 
{

  	int rc;
	rc = register_chrdev(hkm_major, "hkm", &hkm_fops);
    if (rc<0)
    {
        printk(KERN_INFO "short: can't get major number \n");
		return rc;
    }

    irq_counter = 0;
    outb(0,BAHelios+4);                 //Read/Write Interrupt / Counter Control
    
    int result;
    result = request_irq(irq, &my_interrupt, IRQF_DISABLED /*IRQF_TIMER*/, "HeliosTimer", &my_dev_id);
    if (result < 0)
    {
      printk(KERN_ALERT "Error to request IRQ%d : %u\n", irq, result);
      return -1;
    }

    // Start interrupt from borad
    unsigned char temp;
    temp = inb(BAHelios+11);  
    outb(temp & 0x7F,BAHelios+11); //Bit is cleared DIOCTR=0
    //printk( KERN_INFO "hex BAHelios+11= %x \n", temp);
    
    //Load divider in counter 1 = 10000 for 1KGH
//    outb(0x10 ,BAHelios+12);          //Value (divider) for load
//    outb(0x27 ,BAHelios+13);

    //Load divider in counter 1 = 1000 for 10KGH 100mkc(working with IRQF_DISABLED (5-tiks timer printf test.c))
//    outb(0xE8 ,BAHelios+12);            //Value (divider) for load
//    outb(0x03 ,BAHelios+13);
  
   //Load divider in counter 1 = 625 for 16KGH 62mkc(working with IRQF_DISABLED (3-tiks timer printf test.c))
//    outb(0x71 ,BAHelios+12);            //Value (divider) for load
//    outb(0x02 ,BAHelios+13);

   //Load divider in counter 1 = 500 for 20KGH 50mkc(working with IRQF_DISABLED (2-tiks timer printf test.c 25mkc))
//    outb(0xF4 ,BAHelios+12);            //Value (divider) for load
//    outb(0x01 ,BAHelios+13);

   //Load divider in counter 1 = 400 for 25KGH 40mkc(not working)
//    outb(0x90 ,BAHelios+12);            //Value (divider) for load
//    outb(0x01 ,BAHelios+13);

   //Load divider in counter 1 = 250 for 40KGH 25mkc
    outb(0xFA ,BAHelios+12);            //Value (divider) for load
    outb(0x00 ,BAHelios+13);


   //Load divider in counter 1 = 200 for 50KGH 20mkc
//    outb(0xC8 ,BAHelios+12);            //Value (divider) for load
//    outb(0x00 ,BAHelios+13);

    //Load Counter 1
    outb(0x82 ,BAHelios+15); 
	
    outb(0x04 ,BAHelios+4);             //Frequency 10Mgz and Interrupt handling Timer 1
    outb(0x84 ,BAHelios+15);            //Enable count for the timer
   
	printk( KERN_INFO "Successfully loading ISR handler on IRQ %u\n", irq );

    return 0;
}

/* Finalizing (unload) module */
static void __exit my_exit( void ) 
{
	unregister_chrdev(hkm_major, "hkm");

    outb(0x88 ,BAHelios+15);            //Disable count for the timer
	free_irq( irq, &my_dev_id );
	printk( KERN_INFO "Successfully unloading, irq_counter = %u\n", irq_counter);
}

module_init( my_init );
module_exit( my_exit );
MODULE_LICENSE("GPL");
И тестовая прога которая которая получает к себе значение счетчика

Код: Выделить всё

/* 
 * test.c - test module KHM
 *
*/ 

#include <stdio.h> 
#include "hkm.h"
int main(int argc,char *argv[]) 
{ 
unsigned int kernelclock;
    int fd;
    fd = open("/dev/hkm", "r");
    if (fd<0){
        printf("Error to open\n");
    };
    
    while (1){
    if (ioctl(fd, IOCTL_DSCUDKP_CONF, &kernelclock)<0){printf ("Error \n");}
    printf ("Cur Clock Timer from driver %u\n",kernelclock);
    }
    close(fd); 

return(0); 
}

Vladmir
Интересующийся
Сообщения: 3
Зарегистрирован: 28 янв 2013, 17:30
Контактная информация:

Re: Обработка аппаратных прерываний внешнего таймера

Непрочитанное сообщение Vladmir » 29 янв 2013, 11:15

Спасибо большое Olej-у за советы.

В общем Olej был прав что:
Такого быть не может.
. Те не вызываться обработчик прерывания.
Решил проверить линию IRQ5 с помощью осциллографа. И вот что увидел в начале (после загрузки модуля) прерывания генерируются (вижу прям в точности ту частоту которую задаю). Но после прохождения некоторого времени сигнал прерывания остается в верхнем уровне. По-сути получается что Линукс тут не причем он все делает правильно, те нет сигнала он и не вызывает обработчик прерывания. Так же заметил что в момент когда сигнал прерывания исчезает (те остается в вернем уровне) происходит какое-то обращение к диску. В общем нужно копать в эту сторону. А может это связано с тем что мой обработчик прерывается(?) как раз в строке записи в порт устройства значения для сброса прерывания на плате (outb(0x04,BAHelios);) более приоритетным прерыванием и поэтому сигнал(физический) не переходит с верхнего уровня в нижний.

В общем нужно думать. Пока мыслей нет, но есть куда копать.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Обработка аппаратных прерываний внешнего таймера

Непрочитанное сообщение Olej » 29 янв 2013, 13:34

Vladmir писал(а): К сожалению в мануале на железку не описано какой контроллер используется. Хотя предполагаю что он - I8259. Об этом говорит proc/interrupts.

Код: Выделить всё

[root@DSC_DKLinux> cat /proc/interrupts
           CPU0
  0:     342679    XT-PIC-XT        timer
  1:          8    XT-PIC-XT        i8042
  2:          0    XT-PIC-XT        cascade
  5:     901625    XT-PIC-XT        HeliosTimer
  6:          0    XT-PIC-XT        ehci_hcd:usb1
  9:      18581    XT-PIC-XT        ehci_hcd:usb2, ohci_hcd:usb3, eth0
 12:        121    XT-PIC-XT        i8042
 14:      68533    XT-PIC-XT        ide0
 15:          0    XT-PIC-XT        ohci_hcd:usb4
NMI:          0
ERR:          0
Это у вас типичная картина старого контроллера I8259. Это PIC, не APIC.
(или настолько старое ядро Linux?! :-o )
Vladmir писал(а): Процессор Vortex86DX архитектура x86. Те система не многопроцессорная, если я правильно понимаю аббревиатуру SMP.
http://docs2.diamondsystems.com/files/b ... Manual.pdf
http://docs2.diamondsystems.com/products/helios
Где-то (в обсуждениях QNX, кажется, они гораздо лучше разбираются в железках ;-) ) обсуждалось, что эти именно процессоры с какими-то придурями ;-)
Vladmir писал(а):
Картина будет существенным образом зависеть от того, как запрограммирована реакция у вас контроллера прерываний: а). по уровню, б). по фронту.
Честно говоря не заморачивался как запрограммирован контроллер прерываний в линуксе по уровню или по фронту. Ведь прерывания доходят до моего обработчика :
Доходят они "до поры до времени" ;-)
... и тут важнейшим есть не то, как они доходят, а то, как они уходят ;-)
Я так предполагаю (это возможно так):
- у вас стоит прерывание по уровню...
- если вы не успеваете завершить обработку до прихода следующего...
- то уровень запроса IRQ уже не сбрасывается, и следующие прерывания не обрабатываются.
- возможно, есть смысл иметь прерывания по фронту, тогда вы будете пропускать прерывания когда не успеваете (а что с ними ещё делать?), но не будете терять обработку прерывания...

Вы обязательным образом разберитесь с разницей (с последствиями) в обработке IRQ по уровню и по фронту!
Самым внятным и понятным образом это описано в книге: Введение в QNX Neutrino. Руководство для разработчиков приложений реального времени
Изображение
Её достаточно много в Интернет: найдите и скачайте.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Обработка аппаратных прерываний внешнего таймера

Непрочитанное сообщение Olej » 29 янв 2013, 13:53

Vladmir писал(а):
Такого быть не может. Поэтому ищите какое-то другое объяснение наблюдаемому.
Однако есть. Объяснение найти пока не получается.
Я не просто так сказал голословно. ;-) Я достаточно много поработал над модулями линий связи E1/T1/J1 для адаптации их под интерфейс DAHDI для PBX FreeSWITCH. Там весь обмен синхронный и синхронизируется таймерами от плат E1/T1/J1. ... и на сейчас эти модули работают бессменно годами во многих-многих экземплярах ;-)
Так что я уверен, что всё должно работать.
Vladmir писал(а): Выкладываю полный текст модуля и тестовой программы
Я, как вы понимаете, не могу воспроизвести работу вашего модуля, т.к. это специфические порты и механизм, как я понимаю?:

Код: Выделить всё

#define BAHelios 0x280                  /* Base address of the board Helios in the system */
Но по поводу кода модуля (архитектуры), я позволю высказать такие соображения:
- зачем вам ioctl()? это сложный и ненадёжный механизм ... кроме того, он считается устаревшим...
- если вам так хочется, то почему вам не считывать ваш счётчик тиков операцией read() для того же устройства /dev/hkm ?
- но ещё лучше - это сделать отображение вашего счётчика тиков в /proc, а ещё лучше (современнее ;-) ) в /sys - и считывать его оттуда любой POSIX командой.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Обработка аппаратных прерываний внешнего таймера

Непрочитанное сообщение Olej » 29 янв 2013, 14:03

Vladmir писал(а):

Код: Выделить всё

//Вот обработчик
static irqreturn_t my_interrupt( int irq, void *dev_id ) 
{
    irq_counter++;           // Счетчик событий таймера
    outb(0x04,BAHelios); // Сброс состояния прерывания таймера для платы Helios
    return IRQ_HANDLED; 
}
Меня настораживает в вашем примере строка:

Код: Выделить всё

    outb(0x04,BAHelios); // Сброс состояния прерывания таймера для платы Helios
Если это требуемый сброс для самого специфического таймера - это одно дело и это необходимо (возможно).
Но если это аппаратное восстановление состояния контроллера прерываний, то его делать не нужно: Linux, распознав тип контроллера прерываний, должен сам восстанавливать его состояние по return IRQ_HANDLED, и делать это лучше вас ;-)
Vladmir писал(а): Версия ядра 2.6.23.
Очень старое ядро. Поддержка новых чипов (в том числе контроллеров прерываний, тамеров и т.п.) очень сильно развивались.
На время отработки поставьте свежий Linux (я бы советовал Fedora 17) ... а если вам так уж принципиально нужно 2.6.23, то потом вернётесь к нему.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Обработка аппаратных прерываний внешнего таймера

Непрочитанное сообщение Olej » 29 янв 2013, 14:12

Vladmir писал(а): Есть железка Helios (HLV800-256AV) шина PC/104 (ISA) на которой имеется аппаратный таймер.
Я не обратил внимание сразу на "шина PC/104 (ISA)".
Для ISA прерывания могут быть только по фронту, и не могут быть разделяемые.

Ответить

Вернуться в «Linux изнутри»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 17 гостей