Page 1 of 2

Interrupt Processing

Posted: Sat Jul 03, 2010 6:15 pm
by rknox
I see two different techniques used to deal with the 8259 during interrupt processing.

Bran's demo - starts by issuing a CLI - never issues an STI but the IRET rloads the EFlAGS and takes care of the IF. At the end of interrupt processing he then issues a 20 command to port 20.

In Tanenbaum's minix, in the general case he does not issue a CLI. At the start of interrupt processing he sets a bit at port 21 to disable the specific interrupt port involved. Bit 0 for port 0 etc. At the end he clears that bit. In the middle in function interrupt() when he is adjusting a que of waiting tasks he protects with two functions lock() and unlock() which turn out to be CLI and STI.

Being new to this architecture I'm not sure I understand the full implications of
these 2 approaches.

It appears that in Minix the keyboard is free to interrupt while the timer
interrupt is under way. And I dont understand the implications of that. I believe all he is protecting against is having two parts of his code modifying the wait que at the same time.

Does the hardware que interrupts when the IF is clear? And release them when it is set?

What does the controller do when the IF flag is clear and a device wants to signal?

I'm a bit dizzy going around in circles. I really have spent some time looking for the answers and they seem to be well hidden in the mass of Intel docs. An explanation or a pointer to an explanation would be a help.

Re: Interrupt Processing

Posted: Sun Jul 04, 2010 3:47 pm
by rknox
I think I've answered my own question.

On things like a server Tananbaum's solution makes sense. You want high priority items like disks and data communicatinos cards to push ahead of unimportant things like printers, keyboards, timers, etc. You may pay a tiny price in stack size but that is small cpompared with the essence of the mission.

On the other hand, for non server type applications there is no big reason to have those higher priority devices push ahead of the ho hum devices.

Do I have it right?

~

Re: Interrupt Processing

Posted: Sun Jul 04, 2010 5:27 pm
by gerryg400
I'd say the Tanenbaum solution is excellent for a microkernel, where all kernel work is done on a single kernel stack. It scales a little bit and could be modified to work on a multi-core machine. It's also fairly simple to understand, test and debug which would be important to AST.
I think it's entirely reasonable to NOT support re-entrant/concurrent/prioritised interrupts in your first or prototype kernel, so Bran's demo which I think you said leaves interrupts disabled during interrupt processing would be fine.

Re: Interrupt Processing

Posted: Sun Jul 04, 2010 6:26 pm
by TylerH
What needs to be considered if one wishes to implement re-entrant interrupts? Apart from stack overflows, what could go wrong?

Re: Interrupt Processing

Posted: Mon Jul 05, 2010 4:36 am
by Candy
Should this thread really be in auto-delete?

Re: Interrupt Processing

Posted: Mon Jul 05, 2010 8:35 am
by rknox
Gerry, Tyler - thanks
Candy - darned if you do, ditto dont - I'm trying to find the right way to use this resource. Abt 2/3 of my posts draw RTFM type replies. Not easy to gauge what the group feels signif and insignif.

Thanks to all of you


~~

Re: Interrupt Processing

Posted: Mon Jul 05, 2010 3:55 pm
by Combuster
The posting rules apply as much here as it does elsewhere, so that's not an excuse. If you deserve an RTFM, you'll get it.

Re: Interrupt Processing

Posted: Mon Jul 05, 2010 4:14 pm
by gerryg400
What needs to be considered if one wishes to implement re-entrant interrupts? Apart from stack overflows, what could go wrong?
Stack overflow is not really the problem. That can be addressed simply by making the stack(s) bigger. There are at least 2 problems that come to mind.

Firstly, it's about which stack you are operating on and the order that things must be processed. When you begin processing an interrupt on a stack, you need to complete that interrupt before you can get back to the thing you interrupted. This is a problem if the interrupt is a lower priority than the thing you interrupted. Minix handles this by disabling and deferring the interrupt processing until the higher priority interrupt is complete. It can also be done, for example by having separate stacks for each interrupt priority.

Secondly, there is the problem of the interrupt needing to access/modify kernel data structures that were already being modified by the interrupted task or interrupt. This requires locking or some sort of deferral mechanism.

