linux interrupt handler 가 do-while 문으로 만들어지면 그건 thread 아닌가요?

chxooi의 이미지


thread 로 돌면서 for 문으로 체크하면

어차피 polling 이나 , interrupt 나 같은거 아닐까요??


8250.c 를 보면

ret = request_irq(up->port.irq, serial8250_interrupt,
irq_flags, "serial", i);

static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
{
do { iir = serial_in(up, UART_IIR);)} } while (l != end);

}

로 뜹니다.

만약 이렇다면 thread 나 polling 으로 해당 레지트 체크하는거랑 뭐가 다른지 모르겠습니다.

linux interrupt handler 뭐가 다른건가요 thread 나 polling 과 뭐가 다른건가요?

bushi의 이미지

뭘 보고 어떤 생각을 하셨길래 이런 이해하기 힘든 글을 쓰셨는지는 모르겠지만,
보셨던 코드를 단순화시키면 이런 식입니다.

static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
{
...
        struct list_head *l, *end = NULL;
...
        spin_lock(&i->lock);
 
        l = i->head;
 
        do {
...
               up = list_entry(l, struct uart_8250_port, list);
 
               iir = serial_in(up, UART_IIR);
...
               if (!(iir & UART_IIR_NO_INT)) {
...
                        end = NULL;
...
               } else if (end == NULL)
                        end = l;
...
               l = l->next;
 
               if (l == i->head) {
...
                        break;
               }
...
        } while ( l != end);
...
}

shared irq 에 대한 인터럽트 핸들러를 어떤 식으로 만들어야 하는지,
커널의 doubly linked list 를 어떤 식으로 이용하는지,
이 두 가지에 대한 이해가 없으면 코드 해석은 불가능합니다.
이 코드를 보고 "이건 쓰레드에서 레지스터를 읽어대며 폴링하는 것과 똑같잖아?" 라고 생각하셨다면,
올 여름 좀 덥게 보내실 각오 하셔야할 겁니다.

OTL

chxooi의 이미지

double linked list 말구요...

만약 똑같은 function을

do { } while(1)

로만들었다면, 이건 thread 나 polling 이나 같은거겠죠???

더불어 하나 질문...

만약 do while 문이 아니라면

그냥 해당 serial8250_interrupt 핸들러에

printk 만한줄넣었다면, irq 몇번째거든지, 공유된 어디선가 인터럽트가 뜬다면, 해당 printk 가 동작해서 printk 문이 찍혀야 할까요?

올여름 더워질 준비중입니다. ^^ 화이팅..

bushi의 이미지

좀 더워질 각오를 하셨다면 :)

질문을 올리고 답글을 얻었다면 그 답글에 집중하세요.
자신의 생각을 반추하고, 그걸 기준으로 답글을 평가하거나 왜곡시켜 생각하시지 말고.

thread+polling 방식은 interrupt 방식과 최원거리 대척점에 있습니다.

thread+polling 방식에선 do {} while (1) 외의 다른 방법이 없으며,
interrupt 방식에선 do {} while (1) 을 제외한 그 어떤 방법이라도 상관없습니다.

리눅스 커널의 shared irq 핸들링은 단순하고 효율적으로 짜여져 있습니다.
low level 에선 shared irq 인지 아닌지 신경쓰지 않습니다.
어떤 irq 가 발생하면 그 irq 에 연결된 모든 핸들러를 순차적으로 호출합니다.
발생한 irq 가 내것인지 남의 것인지 판단하는 것은 각 핸들러의 몫입니다.
그러므로 shared irq 를 사용하는 인터럽트 핸들러는 컨트롤러의 인터럽트 관련 레지스터를 뒤져서
이 irq 발생이 자기가 담당한 컨트롤러 때문인지 아닌지 먼저 확인해야 합니다.
자기 것이 아니라면 IRQ_NONE 을 리턴하고 바로 종료해야 합니다.

