Skip to content

Pr/fredi/worker liveness check - #1665

Merged
qmonnet merged 13 commits into
mainfrom
pr/fredi/worker_liveness_check
Jul 29, 2026
Merged

Pr/fredi/worker liveness check#1665
qmonnet merged 13 commits into
mainfrom
pr/fredi/worker_liveness_check

Conversation

@Fredi-raspall

@Fredi-raspall Fredi-raspall commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #1653

  • Adds infra to periodically check the status of dataplane workers and their rx tasks
  • Adds a watchdog per worker rx task such that, if not patted in time, an error log is issued.
  • Since logs are gone with the wind, the state of the workers and their RX tasks is exposed through the cli, with current and past stats that can help diagnosing.
  • Note: the state of a worker observed will always be "running" since the process shutdowns as soon as a thread panics.

Sample cli output

For each task, it shows the status (stuck|idle|Active) and

  • pps: packets per second received in last sample
  • total pkts received / sent by the task
  • total drops by pipeline (per task)
  • total drops due to xmit (and serialization).
  • the number of times the task missed to pat its watchdog in time.
dataplane(✔)# show driver status
 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Packet driver status ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 max rx batch: 128 pkts
 activity poll: 1 s  watchdog pat: 2 s  watchdog check: 6 s

   iface             status             pps                pkt-rx                pkt-tx  ppline-drops  tx-drops  wd-misses
 worker 0: running
   eth0              Idle               0.0                     0                     0             0         0          0
   eth1              Idle               0.0                     0                     0             0         0          0
   eth2              Idle               0.0                     0                     0             0         0          0

 worker 1: running
   eth0              Idle               0.0                     0                     0             0         0          0
   eth1              Idle               0.0                     0                     0             0         0          0
   eth2              Idle               0.0                     0                     0             0         0          0

 worker 2: running
   eth0              Idle               0.0                     0                     0             0         0          0
   eth1              Idle               0.0                     0                     0             0         0          0
   eth2              Idle               0.0                     0                     0             0         0          0

[..]

 worker 9: running
   eth0              Active         16658.0               1262725               1262551           174         0          0
   eth1              Idle               0.0                    14                     0            14         0          0
   eth2              Active             4.0                   175                     0           175         0          0

Copilot AI review requested due to automatic review settings July 25, 2026 17:11
@Fredi-raspall
Fredi-raspall requested a review from a team as a code owner July 25, 2026 17:11
@Fredi-raspall
Fredi-raspall requested review from daniel-noland and removed request for a team July 25, 2026 17:11
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds watchdog-based kernel worker supervision and publishes per-worker driver status through shared storage. Wires status into runtime, router sources, and a new show driver status CLI command. Replaces the router IO exit guard with a reusable lifecycle utility and adds shutdown logging.

Changes

Driver status and lifecycle

Layer / File(s) Summary
Status contracts and watchdog storage
common/src/cliprovider.rs, dataplane/Cargo.toml, dataplane/src/drivers/{mod,status,watchdog}.rs
Adds atomic watchdog tracking, driver status models, shared status access, formatting, and CLI provider support.
Worker instrumentation and supervision
dataplane/src/drivers/kernel/{mod,worker}.rs
Adds per-interface watchdogs, packet counters, worker monitors, liveness checks, status publication, and configurable packet batching.
Runtime status and CLI wiring
dataplane/src/runtime.rs, dataplane/src/packet_processor/mod.rs, routing/src/{cli/handler.rs,router/mod.rs}, cli/{bin/cmdtree_dp.rs,src/cliproto.rs}
Passes status readers and writers through startup, exposes them as CLI sources, and adds the show driver status action and command tree.
Shared lifecycle exit guards
lifecycle/src/{lib.rs,utils.rs}, routing/src/router/{rio.rs,mod.rs}
Introduces reusable ExitGuard handling, uses it for RIO termination, and updates shutdown log levels.

Possibly related PRs

Suggested reviewers: daniel-noland

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and directly describes the main change: a worker liveness check.
Description check ✅ Passed The description matches the changeset and summarizes the new watchdog and CLI status output.
Linked Issues check ✅ Passed The changes implement worker watchdogs, periodic liveness checks, and CLI status exposure for #1653.
Out of Scope Changes check ✅ Passed No clear out-of-scope code changes are evident; the edits support the worker liveness/status feature.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Comment @coderabbitai help to get the list of available commands.