By far the simplest approach (and an approach that's possibly okay in a prototype or hobby kernel) is to have interrupts disabled while in the kernel.

Re: Interrupt Processing

Posted: Tue Jul 06, 2010 10:22 am
by bewing
@OP: The "ultimate" form of interrupt handling is quite a mystery, and is one of the things that you should probably put off in your initial kernel design. You will probably still be struggling with it a couple of years from now.

If you are in pmode/longmode, then you need to realize that the IDT handles interrupts in 2 different ways. There are "interrupt gates" and "trap gates". Interrupt gates do an automatic CLI for you; trap gates do not. So, either way, it is utterly stupid to put a CLI at the beginning of your interrupt handler. If you want interrupts off, then you should have used an interrupt gate in the first place. So Bran's tutorial is, shall we say, somewhat idiotic.

One of the main points about reentrant interrupt handling has to do with privilege level switching, and whether you have your interrupt handlers running in Ring0 or Ring3, and which Ring the CPL is typically set to. Let's assume that you have a microkernel design that's well implemented. That means that interrupt handlers are all in Ring3, and the CPU is almost always running usermode code in Ring3. In that case, there is very little (or no) benefit to reentrant drivers. Now assume that the CPU is usually in Ring3, but your interrupt handlers are mostly trusted, and live in Ring0. So every time there is an interrupt, you can have a priv level switch. Priv level switches take a long time. So it is very beneficial to handle as many interrupts as you can while you are still in Ring0. So you do your EOI as early as you can in your handler (because it takes time to work), and you do an STI several instructions before the end (because there is a one instruction delay). This should get you any pending interrupts before you do the IRET back to Ring3.

Masking interrupts in the PIC:
This is a very difficult and mind-bending thing to ponder. There are some devices that fail to share their interrupt lines properly. Most notably: multiple floppy controllers, and the keyboard. If you have multiple keystrokes in the keyboard buffer, the KBC will raise IRQ1 when the first keystroke comes in. If you read that scancode, then IRQ1 will stay high. Which will NOT generate a new IRQ. If you have 2 floppy drives, on two different controllers, and they both need "attention", they will both raise IRQ6. If you service one of them, IRQ6 will stay high and not generate another IRQ. How can you handle this? You can mask the IRQ in the PIC, then unmask it. This will generate a low/high trigger, and a new IRQ -- if there is any reason for the IRQ to still be high. Which will trigger your handler again, to service the devices a second time, and clear the source of the second IRQ. But this is only one way of handling the problem.

This is one of the biggest problems with OSDEV -- there may not be a perfect answer to how to do this interrupt handling stuff. If there is, we do not know it. Nobody has published a testable perfect interrupt handler prototype. The best you can do is take a good guess. Or study the problem in-depth, and tell us all the answer.

Re: Interrupt Processing

Posted: Tue Jul 06, 2010 10:44 am
by Owen
One thing to note is that sending the EOI before you've actually serviced the interrupt is a bad idea for many devices (A prime example is anything PCI - basically, anything level triggered). Its a great way to get yourself hit with a tonne of spurious interrupts.

Also, for those using the PIC: Note that every IN/OUT instruction takes ~150 cycles, so you want to talk to it as little as possible.

The best option for awkward edge triggered devices like the keyboard controller is to send the EOI and then service all pending keystrokes. This is in fact pretty much the only option (aside from the worse idea of fiddling with the PIC masks) that avoids a race condition. Because you serviced all keystrokes, the interrupt line will have dropped for enough time for the (A)PIC to be re-triggered if a new keycoe arrives

If you want to service as many interrupts as possible while in kernel mode, but don't want to deal with the mess of supporting interrupt re-entrancy, then queue them: Have an "Interrupts accepted" bit in your per-CPU structure, and an array for storing the queued interrupts in. Upon each interrupt:
  1. Check if interrupts are being accepted. If they are, block them (by clearing the bit), STI, and then vector the interrupt normally. GOTO 5.
  2. Otherwise, read the number of queued interrupts from the CPU structure, increment it, and update the counter
  3. Append this interrupt's number to the list
  4. IRET
  5. Look at the list of pending interrupts. Handle them in reverse order to they arrived. Note that you'll need to use atomic instructions here (cmpxchg) in order to defend against other incoming interrupts being appended to the queue you're processing.

    Handle them in reverse order because that way you handle high-priority stuff first
  6. IRET

Re: Interrupt Processing

Posted: Tue Jul 06, 2010 10:59 am
by bewing
And then there are exceptions to Owen's method -- notably USB mice. If you try to use his method for reading a keyboard, and you have a USB mouse in PS2 compatibility mode, you will almost certainly lose mouse packets, and mouse packet alignment -- because the mouse and keyboard share port 0x60, and there are problems differentiating mouse bytes from kb scancode bytes.

So, as I said -- be very careful about appyling huge generalizations to your IRQ handlers, because this hardware is FULL of exceptions and gotchas.

Re: Interrupt Processing

Posted: Tue Jul 06, 2010 12:28 pm
by Owen
The keyboard controller's status register has a bit (bit 5) telling you whether the next byte you read is from the keyboard or mouse. Obviously, you should test this when you check if there is any data left (bit 0).

Re: Interrupt Processing

Posted: Tue Jul 06, 2010 2:02 pm
by Benjamin1996
Owen wrote:The keyboard controller's status register has a bit (bit 5) telling you whether the next byte you read is from the keyboard or mouse. Obviously, you should test this when you check if there is any data left (bit 0).
You took the words right out of my mouth there, Owen ;).

Re: Interrupt Processing

Posted: Tue Jul 06, 2010 2:07 pm
by bewing
Bit 5 does not work for USB mice in PS2 compatibility mode on many BIOSes on real hardware.

http://forum.osdev.org/viewtopic.php?f= ... 9&p=154686

Re: Interrupt Processing

Posted: Tue Jul 06, 2010 2:16 pm
by Owen
And why are you using the USB mouse via the PS/2 compatibility layer? Turn on the USB controller and do it properly.