feat(rfm): add rfm-ring mode for one-shot PCIe ring-continuity check

This commit is contained in:
Tony Cao 2026-09-24 15:34:25 +08:00
parent 3f29a86239
commit 8dca6395d0
2 changed files with 29 additions and 3 deletions

View File

@ -16,6 +16,7 @@
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use std::os::unix::io::AsRawFd;
use el6695_rt::{bridge, regs, rt, stats};
use ethercrab::{
@ -65,6 +66,8 @@ enum Mode {
/// RFM bridge: EL6695 RX -> RFM -> remote echo -> RFM -> EL6695 TX.
/// End-to-end CX<->x86 latency via reflective memory hop.
RfmBridge,
/// One-shot hardware ring-continuity check (prints ring=OK/BROKEN, exits).
RfmRing,
}
#[derive(Clone, Copy, Debug, PartialEq)]
@ -154,6 +157,7 @@ fn parse_args() -> Result<Config, String> {
"rfm-ping" => Mode::RfmPing,
"rfm-echo" => Mode::RfmEcho,
"rfm-bridge" => Mode::RfmBridge,
"rfm-ring" => Mode::RfmRing,
other => return Err(format!("unknown mode '{}'", other)),
}
}
@ -239,6 +243,7 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
Mode::RfmPing => "rfm-ping",
Mode::RfmEcho => "rfm-echo",
Mode::RfmBridge => "rfm-bridge",
Mode::RfmRing => "rfm-ring",
},
cfg.duration_sec,
cfg.cycle_us,
@ -249,7 +254,14 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
let pdu_storage = PDU_STORAGE.get_or_init(PduStorage::new);
// ---- RFM-only modes: no EtherCAT bring-up needed ----
if matches!(cfg.mode, Mode::RfmPing | Mode::RfmEcho) {
if matches!(cfg.mode, Mode::RfmPing | Mode::RfmEcho | Mode::RfmRing) {
if cfg.mode == Mode::RfmRing {
let rfm = el6695_rt::rfm::Rfm::open_path(cfg.rfm_dev.as_deref())?;
let fd = rfm.device_fd.as_raw_fd();
let ok = el6695_rt::rfm::ring_cont(fd)?;
println!("ring={}", if ok { "OK" } else { "BROKEN" });
std::process::exit(if ok { 0 } else { 1 });
}
return rfm_mode_main(cfg).await;
}

View File

@ -65,8 +65,7 @@ fn ioctl(fd: i32, req: u32, arg: usize) -> io::Result<i32> {
}
pub struct Rfm {
#[allow(dead_code)]
device_fd: File,
pub device_fd: File,
/// BAR0 (registers) mapping, if present.
pub regs: Option<&'static mut [u8]>,
/// BAR2 reflective memory mapping.
@ -326,6 +325,21 @@ pub fn now_ns() -> u64 {
ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64
}
/// Hardware ring-continuity check: IOCTL_RFM2G_CHECK_RING_CONT sends a packet
/// around the ring (to our own node id) and waits (up to ~500ms in the
/// driver) for it to come back. Returns Ok(true) if the ring is complete
/// (fiber OK + all nodes alive), Ok(false) if broken. NOTE: the driver
/// busy-waits, so call only during a suspected outage, never per-cycle.
pub fn ring_cont(fd: i32) -> io::Result<bool> {
// _IOR(RFM2G_MAGIC=0xeb, 52, RFM2G_UINT8)
const IOCTL_RFM2G_CHECK_RING_CONT: u32 = ior(0xeb, 52, 1);
let mut out: u8 = 0;
match ioctl(fd, IOCTL_RFM2G_CHECK_RING_CONT, &mut out as *mut u8 as usize) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
// ---- device-memory-safe bulk access ----
//
// The reflective memory is mapped non-cached (pgprot_noncached /