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

Limit I/O vector count on Unix #75005

Merged
merged 5 commits into from
Aug 5, 2020
Merged

Limit I/O vector count on Unix #75005

merged 5 commits into from
Aug 5, 2020

Conversation

adamreichold
Copy link
Contributor

@adamreichold adamreichold commented Aug 1, 2020

Unix systems enforce limits on the vector count when performing vectored I/O via the readv and writev system calls and return EINVAL when these limits are exceeded. This changes the standard library to handle those limits as short reads and writes to avoid forcing its users to query these limits using platform specific mechanisms.

Fixes #68042

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @hanna-kruppe (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 1, 2020
@adamreichold
Copy link
Contributor Author

This is my first PR and I am sorry if I messed up the procedure.

I also am rather unsure whether the overhead of SyncOnceCell for each call to writev is tenable but I also did not immediately find another instance where a system configuration parameter is queried via sysconf and then used with a frequently invoked system call.


// 1024 is the default value on modern Linux systems
// and hopefully more useful than `c_int::MAX`.
lim = if ret > 0 { ret as usize } else { 1024 };
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we set errno=0 before sysconf and check it again when ret == -1?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

@adamreichold adamreichold Aug 1, 2020

Choose a reason for hiding this comment

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

I thought about this and decided to not try to handle/propagate errors here and silently fallback to a default value instead as

  • the value will not change for the lifetime of the process and I therefore want to report an error at most once to avoid repeatedly making a failing system call
  • but I also would not like the first call to FileDesc::write_vectored to behave observably different by reporting the error from libc::sysconf
  • thereby logging the error seems to be the only reasonable action in addition to the fallback but I did not find any infrastructure for logging within the standard library

Copy link
Contributor

Choose a reason for hiding this comment

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

While not explicitly check for -1?

Copy link
Member

Choose a reason for hiding this comment

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

I would suggest using 16 as the fallback value since that is the minimum guaranteed by POSIX.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

While not explicitly check for -1?

Accepting zero is not reasonable either as calls to write_vectored would not make progress using that limit, hence the check for ret > 0 instead of checking ret == 0 and ret < 0 separately as the reaction is the same: use the fallback value.

I would suggest using 16 as the fallback value since that is the minimum guaranteed by POSIX.

Will do.

@hanna-kruppe
Copy link
Contributor

I don't really know anything about vectored I/O. r? @Amanieu maybe?

@rust-highfive rust-highfive assigned Amanieu and unassigned hanna-kruppe Aug 1, 2020
@Amanieu
Copy link
Member

Amanieu commented Aug 1, 2020

On Linux UIO_MAXIOV is a constant defined in the kernel headers:

#define UIO_MAXIOV	1024

@adamreichold
Copy link
Contributor Author

On Linux UIO_MAXIOV is a constant defined in the kernel headers:

#define UIO_MAXIOV	1024

Could you expand on the implication you see here? Do you mean that we should just hard code the constant and thereby avoid the call to sysconf? (Even with the plain relaxed load instead of SyncOnceCell?)

@adamreichold
Copy link
Contributor Author

Could you expand on the implication you see here?

Or put differently, isn't the sysconf mechanism exactly meant to query such configuration parameters in a well-defined manner? Otherwise one would need to change our constant value here if the "official" value in the kernel headers changes or someone builds a custom kernel where the value is modified.

@@ -26,6 +27,35 @@ const READ_LIMIT: usize = c_int::MAX as usize - 1;
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = libc::ssize_t::MAX as usize;

#[cfg(any(target_os = "linux", target_os = "macos"))]
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be restricted to just Linux and macOS, all Unix-like OSes have limits on iovec length. Also it seems that all these targets have a definition of _SC_IOV_MAX so there is no need to special case _SC_UIO_MAXIOV for macOS.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This shouldn't be restricted to just Linux and macOS, all Unix-like OSes have limits on iovec length.

What I was wondering here is whether #[cfg(unix)] implies POSIX and hence allows relying on sysconf and friends? Will change the code under that assumption...

so there is no need to special case _SC_UIO_MAXIOV for macOS.

I am admittedly just not knowledgeable on macOS and took that information from the linked bug report. Will change both to just use _SC_IOV_MAX.

@Amanieu
Copy link
Member

Amanieu commented Aug 1, 2020

After thinking about it a bit, I think sticking to sysconf is probably safer in the long run.

@adamreichold adamreichold changed the title Limit I/O vector count on Linux and macOS Limit I/O vector count on Unix Aug 1, 2020
@adamreichold
Copy link
Contributor Author

@Amanieu Is there anything that is still needs of a resolution?

@Amanieu
Copy link
Member

Amanieu commented Aug 5, 2020

@bors r+

@bors
Copy link
Contributor

bors commented Aug 5, 2020

📌 Commit 10203e90f836f4ef9310e028d37b123ea54f9a38 has been approved by Amanieu

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 5, 2020
@bors
Copy link
Contributor

bors commented Aug 5, 2020

⌛ Testing commit 10203e90f836f4ef9310e028d37b123ea54f9a38 with merge 908d12265962911fcf0aeb6cedbacc693dd1c789...

@bors
Copy link
Contributor

bors commented Aug 5, 2020

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Aug 5, 2020
@adamreichold
Copy link
Contributor Author

adamreichold commented Aug 5, 2020

broken_heart Test failed - checks-actions

@Amanieu The build failed as Redox OS' libc does not define _SC_IOV_MAX. What do you think about adding a fallback like

#[cfg(target_os = "redox")]
fn max_iov() -> usize { 16 }

for this case?

@Amanieu
Copy link
Member

Amanieu commented Aug 5, 2020

Sounds good!

Both Linux and MacOS enforce limits on the vector count when performing vectored
I/O via the readv and writev system calls and return EINVAL when these limits
are exceeded. This changes the standard library to handle those limits as short
reads and writes to avoid forcing its users to query these limits using
platform specific mechanisms.
Keep the I/O vector count limit in a `SyncOnceCell` to avoid the overhead of
repeatedly calling `sysconf` as these limits are guaranteed to not change during
the lifetime of a process by POSIX.
All #[cfg(unix)] platforms follow the POSIX standard and define _SC_IOV_MAX so
that we rely purely on POSIX semantics to determine the limits on I/O vector
count.
@adamreichold
Copy link
Contributor Author

Sounds good!

Added it and build tested on x86_64-unknown-redox. Does anybody know any other cfg(unix) targets up front that will need this before bors try this again?

@adamreichold
Copy link
Contributor Author

Does anybody know any other cfg(unix) targets up front that will need this before bors try this again?

Grepping libc sources suggests target_env = "newlib" would be affected as well...

@Amanieu
Copy link
Member

Amanieu commented Aug 5, 2020

@bors r+

@bors
Copy link
Contributor

bors commented Aug 5, 2020

📌 Commit 9073acd has been approved by Amanieu

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 5, 2020
@Amanieu
Copy link
Member

Amanieu commented Aug 5, 2020

@bors rollup=iffy

@bors
Copy link
Contributor

bors commented Aug 5, 2020

⌛ Testing commit 9073acd with merge 52b179b...

@bors
Copy link
Contributor

bors commented Aug 5, 2020

☀️ Test successful - checks-actions, checks-azure
Approved by: Amanieu
Pushing 52b179b to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Aug 5, 2020
@bors bors merged commit 52b179b into rust-lang:master Aug 5, 2020
@adamreichold adamreichold deleted the limit-vector-count branch August 5, 2020 19:57
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Sep 12, 2020
…r=Amanieu

Use IOV_MAX and UIO_MAXIOV constants in limit vectored I/O

Also updates the libc dependency to 0.2.77 (from 0.2.74) as the
constants were only recently added.

Related rust-lang#68042, rust-lang#75005

r? @Amanieu (also reviewed rust-lang#75005)
bors added a commit to rust-lang-ci/rust that referenced this pull request Sep 12, 2020
…Amanieu

Use IOV_MAX and UIO_MAXIOV constants in limit vectored I/O

Also updates the libc dependency to 0.2.77 (from 0.2.74) as the
constants were only recently added.

Related rust-lang#68042, rust-lang#75005

r? `@Amanieu` (also reviewed rust-lang#75005)
@cuviper cuviper added this to the 1.47.0 milestone May 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

std::io::Write::write_vectored() does not properly limit the number of iovecs.
9 participants