Skip to content

Commit

Permalink
perf(d1): optimize interrupt vectoring by indexing
Browse files Browse the repository at this point in the history
Currently, the interrupt vector dispatch code uses a linear search over
the interrupt vector array by checking each vector's ID against the
interrupt number being dispatched. This is inefficient; because we
already generate the vector array in order, we can simply index into it,
which is _O_(1) instead of _O_(_n<sub>interrupts</sub>_). This commit
changes the existing code to do that.

We can rely on the ordering being correct as it's generated by a `const
fn` that should always output the same order. However, I've also added
debug assertions that the index and ID match, just in case the table
somehow gets corrupted or something. I don't think this is really that
necessary, but it seemed nice to have.
  • Loading branch information
hawkw committed Apr 9, 2024
1 parent 55061da commit 3ab577a
Showing 1 changed file with 29 additions and 13 deletions.
42 changes: 29 additions & 13 deletions platforms/allwinner-d1/d1-core/src/plic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,14 @@ impl Plic {
let claim_u16 = claim as u16;

// Is this a known interrupt?
let handler = INTERRUPT_ARRAY.iter().find(|i| i.id == claim_u16);
if let Some(Vectored { id: _id, handler }) = handler {
let handler = INTERRUPT_ARRAY.get(claim_u16 as usize);
if let Some(Vectored { id, handler }) = handler {
debug_assert_eq!(
*id, claim_u16,
"FLAGRANT ERROR: interrupt ID ({id}) does not match index \
({claim_u16}); perhaps the interrupt dispatch table has \
somehow been corrupted?"
);
let ptr = handler.load(Ordering::SeqCst); // todo: ordering
if !ptr.is_null() {
let hdlr = ptr as *mut fn() as fn();

Check failure on line 111 in platforms/allwinner-d1/d1-core/src/plic.rs

View workflow job for this annotation

GitHub Actions / just check

error[E0605]: non-primitive cast: `*mut fn()` as `fn()` --> platforms/allwinner-d1/d1-core/src/plic.rs:111:28 | 111 | let hdlr = ptr as *mut fn() as fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast

Check failure on line 111 in platforms/allwinner-d1/d1-core/src/plic.rs

View workflow job for this annotation

GitHub Actions / just check

error[E0605]: non-primitive cast: `*mut fn()` as `fn()` --> platforms/allwinner-d1/d1-core/src/plic.rs:111:28 | 111 | let hdlr = ptr as *mut fn() as fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast

Check failure on line 111 in platforms/allwinner-d1/d1-core/src/plic.rs

View workflow job for this annotation

GitHub Actions / just clippy

error[E0605]: non-primitive cast: `*mut fn()` as `fn()` --> platforms/allwinner-d1/d1-core/src/plic.rs:111:28 | 111 | let hdlr = ptr as *mut fn() as fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast

Check failure on line 111 in platforms/allwinner-d1/d1-core/src/plic.rs

View workflow job for this annotation

GitHub Actions / just clippy

error[E0605]: non-primitive cast: `*mut fn()` as `fn()` --> platforms/allwinner-d1/d1-core/src/plic.rs:111:28 | 111 | let hdlr = ptr as *mut fn() as fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
Expand All @@ -118,10 +124,17 @@ impl Plic {
}

pub unsafe fn register(&self, interrupt: Interrupt, new_hdl: fn()) {
let v = INTERRUPT_ARRAY.iter().find(|v| v.id == interrupt as u16);
if let Some(Vectored { id: _id, handler }) = v {
handler.store(new_hdl as *mut fn() as *mut (), Ordering::Release);
}
let idx = interrupt as u16;
let Some(Vectored { id, handler }) = INTERRUPT_ARRAY.get(idx as usize) else {
panic!("interrupt not found in dispatch table: {interrupt:?} (index {idx})")
};
debug_assert_eq!(
*id, idx,
"FLAGRANT ERROR: interrupt ID for {interrupt:?} (id) does not \
match index ({idx}); perhaps the interrupt dispatch table has \
somehow been corrupted?"
);
handler.store(new_hdl as *mut fn() as *mut (), Ordering::Release);
}

pub unsafe fn activate(&self, interrupt: Interrupt, prio: Priority) -> Result<(), MaskError> {
Expand All @@ -138,16 +151,19 @@ impl Plic {
}

fn can_mask(&self, interrupt: Interrupt) -> Result<(), MaskError> {
let v = INTERRUPT_ARRAY
.iter()
.find(|v| v.id == interrupt as u16)
let &Vectored { id, ref handler } = INTERRUPT_ARRAY
.get(interrupt as usize)
.ok_or(MaskError::NotFound(interrupt))?;

if v.handler.load(Ordering::SeqCst).is_null() {
Err(MaskError::NoHandler(interrupt))
} else {
Ok(())
if id != interrupt as u16 {
return Err(MaskError::NotFound(interrupt));
}

if handler.load(Ordering::SeqCst).is_null() {
return Err(MaskError::NoHandler(interrupt));
}

Ok(())
}

#[inline(always)]
Expand Down

0 comments on commit 3ab577a

Please sign in to comment.