살펴보신 코드에서 do {} while () 이 사용된 이유는 list 탐색때문입니다.
list 를 탐색하지 않으면 shared irq multi-port UART 를 처리할 수 없습니다.
linked list 를 사용한 이유는 순차탐색만 필요하기 때문이고,
doubly linked 를 사용한 이유는 삽입/삭제에 빠르게 대응하기 위해서입니다.
do {} while () 로 리스트를 탐색한 이유는 실행코드를 간단히 만들기 위해서죠.
list_for_each_entry() 라는 macro 는 for() 문을 사용해서 복잡하게 만들어져 있습니다.

마지막,
인터럽트 핸들러에 printk() 가 있었고, 인터럽트가 발생했다면, printk() 는 동작합니다.
UART 드라이버의 인터럽트 핸들러에 아무 조건 없이 저런 짓을 했고,
serial console 을 사용 중이라면,
무한루프겠죠. 이게 궁금하신 건가요 ?

+

혹시 interrupt 방식이 아니라,
kernel thread 를 만들거나해서 polling 방식으로 UART 를 다루고 싶다는 뜻이라면,
불가능하진 않습니다.
I/O 속도가 아주 느린 경우(UART 컨트롤러에 내장된 Rx FIFO 가 overflow 되지 않는 정도)거나
스펙에 따라 h/w flow control 을 충실히 구현한 경우라면요.
보고 계신 인터럽트 핸들러 코드 중

if (!(iir & UART_IIR_NO_INT)) {
...
}

부분이 interrupt 방식이 아닌 포트를 회피해서 skip 하기 위한 부분입니다.
polling 방식으로 만드는 드라이버에선 해당 포트의 IIR 레지스터의 NO_INT 비트를 set 하면 됩니다.

OTL

chxooi의 이미지

정말감사합니다.

do while 은 리스트를 찾는거라는걸 알았습니다.

정말 몰랐던 정보네요...인터넷을 찾아도 잘 안나왔다는...

interrupt 가 호출될때마다...interrupt 핸들러가 뜨면서 "up" 을 읽어와서, 자기의 register 에 반영되었는지를 찾고 못찾으면 다음 list 를꺼내 검사하고 해서 결국에 맞는 애를 찾을때까지 돌아서 해당 애가 찾아지면, 그애로 나머지 루틴을 수행하게 되는거네요...

맞나요??

정말정말 찾던 정보입니다. 너무 너무 감사드립니다.

여름을 안덥게 보내도될듯하다는....

추가 질문이 하나 있습니다. 8250.c 를 보면, startup에 serial_link_irq_chain 를 쓰고, 그때 interrupt 를 등록하는 코드와 더불어
아래처럼 request_irq 를 수행하게 됩니다.

아직 100% 이해는 못했지만 ,request_irq 하기전에 hlist_add_head(&i->node, h) 등...list 에 add 하는 애를 넣던데...
이것이 나중에 interrupt handler 를 비교하기 위한 정보를 넣는 부분이라고 생각하면될까요???

static int serial_link_irq_chain(struct uart_8250_port *up)
{
    struct hlist_head *h;
    struct hlist_node *n;
    struct irq_info *i;
    int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
 
    mutex_lock(&hash_mutex);
 
    h = &irq_lists[up->port.irq % NR_IRQ_HASH];
 
    hlist_for_each(n, h) {
        i = hlist_entry(n, struct irq_info, node);
        if (i->irq == up->port.irq)
            break;
    }
 
    if (n == NULL) {
        i = kzalloc(sizeof(struct irq_info), GFP_KERNEL);
        if (i == NULL) {
            mutex_unlock(&hash_mutex);
            return -ENOMEM;
        }
        spin_lock_init(&i->lock);
        i->irq = up->port.irq;
        hlist_add_head(&i->node, h);
    }
    mutex_unlock(&hash_mutex);
 
    spin_lock_irq(&i->lock);
 
    if (i->head) {
        list_add(&up->list, i->head);
        spin_unlock_irq(&i->lock);
 
        ret = 0;
    } else {
        INIT_LIST_HEAD(&up->list);
        i->head = &up->list;
        spin_unlock_irq(&i->lock);
        ret = request_irq(up->port.irq, serial8250_interrupt,
                  irq_flags, "serial", i);
 
        if (ret < 0)
            serial_do_unlink(i, up);
    }
 
    return ret;
}

