Skip to content

Improve packet validation, receive buffer handling, and processing efficiency - #111

Open
AlanJAS wants to merge 17 commits into
PlatformLab:mainfrom
AlanJAS:main
Open

AlanJAS wants to merge 17 commits into
PlatformLab:mainfrom
AlanJAS:main

Conversation

@AlanJAS

@AlanJAS AlanJAS commented Oct 3, 2026

Copy link
Copy Markdown

This PR fixes several correctness issues and reduces repeated work in transmission, reception, qdisc, and trace analysis.

  • Validate GRANT/RESEND priorities, normalize configured priority counts, and check GRO headers before accessing packet fields.
  • Handle GSO segmentation failures and roll back partially registered offload callbacks.
  • Bound partial receive copies and add a dedicated buffer-release ioctl so releasing buffers cannot dequeue another message.
  • Cache transmit fragment positions, grow fragment arrays geometrically, and stop ordered gap scans when no overlap is possible.
  • Avoid redundant queue locking in qdisc and free detached packets after releasing the shared lock.
  • Replace repeated front removals in tthoma.py with deque operations.
  • Correct per-CPU GRANT aggregation and elapsed-time reporting in ttmerge.py.

Validation: All 851 kernel-mock unit tests pass with ASan using local compatibility adaptations for the available headers. Differential checks preserve fragment lookup, packet accounting, and trace-analysis results.
Isolated CPU microbenchmarks show approximately 30× faster sequential fragment lookup and 4.9× faster fragment-array allocation for 1 MB messages with 4 KiB pages. Small-message paths show minor additional overhead. These measurements use mocked kernel dependencies; end-to-end throughput and RPC latency have not been measured.

AlanJAS added 14 commits October 3, 2026 02:15
Validate wire priorities before mutating RPC state or indexing priority_map.
Add regression tests for priorities 8 and 255.
Copy only the requested bytes across discontiguous buffer pages. Avoid unsigned offset wraparound in copy_out, get, and contiguous.
Add HOMAIOCRELEASE using the existing receive-argument layout. Return
unreleased tokens after a partial failure and wake buffer waiters. Use it
in receiver::release and document the required module support.
Preserve skb_segment error pointers and handle an empty result before
accessing IP headers. Add allocation-failure injection and a regression test.
Stop on linearization failure and check common and type-specific lengths.
Share the existing header-length table with GRO. Add regression tests for
truncated headers, an invalid type, and a failed pull.
Extend the existing upper clamp with a lower bound of one before walking
the cutoff array. Cover zero, minus one, and INT_MIN in regression tests.
Do not attempt IPv6 registration if IPv4 fails. Unregister IPv4 if IPv6
fails, so a failed module load cannot leave callbacks registered. Add
fault injection, registration tracking, and retry regression tests.
Cache the starting fragment and its byte offset under the RPC lock.
Sequential sends avoid repeated prefix scans; earlier retransmissions
restart at zero. Add reuse and backward-offset regression coverage.
Avoid repeated prefix copies when high-order allocation falls back to
small pages. If the larger allocation fails, retry the original minimum
capacity before reporting ENOMEM. Cover fallback growth and retry.
Use the existing sorted gap invariant to reject early duplicates after
the first nonoverlapping gap. Preserve duplicate handling and metrics.
Add a regression test that checks only one gap is examined.
Use deques for GRO handoffs and sorted clock synchronization samples to make front removals O(1), preserving FIFO order and analysis results.
Use __skb_dequeue under the existing qdev lock to avoid redundant queue locking. Detach deferred packets while holding the lock, then free them after releasing it to reduce contention.
@johnousterhout

johnousterhout commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Thanks very much for this PR. I'm pretty backlogged right now so it may be a couple of weeks before I get to this, but I wanted to let you know that it's now on my radar.

Preserve tests from both branches and update homa_tx_pool_free calls to include the NUMA node ID.
@johnousterhout

Copy link
Copy Markdown
Member

I have started working on merging these commits, and have a question.

In 1b4a677, why is it important not to attempt to initialize IPv6 if IPv4 fails?

randomizedcoder added a commit to randomizedcoder/HomaModule that referenced this pull request Oct 22, 2026
Move round-1 reports to analysis-reports/round1/ and add round 2:
static analysis against 6.18.57 (main does not build there: backported
recvmsg/bind API changes), real-code fuzz-mock campaigns through
homa_softirq (one new finding: zero-length incoming message indexes
small_msg_bytes[-1]; GRANT/RESEND priority OOB duplicates PR PlatformLab#111), and
the PR candidate list. Update STATUS.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AlanJAS

AlanJAS commented Oct 22, 2026 •

Copy link
Copy Markdown
Author

Because continuing could leave IPv6 callbacks registered after the module fails to load.

Before the fix, this sequence was possible:

  • inet_add_offload() fails for IPv4
  • inet6_add_offload() succeeds, registering pointers to Homa’s offload callbacks
  • homa_offload_init() returns the IPv4 error
  • The caller aborts module loading

The crucial detail is that the caller sets init_offload = true only after homa_offload_init() succeeds. Its error path therefore skips homa_offload_end() in this case, leaving the IPv6 registration behind. You can see that in [the initialization and cleanup code (1).

Those registered pointers can then outlive the module’s code and data. A later networking operation could dereference freed memory or call unloaded code, potentially crashing the kernel. The stale registration can also interfere with a subsequent load attempt.

The commit (2) makes setup all-or-nothing:

  • If IPv4 registration fails, return immediately
  • If IPv6 registration fails, unregister the successful IPv4 registration

IPv6 itself doesn’t depend on IPv4 here. Attempting IPv6 afterward could be safe with additional rollback logic, but returning immediately is simpler because the IPv4 failure already means the overall initialization will fail.

(1)

HomaModule/homa_plumbing.c

Lines 609 to 649 in 1b4a677

status = homa_offload_init();
if (status != 0) {
pr_err("Homa couldn't init offloads\n");
goto error;
}
init_offload = true;
status = homa_qdisc_register();
if (status != 0) {
pr_err("Homa couldn't load its qdisc: error %d\n", status);
goto error;
}
init_qdisc = true;
#endif /* See strip.py */
timer_kthread = kthread_run(homa_timer_main, homa, "homa_timer");
if (IS_ERR(timer_kthread)) {
status = PTR_ERR(timer_kthread);
pr_err("couldn't create Homa timer thread: error %d\n",
status);
timer_kthread = NULL;
goto error;
}
#ifndef __STRIP__ /* See strip.py */
homa_hijack_init();
#endif /* See strip.py */
#ifndef __UPSTREAM__ /* See strip.py */
tt_set_temp(homa->temp);
#endif /* See strip.py */
return 0;
error:
if (timer_kthread)
kthread_stop(timer_kthread);
#ifndef __STRIP__ /* See strip.py */
if (init_qdisc)
homa_qdisc_unregister();
if (init_offload)
homa_offload_end();

(2) 1b4a677

@johnousterhout

johnousterhout commented Oct 23, 2026 via email

Copy link
Copy Markdown
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants