Skip to content
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

feat(s2n-quic-platform): wire up tokio sockets to ring #1790

Merged
merged 2 commits into from
Jun 15, 2023

Conversation

camshaft
Copy link
Contributor

@camshaft camshaft commented Jun 5, 2023

Description of changes:

This change implements the new socket traits for tokio sockets.

Testing:

Tests are finally added in #1792.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@camshaft camshaft force-pushed the camshaft/tokio-socket-task branch from b61c15a to 291fd06 Compare June 8, 2023 17:53
events: &mut tx::Events,
) -> io::Result<()> {
let mut index = 0;
while let Some(entry) = entries.get_mut(index) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

My sense is that this can be a regular for loop - we exit early, but the indices here aren't ever stored back anywhere etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep. i think that was left over from something else and i forgot to refactor it back to for

}
Poll::Pending => {
events.blocked();
break;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This specific code makes me wonder if an interface like events.finished(count: usize, res: Poll<Result<T, E>>) would reduce duplication and make things clearer in terms of why we increment self.pending in on_error, for example.

}
}
Poll::Ready(Err(err)) => {
if events.on_error(err).is_break() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

So if I'm reading on_error right and it's not updated later in this PR somewhere, if we keep hitting errors we're going to spin here rather than exiting? Not entirely sure what kind of error that could be...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah so the general case for TX errors is discarding the packet and moving on to the next one. The only reason we'd break is if we got a WouldBlock or Interrupted. Outside of these, the socket could return errors for various reasons like the address not being reachable or MTU being too large (for platforms that don't have a way to disable kernel-level MTU probing). In these cases, the packet transmission will always fail so we just discard it and let the connection assume it was lost.

Copy link
Collaborator

Choose a reason for hiding this comment

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

OK, I think the latest version (with the for loop) fixes the issue I was worried about, or at least does within this function, where we'd get an Err, but not increment the pointer into entries and so infinitely spin in this loop trying to send (and failing).

}
Interrupted => ControlFlow::Break(()),
#[cfg(s2n_quic_platform_gso)]
_ if errno::errno().0 == libc::EIO => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This feels wrong - shouldn't this be error.raw_os_error()? errno here could be anything if we ran some other code between the relevant syscall and here (e.g., for logging or w/e).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I... somehow completely missed that API. Yes we should use that instead.

pub cmsg: Aligned<[u8; cmsg::MAX_LEN]>,
}

#[repr(C, align(8))]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly, how is alignment important? Why 8? I think this deserves a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can comment but the cmsg needs to be aligned to the same as cmsghdr. And I wasn't sure how to make the alignment derived from that so i just made it 8 everywhere :smile. The other fields probably don't need the alignment specification so I can remove those if needed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That makes sense.

unsafe fn update(&mut self, entry: &mut msghdr, payload: &UnsafeCell<u8>, payload_len: u32) {
let iovec = self.iovec.0.get_mut();

iovec.iov_base = payload.get() as *mut _;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would pass/keep payload as *mut u8 rather than &UnsafeCell<u8> - I think as-is this is likely UB under stacked borrows (a proposed memory model), at least when we end up using the iov_base to do useful things.

(cc rust-lang/unsafe-code-guidelines#134)

let storage = T::alloc(entries, payload_len, DATA_OFFSET);

let storage = Arc::new(storage);
let ptr = NonNull::new(storage.as_ref()[0].get()).unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly to above: you probably want as_ptr on the full array rather than get() on the first element.

}

let slice = core::slice::from_raw_parts_mut(ptr.as_ptr() as *mut UnsafeCell<u8>, layout.size());
Box::from_raw(slice).into()
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suspect it may not matter in practice, but I think this may technically be UB -- the expectation is that you use the same Layout when alloc'ing and dealloc'ing, and here Box will end up dealloc'ing with a different layout (see safety on https://doc.rust-lang.org/nightly/std/alloc/trait.GlobalAlloc.html#tymethod.dealloc).

The fix I'd probably go for is to implement your own Box that keeps a NonNull and stores the layout directly within itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that's a good catch


/// Returns the empty messages for the producer
#[inline]
pub fn data(&mut self) -> &mut [T] {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess we already asserted that T here is initialized? Technically this is more of &mut [MaybeUninit<T>] probably in terms of how the caller should treat it -- I wonder if that adds enough verbosity that it's not worth making that the return type. It would help catch typos where we start trying to read from the array while being layout-equivalent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By the time you're calling data, T will be initialized, yes. It's kind of a weird one cause we have initialized the message with valid pointers and lengths, but the actual payload data is garbage.

@camshaft camshaft marked this pull request as ready for review June 14, 2023 03:32
@camshaft camshaft merged commit 5f3abe9 into main Jun 15, 2023
@camshaft camshaft deleted the camshaft/tokio-socket-task branch June 15, 2023 00:27
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.

3 participants