LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   How to run longer code in an ISR - kthread isn't working (https://www.linuxquestions.org/questions/programming-9/how-to-run-longer-code-in-an-isr-kthread-isnt-working-4175583016/)

maduino 06-24-2016 05:15 PM

How to run longer code in an ISR - kthread isn't working
 
Hi,

hopefully I found the right forum to post my question. The basic setting is that I'm developing a device driver as Linux Kernel Module which listens on an interrupt of an external device. Using request_irq(ird_id, (irq_handler_t) handler, IRQF_DISABLED, "yyy", NULL); I can successfully register my callback for the interrupt. The interrupt is also fired at the right moment.

So my final source code - which I need to run after the interrupt was triggered - will take between 500 µs and 10 ms. It works just fine placing the code inside the ISR, but I learned that such "fragile" functions should be as short as possible.

I thought that a kthread would be ideal: it contains the source code to be executed and in the ISR a call to kthread_run() is short and enough to start it. Therefore my ISR looks like:

Code:

struct task_struct *task;

static irqreturn_t handler(int irq, void *data) {
    if (external_condition) {
        task = kthread_run(&thread_cb, NULL, "xxx");
    }
    return IRQ_HANDLED;
}

But when I execute this piece of code (better: the interrupt is triggered and the ISR is executed) the entire system hangs and I need to hard reset it.

Beneath some mistakes in my code (which I'll check a second time for tomorrow): is there any reason why this didn't work? How to fix it? Or is there an even better way to perform my task?

Regards,
maduino

smallpond 06-25-2016 11:08 PM

A typical reason would be getting another interrupt before finishing processing on the first one.

maduino 06-26-2016 04:37 AM

Quote:

Originally Posted by smallpond (Post 5566376)
A typical reason would be getting another interrupt before finishing processing on the first one.

You're totally right. I checked it and I really get a second interrupt which causes the error. I fixed it and it works :-)

Is it save to block an ISR for 250 µs? Where is the border?

smallpond 06-26-2016 01:04 PM

250,000 nsec is a long time. The normal scheme is to do the minimum processing in the ISR, put the work on a queue, and poke the kthread. The kthread wakes up, checks the queue, and runs til its empty. If the queue is full, the ISR starts dropping work and logs an error to avoid hanging the rest of the system. The network stack doesn't even log an error, it's such a common occurrence that it just counts dropped packets and lets the TCP protocol sort it out.

maduino 06-26-2016 05:43 PM

Quote:

Originally Posted by smallpond (Post 5566534)
250,000 nsec is a long time. The normal scheme is to do the minimum processing in the ISR, put the work on a queue, and poke the kthread. The kthread wakes up, checks the queue, and runs til its empty. If the queue is full, the ISR starts dropping work and logs an error to avoid hanging the rest of the system. The network stack doesn't even log an error, it's such a common occurrence that it just counts dropped packets and lets the TCP protocol sort it out.

I got your point. So I went to effort and implemented both variants: the 250µs code inside the ISR and in an separate kthread which is waiting by default (through wait_event_timeout()) and "freed" (through wake_up()) by the ISR. Then I measured the performance and noticed that the kthread-variant is between 20% to 40% slower. And that's not good for me, I really need this high performance. So if I go with the code inside the ISR: which bad thing could happen?

sundialsvcs 06-27-2016 07:46 AM

The key idea here is that it "pokes" the thread, in order to be certain that the thread is not asleep, after first putting a new unit-of-work onto the queue (if it can). Every (productive) interrupt "wakes up the sleepyhead." It does not create the thread, nor start it: the thread is always out there.

The thread, since it does operate asynchronously, can take as long as it needs to, to complete any request. (Of course, it must be mindful that it must not monopolize kernel time, since it can't be pre-empted.)

Another strategy that is sometimes used is a "virtual device driver," which actually exposes a /dev entry (or somesuch ...) that a suitably privileged user-land daemon can use. The kernel thread hands-off the work to user-land, using ordinary and easy-to-use filesystem semantics.

As always, look through the kernel source tree (and GitHub) for "prior art." :) "Somebody out there has already done this ... I don't need to write this from scratch. I just need to steal 'borrow it,' then whack on it until it becomes what I need." (And then, like a good netizen, contribute it back to GitHub ...)


All times are GMT -5. The time now is 10:39 AM.