@Fredi-raspall Fredi-raspall added ci:+release Enable VLAB release tests ci:+vlab Enable VLAB tests labels Jul 25, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds infrastructure for monitoring dataplane driver worker/rx-task liveness and activity, and exposes that state via a new CLI command for operational diagnostics.

Changes:

  • Introduces a per-rx-task lock-free watchdog and a supervisor loop that periodically checks/rearms it and aggregates counters.
  • Publishes driver worker/rx-task status through a shared slot and exposes it via show driver status in the CLI.
  • Centralizes thread/task unexpected-exit fatal reporting via a reusable lifecycle::ExitGuard.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
routing/src/router/rio.rs Switches RIO thread exit guarding to the shared ExitGuard helper and adds an exit log.
routing/src/router/mod.rs Adds a driver_status CLI data source and adjusts router stop logging level.
routing/src/cli/handler.rs Wires ShowDriverStatus to the corresponding CLI provider.
lifecycle/src/utils.rs Adds reusable ExitGuard for fatal-on-unexpected-exit behavior.
lifecycle/src/lib.rs Exposes the new lifecycle utils module and re-exports ExitGuard.
dataplane/src/runtime.rs Creates driver status read/write access and threads it through router + driver startup.
dataplane/src/packet_processor/mod.rs Adds driver-status provider to routing CLI sources.
dataplane/src/drivers/watchdog.rs Implements a lock-free watchdog and activity tracking for rx tasks.
dataplane/src/drivers/status.rs Defines driver/worker/rx-task status types, publication mechanism, and CLI formatting.
dataplane/src/drivers/mod.rs Exposes the new status and watchdog driver modules.
dataplane/src/drivers/kernel/worker.rs Adds rx-task watchdog pat/recording and returns worker monitors for supervision.
dataplane/src/drivers/kernel/mod.rs Adds worker supervisor loop that checks watchdogs and publishes status periodically.
dataplane/Cargo.toml Adds common dependency needed for CLI provider integration.
common/src/cliprovider.rs Switches CLI provider support from ArcSwap* to Slot*.
cli/src/cliproto.rs Adds ShowDriverStatus CLI action.
cli/bin/cmdtree_dp.rs Adds show driver status command wiring to the CLI tree.
Cargo.lock Updates lockfile for new/adjusted dependencies.

