Skip to content

Commit e199470

Browse files
committed
stm32xx-i2c: remove soft timeout, LostInterrupt
I've become convinced that the "LostInterrupt" failures we've intermittently seen in the I2C driver are due to timer notification mismanagement. I recently merged a commit (dd76a4c) that reworked timer notification handling to eliminate a case where the driver could crash _itself_ in a narrow race condition window. That also eliminated a way to crashed it from other task, by posting to its timer notification bit (bit 31) while it was working -- both cases were fixed by double checking that the timer had actually gone off before responding. With that fixed, there's only one way remaining to get the LostInterrupt panic, which is: keep i2c_driver from running for 100ms during any I2C transaction. You can do this pretty easily by triggering a large coredump. As a result, the driver contains a system-load-induced panic that isn't detecting any real error condition, and acts as a load multiplier (by triggering crash response and retries). From what I can tell from the commit history, the software timer (the thing that explodes after 100ms) was added while attempting to track down some _other_ state machine misbehavior, which I believe has since been fixed. Certainly, none of our investigations into the LostInterrupt failures over the past year have yielded any concrete evidence of a hardware bug. They've mostly seemed random... which is what you'd expect if they were being triggered by unrelated system load spikes. This commit removes the entire mechanism. The hardware SMBus 25ms timeout remains, but that's separate and ran in parallel to this software timer. The path leading to the LostInterrupt panic no longer exists in the code. Fixes #2004.
1 parent c07b417 commit e199470

1 file changed

Lines changed: 11 additions & 143 deletions

File tree

drv/stm32xx-i2c/src/lib.rs

Lines changed: 11 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,6 @@ pub struct I2cController<'a> {
6666
pub registers: &'a RegisterBlock,
6767
}
6868

69-
///
70-
/// A structure to denote an absolute number of ticks to wait.
71-
///
72-
pub struct I2cTimeout(pub u64);
73-
74-
pub enum I2cControlResult {
75-
Interrupted,
76-
TimedOut,
77-
}
78-
7969
pub struct I2cTargetControl {
8070
pub enable: fn(u32),
8171
pub wfi: fn(u32),
@@ -148,12 +138,7 @@ pub enum ReadLength {
148138
enum Register {
149139
CR1,
150140
CR2,
151-
OAR1,
152-
OAR2,
153-
TIMINGR,
154-
TIMEOUTR,
155141
ISR,
156-
PECR,
157142
}
158143

159144
#[derive(Copy, Clone, Eq, PartialEq, counters::Count)]
@@ -184,16 +169,6 @@ enum Trace {
184169
BusySleep,
185170
Stop,
186171
RepeatedStart(#[count(children)] bool),
187-
LostInterrupt,
188-
#[count(skip)]
189-
Panic(Register, u32),
190-
#[count(skip)]
191-
IrqStatus {
192-
notification: u32,
193-
pending: bool,
194-
enabled: bool,
195-
posted: bool,
196-
},
197172
}
198173

199174
counted_ringbuf!(Trace, 48, Trace::None);
@@ -456,73 +431,13 @@ impl I2cController<'_> {
456431
}
457432

458433
///
459-
/// A routine to panic. This should not be called merely because something
460-
/// has gone wrong with a device (which should rather be indicated by
461-
/// returning an error and resetting the controller if/as needed), but with
462-
/// the controller itself.
434+
/// A common routine to wait for any of our interrupt-related notification
435+
/// bits. Note that you'll still want to check the actual interrupt status
436+
/// bits to distinguish a real interrupt from a stale or mischieviously
437+
/// posted notification bit.
463438
///
464-
fn panic(&self) -> ! {
465-
let i2c = self.registers;
466-
let tgr = &i2c.timingr;
467-
let tor = &i2c.timeoutr;
468-
469-
ringbuf_entry!(Trace::Panic(Register::CR1, i2c.cr1.read().bits()));
470-
ringbuf_entry!(Trace::Panic(Register::CR2, i2c.cr2.read().bits()));
471-
ringbuf_entry!(Trace::Panic(Register::OAR1, i2c.oar1.read().bits()));
472-
ringbuf_entry!(Trace::Panic(Register::OAR2, i2c.oar2.read().bits()));
473-
ringbuf_entry!(Trace::Panic(Register::TIMINGR, tgr.read().bits()));
474-
ringbuf_entry!(Trace::Panic(Register::TIMEOUTR, tor.read().bits()));
475-
ringbuf_entry!(Trace::Panic(Register::ISR, i2c.isr.read().bits()));
476-
ringbuf_entry!(Trace::Panic(Register::PECR, i2c.pecr.read().bits()));
477-
478-
let irq_status = sys_irq_status(self.notification);
479-
ringbuf_entry!(Trace::IrqStatus {
480-
notification: self.notification,
481-
// Yes, we *could* just record the `IrqStatus` value here, but
482-
// Humility will format it as a hex value rather than knowing about
483-
// the bitflags, which is a bit sad...
484-
enabled: irq_status.contains(IrqStatus::ENABLED),
485-
pending: irq_status.contains(IrqStatus::PENDING),
486-
posted: irq_status.contains(IrqStatus::POSTED),
487-
});
488-
489-
panic!();
490-
}
491-
492-
///
493-
/// A common routine to wait for interrupts, panicking the driver if the
494-
/// interrupt doesn't arrive in an arbitrarily chosen period of time.
495-
///
496-
fn wfi(&self) -> Result<(), drv_i2c_api::ResponseCode> {
497-
//
498-
// A 100 ms timeout is 4x longer than the I2C timeouts, but much shorter
499-
// than potential context switches when dumps are being recorded by a
500-
// high priority task.
501-
//
502-
const TIMEOUT: I2cTimeout = I2cTimeout(100);
503-
504-
match wfi_raw(self.notification, TIMEOUT) {
505-
I2cControlResult::TimedOut => {
506-
//
507-
// This really shouldn't happen: it means that not only did
508-
// we not get our expected interrupt, but that the configured
509-
// timeout in the I2C block also didn't function as expected.
510-
// That said, we got our OS timer interrupt (or we wouldn't be
511-
// here at all), which gives us at least control. While we
512-
// could conceivably return an error code in this condition,
513-
// this condition is so unexpected that we want to instead
514-
// make sure we can debug it: we are going to instead call
515-
// our panic routine, which will record some additional data
516-
// from the I2C controller and explicitly panic. This will
517-
// result in a dump that will effectively preserve this state,
518-
// and will (hopefuflly) allow it to be debugged long after it
519-
// happens.
520-
//
521-
ringbuf_entry!(Trace::LostInterrupt);
522-
self.panic();
523-
}
524-
I2cControlResult::Interrupted => Ok(()),
525-
}
439+
fn wfi(&self) {
440+
sys_recv_notification(self.notification);
526441
}
527442

528443
fn wait_until_notbusy(&self) -> Result<(), drv_i2c_api::ResponseCode> {
@@ -641,7 +556,7 @@ impl I2cController<'_> {
641556
break;
642557
}
643558

644-
self.wfi()?;
559+
self.wfi();
645560
sys_irq_control(notification, true);
646561
}
647562

@@ -671,7 +586,7 @@ impl I2cController<'_> {
671586
break;
672587
}
673588

674-
self.wfi()?;
589+
self.wfi();
675590
sys_irq_control(notification, true);
676591
}
677592
}
@@ -719,7 +634,7 @@ impl I2cController<'_> {
719634
}
720635