정말 마지막으로 이제서야 궁극적으로 질문하고프던걸 하는데요...
사실은제가 멋도 모르고 아래처럼짜서 바보같은짓을 하고있는데요...

만약 드라이버를 만들었는데 함수가 꼴랑

serial_init()
{
ret = request_irq(shared_irq_num, serial8250_interrupt,irq_flags, "serial", NULL);
}
 
serial8250_interrupt
{
printk("hellow")
}

라고 한다면, shared_irq_num 넘버가 다른 interrupt 들도쓴다고 가정할때......해당 printk 문은 ...shared_irq_num 이 어떤 interrupt 즉 머 콘솔,리모콘,등등과 연결되어있다면, 암때나 프린트를 한번씩 그냥 내보낼 수 있는건가요??안나올수도있는건가요???

그리고 linked list 코드한줄도 안넣고 reqest_irq 만으로 인터럽트 linked list 에 핸들러가 들어간다고 볼 수있으까요?
그럼 IRQ 가 뜨면 해당 인터럽트 못발견하면 한번은 뜰 수 있으니 printk 문이 한번은 찍혀야할수있는게 정답일까요?

아무것도 없이 shared irq 를 request_irq 로 위처럼 등록 했을때 interrupt handler 가 어떻게 동작하는지 동작은하는지....궁금하네요...아까 잠깐해본바에 의하면 어떻게 하니까 프린트가 정말 간혹나오던데....이해는 안되고...쩝.

말씀하신대로 list 다 넣고 짜면 모르지만, 초보로서 등록하고 뜨나보자 해서 짜넣은게 다거든요.....

그랬더니 이상하게 동작하는것같아서요..

정리하면
1.doubled-link 에 넣지 않으면 shared 는 이상하게 동작한다.....
2.단순한 printk 는 shared 에서 동작할 수 없을수도있으니, 꼭 linked 로 짜도록 초기화 코드및 interrupt handler 를 넣어야한다.
3.위와같은 심플한 코드도 등록은되지만, 운으로 printk 가찍힐지언정 정상적인 구조는 아니다.

제가 맞게 이해한걸까요????

p.s. 이런내용은 어디서 보신걸까요??그냥 코드분석이라면 정말 존경.....

^^ 다시한번 감사드리며...꾸벅..

bushi의 이미지

shared irq 를 위한 꿍꿍이와 shared handler 를 위한 꿍꿍이가 혼재되어 있으니 코드 잘 뜯어보세요.

shared irq 를 위한 interrupt handler 에 list 를 사용하라는 법은 없습니다.
보고계시는 8250.c 만 하더라도 인트럽트 핸들러에서 list 를 탐색하도록 구현할 수도 있지만,
request_irq() 의 맨 마지막 인자에 list head 가 아니라 list entry 를 넘겨주고,
shared irq 이건 말건 port 갯수만큼 request_irq() 를 호출해버리면 됩니다.
하지만, 앞서 말씀드렸듯 리눅스 커널은 irq 에 대해 등록된 모든 핸들러를 순차적으로 무조건 호출합니다.
따라서, function call 에 대한 overhead 를 줄이기 위해 shared irq 인 UART 포트에 대해선 request_irq() 를 한번만 해주고,
핸들러 안에서 포트 탐색을 하는 방법을 사용한 것입니다.

irq_lists[] 해쉬는 irq 갯수가 많아질 경우에 대한 대비입니다.
shared irq 에 대해 request_irq() 를 한번만 하기로 구현했고, linked list로 정보를 엮어놨으므로,
새로운 포트에 대해 등록요청이 있을 때,
이놈이 shared irq 로 쓰이는 놈이라면 request_irq() 를 하는 대신 기존의 list 에 link 를 해야합니다.
irq 갯수가 많아지면 바로 *기존의 list* 를 찾는 과정에 시간이 오래 걸립니다.
기존의 list(head) 들을 tree 로 엮었다면 그나마 적게 걸리겠지만,
8250.c 에선 hlist 로 엮어놨죠. 처음부터 한개씩 뒤져야만 합니다.
이걸 좀 적게 해보려고 hlist head 들을 배열에 넣어놓고 해쉬를 만들어서 대략의 hlist head 를 먼저 찾고,
그것에 속한 hlist node 를 탐색합니다.

다시 말씀드리지만,
보드에 실장한 8250/16550 호환 컨트롤러 대한 드라이버를 작성하려는 의도라면 serial.h 하나만 만들어주시고,
(io 인터페이스가 엄청 특이한 놈이라면 set_io_from_upio() 를 적절히 수정하면 됩니다)

공부하려는 의도에서 UART 드라이버를 작성하는 거라면 더워질 각오하고 코드를 뜯어보세요.
두어달 되기도 전에 이런 표피적이고 일차원적인 코드들은 시원하게 눈에 들어오실 겁니다. :)

OTL

chxooi의 이미지

의도는...업무네요....
irq 를 모르는상태에서 위에서 지시가 와서 짧은시간안에 uart 를 이용한 remote control code 를 구현해야 하는

윗분들은 모르는상태에서 빨리 만들어내라고 난리고...
4일째 밤새면서 작업중인데....정말 님은 한줄기 빛입니다 ^^;; 감사합니다.

코드 보랴 업무하랴...

단지 궁금한 부분이 많아서..가뜩이나 늦었는데 더 늦어지고 있네요..에구...

정말 마지막(?) 질문입니다.

request_irq()도 분석해야하고 linked list 도 대략(?) 이해했습니다.

제가 UART interrupt 즉 irq=5번(예를들어) 를 할당한 것만 알고있고 handler 를 아래처럼 바보같이
짰다고하면, request_irq 에는 차례대로 등록이 되었고, 말씀하신대로, irq 는순서대로 꺼낸다라고한다면..

외부에서 irq_num=5 에 뭔가 뜨거나이벤트발생했을때.(리모콘입력등)

아래에서 handler_AAA 만 수행되고, handler_BBB, handler_CCC,handler_DDD 는 수행이 안되야 하는게 맞나요?

(물론 실제로, UART 기때문에 이미 console쪽에 등록은 되어있기때문에 실습이 좀 힘들어서요..)

단지 아래처럼 console 과동일한 UART 에, interrupt 넣고 수행하니까 remote control 누를때반응은없는데,
keyboard 를 계속 두들기면 제가 멍청하게 만들어놓은 print 문이 동작하는경우가 발생하긴 합니다.
(shared 이고 keyboard 가 먼저등록되서 그런게 아닐까 생각중)

그래서 그에 대한 확인을 하고자...

동일한 기능에 대해 port 구분을 위해, (shared handler)-->double linked list...

쓰는것과 달리 request_irq 에 아래처럼 차곡 차곡 등록된 애들이 별다른 구분자가 없다면,

분명 잘못짠 코드지만, 프린트 한번씩이라도 찍힐여지가 있는지가 궁금합니다.

(사실 이건 저도 request_irq 분석이 필요하지만,윗분에게 제발 이렇게 하면안된다는걸 알려주고싶은의도이기도합니다.)
윗분은 shared 던 아니던 irq 등록된 function 은 무조건떠야 된다고 꿋꿋하게 믿고 계십니다.
그걸반박해야 하는데 논리적으로 부족해서요....^^;;

serial_init()
{
int shared_irq_num=5;//UART
ret = request_irq(shared_irq_num, handler_AAA ,irq_flags, "serial_AAA", NULL);
ret = request_irq(shared_irq_num, handler_BBB ,irq_flags, "serial_BBB", NULL);
ret = request_irq(shared_irq_num, handler_CCC ,irq_flags, "serial_CCC", NULL);
ret = request_irq(shared_irq_num, handler_DDD ,irq_flags, "serial_DDD", NULL);
}
 
handler_AAA ()
{
printk("serial_AAA");
}
handler_BBB ()
{
printk("serial_BBB");
}
handler_CCC ()
{
printk("serial_CCC");
}
handler_DDD()
{
printk("serial_DDD");
}

즉, request_irq 에 같은 irq 넘버로, 핸들러1, 핸들러2,핸들러3,핸들러4,핸들러 5등이 등록되어있을때,
핸들러3번이 내가 원하는동작을 하게끔하려면,

핸들러에서 register 등을읽어 내꺼인지 아닌지 판단을 한다?
아니다
그렇게 판단전에,
핸들러 3번을 바로 꺼내올 수 있다...??

설명해주신것과 코드를 보면 irq 에 등록된 핸들러가 하나씩 호출되고 즉 핸들러1꺼내보고 아니면 핸들러2 아니면 핸들러3으로, 맞는지 안맞는지 보는것같은데 말이죠..

지금 제가 일부러 콘솔과 같은 인터럽트 라인에, printk 문을 등록해놓고, interrupt 를 체크하면서,
UART1 을 쳐다 보면서 print 하게 해놨는데 수행이 안되서요..
분명히 키보드는 자기 레지스터에 반영이 안될테고 리모콘은 자기 레지스터에 반영이 되니까 그부분만 보면되는데...

제가 맞게 이해한건가요??

갈수록 어려워지네요...시간만 좀 많았으면 좋을텐데..

int init__()
{
ret = request_irq(shared_irq_num, handler_printk ,irq_flags, "remocon", NULL);
}
 
handler_printk()
{
printk("serial_DDD");
}

정말^100 많은 도움이 되고 있네요...^^ 정말 정말 감사드립니다.
옆에계심 밥이라도 사드리고 싶네요...정말 꾸벅...

^_______________^

bushi의 이미지

짧게 반복하겠습니다.

> 아래에서 handler_AAA 만 수행되고, handler_BBB, handler_CCC,handler_DDD 는 수행이 안되야 하는게 맞나요?
>
: 아닙니다. 전부, 단 한개도 빠짐없이 호출됩니다.

> 단지 아래처럼 console 과동일한 UART 에, interrupt 넣고 수행하니까 remote control 누를때반응은없는데,
> keyboard 를 계속 두들기면 제가 멍청하게 만들어놓은 print 문이 동작하는경우가 발생하긴 합니다.
>
: keyboard 를 단 한번이라도 누르거나, 커널의 어딘가에서 printk()가 호출되거나, 심지어 유저어플에서 printf() 만 하더라도,
그 즉시 interrupt 무한반복이 시작되어야 정상입니다.

> 윗분은 shared 던 아니던 irq 등록된 function 은 무조건떠야 된다고 꿋꿋하게 믿고 계십니다.
>
: 정확합니다. 바로 그렇습니다. 등록이 됐다면 말이죠.

> request_irq 에 같은 irq 넘버로, 핸들러1, 핸들러2,핸들러3,핸들러4,핸들러 5등이 등록되어있을때
>
: 표현이 틀렸습니다.
"같은 irq 넘버로 핸들러1, 핸들러2, 핸들러3, 핸들러4, 핸들러5 등을 모두 request_irq() 했다면,"

> 핸들러에서 register 등을읽어 내꺼인지 아닌지 판단을 한다?
>
: 그렇습니다.

> 지금 제가 일부러 콘솔과 같은 인터럽트 라인에, printk 문을 등록해놓고, interrupt 를 체크하면서,
> UART1 을 쳐다 보면서 print 하게 해놨는데 수행이 안되서요..
>
: request_irq(..., IRQF_SHARED, ...) 로 핸들러를 추가하려면,
처음부터 그 irq 는 이미 IRQF_SHARED 로 등록되었던 놈이라야만 합니다.
그렇지 않다면 request_irq() 는 error를 리턴 합니다.
물론 핸들러 추가도 되지 않습니다.
cat /proc/interrupts
명령으로 "remocon" 이라는 문자열이 있는지 보세요.

OTL

chxooi의 이미지

일단 device 동작에 대해 많이 도움이 되었습니다.

정말정말 정말 감사합니다.

^____________________________^

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.