GIC dynamic interrupt priority change -- possible?

Hello,

I face a challenging task: I need to dynamically change the priority of a potentially pending interrupt and make sure the priority change takes effect. "Takes effect" means if the new priority makes the interrupt deliverable, it is delivered. What makes it challenging is the following note in the GICv3 Spec describing the GICR_IPRIORITYRn registers:

"Implementations must ensure that an interrupt that is pending at the time of the write uses either the old value or the new value and must ensure that the interrupt is neither lost nor handled more than once. The effect of the change must be visible in finite time."

It's basically saying that if the interrupt is already pending, your write to GICR_IPRIORITYRn may not actually affect its priority (because the GIC would "use the old value"), presumably until the interrupt signal is deasserted.

How can I make sure the priority change takes effect on the pending interrupt? My focus right now is on level-triggered PPIs, namely the generic timer and PMU interrupts. My current idea is that I "flicker" the interrupt: deassert it, then assert it again. The new assertion should then use the new priority. My code is going to look like this:

- Write the new priority to GICR_IPRIORITYn (mapped with Device nGnRnE attributes)
- Barrier
- Set CNTP_CTL_EL0.IMASK
- Clear CNTP_CTL_EL0.IMASK

My questions are:

1. Will the GIC see this flicker?  The two back-to-back CPU register writes are pretty fast. Do I need to care about GIC level-sensitive interrupt line sampling rate?

2. What kind of barrier do I need? Obviously at least an ISB, do I need a DSB as well?

3. Is even the DSB enough? "The effect of the change must be visible in finite time." Does it mean the priority write is not guaranteed to have taken effect even after the DSB?

4. Am I wasting my time because GIC is simply not designed to support dynamic interrupt priority change?

P.S. My code is not written for a particular CPU/GIC model, it needs to work on any future Arm platform. This code is part of a VM context switch, so it must be as fast as possible and avoid any polling, or other kinds of long delays.

Thank you.

Parents
  • 4. Am I wasting my time because GIC is simply not designed to support dynamic interrupt priority change?

    You're not wasting your time.  The assumption in the spec is that changing priority happens, but not frequently.

    To change an  INTID's config, and guarantee the old config is no longer visible, you'd need to:

    • Disable the interrupt (write to GICx_ICENABLERn)
    • Poll GICx_CTLR.RWP until it reads as 0.
    • Change the config
    • Enable the interrupt (write to GICx_ISENABLERn)

    Clearing the individual enable forces the GIC to recall the interrupt if it's pending on a CPU interface somewhere.  The RWP bit won't read as 0 until that recall has completed.  You can then change the config (target/group/...) knowing the interrupt isn't signal-able.

    Without following the sequence above the changes to the config will still take effect - eventually (aka finite time).  Setting up a race condition.  Now in some cases you might not care about the race, in which case you could skip the disable-reconfig-enable sequence.  But if you care, then you should follow the sequence.

    "Takes effect" means if the new priority makes the interrupt deliverable, it is delivered.

    That's interesting... do you mean the old/original priority meant it wasn't deliverable?

    For example:

    • INTID A has priority 0x80 and is pending
    • The PMR on the target PE is set to 0x80

    In this case the interrupt cannot be signalled due to the PMR (A must be higher priority than the PMR - numerically lower).  If you then change the priority of INTID A to 0x00, then you are guaranteed that when (the finite time guarantee in the spec) it gets signalled, then it's with the new priority.  As for as long as the old priority applies, it wouldn't be beating the PMR.

Reply
  • 4. Am I wasting my time because GIC is simply not designed to support dynamic interrupt priority change?

    You're not wasting your time.  The assumption in the spec is that changing priority happens, but not frequently.

    To change an  INTID's config, and guarantee the old config is no longer visible, you'd need to:

    • Disable the interrupt (write to GICx_ICENABLERn)
    • Poll GICx_CTLR.RWP until it reads as 0.
    • Change the config
    • Enable the interrupt (write to GICx_ISENABLERn)

    Clearing the individual enable forces the GIC to recall the interrupt if it's pending on a CPU interface somewhere.  The RWP bit won't read as 0 until that recall has completed.  You can then change the config (target/group/...) knowing the interrupt isn't signal-able.

    Without following the sequence above the changes to the config will still take effect - eventually (aka finite time).  Setting up a race condition.  Now in some cases you might not care about the race, in which case you could skip the disable-reconfig-enable sequence.  But if you care, then you should follow the sequence.

    "Takes effect" means if the new priority makes the interrupt deliverable, it is delivered.

    That's interesting... do you mean the old/original priority meant it wasn't deliverable?

    For example:

    • INTID A has priority 0x80 and is pending
    • The PMR on the target PE is set to 0x80

    In this case the interrupt cannot be signalled due to the PMR (A must be higher priority than the PMR - numerically lower).  If you then change the priority of INTID A to 0x00, then you are guaranteed that when (the finite time guarantee in the spec) it gets signalled, then it's with the new priority.  As for as long as the old priority applies, it wouldn't be beating the PMR.

Children