721636
loop {
722-
self.wfi()?;
637+
self.wfi();
723638
sys_irq_control(notification, true);
724639

725640
let isr = i2c.isr.read();
@@ -774,7 +689,7 @@ impl I2cController<'_> {
774689

775690
self.check_errors(&isr)?;
776691

777-
self.wfi()?;
692+
self.wfi();
778693
sys_irq_control(notification, true);
779694
}
780695
}
@@ -852,7 +767,7 @@ impl I2cController<'_> {
852767
break;
853768
}
854769

855-
self.wfi()?;
770+
self.wfi();
856771
sys_irq_control(notification, true);
857772
}
858773
}
@@ -1179,50 +1094,3 @@ impl I2cController<'_> {
11791094
}
11801095
}
11811096
}
1182-
1183-
/// Waits for `event_mask` or `timeout`, whichever comes first. This is
1184-
/// factored out of `wfi` above for clarity.
1185-
///
1186-
/// This function, like `userlib::hl`, assumes that notification bit 31 is safe
1187-
/// to use for the timer. If `event_mask` also has bit 31 set, weird things
1188-
/// will happen, don't do that.
1189-
fn wfi_raw(event_mask: u32, timeout: I2cTimeout) -> I2cControlResult {
1190-
const TIMER_NOTIFICATION: u32 = 1 << 31;
1191-
1192-
// If the driver passes in a timeout that is large enough that it would
1193-
// overflow the kernel's 64-bit timestamp space... well, we'll do the
1194-
// best we can without compiling in an unlikely panic.
1195-
let dead = sys_get_timer().now.saturating_add(timeout.0);
1196-
1197-
sys_set_timer(Some(dead), TIMER_NOTIFICATION);
1198-
1199-
loop {
1200-
let received = sys_recv_notification(event_mask | TIMER_NOTIFICATION);
1201-
1202-
// If the event arrived _and_ our timer went off, prioritize the event and
1203-
// ignore the timeout.
1204-
if received & event_mask != 0 {
1205-
// Attempt to clear our timer. Note that this does not protect against
1206-
// the race where the kernel decided to wake us up, but the timer went
1207-
// off before we got to this point.
1208-
sys_set_timer(None, TIMER_NOTIFICATION);
1209-
break I2cControlResult::Interrupted;
1210-
} else {
1211-
// The timer bit must have been set. Verify that our timer has
1212-
// actually expired:
1213-
if sys_get_timer().now >= dead {
1214-
break I2cControlResult::TimedOut;
1215-
}
1216-
1217-
// Otherwise, one of two things has happened:
1218-
// 1. Some joker has posted to our timer bit. Ha ha very funny.
1219-
// 2. The timer bit was _already set_ on entry to this routine, as a
1220-
// hangover from a previous run.
1221-
//
1222-
// We don't need to re-set our timer here because we sampled
1223-
// sys_get_timer _after_ receiving notifications, meaning it either
1224-
// hasn't gone off, or it has gone off but that fact is still stored
1225-
// in our notification bits.
1226-
}
1227-
}
1228-
}

0 commit comments

Comments
 (0)