-
Notifications
You must be signed in to change notification settings - Fork 288
fix(iroh): Re-batch received relay datagram batches in case they exceed max_receive_segments
#3414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ce1c82f
dd65c89
70ac15b
1cae9a2
48bdc0d
5d5398e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -157,6 +157,49 @@ impl<T: AsRef<[u8]>> From<T> for Datagrams { | |
} | ||
|
||
impl Datagrams { | ||
/// Splits the current datagram into at maximum `num_segments` segments, potentially returning | ||
/// the batch with at most `num_segments` and leaving only the rest in `self`. | ||
/// | ||
/// Calling this on a datagram batch that only contains a single datagram (`segment_size == None`) | ||
/// will result in returning essentially `Some(self.clone())`, while making `self` empty afterwards. | ||
/// | ||
/// Calling this on a datagram batch with e.g. 15 datagrams with `num_segments == 10` will | ||
/// result in returning `Some(datagram_batch)` where that `datagram_batch` contains the first | ||
/// 10 datagrams and `self` contains the remaining 5 datagrams. | ||
/// | ||
/// Calling this on a datagram batch that doesn't contain `num_segments` datagrams, but less | ||
/// will result in making `self` empty and returning essentially a clone of `self`. | ||
/// | ||
/// Calling this on an empty datagram batch (i.e. one where `contents.is_empty()`) will return `None`. | ||
pub fn take_segments(&mut self, num_segments: usize) -> Option<Datagrams> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Goodness I love |
||
if self.contents.is_empty() { | ||
return None; | ||
} | ||
|
||
let Some(segment_size) = self.segment_size else { | ||
let contents = std::mem::take(&mut self.contents); | ||
return Some(Datagrams { | ||
ecn: self.ecn, | ||
segment_size: None, | ||
contents, | ||
}); | ||
}; | ||
|
||
let usize_segment_size = usize::from(u16::from(segment_size)); | ||
let max_content_len = num_segments * usize_segment_size; | ||
let contents = self | ||
.contents | ||
.split_to(std::cmp::min(max_content_len, self.contents.len())); | ||
|
||
let is_datagram_batch = num_segments > 1 && usize_segment_size < contents.len(); | ||
|
||
Some(Datagrams { | ||
ecn: self.ecn, | ||
segment_size: is_datagram_batch.then_some(segment_size), | ||
contents, | ||
}) | ||
} | ||
|
||
fn write_to<O: BufMut>(&self, mut dst: O) -> O { | ||
let ecn = self.ecn.map_or(0, |ecn| ecn as u8); | ||
dst.put_u8(ecn); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,6 +100,25 @@ impl RelayTransport { | |
} | ||
}; | ||
|
||
if buf_out.len() < dm.datagrams.contents.len() { | ||
// Our receive buffer isn't big enough to process this datagram. | ||
// Continuing would cause a panic. | ||
warn!( | ||
quinn_buf_len = buf_out.len(), | ||
datagram_len = dm.datagrams.contents.len(), | ||
segment_size = ?dm.datagrams.segment_size, | ||
"dropping received datagram: quinn buffer too small" | ||
); | ||
break; | ||
matheus23 marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One, in my opinion large, downside of your approach is that you have to plumb the signal of how large this an be all the way through to the ActiveRelayActor. I find all that plumbing really unfortunate and complex. Have you considered having an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't consider that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW I think this is very similar to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you really need to mess with wakers? When you receive something that doesn't fit you will return a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think you're right. I'll look into following up this PR with a little bit of a cleanup. |
||
// In theory we could put some logic in here to fragment the datagram in case | ||
// we still have enough room in our `buf_out` left to fit a couple of | ||
// `dm.datagrams.segment_size`es, but we *should* have cut those datagrams | ||
// to appropriate sizes earlier in the pipeline (just before we put them | ||
// into the `relay_datagram_recv_queue` in the `ActiveRelayActor`). | ||
// So the only case in which this happens is we receive a datagram via the relay | ||
// that's essentially bigger than our configured `max_udp_payload_size`. | ||
// In that case we drop it and let MTU discovery take over. | ||
} | ||
buf_out[..dm.datagrams.contents.len()].copy_from_slice(&dm.datagrams.contents); | ||
meta_out.len = dm.datagrams.contents.len(); | ||
meta_out.stride = dm | ||
|
Uh oh!
There was an error while loading. Please reload this page.