Comment thread dataplane/src/drivers/kernel/worker.rs Outdated
Comment on lines 464 to 468
match guard.try_io(|fd| {
packet_recv(
id,
intf.if_name.as_str(),
fd.as_raw_fd(),
Comment thread lifecycle/src/utils.rs Outdated
Comment thread dataplane/src/drivers/kernel/mod.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
dataplane/src/drivers/kernel/worker.rs (1)

501-506: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

A bool return conflates intentional pipeline drops with real TX failures.

Packets that the pipeline emits with a DoneReason and no oif return false here, so the caller (line 242) counts them as tx_drops alongside genuine serialize/write failures. Consider a small enum (Sent / Dropped / Failed) so total_tx_drops reflects only transmit errors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dataplane/src/drivers/kernel/worker.rs` around lines 501 - 506, Replace the
bool result of tx_packet with a three-state outcome distinguishing Sent,
Dropped, and Failed. Return Dropped for pipeline-completed packets with no oif,
Failed only for serialization or write errors, and update the caller’s tx_drops
accounting to count only Failed outcomes while preserving successful sends.
🧹 Nitpick comments (4)
dataplane/src/drivers/watchdog.rs (2)

116-124: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider deriving Debug on Activity.

ActivityRecord derives Debug but the enclosing enum does not, so Activity cannot be used in {:?} diagnostics.

♻️ Proposed change
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub enum Activity {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dataplane/src/drivers/watchdog.rs` around lines 116 - 124, Update the
Activity enum derive attributes to include Debug, relying on ActivityRecord’s
existing Debug implementation so all variants support {:?} diagnostics.

51-84: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the four identical accumulate blocks into a helper.

Each field repeats the same fetch_update + saturating_add pattern. A small free function would cut this to four calls.

♻️ Proposed refactor
+fn accumulate(counter: &AtomicU64, delta: u64) {
+    if delta > 0 {
+        let _ = counter.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
+            Some(v.saturating_add(delta))
+        });
+    }
+}
+
 impl Watchdog {
     pub fn record(&self, rx: u64, tx: u64, ppline_drops: u64, tx_drops: u64) {
-        if rx > 0 {
-            let _ = self
-                .0
-                .rx
-                .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
-                    Some(v.saturating_add(rx))
-                });
-        }
-        if tx > 0 {
-            ...
-        }
+        accumulate(&self.0.rx, rx);
+        accumulate(&self.0.tx, tx);
+        accumulate(&self.0.ppline_drops, ppline_drops);
+        accumulate(&self.0.tx_drops, tx_drops);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dataplane/src/drivers/watchdog.rs` around lines 51 - 84, Refactor the
repeated accumulation logic in Watchdog::record into a small free helper that
performs fetch_update with Relaxed ordering and saturating_add. Replace the four
field-specific blocks with calls to that helper, preserving the existing
positive-value checks and counter behavior.
dataplane/src/drivers/kernel/worker.rs (1)

358-366: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Replace the name-based watchdog lookup with a positional zip.

ifmonitors is built by mapping over this same interfaces slice in Worker::start, so it is already index-parallel. Zipping removes the O(n²) scan and the unreachable!() panic path, which would otherwise kill the worker thread if a name ever failed to match.

♻️ Proposed refactor
-    for kif in interfaces {
-        // find the watchdog for this interface
-        let watchdog = ifmonitors
-            .iter()
-            .find(|ifm| *ifm.ifname == *kif.name)
-            .map_or_else(|| unreachable!(), |ifm| ifm.watchdog.clone());
-
+    debug_assert_eq!(interfaces.len(), ifmonitors.len());
+    for (kif, ifm) in interfaces.iter().zip(ifmonitors) {
+        debug_assert_eq!(*ifm.ifname, *kif.name);
+        let watchdog = ifm.watchdog.clone();
+
         let (writer, reader) =
             create_worker_interface(id, total_workers, &kif.name, kif.ifindex, watchdog)?;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dataplane/src/drivers/kernel/worker.rs` around lines 358 - 366, Update the
interface iteration in Worker::start to zip interfaces with ifmonitors and
obtain each watchdog from the paired monitor directly. Remove the name-based
find/map_or_else lookup and its unreachable!() fallback while preserving the
existing create_worker_interface arguments and error propagation.
dataplane/src/drivers/kernel/mod.rs (1)

50-51: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Narrow the cast_precision_loss allow to the offending statement.

Applying it to the whole impl block suppresses the lint for all future code in it. Only the pps computation at line 246 needs it.

♻️ Proposed change
-#[allow(clippy::cast_precision_loss)]
 impl DriverKernel {

Then at the pps assignment:

#[allow(clippy::cast_precision_loss)]
{
    rx_task_status.pps = record.rx as f64 / f64::from(Self::TASK_POLL_PERIOD);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dataplane/src/drivers/kernel/mod.rs` around lines 50 - 51, Remove the
impl-level #[allow(clippy::cast_precision_loss)] from DriverKernel and scope the
allowance only around the pps assignment in the relevant method, preserving the
existing calculation while limiting suppression to that statement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dataplane/src/drivers/kernel/mod.rs`:
- Around line 212-255: Clear each terminated worker’s RX-task activity and
packet rate when handling the finished-worker branch in the monitor loop, and
also ensure the already-joined branch cannot retain stale values. Update the
corresponding entries in wk_status.rx_tasks to the inactive state with zero pps
while preserving accumulated counters and termination status.

In `@dataplane/src/drivers/kernel/worker.rs`:
- Around line 224-229: Update the ppline_drops calculation in the worker’s
pipeline processing flow to avoid subtracting output packet count from rx_pkts
when the pipeline expands packets. Count drops only when out_pkts.len() is less
than rx_pkts, otherwise use zero, and preserve the existing total_ppline_drops
accounting.

---

Outside diff comments:
In `@dataplane/src/drivers/kernel/worker.rs`:
- Around line 501-506: Replace the bool result of tx_packet with a three-state
outcome distinguishing Sent, Dropped, and Failed. Return Dropped for
pipeline-completed packets with no oif, Failed only for serialization or write
errors, and update the caller’s tx_drops accounting to count only Failed
outcomes while preserving successful sends.

---

Nitpick comments:
In `@dataplane/src/drivers/kernel/mod.rs`:
- Around line 50-51: Remove the impl-level #[allow(clippy::cast_precision_loss)]
from DriverKernel and scope the allowance only around the pps assignment in the
relevant method, preserving the existing calculation while limiting suppression
to that statement.

In `@dataplane/src/drivers/kernel/worker.rs`:
- Around line 358-366: Update the interface iteration in Worker::start to zip
interfaces with ifmonitors and obtain each watchdog from the paired monitor
directly. Remove the name-based find/map_or_else lookup and its unreachable!()
fallback while preserving the existing create_worker_interface arguments and
error propagation.

In `@dataplane/src/drivers/watchdog.rs`:
- Around line 116-124: Update the Activity enum derive attributes to include
Debug, relying on ActivityRecord’s existing Debug implementation so all variants
support {:?} diagnostics.
- Around line 51-84: Refactor the repeated accumulation logic in
Watchdog::record into a small free helper that performs fetch_update with
Relaxed ordering and saturating_add. Replace the four field-specific blocks with
calls to that helper, preserving the existing positive-value checks and counter
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 96ba7e23-0963-47bf-9ce7-81435ba64639

📥 Commits

Reviewing files that changed from the base of the PR and between 361d547 and 3210be7.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (16)
  • cli/bin/cmdtree_dp.rs
  • cli/src/cliproto.rs
  • common/src/cliprovider.rs
  • dataplane/Cargo.toml
  • dataplane/src/drivers/kernel/mod.rs
  • dataplane/src/drivers/kernel/worker.rs
  • dataplane/src/drivers/mod.rs
  • dataplane/src/drivers/status.rs
  • dataplane/src/drivers/watchdog.rs
  • dataplane/src/packet_processor/mod.rs
  • dataplane/src/runtime.rs
  • lifecycle/src/lib.rs
  • lifecycle/src/utils.rs
  • routing/src/cli/handler.rs
  • routing/src/router/mod.rs
  • routing/src/router/rio.rs

Comment thread dataplane/src/drivers/kernel/mod.rs
Comment thread dataplane/src/drivers/kernel/worker.rs Outdated
Copilot AI review requested due to automatic review settings July 26, 2026 13:54
@Fredi-raspall
Fredi-raspall force-pushed the pr/fredi/worker_liveness_check branch from 3210be7 to ab6c898 Compare July 26, 2026 13:54

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
dataplane/src/drivers/kernel/mod.rs (1)

139-141: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stale comment.

This comment still describes the pre-refactor "join-and-log" supervisor; the code below now also does watchdog liveness checks, per-RX-task status updates, and periodic status publishing. Worth a quick wording update for future readers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dataplane/src/drivers/kernel/mod.rs` around lines 139 - 141, Update the
supervisor comment near the worker-monitor loop to describe its current
responsibilities, including watchdog liveness checks, per-RX-task status
updates, and periodic status publishing, while retaining the note that worker
fatal reporting is handled by each worker thread’s ExitGuard.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dataplane/src/drivers/kernel/mod.rs`:
- Around line 184-195: Update the cancellation branch in the worker supervisor
loop to mark each joined worker as WorkerState::Terminated and reset its
rx_tasks activity/pps fields, matching the handle.is_finished() path. Publish
the updated workers_status with status_writer.publish(...) before breaking so
graceful-shutdown status queries cannot retain stale Running/Active state.

---

Nitpick comments:
In `@dataplane/src/drivers/kernel/mod.rs`:
- Around line 139-141: Update the supervisor comment near the worker-monitor
loop to describe its current responsibilities, including watchdog liveness
checks, per-RX-task status updates, and periodic status publishing, while
retaining the note that worker fatal reporting is handled by each worker
thread’s ExitGuard.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2448d19c-974d-40ab-a5b0-9bd6232f7d64

📥 Commits

Reviewing files that changed from the base of the PR and between 3210be7 and ab6c898.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • cli/bin/cmdtree_dp.rs
  • cli/src/cliproto.rs
  • common/src/cliprovider.rs
  • dataplane/Cargo.toml
  • dataplane/src/drivers/kernel/mod.rs
  • dataplane/src/drivers/kernel/worker.rs
  • dataplane/src/drivers/mod.rs
  • dataplane/src/drivers/status.rs
  • dataplane/src/drivers/watchdog.rs
  • dataplane/src/packet_processor/mod.rs
  • dataplane/src/runtime.rs
  • lifecycle/src/utils.rs
  • routing/src/cli/handler.rs
  • routing/src/router/mod.rs
💤 Files with no reviewable changes (1)
  • lifecycle/src/utils.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • dataplane/src/drivers/mod.rs
  • cli/src/cliproto.rs
  • routing/src/cli/handler.rs
  • routing/src/router/mod.rs
  • dataplane/src/runtime.rs
  • dataplane/src/drivers/status.rs
  • dataplane/src/drivers/watchdog.rs
  • common/src/cliprovider.rs
  • dataplane/src/drivers/kernel/worker.rs

Comment thread dataplane/src/drivers/kernel/mod.rs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

dataplane/src/drivers/kernel/mod.rs:215

confidence: 10
tags: [logic]

`ScopedJoinHandle::is_finished()` is not available on the loom/shuttle backend’s `concurrency::thread::ScopedJoinHandle` (it only provides `join`). This breaks backend portability and will fail to compile under `--features loom`.
                if let Some(handle) = monitor.handle.take() {
                    if handle.is_finished() {
                        let result = join_and_log(monitor.id, handle);
                        wk_status.state = WorkerState::Terminated(result);
</details>

Comment thread dataplane/src/drivers/kernel/mod.rs
Comment thread dataplane/src/drivers/kernel/worker.rs Outdated
Comment thread dataplane/src/drivers/kernel/worker.rs
Copilot AI review requested due to automatic review settings July 27, 2026 12:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

dataplane/src/drivers/kernel/worker.rs:270

confidence: 9
tags: [logic]

`rx_pkts + injected - output_by_pipeline` can underflow when the pipeline outputs more packets than it received (e.g., injection/duplication), wrapping to a huge `u64` and corrupting stats.
            let ppline_drops = rx_pkts + injected - output_by_pipeline;
**dataplane/src/drivers/kernel/worker.rs:390**
* ```yaml
confidence: 8
tags: [style]

This error message loses the interface context, which makes diagnosing mismatches between interfaces and ifmonitors harder.

        let watchdog = ifmonitors
            .iter()
            .find(|ifm| ifm.ifname.as_ref() == kif.name.as_str())
            .map(|ifm| ifm.watchdog.clone())
            .ok_or(io::Error::other("Failed to find interface watchdog"))?;

Comment thread dataplane/src/drivers/kernel/mod.rs
Copilot AI review requested due to automatic review settings July 27, 2026 13:13
@Fredi-raspall
Fredi-raspall force-pushed the pr/fredi/worker_liveness_check branch from fa6ddff to 297734b Compare July 27, 2026 13:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (2)

dataplane/src/drivers/kernel/worker.rs:270

confidence: 9
tags: [logic]

`ppline_drops` is computed with plain `u64` subtraction (`rx_pkts + injected - output_by_pipeline`), which can underflow if the pipeline outputs more packets than it was given (the `NetworkFunction::process` contract does not guarantee a 1:1 output). This will wrap to a huge value in release builds and corrupt drop stats.

Use saturating arithmetic so the stat is never negative.
            let ppline_drops = rx_pkts + injected - output_by_pipeline;
**dataplane/src/drivers/kernel/worker.rs:256**
* ```yaml
confidence: 8
tags: [style]

The if false { /* injected */ } block is unreachable and makes it look like injected packets are already tracked when they are not. This is easy to forget later and will keep injected permanently at 0.

Either implement the injected-packet detection or replace this with a TODO comment until the metadata exists.

                    if false {
                        /* packet was injected by us */
                        injected += 1;
                    }

Comment thread common/src/cliprovider.rs
Comment on lines +70 to 73
impl<T> CliDataProvider for SlotOption<T>
where
T: CliDataProvider,
{
Comment thread dataplane/src/drivers/kernel/mod.rs Outdated

@qmonnet qmonnet left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good as far as I can tell. I've got some minor nits and questions, but nothing blocking. Copilot's comment about MSG_DONTWAIT may need to be addressed, though?

Thanks!

Comment thread lifecycle/src/utils.rs
Comment on lines +41 to +42
}
impl Drop for ExitGuard {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Add a newline between the two impl

let cancel = subsystem.cancel_token();
let interfaces = interfaces.to_vec();

let thread_builder = thread::Builder::new().name(format!("dp-worker-{id}"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Commit title has a typo, feat(kernel-driver): do not pass thread builer (builder)

Comment thread dataplane/src/drivers/watchdog.rs
Comment thread dataplane/src/drivers/watchdog.rs Outdated
Comment thread dataplane/src/drivers/kernel/worker.rs Outdated
Comment thread dataplane/src/drivers/kernel/worker.rs
Comment thread dataplane/src/drivers/kernel/mod.rs
@Fredi-raspall

Copy link
Copy Markdown
Contributor Author

Looks good as far as I can tell. I've got some minor nits and questions, but nothing blocking. Copilot's comment about MSG_DONTWAIT may need to be addressed, though?

yeah, although it is not the result of a change in this PR.

@qmonnet qmonnet mentioned this pull request Jul 28, 2026
Add a generic ExitGuard to the lifecycle crate to avoid ad-hoc
definitions.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Do not pass a thread builder but create one in Worker::start()

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Let Worker::start() consume the Worker (non-mutably), as the
worker is not use once start() is called and this avoids several
clones(). Also, let start() return the id of the worker, in all
cases (except panic). This is needed for subsequent changes since
nowadays we rely on the ordering in the iterator of join handles
for the worker id, which will not respect ids if elements are
removed.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Reorganize Worker::start() moving the per interface task logic to
a separate method spawn_worker_interface_reader() and document the
code. This allows removing #[allow(clippy::too_many_lines)] and
has no functional change.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Copilot AI review requested due to automatic review settings July 28, 2026 17:03
@Fredi-raspall
Fredi-raspall force-pushed the pr/fredi/worker_liveness_check branch from 297734b to 1f40bba Compare July 28, 2026 17:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

dataplane/src/drivers/kernel/mod.rs:78

confidence: 10
tags: [logic]

`WorkerMonitor` is declared with a lifetime parameter (`WorkerMonitor<'scope>`), but this local type annotation omits it, which will not compile. Either include the lifetime or let inference handle the type.
) -> Result<Vec<WorkerMonitor<'scope>>, std::io::Error> {
    let mut monitors: Vec<WorkerMonitor> = Vec::with_capacity(num_workers);
**dataplane/src/drivers/kernel/mod.rs:281**
* ```yaml
confidence: 9
tags: [logic]

concurrency::thread::ScopedJoinHandle under the loom backend (and likely shuttle) does not provide is_finished(), so this supervisor loop won’t compile under those backends. Consider cfg-gating the non-blocking termination check and falling back to only joining on cancellation for loom/shuttle.

                    if let Some(handle) = monitor.handle.take() {
                        if handle.is_finished() {
                            // join the worker
                            let result = Self::join_worker(monitor.id, handle);

dataplane/src/drivers/kernel/worker.rs:271

confidence: 9
tags: [logic]

`rx_pkts + injected - output_by_pipeline` can underflow when the pipeline outputs more packets than it consumes (e.g., injection/duplication), which will wrap in release builds and corrupt stats. Use a saturating/checked computation to keep stats bounded.
            let ppline_drops = rx_pkts + injected - output_by_pipeline;
**common/src/cliprovider.rs:73**
* ```yaml
confidence: 8
tags: [docs]

This impl is now for SlotOption<T>, but the inline comment below still references arc_swap::ArcSwapOption. Updating that comment would avoid confusion about why type inference is needed here (backend-dependent Arc types).

impl<T> CliDataProvider for SlotOption<T>
where
    T: CliDataProvider,
{

dataplane/src/drivers/status.rs:6

confidence: 8
tags: [docs]

The module docs say these types ideally wouldn’t depend on the driver type, but this module imports `DriverKernel` for formatting constants. Either remove that dependency or update the docs to match the current design.

//! Driver status, published by the supervisor
//! Ideally, these types would not depend on the type of driver.
//!

</details>

The watchdog is meant to detect if a worker's rx task is stuck
and also measure its activity, such as the packets it received,
sent or dropped on tx or its pipeline.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Defines type DriverStatus that summarizes the state of the kernel
driver. The model is as follows.

The DriverStatus has list of WorkerStatus, which indicates the
state of a worker thread. A worker can be running or terminated.

Each WorkerStatus in turn contains a list of RxTaskStatus
one for each rx task spawn by the worker (there is a task per
interface). A RxTaskStatus indicates the status of the task
(idle, active or stuck) and keeps activity stats for the task,
such as the number of packets it processed, sent or dropped.

This commit also defines types DriverStatusReader/Writer to be able
to access the status of a driver from other threads and implements
Display for the above types to be able to show their contents over
the CLI.

Lastly, several constants (defaults) used to monitor the state of
workers and their tasks are introduced.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Reworks worker start, the supervisor and worker rx tasks as follows.

1) When started, each worker gets a WorkerMonitor for the supervisor
   to monitor its state and that of its rx tasks. Each task is
   monitored with a WorkerIfaceMonitor.

2) Each worker rx task has a WorkerInterfaceReader that is augmented
   with a watchdog that the rx task has to pat in time. The watchdog
   object also includes activity stats set by the task to know how
   active it is.

3) the prior worker supervisor would just wait (join) for workers
   to finish and cancel them if needed. It is reworked now to loop
   and periodically check worker's status and that of their tasks,
   arming the watchdogs and collecting rx task stats. Watchdog
   checking (and rearm), and activity collection are decoupled.

4) When started, the kernel driver is given a DriverStatusWriter
   for it to publish the status of the workers and their tasks
   outside of the supervisor thread. The CLI is given a
   DriverStatusReader to be able to access the status and show it.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Adds a DoneReason in a case that should never happen in practice

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
All packets being processed by a pipeline should get a verdict
(DoneReason) at the end of it, which is checked by the last stage
that collects stats. Prefer an error log over a panic if a
packet does not get a verdict.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Copilot AI review requested due to automatic review settings July 28, 2026 19:13
@Fredi-raspall
Fredi-raspall force-pushed the pr/fredi/worker_liveness_check branch from 1f40bba to 408e8a3 Compare July 28, 2026 19:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 19 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

dataplane/src/drivers/kernel/mod.rs:77

confidence: 9
tags: [logic]

`WorkerMonitor` has a lifetime parameter (`WorkerMonitor<'scope>`), so `Vec<WorkerMonitor>` is not a valid concrete type here and can fail to compile depending on the compiler/version. Let inference pick up the correct lifetime from the function’s return type (or spell it explicitly).
    let mut monitors: Vec<WorkerMonitor> = Vec::with_capacity(num_workers);
**dataplane/src/drivers/kernel/mod.rs:280**
* ```yaml
confidence: 9
tags: [logic]

handle.is_finished() is used here, but the loom implementation of concurrency::thread::ScopedJoinHandle does not define is_finished (see concurrency/src/thread/loom_scope.rs, where only join(self) exists). This makes the driver supervisor non-portable to loom/shuttle backends.

If you need to keep the non-blocking check, concurrency::thread::ScopedJoinHandle should grow an is_finished(&self) -> bool API across backends.

                    if let Some(handle) = monitor.handle.take() {
                        if handle.is_finished() {
                            // join the worker

dataplane/src/drivers/kernel/worker.rs:260

confidence: 9
tags: [logic]

This block contains dead code (`if false`) so `injected` is always 0, and `rx_pkts + injected - num_out_pkts` can underflow (wrap to a huge `u64`) if the pipeline ever emits more packets than it ingests (which the surrounding comment explicitly calls out as possible with injection). Use saturating arithmetic and remove the placeholder branch.
            let mut injected: u64 = 0;

            // send each of the packets
            for out_pkt in out_pkts {
                if false {
**dataplane/src/drivers/kernel/mod.rs:126**
* ```yaml
confidence: 10
tags: [docs, logic]

The docstring says this returns true when the subsystem was cancelled, but the implementation returns true when it is not cancelled. This makes the supervisor loop logic harder to reason about.

    /// Check if the `Subsystem` got cancelled (and we must shutdown). If so,
    /// join all of the workers. This will wait for all workers to finish.
    /// Returns `true` if the subsystem was cancelled and `false` otherwise.
    fn must_run(

@Fredi-raspall
Fredi-raspall requested a review from qmonnet July 28, 2026 19:18
@Frostman

Copy link
Copy Markdown
Member

Seems like iperf3 is just fine:

[SUM]   0.00-10.01  sec   140 GBytes   120 Gbits/sec  273790             sender
[SUM]   0.00-10.01  sec   140 GBytes   120 Gbits/sec                  receiver

@qmonnet qmonnet left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fredi-raspall requested a review from qmonnet 15 hours ago

I already approved the previous version, this still looks good 🙂

@qmonnet
qmonnet added this pull request to the merge queue Jul 29, 2026
Merged via the queue into main with commit 75601ee Jul 29, 2026
50 of 52 checks passed
@qmonnet
qmonnet deleted the pr/fredi/worker_liveness_check branch July 29, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:+release Enable VLAB release tests ci:+vlab Enable VLAB tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Watchdog for worker threads

4 participants