James Harris
2021-04-17 10:56:24 UTC
It's not possible to implement custom IRQ priorities on a traditional
PC, right?
That's what I've thought for many years but I might have finally found a
way to do it!
Here's the idea. Feel free to comment.
The principle is to get the PICs to inform us immediately of any IRQs
which are requesting service so we gain a complete picture of all the
IRQs which require attention.
Then, since we will know of all IRQs which need attention we can process
them in whatever order we want.
The key change from normal is for an interrupt to be EOId before it is
handled rather than afterwards, and for the IMR to be used to prevent
that same interrupt number from firing again.
But as well as masking an interrupt it would be added to a waiting list.
The waiting list could be as simple as a bit array with one bit for each
IRQ number or it could be more extensive but the point is that it would
keep track of which IRQs had been signalled but had not yet been processed.
To illustrate, here's a piece of pseudocode to show what would happen
when an interrupt comes in. All that would have happened before it
starts is that the IRQ number would have been determined and EOI would
have been issued automatically (the AEOI setting).
On an interrupt:
Push a minimal set of registers
Mask this interrupt in the relevant IMR
Add this interrupt to the set of waiting interrupts
...
Pop the registers pushed earlier
iret
Aside from saving and restoring registers all that would be done would
be to mask off the current interrupt and to leave a note that the
interrupt is awaiting service.
If half a dozen interrupts all fire at once then each of the six would,
in turn, follow the above code path. That would leave all six masked off
and recorded as awaiting service.
But, naturally, something has to do the actual servicing. It could be
one of the interrupts or a high-priority task.
In the code below I'll make it one of the interrupts. I'll use a nesting
level to determine whether an interrupt is already being serviced or
not. Nesting level will be zero on initial entry and will be 1 if an
interrupt is already being serviced. There would only be the two levels.
The code in the above which is shown as "..." would be
If nesting level is zero
Increment nesting level
Push more registers, as required
Loop while there are any interrupts waiting
Pick the IRQ /we/ want to treat as of highest priority
Delete it from waiting list
Enable interrupts
Handle the IRQ
Disable interrupts
Unmask the IRQ
Endloop
Pop the registers pushed earlier in this fragment
Decrement nesting level
Endif
The loop would take interrupts in any order we wanted, thus implementing
custom prioritisation, and it would unmask each one as it completed it.
If any new interrupts fired while the code was running they would be
added to the waiting list and then would be processed in their turn
before the loop completed.
Once the waiting list was empty the loop would terminate. The code would
then, as normal, ireq back to whatever had been interrupted.
I think that's it. What do you think? Would it work? Can it be improved?
PC, right?
That's what I've thought for many years but I might have finally found a
way to do it!
Here's the idea. Feel free to comment.
The principle is to get the PICs to inform us immediately of any IRQs
which are requesting service so we gain a complete picture of all the
IRQs which require attention.
Then, since we will know of all IRQs which need attention we can process
them in whatever order we want.
The key change from normal is for an interrupt to be EOId before it is
handled rather than afterwards, and for the IMR to be used to prevent
that same interrupt number from firing again.
But as well as masking an interrupt it would be added to a waiting list.
The waiting list could be as simple as a bit array with one bit for each
IRQ number or it could be more extensive but the point is that it would
keep track of which IRQs had been signalled but had not yet been processed.
To illustrate, here's a piece of pseudocode to show what would happen
when an interrupt comes in. All that would have happened before it
starts is that the IRQ number would have been determined and EOI would
have been issued automatically (the AEOI setting).
On an interrupt:
Push a minimal set of registers
Mask this interrupt in the relevant IMR
Add this interrupt to the set of waiting interrupts
...
Pop the registers pushed earlier
iret
Aside from saving and restoring registers all that would be done would
be to mask off the current interrupt and to leave a note that the
interrupt is awaiting service.
If half a dozen interrupts all fire at once then each of the six would,
in turn, follow the above code path. That would leave all six masked off
and recorded as awaiting service.
But, naturally, something has to do the actual servicing. It could be
one of the interrupts or a high-priority task.
In the code below I'll make it one of the interrupts. I'll use a nesting
level to determine whether an interrupt is already being serviced or
not. Nesting level will be zero on initial entry and will be 1 if an
interrupt is already being serviced. There would only be the two levels.
The code in the above which is shown as "..." would be
If nesting level is zero
Increment nesting level
Push more registers, as required
Loop while there are any interrupts waiting
Pick the IRQ /we/ want to treat as of highest priority
Delete it from waiting list
Enable interrupts
Handle the IRQ
Disable interrupts
Unmask the IRQ
Endloop
Pop the registers pushed earlier in this fragment
Decrement nesting level
Endif
The loop would take interrupts in any order we wanted, thus implementing
custom prioritisation, and it would unmask each one as it completed it.
If any new interrupts fired while the code was running they would be
added to the waiting list and then would be processed in their turn
before the loop completed.
Once the waiting list was empty the loop would terminate. The code would
then, as normal, ireq back to whatever had been interrupted.
I think that's it. What do you think? Would it work? Can it be improved?
--
James Harris
James Harris