├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── props └── text.txt ├── src ├── completion_queue.rs ├── cqe.rs ├── lib.rs ├── probe.rs ├── registrar │ ├── mod.rs │ └── registered.rs ├── sqe.rs └── submission_queue.rs └── tests ├── accept.rs ├── connect.rs ├── cqes-iter.rs ├── exhaust-queue.rs ├── fileset-placeholder.rs ├── fixed-file-write.rs ├── noop.rs ├── poll.rs ├── probe.rs ├── read.rs ├── register-buffers.rs └── write.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at boats@mozilla.com. All complaints 59 | will be reviewed and investigated and will result in a response that is deemed 60 | necessary and appropriate to the circumstances. The project team is obligated 61 | to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iou" 3 | version = "0.3.3" 4 | description = "io_uring bindings" 5 | documentation = "https://docs.rs/iou" 6 | repository = "https://github.com/withoutboats/iou" 7 | keywords = ["io_uring", "liburing", "async", "futures"] 8 | license = "MIT OR Apache-2.0" 9 | authors = ["Without Boats "] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | bitflags = "1.2.0" 14 | nix = "0.18.0" 15 | uring-sys = "0.7.4" 16 | libc = "0.2.77" 17 | 18 | [dev-dependencies] 19 | semver = "0.9.0" 20 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (c) 2019 Without Boats 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Without Boats 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interface to Linux's io_uring interface 2 | 3 | `iou` is a wrapper around the [liburing][liburing] library, which provides a 4 | high level interface to Linux's new [io_uring][io_uring] interface. It is 5 | intended to be extensible and flexible for any use case of io_uring, while 6 | still resolving many of the basic safety issues on users' behalf. 7 | 8 | The primary API of iou is the `IoUring` type and its components, the 9 | `SubmissionQueue`, `CompletionQueue` and `Registrar`. This provides a Rust-like 10 | and high level API that manages the io_uring for you. 11 | 12 | ## Safety 13 | 14 | Most of the APIs in iou are safe, and many of the safety issues in using 15 | io_uring are completely resolved. In particular, the liburing library which iou 16 | is based on correctly implements the atomics necessary to coordinate with the 17 | kernel across the io_uring interface. However, some key interfaces remain 18 | unsafe. In particular, preparing IO events to be submitted to the io_uring is 19 | not safe: users must ensure that the buffers and file descriptors are regarded 20 | as borrowed during the lifetime of the IO. 21 | 22 | [io_uring]: http://kernel.dk/io_uring.pdf 23 | [liburing]: http://git.kernel.dk/cgit/liburing/ 24 | 25 | ## Kernel support 26 | 27 | In order to use io_uring, the machine you are running your code on must have a 28 | kernel which supports that interface. The first version of io_uring was added 29 | in Linux 5.1, but it did not include all of the features supported by this 30 | library. Some features of this library may not work depending on which version 31 | of Linux you are using. 32 | -------------------------------------------------------------------------------- /props/text.txt: -------------------------------------------------------------------------------- 1 | I really wanna stop 2 | But I just gotta taste for it 3 | I feel like I could fly with the ball on the moon 4 | So honey hold my hand you like making me wait for it 5 | I feel like I could die walking up to the room, oh yeah 6 | 7 | Late night watching television 8 | But how we get in this position? 9 | It's way too soon, I know this isn't love 10 | But I need to tell you something 11 | -------------------------------------------------------------------------------- /src/completion_queue.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io; 3 | use std::marker::PhantomData; 4 | use std::mem::MaybeUninit; 5 | use std::ptr::{self, NonNull}; 6 | 7 | use super::{IoUring, CQE, CQEs, CQEsBlocking, resultify}; 8 | 9 | /// The queue of completed IO events. 10 | /// 11 | /// Each element is a [`CQE`](crate::cqe::CQE). 12 | /// 13 | /// Completion does not imply success. Completed events may be [timeouts](crate::cqe::CQE::is_iou_timeout). 14 | pub struct CompletionQueue<'ring> { 15 | pub(crate) ring: NonNull, 16 | _marker: PhantomData<&'ring mut IoUring>, 17 | } 18 | 19 | impl<'ring> CompletionQueue<'ring> { 20 | pub(crate) fn new(ring: &'ring IoUring) -> CompletionQueue<'ring> { 21 | CompletionQueue { 22 | ring: NonNull::from(&ring.ring), 23 | _marker: PhantomData, 24 | } 25 | } 26 | 27 | /// Returns the next CQE if any are available. 28 | pub fn peek_for_cqe(&mut self) -> Option { 29 | unsafe { 30 | let mut cqe = MaybeUninit::uninit(); 31 | uring_sys::io_uring_peek_cqe(self.ring.as_ptr(), cqe.as_mut_ptr()); 32 | let cqe = cqe.assume_init(); 33 | if !cqe.is_null() { 34 | Some(CQE::new(self.ring, &mut *cqe)) 35 | } else { 36 | None 37 | } 38 | } 39 | } 40 | 41 | /// Returns the next CQE, blocking the thread until one is ready if necessary. 42 | pub fn wait_for_cqe(&mut self) -> io::Result { 43 | self.wait_for_cqes(1) 44 | } 45 | 46 | #[inline(always)] 47 | pub(crate) fn wait_for_cqes(&mut self, count: u32) -> io::Result { 48 | let ring = self.ring; 49 | self.wait_inner(count).map(|cqe| CQE::new(ring, cqe)) 50 | } 51 | 52 | /// Block the thread until at least `count` CQEs are ready. 53 | /// 54 | /// These CQEs can be processed using `peek_for_cqe` or the `cqes` iterator. 55 | pub fn wait(&mut self, count: u32) -> io::Result<()> { 56 | self.wait_inner(count).map(|_| ()) 57 | } 58 | 59 | #[inline(always)] 60 | fn wait_inner(&mut self, count: u32) -> io::Result<&mut uring_sys::io_uring_cqe> { 61 | unsafe { 62 | let mut cqe = MaybeUninit::uninit(); 63 | 64 | resultify(uring_sys::io_uring_wait_cqes( 65 | self.ring.as_ptr(), 66 | cqe.as_mut_ptr(), 67 | count as _, 68 | ptr::null(), 69 | ptr::null(), 70 | ))?; 71 | 72 | Ok(&mut *cqe.assume_init()) 73 | } 74 | } 75 | 76 | /// Returns an iterator of ready CQEs. 77 | /// 78 | /// When there are no CQEs ready to process, the iterator will end. It will never 79 | /// block the thread to wait for CQEs to be completed. 80 | pub fn cqes(&mut self) -> CQEs<'_> { 81 | CQEs::new(self.ring) 82 | } 83 | 84 | /// Returns an iterator of ready CQEs, blocking when there are none ready. 85 | /// 86 | /// This iterator never ends. Whenever there are no CQEs ready, it will block 87 | /// the thread until at least `wait_for` CQEs are ready. 88 | pub fn cqes_blocking(&mut self, wait_for: u32) -> CQEsBlocking<'_> { 89 | CQEsBlocking::new(self.ring, wait_for) 90 | } 91 | 92 | pub fn ready(&self) -> u32 { 93 | unsafe { uring_sys::io_uring_cq_ready(self.ring.as_ptr()) } 94 | } 95 | 96 | pub fn eventfd_enabled(&self) -> bool { 97 | unsafe { uring_sys::io_uring_cq_eventfd_enabled(self.ring.as_ptr()) } 98 | } 99 | 100 | pub fn eventfd_toggle(&mut self, enabled: bool) -> io::Result<()> { 101 | resultify(unsafe { uring_sys::io_uring_cq_eventfd_toggle(self.ring.as_ptr(), enabled) })?; 102 | Ok(()) 103 | } 104 | } 105 | 106 | impl fmt::Debug for CompletionQueue<'_> { 107 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 108 | let fd = unsafe { self.ring.as_ref().ring_fd }; 109 | f.debug_struct(std::any::type_name::()).field("fd", &fd).finish() 110 | } 111 | } 112 | 113 | unsafe impl<'ring> Send for CompletionQueue<'ring> { } 114 | unsafe impl<'ring> Sync for CompletionQueue<'ring> { } 115 | -------------------------------------------------------------------------------- /src/cqe.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::marker::PhantomData; 3 | use std::mem::MaybeUninit; 4 | use std::ptr::{self, NonNull}; 5 | 6 | use super::{IoUring, resultify}; 7 | 8 | /// A completed IO event. 9 | #[derive(Debug)] 10 | pub struct CQE { 11 | user_data: u64, 12 | res: i32, 13 | flags: CompletionFlags, 14 | } 15 | 16 | impl CQE { 17 | pub fn from_raw(cqe: uring_sys::io_uring_cqe) -> CQE { 18 | CQE { 19 | user_data: cqe.user_data, 20 | res: cqe.res, 21 | flags: CompletionFlags::from_bits_truncate(cqe.flags), 22 | } 23 | } 24 | 25 | pub fn from_raw_parts(user_data: u64, res: i32, flags: CompletionFlags) -> CQE { 26 | CQE { 27 | user_data, res, flags, 28 | } 29 | } 30 | 31 | pub(crate) fn new(ring: NonNull, cqe: &mut uring_sys::io_uring_cqe) -> CQE { 32 | let user_data = cqe.user_data; 33 | let res = cqe.res; 34 | let flags = CompletionFlags::from_bits_truncate(cqe.flags); 35 | 36 | unsafe { 37 | uring_sys::io_uring_cqe_seen(ring.as_ptr(), cqe); 38 | } 39 | 40 | CQE::from_raw_parts(user_data, res, flags) 41 | } 42 | 43 | pub fn user_data(&self) -> u64 { 44 | self.user_data as u64 45 | } 46 | 47 | pub fn result(&self) -> io::Result { 48 | resultify(self.res) 49 | } 50 | 51 | pub fn flags(&self) -> CompletionFlags { 52 | self.flags 53 | } 54 | 55 | pub fn raw_result(&self) -> i32 { 56 | self.res 57 | } 58 | 59 | pub fn raw_flags(&self) -> u32 { 60 | self.flags.bits() 61 | } 62 | } 63 | 64 | unsafe impl Send for CQE { } 65 | unsafe impl Sync for CQE { } 66 | 67 | /// An iterator of [`CQE`]s from the [`CompletionQueue`](crate::CompletionQueue). 68 | /// 69 | /// This iterator will be exhausted when there are no `CQE`s ready, and return `None`. 70 | pub struct CQEs<'a> { 71 | ring: NonNull, 72 | ready: u32, 73 | marker: PhantomData<&'a mut IoUring>, 74 | } 75 | 76 | impl<'a> CQEs<'a> { 77 | pub(crate) fn new(ring: NonNull) -> CQEs<'a> { 78 | CQEs { ring, ready: 0, marker: PhantomData } 79 | } 80 | 81 | #[inline(always)] 82 | fn ready(&self) -> u32 { 83 | unsafe { uring_sys::io_uring_cq_ready(self.ring.as_ptr()) } 84 | } 85 | 86 | #[inline(always)] 87 | fn peek_for_cqe(&mut self) -> Option { 88 | unsafe { 89 | let mut cqe = MaybeUninit::uninit(); 90 | uring_sys::io_uring_peek_cqe(self.ring.as_ptr(), cqe.as_mut_ptr()); 91 | let cqe = cqe.assume_init(); 92 | if !cqe.is_null() { 93 | Some(CQE::new(self.ring, &mut *cqe)) 94 | } else { 95 | None 96 | } 97 | } 98 | } 99 | } 100 | 101 | impl Iterator for CQEs<'_> { 102 | type Item = CQE; 103 | 104 | fn next(&mut self) -> Option { 105 | if self.ready == 0 { 106 | self.ready = self.ready(); 107 | if self.ready == 0 { 108 | return None; 109 | } 110 | } 111 | 112 | self.ready -= 1; 113 | self.peek_for_cqe() 114 | } 115 | } 116 | 117 | 118 | /// An iterator of [`CQE`]s from the [`CompletionQueue`](crate::CompletionQueue). 119 | /// 120 | /// This iterator will never be exhausted; if there are no `CQE`s ready, it will block until there 121 | /// are. 122 | pub struct CQEsBlocking<'a> { 123 | ring: NonNull, 124 | ready: u32, 125 | wait_for: u32, 126 | marker: PhantomData<&'a mut IoUring>, 127 | } 128 | 129 | impl<'a> CQEsBlocking<'a> { 130 | pub(crate) fn new(ring: NonNull, wait_for: u32) -> CQEsBlocking<'a> { 131 | CQEsBlocking { ring, ready: 0, wait_for, marker: PhantomData } 132 | } 133 | 134 | #[inline(always)] 135 | fn ready(&self) -> u32 { 136 | unsafe { uring_sys::io_uring_cq_ready(self.ring.as_ptr()) } 137 | } 138 | 139 | #[inline(always)] 140 | fn peek_for_cqe(&mut self) -> Option { 141 | unsafe { 142 | let mut cqe = MaybeUninit::uninit(); 143 | uring_sys::io_uring_peek_cqe(self.ring.as_ptr(), cqe.as_mut_ptr()); 144 | let cqe = cqe.assume_init(); 145 | if !cqe.is_null() { 146 | Some(CQE::new(self.ring, &mut *cqe)) 147 | } else { 148 | None 149 | } 150 | } 151 | } 152 | 153 | #[inline(always)] 154 | fn wait(&mut self) -> io::Result<&mut uring_sys::io_uring_cqe> { 155 | unsafe { 156 | let mut cqe = MaybeUninit::uninit(); 157 | 158 | resultify(uring_sys::io_uring_wait_cqes( 159 | self.ring.as_ptr(), 160 | cqe.as_mut_ptr(), 161 | self.wait_for as _, 162 | ptr::null(), 163 | ptr::null(), 164 | ))?; 165 | 166 | Ok(&mut *cqe.assume_init()) 167 | } 168 | } 169 | } 170 | 171 | impl Iterator for CQEsBlocking<'_> { 172 | type Item = io::Result; 173 | 174 | fn next(&mut self) -> Option { 175 | if self.ready == 0 { 176 | self.ready = self.ready(); 177 | if self.ready == 0 { 178 | let ring = self.ring; 179 | return Some(self.wait().map(|cqe| CQE::new(ring, cqe))) 180 | } 181 | } 182 | 183 | self.ready -= 1; 184 | self.peek_for_cqe().map(Ok) 185 | } 186 | } 187 | 188 | bitflags::bitflags! { 189 | /// Flags that can be returned from the kernel on [`CQE`]s. 190 | pub struct CompletionFlags: u32 { 191 | const BUFFER_SHIFT = 1 << 0; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Idiomatic Rust bindings to liburing. 2 | //! 3 | //! This gives users an idiomatic Rust interface for interacting with the Linux kernel's `io_uring` 4 | //! interface for async IO. Despite being idiomatic Rust, this interface is still very low level 5 | //! and some fundamental operations remain unsafe. 6 | //! 7 | //! The core entry point to the API is the `IoUring` type, which manages an `io_uring` object for 8 | //! interfacing with the kernel. Using this, users can submit IO events and wait for their 9 | //! completion. 10 | //! 11 | //! It is also possible to "split" an `IoUring` instance into its constituent components - a 12 | //! `SubmissionQueue`, a `CompletionQueue`, and a `Registrar` - in order to operate on them 13 | //! separately without synchronization. 14 | //! 15 | //! # Submitting events 16 | //! 17 | //! You can prepare new IO events using the `SQE` type. Once an event has been 18 | //! prepared, the next call to submit will submit that event. Eventually, those events will 19 | //! complete, and that a `CQE` will appear on the completion queue indicating that 20 | //! the event is complete. 21 | //! 22 | //! Preparing IO events is inherently unsafe, as you must guarantee that the buffers and file 23 | //! descriptors used for that IO are alive long enough for the kernel to perform the IO operation 24 | //! with them. 25 | //! 26 | //! # Timeouts 27 | //! 28 | //! Some APIs allow you to time out a call into the kernel. It's important to note how this works 29 | //! with io_uring. 30 | //! 31 | //! A timeout is submitted as an additional IO event which completes after the specified time. 32 | //! Therefore when you create a timeout, all that happens is that a completion event will appear 33 | //! after that specified time. This also means that when processing completion events, you need to 34 | //! be prepared for the possibility that the completion represents a timeout and not a normal IO 35 | //! event (`CQE` has a method to check for this). 36 | 37 | /// Types related to completion queue events. 38 | pub mod cqe; 39 | /// Types related to submission queue events. 40 | /// 41 | /// The most important types here are [`SQE`], which represents a single submission queue event, 42 | /// and [`SQEs`], which represents a sequence of events that can be prepared at once. 43 | /// 44 | /// Many of the types in this module are re-exported from the `nix` crate, and are used when 45 | /// preparing [`SQE`]s associated with specific Linux system operations. 46 | pub mod sqe; 47 | 48 | mod completion_queue; 49 | mod submission_queue; 50 | 51 | mod probe; 52 | 53 | pub mod registrar; 54 | 55 | use std::fmt; 56 | use std::io; 57 | use std::mem::{self, MaybeUninit}; 58 | use std::os::unix::io::RawFd; 59 | use std::ptr::{self, NonNull}; 60 | use std::time::Duration; 61 | 62 | #[doc(inline)] 63 | pub use sqe::{SQE, SQEs}; 64 | #[doc(inline)] 65 | pub use cqe::{CQE, CQEs, CQEsBlocking}; 66 | 67 | pub use completion_queue::CompletionQueue; 68 | pub use submission_queue::SubmissionQueue; 69 | 70 | pub use probe::Probe; 71 | #[doc(inline)] 72 | pub use registrar::{Registrar, Personality}; 73 | 74 | bitflags::bitflags! { 75 | /// [`IoUring`] initialization flags for advanced use cases. 76 | /// 77 | /// ```no_run 78 | /// # use std::io; 79 | /// # use iou::{IoUring, SetupFlags, SetupFeatures}; 80 | /// # fn main() -> io::Result<()> { 81 | /// let no_features = SetupFeatures::empty(); 82 | /// 83 | /// // specify polled IO 84 | /// let mut ring = IoUring::new_with_flags(32, SetupFlags::IOPOLL, no_features)?; 85 | /// 86 | /// // assign a kernel thread to poll the submission queue 87 | /// let mut ring = IoUring::new_with_flags(8, SetupFlags::SQPOLL, no_features)?; 88 | /// 89 | /// // force the kernel thread to use the same cpu as the submission queue 90 | /// let mut ring = IoUring::new_with_flags(8, 91 | /// SetupFlags::IOPOLL | SetupFlags::SQPOLL | SetupFlags::SQ_AFF, no_features)?; 92 | /// 93 | /// // setting `SQ_AFF` without `SQPOLL` is an error 94 | /// assert!(IoUring::new_with_flags(8, SetupFlags::SQ_AFF, no_features).is_err()); 95 | /// # Ok(()) 96 | /// # } 97 | /// ``` 98 | pub struct SetupFlags: u32 { 99 | /// Poll the IO context instead of defaulting to interrupts. 100 | const IOPOLL = 1 << 0; /* io_context is polled */ 101 | /// Assign a kernel thread to poll the submission queue. Requires elevated privileges to set. 102 | const SQPOLL = 1 << 1; /* SQ poll thread */ 103 | /// Force the kernel thread created with `SQPOLL` to be bound to the CPU used by the 104 | /// `SubmissionQueue`. Requires `SQPOLL` set. 105 | const SQ_AFF = 1 << 2; /* sq_thread_cpu is valid */ 106 | 107 | const CQSIZE = 1 << 3; 108 | const CLAMP = 1 << 4; 109 | const ATTACH_WQ = 1 << 5; 110 | } 111 | } 112 | 113 | bitflags::bitflags! { 114 | /// Advanced features that can be enabled when setting up an [`IoUring`] instance. 115 | pub struct SetupFeatures: u32 { 116 | const SINGLE_MMAP = 1 << 0; 117 | const NODROP = 1 << 1; 118 | const SUBMIT_STABLE = 1 << 2; 119 | const RW_CUR_POS = 1 << 3; 120 | const CUR_PERSONALITY = 1 << 4; 121 | const FAST_POLL = 1 << 5; 122 | const POLL_32BITS = 1 << 6; 123 | } 124 | } 125 | 126 | /// The main interface to kernel IO using `io_uring`. 127 | /// 128 | /// `IoUring` is a high-level wrapper around an [`io_uring`](uring_sys::io_uring) object. 129 | /// 130 | /// `IoUring`s are constructed with a requested number of ring buffer entries and possibly a set of 131 | /// [`SetupFlags`](SetupFlags). Allocations for `IoUring` are `memlocked` and will not be paged 132 | /// out. 133 | /// 134 | /// ``` 135 | /// # use std::io; 136 | /// # use iou::{IoUring, SetupFlags, SetupFeatures}; 137 | /// # fn main() -> io::Result<()> { 138 | /// // make a IoUring with 16 entries 139 | /// let mut ring = IoUring::new(16)?; 140 | /// 141 | /// // make a IoUring set to poll the IO context 142 | /// let mut ring = IoUring::new_with_flags(32, SetupFlags::IOPOLL, SetupFeatures::empty())?; 143 | /// # Ok(()) 144 | /// # } 145 | /// ``` 146 | /// 147 | /// `IoUring`s can either be used directly, or split into separate parts and 148 | /// operated on without synchronization. 149 | /// ``` 150 | /// # use std::io; 151 | /// # use iou::{IoUring, SetupFlags, CompletionQueue, SubmissionQueue, Registrar}; 152 | /// # fn main() -> io::Result<()> { 153 | /// # let mut ring = IoUring::new(32)?; 154 | /// // split an IoUring piecewise 155 | /// let sq: SubmissionQueue = ring.sq(); 156 | /// let cq: CompletionQueue = ring.cq(); 157 | /// let reg: Registrar = ring.registrar(); 158 | /// 159 | /// // split an IoUring into its three parts all at once 160 | /// let (sq, cq, reg) = ring.queues(); 161 | /// # Ok(()) 162 | /// # } 163 | /// ``` 164 | pub struct IoUring { 165 | ring: uring_sys::io_uring, 166 | } 167 | 168 | impl IoUring { 169 | /// Creates a new `IoUring` without any setup flags. `IoUring`'s created using this method will 170 | /// use interrupt-driven IO. 171 | /// 172 | /// The number of entries must be in the range of 1..4096 (inclusive) and 173 | /// it's recommended to be a power of two. 174 | /// 175 | /// The underlying `SubmissionQueue` and `CompletionQueue` will each have this number of 176 | /// entries. 177 | pub fn new(entries: u32) -> io::Result { 178 | IoUring::new_with_flags(entries, SetupFlags::empty(), SetupFeatures::empty()) 179 | } 180 | 181 | /// Creates a new `IoUring` using a set of `SetupFlags` and `SetupFeatures` for advanced 182 | /// use cases. 183 | pub fn new_with_flags(entries: u32, flags: SetupFlags, features: SetupFeatures) -> io::Result { 184 | unsafe { 185 | let mut params: uring_sys::io_uring_params = mem::zeroed(); 186 | params.flags = flags.bits(); 187 | params.features = features.bits(); 188 | let mut ring = MaybeUninit::uninit(); 189 | resultify(uring_sys::io_uring_queue_init_params( 190 | entries as _, 191 | ring.as_mut_ptr(), 192 | &mut params, 193 | ))?; 194 | Ok(IoUring { ring: ring.assume_init() }) 195 | } 196 | } 197 | 198 | /// Returns the `SubmissionQueue` part of the `IoUring`. 199 | pub fn sq(&mut self) -> SubmissionQueue<'_> { 200 | SubmissionQueue::new(&*self) 201 | } 202 | 203 | /// Returns the `CompletionQueue` part of the `IoUring`. 204 | pub fn cq(&mut self) -> CompletionQueue<'_> { 205 | CompletionQueue::new(&*self) 206 | } 207 | 208 | /// Returns the `Registrar` part of the `IoUring`. 209 | pub fn registrar(&self) -> Registrar<'_> { 210 | Registrar::new(self) 211 | } 212 | 213 | /// Returns the three constituent parts of the `IoUring`. 214 | pub fn queues(&mut self) -> (SubmissionQueue<'_>, CompletionQueue<'_>, Registrar<'_>) { 215 | (SubmissionQueue::new(&*self), CompletionQueue::new(&*self), Registrar::new(&*self)) 216 | } 217 | 218 | pub fn probe(&mut self) -> io::Result { 219 | Probe::for_ring(&mut self.ring) 220 | } 221 | 222 | /// Returns the next [`SQE`] which can be prepared to submit. 223 | pub fn prepare_sqe(&mut self) -> Option> { 224 | unsafe { 225 | submission_queue::prepare_sqe(&mut self.ring) 226 | } 227 | } 228 | 229 | /// Returns the next `count` [`SQE`]s which can be prepared to submit as an iterator. 230 | /// 231 | /// See the [`SQEs`] type for more information about how these multiple SQEs can be used. 232 | pub fn prepare_sqes(&mut self, count: u32) -> Option> { 233 | unsafe { 234 | submission_queue::prepare_sqes(&mut self.ring.sq, count) 235 | } 236 | } 237 | 238 | /// Submit all prepared [`SQE`]s to the kernel. 239 | pub fn submit_sqes(&mut self) -> io::Result { 240 | self.sq().submit() 241 | } 242 | 243 | /// Submit all prepared [`SQE`]s to the kernel and wait until at least `wait_for` events have 244 | /// completed. 245 | pub fn submit_sqes_and_wait(&mut self, wait_for: u32) -> io::Result { 246 | self.sq().submit_and_wait(wait_for) 247 | } 248 | 249 | 250 | /// Submit all prepared [`SQE`]s to the kernel and wait until at least `wait_for` events have 251 | /// completed or `duration` has passed. 252 | pub fn submit_sqes_and_wait_with_timeout(&mut self, wait_for: u32, duration: Duration) 253 | -> io::Result 254 | { 255 | self.sq().submit_and_wait_with_timeout(wait_for, duration) 256 | } 257 | 258 | /// Peek for any [`CQE`] that is already completed, without blocking. This will consume that 259 | /// CQE. 260 | pub fn peek_for_cqe(&mut self) -> Option { 261 | unsafe { 262 | let mut cqe = MaybeUninit::uninit(); 263 | let count = uring_sys::io_uring_peek_batch_cqe(&mut self.ring, cqe.as_mut_ptr(), 1); 264 | 265 | if count > 0 { 266 | Some(CQE::new(NonNull::from(&self.ring), &mut *cqe.assume_init())) 267 | } else { 268 | None 269 | } 270 | } 271 | } 272 | 273 | /// Block until at least one [`CQE`] is completed. This will consume that CQE. 274 | pub fn wait_for_cqe(&mut self) -> io::Result { 275 | let ring = NonNull::from(&self.ring); 276 | self.inner_wait_for_cqes(1, ptr::null()).map(|cqe| CQE::new(ring, cqe)) 277 | } 278 | 279 | /// Block until a [`CQE`] is ready or timeout. 280 | pub fn wait_for_cqe_with_timeout(&mut self, duration: Duration) 281 | -> io::Result 282 | { 283 | let ts = uring_sys::__kernel_timespec { 284 | tv_sec: duration.as_secs() as _, 285 | tv_nsec: duration.subsec_nanos() as _ 286 | }; 287 | 288 | let ring = NonNull::from(&self.ring); 289 | self.inner_wait_for_cqes(1, &ts).map(|cqe| CQE::new(ring, cqe)) 290 | } 291 | 292 | /// Returns an iterator of [`CQE`]s which are ready from the kernel. 293 | pub fn cqes(&mut self) -> CQEs<'_> { 294 | CQEs::new(NonNull::from(&mut self.ring)) 295 | } 296 | 297 | /// Returns an iterator of [`CQE`]s which will block when there are no CQEs ready. It will 298 | /// block until at least `count` are ready, and then continue iterating. 299 | /// 300 | /// This iterator will never be exhausted; every time it runs out of CQEs it will block the 301 | /// thread and wait for more to be ready. 302 | pub fn cqes_blocking(&mut self, count: u32) -> CQEsBlocking<'_> { 303 | CQEsBlocking::new(NonNull::from(&mut self.ring), count) 304 | } 305 | 306 | /// Wait until `count` [`CQE`]s are ready, without submitting any events. 307 | pub fn wait_for_cqes(&mut self, count: u32) -> io::Result<()> { 308 | self.inner_wait_for_cqes(count as _, ptr::null()).map(|_| ()) 309 | } 310 | 311 | fn inner_wait_for_cqes(&mut self, count: u32, ts: *const uring_sys::__kernel_timespec) 312 | -> io::Result<&mut uring_sys::io_uring_cqe> 313 | { 314 | unsafe { 315 | let mut cqe = MaybeUninit::uninit(); 316 | 317 | resultify(uring_sys::io_uring_wait_cqes( 318 | &mut self.ring, 319 | cqe.as_mut_ptr(), 320 | count, 321 | ts, 322 | ptr::null(), 323 | ))?; 324 | 325 | Ok(&mut *cqe.assume_init()) 326 | } 327 | } 328 | 329 | pub fn raw(&self) -> &uring_sys::io_uring { 330 | &self.ring 331 | } 332 | 333 | pub unsafe fn raw_mut(&mut self) -> &mut uring_sys::io_uring { 334 | &mut self.ring 335 | } 336 | 337 | pub fn cq_ready(&mut self) -> u32 { 338 | self.cq().ready() 339 | } 340 | 341 | pub fn sq_ready(&mut self) -> u32 { 342 | self.sq().ready() 343 | } 344 | 345 | pub fn sq_space_left(&mut self) -> u32 { 346 | self.sq().space_left() 347 | } 348 | 349 | pub fn cq_eventfd_enabled(&mut self) -> bool { 350 | self.cq().eventfd_enabled() 351 | } 352 | 353 | pub fn cq_eventfd_toggle(&mut self, enabled: bool) -> io::Result<()> { 354 | self.cq().eventfd_toggle(enabled) 355 | } 356 | 357 | pub fn raw_fd(&self) -> RawFd { 358 | self.ring.ring_fd 359 | } 360 | } 361 | 362 | impl fmt::Debug for IoUring { 363 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 364 | f.debug_struct(std::any::type_name::()).field("fd", &self.ring.ring_fd).finish() 365 | } 366 | } 367 | 368 | impl Drop for IoUring { 369 | fn drop(&mut self) { 370 | unsafe { uring_sys::io_uring_queue_exit(&mut self.ring) }; 371 | } 372 | } 373 | 374 | unsafe impl Send for IoUring { } 375 | unsafe impl Sync for IoUring { } 376 | 377 | fn resultify(x: i32) -> io::Result { 378 | match x >= 0 { 379 | true => Ok(x as u32), 380 | false => Err(io::Error::from_raw_os_error(-x)), 381 | } 382 | } 383 | 384 | #[cfg(test)] 385 | mod tests { 386 | use super::resultify; 387 | 388 | #[test] 389 | fn test_resultify() { 390 | let side_effect = |i, effect: &mut _| -> i32 { 391 | *effect += 1; 392 | return i; 393 | }; 394 | 395 | let mut calls = 0; 396 | let ret = resultify(side_effect(0, &mut calls)); 397 | assert!(match ret { Ok(0) => true, _ => false }); 398 | assert_eq!(calls, 1); 399 | 400 | calls = 0; 401 | let ret = resultify(side_effect(1, &mut calls)); 402 | assert!(match ret { Ok(1) => true, _ => false }); 403 | assert_eq!(calls, 1); 404 | 405 | calls = 0; 406 | let ret = resultify(side_effect(-1, &mut calls)); 407 | assert!(match ret { Err(e) if e.raw_os_error() == Some(1) => true, _ => false }); 408 | assert_eq!(calls, 1); 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /src/probe.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::ptr::NonNull; 3 | 4 | /// A probe of the operations supported by this kernel version's io-uring interface. 5 | #[derive(Debug)] 6 | pub struct Probe { 7 | probe: NonNull, 8 | } 9 | 10 | impl Probe { 11 | pub fn new() -> io::Result { 12 | unsafe { 13 | let probe = uring_sys::io_uring_get_probe(); 14 | NonNull::new(probe).ok_or_else(io::Error::last_os_error).map(|probe| Probe { probe }) 15 | } 16 | } 17 | 18 | pub(crate) fn for_ring(ring: *mut uring_sys::io_uring) -> io::Result { 19 | unsafe { 20 | let probe = uring_sys::io_uring_get_probe_ring(ring); 21 | NonNull::new(probe).ok_or_else(io::Error::last_os_error).map(|probe| Probe { probe }) 22 | } 23 | } 24 | 25 | pub fn supports(&self, op: uring_sys::IoRingOp) -> bool { 26 | unsafe { uring_sys::io_uring_opcode_supported(self.probe.as_ptr(), op as _) != 0 } 27 | } 28 | } 29 | 30 | impl Drop for Probe { 31 | fn drop(&mut self) { 32 | unsafe { libc::free(self.probe.as_ptr() as *mut _) } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/registrar/mod.rs: -------------------------------------------------------------------------------- 1 | //! Types related to registration and registered resources. 2 | //! 3 | //! The [`Registrar`] type can be used to register resources with the kernel that will be used with 4 | //! a particular [`IoUring`] instance. This can improve performance by avoiding the kernel from 5 | //! reallocating resources for each IO events performed against those resources. 6 | //! 7 | //! When file descriptors and buffers are registered with the kernel, an iterator of the type-safe 8 | //! [`Registered`] wrapper is returned. This wrapper makes it easier to correctly use 9 | //! pre-registered resources. By passing a [`RegisteredFd`] or the correct type of registered 10 | //! buffer to an [`SQE`][crate::SQE]'s prep methods, the SQE will be properly prepared to use the 11 | //! pre-registered object. 12 | mod registered; 13 | 14 | use std::fmt; 15 | use std::io; 16 | use std::marker::PhantomData; 17 | use std::ptr::NonNull; 18 | use std::os::unix::io::RawFd; 19 | 20 | use crate::{IoUring, Probe, resultify}; 21 | 22 | pub use registered::*; 23 | 24 | /// A `Registrar` creates ahead-of-time kernel references to files and user buffers. 25 | /// 26 | /// Preregistration significantly reduces per-IO overhead, so consider registering frequently 27 | /// used files and buffers. For file IO, preregistration lets the kernel skip the atomic acquire and 28 | /// release of a kernel-specific file descriptor. For buffer IO, the kernel can avoid mapping kernel 29 | /// memory for every operation. 30 | /// 31 | /// Beware that registration is relatively expensive and should be done before any performance 32 | /// sensitive code. 33 | /// 34 | /// If you want to register a file but don't have an open file descriptor yet, you can register 35 | /// a [placeholder](PLACEHOLDER_FD) descriptor and 36 | /// [update](crate::registrar::Registrar::update_registered_files) it later. 37 | /// ``` 38 | /// # use iou::{IoUring, Registrar, registrar::RegisteredFd}; 39 | /// # fn main() -> std::io::Result<()> { 40 | /// let mut ring = IoUring::new(8)?; 41 | /// let mut registrar: Registrar = ring.registrar(); 42 | /// # let fds = &[0, 1]; 43 | /// let registered_files: Vec = registrar.register_files(fds)?.collect(); 44 | /// # Ok(()) 45 | /// # } 46 | /// ``` 47 | pub struct Registrar<'ring> { 48 | ring: NonNull, 49 | _marker: PhantomData<&'ring mut IoUring>, 50 | } 51 | 52 | impl<'ring> Registrar<'ring> { 53 | pub(crate) fn new(ring: &'ring IoUring) -> Registrar<'ring> { 54 | Registrar { 55 | ring: NonNull::from(&ring.ring), 56 | _marker: PhantomData, 57 | } 58 | } 59 | 60 | pub fn register_buffers(&self, buffers: Vec>) 61 | -> io::Result> 62 | { 63 | let len = buffers.len(); 64 | let addr = buffers.as_ptr() as *const _; 65 | resultify(unsafe { 66 | uring_sys::io_uring_register_buffers(self.ring.as_ptr(), addr, len as _) 67 | })?; 68 | Ok(buffers 69 | .into_iter() 70 | .enumerate() 71 | .map(|(i, buf)| RegisteredBuf::new(i as u32, buf)) 72 | ) 73 | } 74 | 75 | pub fn register_buffers_by_ref<'a>(&self, buffers: &'a [&'a [u8]]) 76 | -> io::Result> + 'a> 77 | { 78 | let len = buffers.len(); 79 | let addr = buffers.as_ptr() as *const _; 80 | resultify(unsafe { 81 | uring_sys::io_uring_register_buffers(self.ring.as_ptr(), addr, len as _) 82 | })?; 83 | Ok(buffers 84 | .iter() 85 | .enumerate() 86 | .map(|(i, buf)| Registered::new(i as u32, &**buf)) 87 | ) 88 | } 89 | 90 | pub fn register_buffers_by_mut<'a>(&self, buffers: &'a mut [&'a mut [u8]]) 91 | -> io::Result> + 'a> 92 | { 93 | let len = buffers.len(); 94 | let addr = buffers.as_ptr() as *const _; 95 | resultify(unsafe { 96 | uring_sys::io_uring_register_buffers(self.ring.as_ptr(), addr, len as _) 97 | })?; 98 | Ok(buffers 99 | .iter_mut() 100 | .enumerate() 101 | .map(|(i, buf)| Registered::new(i as u32, &mut **buf)) 102 | ) 103 | } 104 | 105 | /// Unregister all currently registered buffers. An explicit call to this method is often unecessary, 106 | /// because all buffers will be unregistered automatically when the ring is dropped. 107 | pub fn unregister_buffers(&self) -> io::Result<()> { 108 | resultify(unsafe { 109 | uring_sys::io_uring_unregister_buffers(self.ring.as_ptr()) 110 | })?; 111 | Ok(()) 112 | } 113 | 114 | /// Register a set of files with the kernel. Registered files handle kernel fileset indexing 115 | /// behind the scenes and can often be used in place of raw file descriptors. 116 | /// 117 | /// # Errors 118 | /// Returns an error if 119 | /// * there is a preexisting set of registered files, 120 | /// * the `files` slice was empty, 121 | /// * the inner [`io_uring_register_files`](uring_sys::io_uring_register_files) call failed for 122 | /// another reason 123 | /// ```no_run 124 | /// # use iou::IoUring; 125 | /// # fn main() -> std::io::Result<()> { 126 | /// # let mut ring = IoUring::new(2)?; 127 | /// # let mut registrar = ring.registrar(); 128 | /// # let raw_fds = [1, 2]; 129 | /// # let bufs = &[std::io::IoSlice::new(b"hi")]; 130 | /// let fileset: Vec<_> = registrar.register_files(&raw_fds)?.collect(); 131 | /// let reg_file = fileset[0]; 132 | /// # let mut sqe = ring.prepare_sqe().unwrap(); 133 | /// unsafe { sqe.prep_write_vectored(reg_file, bufs, 0); } 134 | /// # Ok(()) 135 | /// # } 136 | /// ``` 137 | pub fn register_files<'a>(&self, files: &'a [RawFd]) -> io::Result + 'a> { 138 | assert!(files.len() <= u32::MAX as usize); 139 | resultify(unsafe { 140 | uring_sys::io_uring_register_files( 141 | self.ring.as_ptr(), 142 | files.as_ptr() as *const _, 143 | files.len() as _ 144 | ) 145 | })?; 146 | Ok(files 147 | .iter() 148 | .enumerate() 149 | .map(|(i, &fd)| RegisteredFd::new(i as u32, fd)) 150 | ) 151 | } 152 | 153 | /// Update the currently registered kernel fileset. It is usually more efficient to reserve space 154 | /// for files before submitting events, because `IoUring` will wait until the submission queue is 155 | /// empty before registering files. 156 | /// # Errors 157 | /// Returns an error if 158 | /// * there isn't a registered fileset, 159 | /// * the `files` slice was empty, 160 | /// * `offset` is out of bounds, 161 | /// * the `files` slice was too large, 162 | /// * the inner [`io_uring_register_files_update`](uring_sys::io_uring_register_files_update) call 163 | /// failed for another reason 164 | pub fn update_registered_files<'a>(&mut self, offset: usize, files: &'a [RawFd]) -> io::Result + 'a> { 165 | assert!(files.len() + offset <= u32::MAX as usize); 166 | resultify(unsafe { 167 | uring_sys::io_uring_register_files_update( 168 | self.ring.as_ptr(), 169 | offset as _, 170 | files.as_ptr() as *const _, 171 | files.len() as _, 172 | ) 173 | })?; 174 | Ok(files 175 | .iter() 176 | .enumerate() 177 | .map(move |(i, &fd)| RegisteredFd::new((i + offset) as u32, fd)) 178 | ) 179 | } 180 | 181 | /// Unregister all currently registered files. An explicit call to this method is often unecessary, 182 | /// because all files will be unregistered automatically when the ring is dropped. 183 | /// 184 | /// # Errors 185 | /// Returns an error if 186 | /// * there isn't a registered fileset, 187 | /// * the inner [`io_uring_unregister_files`](uring_sys::io_uring_unregister_files) call 188 | /// failed for another reason 189 | /// 190 | /// You can use this method to replace an existing fileset: 191 | /// ``` 192 | /// # use iou::IoUring; 193 | /// # fn main() -> std::io::Result<()> { 194 | /// # let mut ring = IoUring::new(2)?; 195 | /// # let mut registrar = ring.registrar(); 196 | /// let raw_fds = [0, 1]; 197 | /// let fds: Vec<_> = registrar.register_files(&raw_fds)?.collect(); 198 | /// assert_eq!(fds.len(), 2); 199 | /// 200 | /// registrar.unregister_files()?; 201 | /// 202 | /// let other_raw_fds = [0, 1, 2]; 203 | /// let new_fds: Vec<_> = registrar.register_files(&other_raw_fds)?.collect(); 204 | /// assert_eq!(new_fds.len(), 3); 205 | /// # Ok(()) 206 | /// # } 207 | /// ``` 208 | pub fn unregister_files(&self) -> io::Result<()> { 209 | resultify(unsafe { uring_sys::io_uring_unregister_files(self.ring.as_ptr()) })?; 210 | Ok(()) 211 | } 212 | 213 | pub fn register_eventfd(&self, eventfd: RawFd) -> io::Result<()> { 214 | resultify(unsafe { 215 | uring_sys::io_uring_register_eventfd(self.ring.as_ptr(), eventfd) 216 | })?; 217 | Ok(()) 218 | } 219 | 220 | pub fn register_eventfd_async(&self, eventfd: RawFd) -> io::Result<()> { 221 | resultify(unsafe { 222 | uring_sys::io_uring_register_eventfd_async(self.ring.as_ptr(), eventfd) 223 | })?; 224 | Ok(()) 225 | } 226 | 227 | pub fn unregister_eventfd(&self) -> io::Result<()> { 228 | resultify(unsafe { 229 | uring_sys::io_uring_unregister_eventfd(self.ring.as_ptr()) 230 | })?; 231 | Ok(()) 232 | } 233 | 234 | pub fn register_personality(&self) -> io::Result { 235 | let id = resultify(unsafe { uring_sys::io_uring_register_personality(self.ring.as_ptr()) })?; 236 | debug_assert!(id < u16::MAX as u32); 237 | Ok(Personality { id: id as u16 }) 238 | } 239 | 240 | pub fn unregister_personality(&self, personality: Personality) -> io::Result<()> { 241 | resultify(unsafe { 242 | uring_sys::io_uring_unregister_personality(self.ring.as_ptr(), personality.id as _) 243 | })?; 244 | Ok(()) 245 | } 246 | 247 | pub fn probe(&self) -> io::Result { 248 | Probe::for_ring(self.ring.as_ptr()) 249 | } 250 | } 251 | 252 | impl fmt::Debug for Registrar<'_> { 253 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 254 | let fd = unsafe { self.ring.as_ref().ring_fd }; 255 | f.debug_struct(std::any::type_name::()).field("fd", &fd).finish() 256 | } 257 | } 258 | 259 | unsafe impl<'ring> Send for Registrar<'ring> { } 260 | unsafe impl<'ring> Sync for Registrar<'ring> { } 261 | 262 | #[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Copy)] 263 | pub struct Personality { 264 | pub(crate) id: u16, 265 | } 266 | 267 | impl From for Personality { 268 | fn from(id: u16) -> Personality { 269 | Personality { id } 270 | } 271 | } 272 | 273 | #[cfg(test)] 274 | mod tests { 275 | 276 | use super::*; 277 | use std::os::unix::io::AsRawFd; 278 | 279 | #[test] 280 | #[should_panic(expected = "Invalid argument")] 281 | fn register_empty_slice() { 282 | let ring = IoUring::new(1).unwrap(); 283 | let _ = ring.registrar().register_files(&[]).unwrap(); 284 | } 285 | 286 | #[test] 287 | #[should_panic(expected = "Bad file descriptor")] 288 | fn register_bad_fd() { 289 | let ring = IoUring::new(1).unwrap(); 290 | let _ = ring.registrar().register_files(&[-100]).unwrap(); 291 | } 292 | 293 | #[test] 294 | #[should_panic(expected = "Device or resource busy")] 295 | fn double_register() { 296 | let ring = IoUring::new(1).unwrap(); 297 | let _ = ring.registrar().register_files(&[1]).unwrap(); 298 | let _ = ring.registrar().register_files(&[1]).unwrap(); 299 | } 300 | 301 | #[test] 302 | #[should_panic(expected = "No such device or address")] 303 | fn empty_unregister_err() { 304 | let ring = IoUring::new(1).unwrap(); 305 | let _ = ring.registrar().unregister_files().unwrap(); 306 | } 307 | 308 | #[test] 309 | #[should_panic(expected = "No such device or address")] 310 | fn empty_update_err() { 311 | let ring = IoUring::new(1).unwrap(); 312 | let _ = ring.registrar().update_registered_files(0, &[1]).unwrap(); 313 | } 314 | 315 | #[test] 316 | #[should_panic(expected = "Invalid argument")] 317 | fn offset_out_of_bounds_update() { 318 | let raw_fds = [1, 2]; 319 | let ring = IoUring::new(1).unwrap(); 320 | let _ = ring.registrar().register_files(&raw_fds).unwrap(); 321 | let _ = ring.registrar().update_registered_files(2, &raw_fds).unwrap(); 322 | } 323 | 324 | #[test] 325 | #[should_panic(expected = "Invalid argument")] 326 | fn slice_len_out_of_bounds_update() { 327 | let ring = IoUring::new(1).unwrap(); 328 | let _ = ring.registrar().register_files(&[1, 1]).unwrap(); 329 | let _ = ring.registrar().update_registered_files(0, &[1, 1, 1]).unwrap(); 330 | } 331 | 332 | #[test] 333 | fn valid_fd_update() { 334 | let ring = IoUring::new(1).unwrap(); 335 | 336 | let file = std::fs::File::create("tmp.txt").unwrap(); 337 | let _ = ring.registrar().register_files(&[file.as_raw_fd()]).unwrap(); 338 | 339 | let new_file = std::fs::File::create("new_tmp.txt").unwrap(); 340 | let _ = ring.registrar().update_registered_files(0, &[new_file.as_raw_fd()]).unwrap(); 341 | 342 | let _ = std::fs::remove_file("tmp.txt"); 343 | let _ = std::fs::remove_file("new_tmp.txt"); 344 | } 345 | 346 | #[test] 347 | fn placeholder_update() { 348 | let ring = IoUring::new(1).unwrap(); 349 | let _ = ring.registrar().register_files(&[-1, -1, -1]).unwrap(); 350 | 351 | let file = std::fs::File::create("tmp.txt").unwrap(); 352 | let _ = ring.registrar().update_registered_files(0, &[file.as_raw_fd()]).unwrap(); 353 | let _ = std::fs::remove_file("tmp.txt"); 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /src/registrar/registered.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::ops::*; 3 | use std::os::unix::io::{AsRawFd, RawFd}; 4 | 5 | use crate::SQE; 6 | 7 | pub const PLACEHOLDER_FD: RawFd = -1; 8 | 9 | /// A member of the kernel's registered fileset. 10 | /// 11 | /// Valid `RegisteredFd`s can be obtained through a [`Registrar`](crate::registrar::Registrar). 12 | /// 13 | /// Registered files handle kernel fileset indexing behind the scenes and can often be used in place 14 | /// of raw file descriptors. Not all IO operations support registered files. 15 | /// 16 | /// Submission event prep methods on `RegisteredFd` will ensure that the submission event's 17 | /// `SubmissionFlags::FIXED_FILE` flag is properly set. 18 | pub type RegisteredFd = Registered; 19 | pub type RegisteredBuf = Registered>; 20 | pub type RegisteredBufRef<'a> = Registered<&'a [u8]>; 21 | pub type RegisteredBufMut<'a> = Registered<&'a mut [u8]>; 22 | 23 | /// An object registered with an io-uring instance through a [`Registrar`](crate::Registrar). 24 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] 25 | pub struct Registered { 26 | data: T, 27 | index: u32, 28 | } 29 | 30 | impl Registered { 31 | pub fn new(index: u32, data: T) -> Registered { 32 | Registered { data, index } 33 | } 34 | 35 | pub fn index(&self) -> u32 { 36 | self.index 37 | } 38 | 39 | pub fn into_inner(self) -> T { 40 | self.data 41 | } 42 | } 43 | 44 | impl RegisteredFd { 45 | pub fn is_placeholder(&self) -> bool { 46 | self.data == PLACEHOLDER_FD 47 | } 48 | } 49 | 50 | impl AsRawFd for RegisteredFd { 51 | fn as_raw_fd(&self) -> RawFd { 52 | self.data 53 | } 54 | } 55 | 56 | impl RegisteredBuf { 57 | pub fn as_ref(&self) -> RegisteredBufRef<'_> { 58 | Registered::new(self.index, &self.data[..]) 59 | } 60 | 61 | pub fn as_mut(&mut self) -> RegisteredBufMut<'_> { 62 | Registered::new(self.index, &mut self.data[..]) 63 | } 64 | 65 | pub fn slice(&self, range: Range) -> RegisteredBufRef<'_> { 66 | Registered::new(self.index, &self.data[range]) 67 | } 68 | 69 | pub fn slice_mut(&mut self, range: Range) -> RegisteredBufMut<'_> { 70 | Registered::new(self.index, &mut self.data[range]) 71 | } 72 | 73 | pub fn slice_to(&self, index: usize) -> RegisteredBufRef<'_> { 74 | Registered::new(self.index, &self.data[..index]) 75 | } 76 | 77 | pub fn slice_to_mut(&mut self, index: usize) -> RegisteredBufMut<'_> { 78 | Registered::new(self.index, &mut self.data[..index]) 79 | } 80 | 81 | pub fn slice_from(&self, index: usize) -> RegisteredBufRef<'_> { 82 | Registered::new(self.index, &self.data[index..]) 83 | } 84 | 85 | pub fn slice_from_mut(&mut self, index: usize) -> RegisteredBufMut<'_> { 86 | Registered::new(self.index, &mut self.data[index..]) 87 | } 88 | } 89 | 90 | impl<'a> RegisteredBufRef<'a> { 91 | pub fn as_ref(&self) -> RegisteredBufRef<'_> { 92 | Registered::new(self.index, &self.data[..]) 93 | } 94 | 95 | pub fn slice(self, range: Range) -> RegisteredBufRef<'a> { 96 | Registered::new(self.index, &self.data[range]) 97 | } 98 | 99 | pub fn slice_to(&self, index: usize) -> RegisteredBufRef<'_> { 100 | Registered::new(self.index, &self.data[..index]) 101 | } 102 | 103 | pub fn slice_from(&self, index: usize) -> RegisteredBufRef<'_> { 104 | Registered::new(self.index, &self.data[index..]) 105 | } 106 | } 107 | 108 | impl<'a> RegisteredBufMut<'a> { 109 | pub fn as_ref(&self) -> RegisteredBufRef<'_> { 110 | Registered::new(self.index, &self.data[..]) 111 | } 112 | 113 | pub fn as_mut(&mut self) -> RegisteredBufMut<'_> { 114 | Registered::new(self.index, &mut self.data[..]) 115 | } 116 | 117 | pub fn slice(self, range: Range) -> RegisteredBufRef<'a> { 118 | Registered::new(self.index, &self.data[range]) 119 | } 120 | 121 | pub fn slice_mut(self, range: Range) -> RegisteredBufMut<'a> { 122 | Registered::new(self.index, &mut self.data[range]) 123 | } 124 | 125 | pub fn slice_to(&self, index: usize) -> RegisteredBufRef<'_> { 126 | Registered::new(self.index, &self.data[..index]) 127 | } 128 | 129 | pub fn slice_to_mut(&mut self, index: usize) -> RegisteredBufMut<'_> { 130 | Registered::new(self.index, &mut self.data[..index]) 131 | } 132 | 133 | pub fn slice_from(&self, index: usize) -> RegisteredBufRef<'_> { 134 | Registered::new(self.index, &self.data[index..]) 135 | } 136 | 137 | pub fn slice_from_mut(&mut self, index: usize) -> RegisteredBufMut<'_> { 138 | Registered::new(self.index, &mut self.data[index..]) 139 | } 140 | } 141 | 142 | impl Deref for RegisteredBuf { 143 | type Target = [u8]; 144 | 145 | fn deref(&self) -> &[u8] { 146 | &self.data[..] 147 | } 148 | } 149 | 150 | impl Deref for RegisteredBufRef<'_> { 151 | type Target = [u8]; 152 | 153 | fn deref(&self) -> &[u8] { 154 | &self.data[..] 155 | } 156 | } 157 | 158 | impl Deref for RegisteredBufMut<'_> { 159 | type Target = [u8]; 160 | 161 | fn deref(&self) -> &[u8] { 162 | &self.data[..] 163 | } 164 | } 165 | 166 | impl DerefMut for RegisteredBuf { 167 | fn deref_mut(&mut self) -> &mut [u8] { 168 | &mut self.data[..] 169 | } 170 | } 171 | 172 | impl DerefMut for RegisteredBufMut<'_> { 173 | fn deref_mut(&mut self) -> &mut [u8] { 174 | &mut self.data[..] 175 | } 176 | } 177 | /// A file descriptor that can be used to prepare SQEs. 178 | /// 179 | /// The standard library's [`RawFd`] type implements this trait, but so does [`RegisteredFd`], a 180 | /// type which is returned when a user pre-registers file descriptors with an io-uring instance. 181 | pub trait UringFd { 182 | fn as_raw_fd(&self) -> RawFd; 183 | fn update_sqe(&self, sqe: &mut SQE<'_>); 184 | } 185 | 186 | impl UringFd for RawFd { 187 | fn as_raw_fd(&self) -> RawFd { 188 | *self 189 | } 190 | 191 | fn update_sqe(&self, _: &mut SQE<'_>) { } 192 | } 193 | 194 | impl UringFd for RegisteredFd { 195 | fn as_raw_fd(&self) -> RawFd { 196 | AsRawFd::as_raw_fd(self) 197 | } 198 | 199 | fn update_sqe(&self, sqe: &mut SQE<'_>) { 200 | unsafe { sqe.raw_mut().fd = self.index as RawFd; } 201 | sqe.set_fixed_file(); 202 | } 203 | } 204 | 205 | /// A buffer that can be used to prepare read events. 206 | pub trait UringReadBuf { 207 | unsafe fn prep_read(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64); 208 | } 209 | 210 | /// A buffer that can be used to prepare write events. 211 | pub trait UringWriteBuf { 212 | unsafe fn prep_write(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64); 213 | } 214 | 215 | impl UringReadBuf for RegisteredBufMut<'_> { 216 | unsafe fn prep_read(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 217 | uring_sys::io_uring_prep_read_fixed( 218 | sqe.raw_mut(), 219 | fd.as_raw_fd(), 220 | self.data.as_mut_ptr() as _, 221 | self.data.len() as _, 222 | offset as _, 223 | self.index() as _ 224 | ); 225 | fd.update_sqe(sqe); 226 | } 227 | } 228 | 229 | impl UringReadBuf for &'_ mut [u8] { 230 | unsafe fn prep_read(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 231 | uring_sys::io_uring_prep_read( 232 | sqe.raw_mut(), 233 | fd.as_raw_fd(), 234 | self.as_mut_ptr() as _, 235 | self.len() as _, 236 | offset as _, 237 | ); 238 | fd.update_sqe(sqe); 239 | } 240 | } 241 | 242 | impl UringReadBuf for io::IoSliceMut<'_> { 243 | unsafe fn prep_read(mut self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 244 | uring_sys::io_uring_prep_read( 245 | sqe.raw_mut(), 246 | fd.as_raw_fd(), 247 | self.as_mut_ptr() as _, 248 | self.len() as _, 249 | offset as _, 250 | ); 251 | fd.update_sqe(sqe); 252 | } 253 | } 254 | 255 | impl UringReadBuf for &'_ mut [&'_ mut [u8]] { 256 | unsafe fn prep_read(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 257 | uring_sys::io_uring_prep_readv( 258 | sqe.raw_mut(), 259 | fd.as_raw_fd(), 260 | self.as_mut_ptr() as _, 261 | self.len() as _, 262 | offset as _, 263 | ); 264 | fd.update_sqe(sqe); 265 | } 266 | } 267 | 268 | impl UringReadBuf for &'_ mut [io::IoSliceMut<'_>] { 269 | unsafe fn prep_read(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 270 | uring_sys::io_uring_prep_readv( 271 | sqe.raw_mut(), 272 | fd.as_raw_fd(), 273 | self.as_mut_ptr() as _, 274 | self.len() as _, 275 | offset as _, 276 | ); 277 | fd.update_sqe(sqe); 278 | } 279 | } 280 | 281 | impl UringWriteBuf for RegisteredBufRef<'_> { 282 | unsafe fn prep_write(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 283 | uring_sys::io_uring_prep_write_fixed( 284 | sqe.raw_mut(), 285 | fd.as_raw_fd(), 286 | self.data.as_ptr() as _, 287 | self.data.len() as _, 288 | offset as _, 289 | self.index() as _ 290 | ); 291 | fd.update_sqe(sqe); 292 | } 293 | } 294 | 295 | impl UringWriteBuf for &'_ [u8] { 296 | unsafe fn prep_write(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 297 | uring_sys::io_uring_prep_write( 298 | sqe.raw_mut(), 299 | fd.as_raw_fd(), 300 | self.as_ptr() as _, 301 | self.len() as _, 302 | offset as _, 303 | ); 304 | fd.update_sqe(sqe); 305 | } 306 | } 307 | 308 | impl UringWriteBuf for io::IoSlice<'_> { 309 | unsafe fn prep_write(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 310 | uring_sys::io_uring_prep_write( 311 | sqe.raw_mut(), 312 | fd.as_raw_fd(), 313 | self.as_ptr() as _, 314 | self.len() as _, 315 | offset as _, 316 | ); 317 | fd.update_sqe(sqe); 318 | } 319 | } 320 | 321 | impl UringWriteBuf for &'_ [io::IoSlice<'_>] { 322 | unsafe fn prep_write(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 323 | uring_sys::io_uring_prep_writev( 324 | sqe.raw_mut(), 325 | fd.as_raw_fd(), 326 | self.as_ptr() as _, 327 | self.len() as _, 328 | offset as _, 329 | ); 330 | fd.update_sqe(sqe); 331 | } 332 | } 333 | 334 | impl UringWriteBuf for &'_ [&'_ [u8]] { 335 | unsafe fn prep_write(self, fd: impl UringFd, sqe: &mut SQE<'_>, offset: u64) { 336 | uring_sys::io_uring_prep_writev( 337 | sqe.raw_mut(), 338 | fd.as_raw_fd(), 339 | self.as_ptr() as _, 340 | self.len() as _, 341 | offset as _, 342 | ); 343 | fd.update_sqe(sqe); 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /src/sqe.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::mem; 3 | use std::ffi::CStr; 4 | use std::ops::{Deref, DerefMut}; 5 | use std::os::unix::io::RawFd; 6 | use std::ptr; 7 | use std::slice; 8 | 9 | use crate::registrar::{UringFd, UringReadBuf, UringWriteBuf}; 10 | 11 | pub use nix::fcntl::{OFlag, FallocateFlags, PosixFadviseAdvice}; 12 | pub use nix::poll::PollFlags; 13 | pub use nix::sys::epoll::{EpollOp, EpollEvent}; 14 | pub use nix::sys::mman::MmapAdvise; 15 | pub use nix::sys::stat::Mode; 16 | pub use nix::sys::socket::{SockAddr, SockFlag, MsgFlags}; 17 | 18 | use crate::Personality; 19 | 20 | /// A pending IO event. 21 | /// 22 | /// Can be configured with a set of [`SubmissionFlags`](crate::sqe::SubmissionFlags). 23 | /// 24 | pub struct SQE<'a> { 25 | sqe: &'a mut uring_sys::io_uring_sqe, 26 | } 27 | 28 | impl<'a> SQE<'a> { 29 | pub(crate) fn new(sqe: &'a mut uring_sys::io_uring_sqe) -> SQE<'a> { 30 | SQE { sqe } 31 | } 32 | 33 | /// Get this event's user data. 34 | #[inline] 35 | pub fn user_data(&self) -> u64 { 36 | self.sqe.user_data as u64 37 | } 38 | 39 | /// Set this event's user data. User data is intended to be used by the application after 40 | /// completion. 41 | /// 42 | /// Note that you should not set user_data to `u64::MAX`. This value is reserved for timeouts 43 | /// generated by this library, setting an events user_data to that value will cause the 44 | /// event's completion to swallowed by the library and you will never find out that the event 45 | /// completed. 46 | /// 47 | /// # Safety 48 | /// 49 | /// This function is marked `unsafe`. The library from which you obtained this 50 | /// `SQE` may impose additional safety invariants which you must adhere to 51 | /// when setting the user_data for a submission queue event, which it may rely on when 52 | /// processing the corresponding completion queue event. For example, the library 53 | /// [ringbahn][ringbahn] 54 | /// 55 | /// # Example 56 | /// 57 | /// ```rust 58 | /// # use iou::IoUring; 59 | /// # fn main() -> std::io::Result<()> { 60 | /// # let mut ring = IoUring::new(2)?; 61 | /// # let mut sq_event = ring.prepare_sqe().unwrap(); 62 | /// # 63 | /// unsafe { sq_event.set_user_data(0xB00); } 64 | /// ring.submit_sqes()?; 65 | /// 66 | /// let cq_event = ring.wait_for_cqe()?; 67 | /// assert_eq!(cq_event.user_data(), 0xB00); 68 | /// # Ok(()) 69 | /// # } 70 | /// ``` 71 | /// 72 | /// [ringbahn]: https://crates.io/crates/ringbahn 73 | pub unsafe fn set_user_data(&mut self, user_data: u64) { 74 | self.sqe.user_data = user_data as _; 75 | } 76 | 77 | /// Get this event's flags. 78 | #[inline] 79 | pub fn flags(&self) -> SubmissionFlags { 80 | unsafe { SubmissionFlags::from_bits_unchecked(self.sqe.flags as _) } 81 | } 82 | 83 | /// Overwrite this event's flags. 84 | pub fn overwrite_flags(&mut self, flags: SubmissionFlags) { 85 | self.sqe.flags = flags.bits() as _; 86 | } 87 | 88 | // must be called after any prep methods to properly complete mapped kernel IO 89 | #[inline] 90 | pub(crate) fn set_fixed_file(&mut self) { 91 | self.set_flags(SubmissionFlags::FIXED_FILE); 92 | } 93 | 94 | /// Set these flags for this event (any flags already set will still be set). 95 | #[inline] 96 | pub fn set_flags(&mut self, flags: SubmissionFlags) { 97 | self.sqe.flags |= flags.bits(); 98 | } 99 | 100 | /// Set the [`Personality`] associated with this submission. 101 | #[inline] 102 | pub fn set_personality(&mut self, personality: Personality) { 103 | self.sqe.buf_index.buf_index.personality = personality.id; 104 | } 105 | 106 | /// Prepare a read on a file descriptor. 107 | /// 108 | /// Both the file descriptor and the buffer can be pre-registered. See the 109 | /// [`registrar][crate::registrar] module for more information. 110 | #[inline] 111 | pub unsafe fn prep_read( 112 | &mut self, 113 | fd: impl UringFd, 114 | buf: impl UringReadBuf, 115 | offset: u64, 116 | ) { 117 | buf.prep_read(fd, self, offset); 118 | } 119 | 120 | /// Prepare a vectored read on a file descriptor. 121 | #[inline] 122 | pub unsafe fn prep_read_vectored( 123 | &mut self, 124 | fd: impl UringFd, 125 | bufs: &mut [io::IoSliceMut<'_>], 126 | offset: u64, 127 | ) { 128 | let len = bufs.len(); 129 | let addr = bufs.as_mut_ptr(); 130 | uring_sys::io_uring_prep_readv(self.sqe, fd.as_raw_fd(), addr as _, len as _, offset as _); 131 | fd.update_sqe(self); 132 | } 133 | 134 | /// Prepare a read into a fixed, pre-registered buffer on a file descriptor. 135 | #[inline] 136 | pub unsafe fn prep_read_fixed( 137 | &mut self, 138 | fd: impl UringFd, 139 | buf: &mut [u8], 140 | offset: u64, 141 | buf_index: u32, 142 | ) { 143 | let len = buf.len(); 144 | let addr = buf.as_mut_ptr(); 145 | uring_sys::io_uring_prep_read_fixed(self.sqe, 146 | fd.as_raw_fd(), 147 | addr as _, 148 | len as _, 149 | offset as _, 150 | buf_index as _); 151 | fd.update_sqe(self); 152 | } 153 | 154 | /// Prepare a write on a file descriptor. 155 | /// 156 | /// Both the file descriptor and the buffer can be pre-registered. See the 157 | /// [`registrar][crate::registrar] module for more information. 158 | #[inline] 159 | pub unsafe fn prep_write( 160 | &mut self, 161 | fd: impl UringFd, 162 | buf: impl UringWriteBuf, 163 | offset: u64, 164 | ) { 165 | buf.prep_write(fd, self, offset) 166 | } 167 | 168 | /// Prepare a vectored write on a file descriptor. 169 | #[inline] 170 | pub unsafe fn prep_write_vectored( 171 | &mut self, 172 | fd: impl UringFd, 173 | bufs: &[io::IoSlice<'_>], 174 | offset: u64, 175 | ) { 176 | let len = bufs.len(); 177 | let addr = bufs.as_ptr(); 178 | uring_sys::io_uring_prep_writev(self.sqe, 179 | fd.as_raw_fd(), 180 | addr as _, 181 | len as _, 182 | offset as _); 183 | fd.update_sqe(self); 184 | } 185 | 186 | /// Prepare a write on a file descriptor from a fixed, pre-registered buffer. 187 | #[inline] 188 | pub unsafe fn prep_write_fixed( 189 | &mut self, 190 | fd: impl UringFd, 191 | buf: &[u8], 192 | offset: u64, 193 | buf_index: usize, 194 | ) { 195 | let len = buf.len(); 196 | let addr = buf.as_ptr(); 197 | uring_sys::io_uring_prep_write_fixed(self.sqe, 198 | fd.as_raw_fd(), 199 | addr as _, 200 | len as _, 201 | offset as _, 202 | buf_index as _); 203 | fd.update_sqe(self); 204 | } 205 | 206 | /// Prepare an fsync on a file descriptor. 207 | #[inline] 208 | pub unsafe fn prep_fsync(&mut self, fd: impl UringFd, flags: FsyncFlags) { 209 | uring_sys::io_uring_prep_fsync(self.sqe, fd.as_raw_fd(), flags.bits() as _); 210 | fd.update_sqe(self); 211 | } 212 | 213 | /// Prepare a splice, copying data from one file descriptor to another. 214 | #[inline] 215 | pub unsafe fn prep_splice( 216 | &mut self, 217 | fd_in: RawFd, 218 | off_in: i64, 219 | fd_out: RawFd, 220 | off_out: i64, 221 | count: u32, 222 | flags: SpliceFlags, 223 | ) { 224 | uring_sys::io_uring_prep_splice(self.sqe, fd_in, off_in, fd_out, off_out, count, flags.bits()); 225 | } 226 | 227 | /// Prepare a recv event on a file descriptor. 228 | #[inline] 229 | pub unsafe fn prep_recv(&mut self, fd: impl UringFd, buf: &mut [u8], flags: MsgFlags) { 230 | let data = buf.as_mut_ptr() as *mut libc::c_void; 231 | let len = buf.len(); 232 | uring_sys::io_uring_prep_recv(self.sqe, fd.as_raw_fd(), data, len, flags.bits()); 233 | fd.update_sqe(self); 234 | } 235 | 236 | /// Prepare a send event on a file descriptor. 237 | #[inline] 238 | pub unsafe fn prep_send(&mut self, fd: impl UringFd, buf: &[u8], flags: MsgFlags) { 239 | let data = buf.as_ptr() as *const libc::c_void as *mut libc::c_void; 240 | let len = buf.len(); 241 | uring_sys::io_uring_prep_send(self.sqe, fd.as_raw_fd(), data, len, flags.bits()); 242 | fd.update_sqe(self); 243 | } 244 | 245 | /// Prepare a recvmsg event on a file descriptor. 246 | pub unsafe fn prep_recvmsg(&mut self, fd: impl UringFd, msg: *mut libc::msghdr, flags: MsgFlags) { 247 | uring_sys::io_uring_prep_recvmsg(self.sqe, fd.as_raw_fd(), msg, flags.bits() as _); 248 | fd.update_sqe(self); 249 | } 250 | 251 | /// Prepare a sendmsg event on a file descriptor. 252 | pub unsafe fn prep_sendmsg(&mut self, fd: impl UringFd, msg: *mut libc::msghdr, flags: MsgFlags) { 253 | uring_sys::io_uring_prep_sendmsg(self.sqe, fd.as_raw_fd(), msg, flags.bits() as _); 254 | fd.update_sqe(self); 255 | } 256 | 257 | /// Prepare a fallocate event. 258 | #[inline] 259 | pub unsafe fn prep_fallocate(&mut self, fd: impl UringFd, 260 | offset: u64, size: u64, 261 | flags: FallocateFlags) { 262 | uring_sys::io_uring_prep_fallocate(self.sqe, fd.as_raw_fd(), 263 | flags.bits() as _, 264 | offset as _, 265 | size as _); 266 | fd.update_sqe(self); 267 | } 268 | 269 | /// Prepare a statx event. 270 | #[inline] 271 | pub unsafe fn prep_statx( 272 | &mut self, 273 | dirfd: impl UringFd, 274 | path: &CStr, 275 | flags: StatxFlags, 276 | mask: StatxMode, 277 | buf: &mut libc::statx, 278 | ) { 279 | uring_sys::io_uring_prep_statx(self.sqe, dirfd.as_raw_fd(), path.as_ptr() as _, 280 | flags.bits() as _, mask.bits() as _, 281 | buf as _); 282 | } 283 | 284 | /// Prepare an openat event. 285 | #[inline] 286 | pub unsafe fn prep_openat( 287 | &mut self, 288 | fd: impl UringFd, 289 | path: &CStr, 290 | flags: OFlag, 291 | mode: Mode, 292 | ) { 293 | uring_sys::io_uring_prep_openat(self.sqe, fd.as_raw_fd(), path.as_ptr() as _, flags.bits(), mode.bits()); 294 | } 295 | 296 | // TODO openat2 297 | 298 | /// Prepare a close event on a file descriptor. 299 | #[inline] 300 | pub unsafe fn prep_close(&mut self, fd: impl UringFd) { 301 | uring_sys::io_uring_prep_close(self.sqe, fd.as_raw_fd()); 302 | } 303 | 304 | 305 | /// Prepare a timeout event. 306 | /// 307 | /// ``` 308 | /// # use iou::IoUring; 309 | /// # use iou::sqe::TimeoutFlags; 310 | /// # fn main() -> std::io::Result<()> { 311 | /// # let mut ring = IoUring::new(1)?; 312 | /// # let mut sqe = ring.prepare_sqe().unwrap(); 313 | /// # 314 | /// // make a one-second timeout 315 | /// let timeout_spec: _ = uring_sys::__kernel_timespec { 316 | /// tv_sec: 1 as _, 317 | /// tv_nsec: 0 as _, 318 | /// }; 319 | /// 320 | /// unsafe { sqe.prep_timeout(&timeout_spec, 0, TimeoutFlags::empty()); } 321 | /// 322 | /// ring.submit_sqes()?; 323 | /// # Ok(()) 324 | /// # } 325 | ///``` 326 | #[inline] 327 | pub unsafe fn prep_timeout(&mut self, ts: &uring_sys::__kernel_timespec, events: u32, flags: TimeoutFlags) { 328 | uring_sys::io_uring_prep_timeout(self.sqe, 329 | ts as *const _ as *mut _, 330 | events as _, 331 | flags.bits() as _); 332 | } 333 | 334 | #[inline] 335 | pub unsafe fn prep_timeout_remove(&mut self, user_data: u64) { 336 | uring_sys::io_uring_prep_timeout_remove(self.sqe, user_data as _, 0); 337 | } 338 | 339 | #[inline] 340 | pub unsafe fn prep_link_timeout(&mut self, ts: &uring_sys::__kernel_timespec) { 341 | uring_sys::io_uring_prep_link_timeout(self.sqe, ts as *const _ as *mut _, 0); 342 | } 343 | 344 | #[inline] 345 | pub unsafe fn prep_poll_add(&mut self, fd: impl UringFd, poll_flags: PollFlags) { 346 | uring_sys::io_uring_prep_poll_add(self.sqe, fd.as_raw_fd(), poll_flags.bits()); 347 | fd.update_sqe(self); 348 | } 349 | 350 | #[inline] 351 | pub unsafe fn prep_poll_remove(&mut self, user_data: u64) { 352 | uring_sys::io_uring_prep_poll_remove(self.sqe, user_data as _) 353 | } 354 | 355 | #[inline] 356 | pub unsafe fn prep_connect(&mut self, fd: impl UringFd, socket_addr: &SockAddr) { 357 | let (addr, len) = socket_addr.as_ffi_pair(); 358 | uring_sys::io_uring_prep_connect(self.sqe, fd.as_raw_fd(), addr as *const _ as *mut _, len); 359 | fd.update_sqe(self); 360 | } 361 | 362 | #[inline] 363 | pub unsafe fn prep_accept(&mut self, fd: impl UringFd, accept: Option<&mut SockAddrStorage>, flags: SockFlag) { 364 | let (addr, len) = match accept { 365 | Some(accept) => (accept.storage.as_mut_ptr() as *mut _, &mut accept.len as *mut _ as *mut _), 366 | None => (std::ptr::null_mut(), std::ptr::null_mut()) 367 | }; 368 | uring_sys::io_uring_prep_accept(self.sqe, fd.as_raw_fd(), addr, len, flags.bits()); 369 | fd.update_sqe(self); 370 | } 371 | 372 | #[inline] 373 | pub unsafe fn prep_fadvise(&mut self, fd: impl UringFd, off: u64, len: u64, advice: PosixFadviseAdvice) { 374 | use PosixFadviseAdvice::*; 375 | let advice = match advice { 376 | POSIX_FADV_NORMAL => libc::POSIX_FADV_NORMAL, 377 | POSIX_FADV_SEQUENTIAL => libc::POSIX_FADV_SEQUENTIAL, 378 | POSIX_FADV_RANDOM => libc::POSIX_FADV_RANDOM, 379 | POSIX_FADV_NOREUSE => libc::POSIX_FADV_NOREUSE, 380 | POSIX_FADV_WILLNEED => libc::POSIX_FADV_WILLNEED, 381 | POSIX_FADV_DONTNEED => libc::POSIX_FADV_DONTNEED, 382 | }; 383 | uring_sys::io_uring_prep_fadvise(self.sqe, fd.as_raw_fd(), off as _, len as _, advice); 384 | fd.update_sqe(self); 385 | } 386 | 387 | #[inline] 388 | pub unsafe fn prep_madvise(&mut self, data: &mut [u8], advice: MmapAdvise) { 389 | use MmapAdvise::*; 390 | let advice = match advice { 391 | MADV_NORMAL => libc::MADV_NORMAL, 392 | MADV_RANDOM => libc::MADV_RANDOM, 393 | MADV_SEQUENTIAL => libc::MADV_SEQUENTIAL, 394 | MADV_WILLNEED => libc::MADV_WILLNEED, 395 | MADV_DONTNEED => libc::MADV_DONTNEED, 396 | MADV_REMOVE => libc::MADV_REMOVE, 397 | MADV_DONTFORK => libc::MADV_DONTFORK, 398 | MADV_DOFORK => libc::MADV_DOFORK, 399 | MADV_HWPOISON => libc::MADV_HWPOISON, 400 | MADV_MERGEABLE => libc::MADV_MERGEABLE, 401 | MADV_UNMERGEABLE => libc::MADV_UNMERGEABLE, 402 | MADV_SOFT_OFFLINE => libc::MADV_SOFT_OFFLINE, 403 | MADV_HUGEPAGE => libc::MADV_HUGEPAGE, 404 | MADV_NOHUGEPAGE => libc::MADV_NOHUGEPAGE, 405 | MADV_DONTDUMP => libc::MADV_DONTDUMP, 406 | MADV_DODUMP => libc::MADV_DODUMP, 407 | MADV_FREE => libc::MADV_FREE, 408 | }; 409 | uring_sys::io_uring_prep_madvise(self.sqe, data.as_mut_ptr() as *mut _, data.len() as _, advice); 410 | } 411 | 412 | #[inline] 413 | pub unsafe fn prep_epoll_ctl(&mut self, epoll_fd: RawFd, op: EpollOp, fd: RawFd, event: Option<&mut EpollEvent>) { 414 | let op = match op { 415 | EpollOp::EpollCtlAdd => libc::EPOLL_CTL_ADD, 416 | EpollOp::EpollCtlDel => libc::EPOLL_CTL_DEL, 417 | EpollOp::EpollCtlMod => libc::EPOLL_CTL_MOD, 418 | }; 419 | let event = event.map_or(ptr::null_mut(), |event| event as *mut EpollEvent as *mut _); 420 | uring_sys::io_uring_prep_epoll_ctl(self.sqe, epoll_fd, fd, op, event); 421 | } 422 | 423 | #[inline] 424 | pub unsafe fn prep_files_update(&mut self, files: &[RawFd], offset: u32) { 425 | let addr = files.as_ptr() as *mut RawFd; 426 | let len = files.len() as u32; 427 | uring_sys::io_uring_prep_files_update(self.sqe, addr, len, offset as _); 428 | } 429 | 430 | pub unsafe fn prep_provide_buffers(&mut self, 431 | buffers: &mut [u8], 432 | count: u32, 433 | group: BufferGroupId, 434 | index: u32, 435 | ) { 436 | let addr = buffers.as_mut_ptr() as *mut libc::c_void; 437 | let len = buffers.len() as u32 / count; 438 | uring_sys::io_uring_prep_provide_buffers(self.sqe, addr, len as _, count as _, group.id as _, index as _); 439 | } 440 | 441 | pub unsafe fn prep_remove_buffers(&mut self, count: u32, id: BufferGroupId) { 442 | uring_sys::io_uring_prep_remove_buffers(self.sqe, count as _, id.id as _); 443 | } 444 | 445 | #[inline] 446 | pub unsafe fn prep_cancel(&mut self, user_data: u64, flags: i32) { 447 | uring_sys::io_uring_prep_cancel(self.sqe, user_data as _, flags); 448 | } 449 | 450 | 451 | /// Prepare a no-op event. 452 | /// ``` 453 | /// # use iou::{IoUring, sqe::SubmissionFlags}; 454 | /// # fn main() -> std::io::Result<()> { 455 | /// # let mut ring = IoUring::new(1)?; 456 | /// # 457 | /// // example: use a no-op to force a drain 458 | /// 459 | /// let mut nop = ring.prepare_sqe().unwrap(); 460 | /// 461 | /// nop.set_flags(SubmissionFlags::IO_DRAIN); 462 | /// unsafe { nop.prep_nop(); } 463 | /// 464 | /// ring.submit_sqes()?; 465 | /// # Ok(()) 466 | /// # } 467 | ///``` 468 | #[inline] 469 | pub unsafe fn prep_nop(&mut self) { 470 | uring_sys::io_uring_prep_nop(self.sqe); 471 | } 472 | 473 | /// Clear event. Clears user data, flags, and any event setup. 474 | /// ``` 475 | /// # use iou::{IoUring, sqe::SubmissionFlags}; 476 | /// # 477 | /// # fn main() -> std::io::Result<()> { 478 | /// # let mut ring = IoUring::new(1)?; 479 | /// # let mut sqe = ring.prepare_sqe().unwrap(); 480 | /// # 481 | /// unsafe { sqe.set_user_data(0x1010); } 482 | /// sqe.set_flags(SubmissionFlags::IO_DRAIN); 483 | /// 484 | /// sqe.clear(); 485 | /// 486 | /// assert_eq!(sqe.user_data(), 0x0); 487 | /// assert_eq!(sqe.flags(), SubmissionFlags::empty()); 488 | /// # Ok(()) 489 | /// # } 490 | /// ``` 491 | pub fn clear(&mut self) { 492 | *self.sqe = unsafe { mem::zeroed() }; 493 | } 494 | 495 | /// Get a reference to the underlying [`uring_sys::io_uring_sqe`](uring_sys::io_uring_sqe) object. 496 | /// 497 | /// You can use this method to inspect the low-level details of an event. 498 | /// ``` 499 | /// # use iou::{IoUring}; 500 | /// # 501 | /// # fn main() -> std::io::Result<()> { 502 | /// # let mut ring = IoUring::new(1)?; 503 | /// # let mut sqe = ring.prepare_sqe().unwrap(); 504 | /// # 505 | /// unsafe { sqe.prep_nop(); } 506 | /// 507 | /// let sqe_ref = sqe.raw(); 508 | /// 509 | /// assert_eq!(sqe_ref.len, 0); 510 | /// # Ok(()) 511 | /// # } 512 | /// 513 | /// ``` 514 | pub fn raw(&self) -> &uring_sys::io_uring_sqe { 515 | &self.sqe 516 | } 517 | 518 | pub unsafe fn raw_mut(&mut self) -> &mut uring_sys::io_uring_sqe { 519 | &mut self.sqe 520 | } 521 | } 522 | 523 | unsafe impl<'a> Send for SQE<'a> { } 524 | unsafe impl<'a> Sync for SQE<'a> { } 525 | 526 | #[derive(Debug)] 527 | pub struct SockAddrStorage { 528 | storage: mem::MaybeUninit, 529 | len: usize, 530 | } 531 | 532 | impl SockAddrStorage { 533 | pub fn uninit() -> Self { 534 | let storage = mem::MaybeUninit::uninit(); 535 | let len = mem::size_of::(); 536 | SockAddrStorage { 537 | storage, 538 | len 539 | } 540 | } 541 | 542 | pub unsafe fn as_socket_addr(&self) -> io::Result { 543 | let storage = &*self.storage.as_ptr(); 544 | nix::sys::socket::sockaddr_storage_to_addr(storage, self.len).map_err(|e| { 545 | let err_no = e.as_errno(); 546 | match err_no { 547 | Some(err_no) => io::Error::from_raw_os_error(err_no as _), 548 | None => io::Error::new(io::ErrorKind::Other, "Unknown error") 549 | } 550 | }) 551 | } 552 | } 553 | 554 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 555 | pub struct BufferGroupId { 556 | pub id: u32, 557 | } 558 | 559 | bitflags::bitflags! { 560 | /// [`SQE`](SQE) configuration flags. 561 | pub struct SubmissionFlags: u8 { 562 | /// This event's file descriptor is an index into the preregistered set of files. 563 | const FIXED_FILE = 1 << 0; /* use fixed fileset */ 564 | /// Submit this event only after completing all ongoing submission events. 565 | const IO_DRAIN = 1 << 1; /* issue after inflight IO */ 566 | /// Force the next submission event to wait until this event has completed sucessfully. 567 | /// 568 | /// An event's link only applies to the next event, but link chains can be 569 | /// arbitrarily long. 570 | const IO_LINK = 1 << 2; /* next IO depends on this one */ 571 | 572 | const IO_HARDLINK = 1 << 3; 573 | const ASYNC = 1 << 4; 574 | const BUFFER_SELECT = 1 << 5; 575 | } 576 | } 577 | 578 | bitflags::bitflags! { 579 | pub struct FsyncFlags: u32 { 580 | /// Sync file data without an immediate metadata sync. 581 | const FSYNC_DATASYNC = 1 << 0; 582 | } 583 | } 584 | 585 | bitflags::bitflags! { 586 | pub struct StatxFlags: i32 { 587 | const AT_STATX_SYNC_AS_STAT = 0; 588 | const AT_SYMLINK_NOFOLLOW = 1 << 10; 589 | const AT_NO_AUTOMOUNT = 1 << 11; 590 | const AT_EMPTY_PATH = 1 << 12; 591 | const AT_STATX_FORCE_SYNC = 1 << 13; 592 | const AT_STATX_DONT_SYNC = 1 << 14; 593 | } 594 | } 595 | 596 | bitflags::bitflags! { 597 | pub struct StatxMode: i32 { 598 | const STATX_TYPE = 1 << 0; 599 | const STATX_MODE = 1 << 1; 600 | const STATX_NLINK = 1 << 2; 601 | const STATX_UID = 1 << 3; 602 | const STATX_GID = 1 << 4; 603 | const STATX_ATIME = 1 << 5; 604 | const STATX_MTIME = 1 << 6; 605 | const STATX_CTIME = 1 << 7; 606 | const STATX_INO = 1 << 8; 607 | const STATX_SIZE = 1 << 9; 608 | const STATX_BLOCKS = 1 << 10; 609 | const STATX_BTIME = 1 << 11; 610 | } 611 | } 612 | 613 | bitflags::bitflags! { 614 | pub struct TimeoutFlags: u32 { 615 | const TIMEOUT_ABS = 1 << 0; 616 | } 617 | } 618 | 619 | bitflags::bitflags! { 620 | pub struct SpliceFlags: u32 { 621 | const F_FD_IN_FIXED = 1 << 31; 622 | } 623 | } 624 | 625 | /// A sequence of [`SQE`]s from the [`SubmissionQueue`][crate::SubmissionQueue]. 626 | pub struct SQEs<'ring> { 627 | sqes: slice::IterMut<'ring, uring_sys::io_uring_sqe>, 628 | } 629 | 630 | impl<'ring> SQEs<'ring> { 631 | pub(crate) fn new(slice: &'ring mut [uring_sys::io_uring_sqe]) -> SQEs<'ring> { 632 | SQEs { 633 | sqes: slice.iter_mut(), 634 | } 635 | } 636 | 637 | /// Consumes all remaining [`SQE`]s, returning the last one. Subsequent attempts to get 638 | /// additional [`SQE`]s will return `None`. 639 | pub fn single(&mut self) -> Option> { 640 | let mut next = None; 641 | while let Some(sqe) = self.consume() { next = Some(sqe) } 642 | next 643 | } 644 | 645 | /// An iterator of [`HardLinkedSQE`]s. These will be [`SQE`]s that are *hard-linked* together. 646 | /// 647 | /// Hard-linked SQEs will occur sequentially. All of them will be completed, even if one of the 648 | /// events resolves to an error. 649 | pub fn hard_linked(&mut self) -> HardLinked<'ring, '_> { 650 | HardLinked { sqes: self } 651 | } 652 | 653 | /// An iterator of [`SoftLinkedSQE`]s. These will be [`SQE`]s that are *soft-linked* together. 654 | /// 655 | /// Soft-linked SQEs will occur sequentially. If one the events errors, all events after it 656 | /// will be cancelled. 657 | pub fn soft_linked(&mut self) -> SoftLinked<'ring, '_> { 658 | SoftLinked { sqes: self } 659 | } 660 | 661 | /// Remaining [`SQE`]s that can be modified. 662 | pub fn remaining(&self) -> u32 { 663 | self.sqes.len() as u32 664 | } 665 | 666 | fn consume(&mut self) -> Option> { 667 | self.sqes.next().map(|sqe| { 668 | unsafe { uring_sys::io_uring_prep_nop(sqe) } 669 | SQE { sqe } 670 | }) 671 | } 672 | } 673 | 674 | impl<'ring> Iterator for SQEs<'ring> { 675 | type Item = SQE<'ring>; 676 | 677 | fn next(&mut self) -> Option> { 678 | self.consume() 679 | } 680 | } 681 | 682 | /// An Iterator of [`SQE`]s which will be hard linked together. 683 | pub struct HardLinked<'ring, 'a> { 684 | sqes: &'a mut SQEs<'ring>, 685 | } 686 | 687 | impl<'ring> HardLinked<'ring, '_> { 688 | pub fn terminate(self) -> Option> { 689 | self.sqes.consume() 690 | } 691 | } 692 | 693 | impl<'ring> Iterator for HardLinked<'ring, '_> { 694 | type Item = HardLinkedSQE<'ring>; 695 | 696 | fn next(&mut self) -> Option { 697 | let is_final = self.sqes.remaining() == 1; 698 | self.sqes.consume().map(|sqe| HardLinkedSQE { sqe, is_final }) 699 | } 700 | } 701 | 702 | pub struct HardLinkedSQE<'ring> { 703 | sqe: SQE<'ring>, 704 | is_final: bool, 705 | } 706 | 707 | impl<'ring> Deref for HardLinkedSQE<'ring> { 708 | type Target = SQE<'ring>; 709 | 710 | fn deref(&self) -> &SQE<'ring> { 711 | &self.sqe 712 | } 713 | } 714 | 715 | impl<'ring> DerefMut for HardLinkedSQE<'ring> { 716 | fn deref_mut(&mut self) -> &mut SQE<'ring> { 717 | &mut self.sqe 718 | } 719 | } 720 | 721 | impl<'ring> Drop for HardLinkedSQE<'ring> { 722 | fn drop(&mut self) { 723 | if !self.is_final { 724 | self.sqe.set_flags(SubmissionFlags::IO_HARDLINK); 725 | } 726 | } 727 | } 728 | 729 | /// An Iterator of [`SQE`]s which will be soft linked together. 730 | pub struct SoftLinked<'ring, 'a> { 731 | sqes: &'a mut SQEs<'ring>, 732 | } 733 | 734 | impl<'ring> SoftLinked<'ring, '_> { 735 | pub fn terminate(self) -> Option> { 736 | self.sqes.consume() 737 | } 738 | } 739 | 740 | impl<'ring> Iterator for SoftLinked<'ring, '_> { 741 | type Item = SoftLinkedSQE<'ring>; 742 | 743 | fn next(&mut self) -> Option { 744 | let is_final = self.sqes.remaining() == 1; 745 | self.sqes.consume().map(|sqe| SoftLinkedSQE { sqe, is_final }) 746 | } 747 | } 748 | 749 | pub struct SoftLinkedSQE<'ring> { 750 | sqe: SQE<'ring>, 751 | is_final: bool, 752 | } 753 | 754 | impl<'ring> Deref for SoftLinkedSQE<'ring> { 755 | type Target = SQE<'ring>; 756 | 757 | fn deref(&self) -> &SQE<'ring> { 758 | &self.sqe 759 | } 760 | } 761 | 762 | impl<'ring> DerefMut for SoftLinkedSQE<'ring> { 763 | fn deref_mut(&mut self) -> &mut SQE<'ring> { 764 | &mut self.sqe 765 | } 766 | } 767 | 768 | impl<'ring> Drop for SoftLinkedSQE<'ring> { 769 | fn drop(&mut self) { 770 | if !self.is_final { 771 | self.sqe.set_flags(SubmissionFlags::IO_LINK); 772 | } 773 | } 774 | } 775 | -------------------------------------------------------------------------------- /src/submission_queue.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io; 3 | use std::ptr::NonNull; 4 | use std::marker::PhantomData; 5 | use std::slice; 6 | use std::time::Duration; 7 | use std::sync::atomic::{self, Ordering}; 8 | 9 | use super::{IoUring, SQE, SQEs, resultify}; 10 | 11 | /// The queue of pending IO events. 12 | /// 13 | /// Each element is a [`SQE`](crate::sqe::SQE). 14 | /// By default, events are processed in parallel after being submitted. 15 | /// You can modify this behavior for specific events using event [`SubmissionFlags`](crate::sqe::SubmissionFlags). 16 | /// 17 | /// # Examples 18 | /// Consider a read event that depends on a successful write beforehand. 19 | /// 20 | /// We reify this relationship by using `IO_LINK` to link these events. 21 | /// ```rust 22 | /// # use std::error::Error; 23 | /// # use std::fs::File; 24 | /// # use std::os::unix::io::{AsRawFd, RawFd}; 25 | /// # use iou::{IoUring, sqe::SubmissionFlags}; 26 | /// # 27 | /// # fn main() -> Result<(), Box> { 28 | /// # let mut ring = IoUring::new(2)?; 29 | /// # let mut sq = ring.sq(); 30 | /// # 31 | /// let mut write_event = sq.prepare_sqe().unwrap(); 32 | /// 33 | /// // -- write event prep elided 34 | /// 35 | /// // set IO_LINK to link the next event to this one 36 | /// write_event.set_flags(SubmissionFlags::IO_LINK); 37 | /// 38 | /// let mut read_event = sq.prepare_sqe().unwrap(); 39 | /// 40 | /// // -- read event prep elided 41 | /// 42 | /// // read_event only occurs if write_event was successful 43 | /// sq.submit()?; 44 | /// # Ok(()) 45 | /// # } 46 | /// ``` 47 | pub struct SubmissionQueue<'ring> { 48 | ring: NonNull, 49 | _marker: PhantomData<&'ring mut IoUring>, 50 | } 51 | 52 | impl<'ring> SubmissionQueue<'ring> { 53 | pub(crate) fn new(ring: &'ring IoUring) -> SubmissionQueue<'ring> { 54 | SubmissionQueue { 55 | ring: NonNull::from(&ring.ring), 56 | _marker: PhantomData, 57 | } 58 | } 59 | 60 | /// Returns new [`SQE`s](crate::sqe::SQE) until the queue size is reached. After that, will return `None`. 61 | /// ```rust 62 | /// # use iou::IoUring; 63 | /// # use std::error::Error; 64 | /// # fn main() -> std::io::Result<()> { 65 | /// # let ring_size = 2; 66 | /// let mut ring = IoUring::new(ring_size)?; 67 | /// 68 | /// let mut counter = 0; 69 | /// 70 | /// while let Some(event) = ring.prepare_sqe() { 71 | /// counter += 1; 72 | /// } 73 | /// 74 | /// assert_eq!(counter, ring_size); 75 | /// assert!(ring.prepare_sqe().is_none()); 76 | /// # Ok(()) 77 | /// # } 78 | /// 79 | pub fn prepare_sqe<'a>(&'a mut self) -> Option> { 80 | unsafe { 81 | prepare_sqe(self.ring.as_mut()) 82 | } 83 | } 84 | 85 | pub fn prepare_sqes<'a>(&'a mut self, count: u32) -> Option> { 86 | unsafe { 87 | let sq: &mut uring_sys::io_uring_sq = &mut (*self.ring.as_ptr()).sq; 88 | prepare_sqes(sq, count) 89 | } 90 | } 91 | 92 | /// Submit all events in the queue. Returns the number of submitted events. 93 | /// 94 | /// If this function encounters any IO errors an [`io::Error`](std::io::Result) variant is returned. 95 | pub fn submit(&mut self) -> io::Result { 96 | resultify(unsafe { uring_sys::io_uring_submit(self.ring.as_ptr()) }) 97 | } 98 | 99 | pub fn submit_and_wait(&mut self, wait_for: u32) -> io::Result { 100 | resultify(unsafe { uring_sys::io_uring_submit_and_wait(self.ring.as_ptr(), wait_for as _) }) 101 | } 102 | 103 | pub fn submit_and_wait_with_timeout(&mut self, wait_for: u32, duration: Duration) 104 | -> io::Result 105 | { 106 | let ts = uring_sys::__kernel_timespec { 107 | tv_sec: duration.as_secs() as _, 108 | tv_nsec: duration.subsec_nanos() as _ 109 | }; 110 | 111 | loop { 112 | if let Some(mut sqe) = self.prepare_sqe() { 113 | sqe.clear(); 114 | unsafe { 115 | sqe.prep_timeout(&ts, 0, crate::sqe::TimeoutFlags::empty()); 116 | sqe.set_user_data(uring_sys::LIBURING_UDATA_TIMEOUT); 117 | return resultify(uring_sys::io_uring_submit_and_wait(self.ring.as_ptr(), wait_for as _)) 118 | } 119 | } 120 | 121 | self.submit()?; 122 | } 123 | } 124 | 125 | pub fn ready(&self) -> u32 { 126 | unsafe { uring_sys::io_uring_sq_ready(self.ring.as_ptr()) as u32 } 127 | } 128 | 129 | pub fn space_left(&self) -> u32 { 130 | unsafe { uring_sys::io_uring_sq_space_left(self.ring.as_ptr()) as u32 } 131 | } 132 | } 133 | 134 | impl fmt::Debug for SubmissionQueue<'_> { 135 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 136 | let fd = unsafe { self.ring.as_ref().ring_fd }; 137 | f.debug_struct(std::any::type_name::()).field("fd", &fd).finish() 138 | } 139 | } 140 | 141 | unsafe impl<'ring> Send for SubmissionQueue<'ring> { } 142 | unsafe impl<'ring> Sync for SubmissionQueue<'ring> { } 143 | 144 | pub(crate) unsafe fn prepare_sqe<'a>(ring: &mut uring_sys::io_uring) -> Option> { 145 | let sqe = uring_sys::io_uring_get_sqe(ring); 146 | if !sqe.is_null() { 147 | let mut sqe = SQE::new(&mut *sqe); 148 | sqe.clear(); 149 | Some(sqe) 150 | } else { 151 | None 152 | } 153 | } 154 | 155 | pub(crate) unsafe fn prepare_sqes<'a>(sq: &mut uring_sys::io_uring_sq, count: u32) 156 | -> Option> 157 | { 158 | atomic::fence(Ordering::Acquire); 159 | 160 | let head: u32 = *sq.khead; 161 | let next: u32 = sq.sqe_tail + count; 162 | 163 | if next - head <= *sq.kring_entries { 164 | let sqe = sq.sqes.offset((sq.sqe_tail & *sq.kring_mask) as isize); 165 | sq.sqe_tail = next; 166 | Some(SQEs::new(slice::from_raw_parts_mut(sqe, count as usize))) 167 | } else { 168 | None 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /tests/accept.rs: -------------------------------------------------------------------------------- 1 | use nix::sys::socket::InetAddr; 2 | use std::{ 3 | io::{self, Read, Write}, 4 | net::{TcpListener, TcpStream}, 5 | os::unix::io::{AsRawFd, FromRawFd}, 6 | }; 7 | use iou::sqe::SockAddr; 8 | 9 | const MESSAGE: &'static [u8] = b"Hello World"; 10 | 11 | #[test] 12 | #[ignore] // kernel 5.5 needed for accept 13 | fn accept() -> io::Result<()> { 14 | let mut ring = iou::IoUring::new(1)?; 15 | 16 | let listener = TcpListener::bind(("0.0.0.0", 0))?; 17 | listener.set_nonblocking(true)?; 18 | 19 | let mut stream = TcpStream::connect(listener.local_addr()?)?; 20 | stream.write_all(MESSAGE)?; 21 | 22 | let fd = listener.as_raw_fd(); 23 | let mut sq = ring.sq(); 24 | let mut sqe = sq.prepare_sqe().expect("failed to get sqe"); 25 | unsafe { 26 | sqe.prep_accept(fd, None, iou::sqe::SockFlag::empty()); 27 | sq.submit()?; 28 | } 29 | let cqe = ring.wait_for_cqe()?; 30 | let accept_fd = cqe.result()?; 31 | let mut accept_buf = [0; MESSAGE.len()]; 32 | let mut stream = unsafe { TcpStream::from_raw_fd(accept_fd as _) }; 33 | stream.read_exact(&mut accept_buf)?; 34 | assert_eq!(accept_buf, MESSAGE); 35 | Ok(()) 36 | } 37 | 38 | #[test] 39 | #[ignore] // kernel 5.5 needed for accept 40 | fn accept_with_params() -> io::Result<()> { 41 | let mut ring = iou::IoUring::new(1)?; 42 | 43 | let listener = TcpListener::bind(("0.0.0.0", 0))?; 44 | listener.set_nonblocking(true)?; 45 | 46 | let mut connection_stream = TcpStream::connect(listener.local_addr()?)?; 47 | connection_stream.write_all(MESSAGE)?; 48 | 49 | let fd = listener.as_raw_fd(); 50 | let mut sq = ring.sq(); 51 | let mut sqe = sq.prepare_sqe().expect("failed to get sqe"); 52 | let mut accept_params = iou::sqe::SockAddrStorage::uninit(); 53 | unsafe { 54 | sqe.prep_accept(fd, Some(&mut accept_params), iou::sqe::SockFlag::empty()); 55 | sq.submit()?; 56 | } 57 | let cqe = ring.wait_for_cqe()?; 58 | let accept_fd = cqe.result()?; 59 | let mut accept_buf = [0; MESSAGE.len()]; 60 | let mut accepted_stream = unsafe { TcpStream::from_raw_fd(accept_fd as _) }; 61 | accepted_stream.read_exact(&mut accept_buf)?; 62 | assert_eq!(accept_buf, MESSAGE); 63 | 64 | let addr = unsafe { accept_params.as_socket_addr()? }; 65 | let connection_addr = SockAddr::Inet(InetAddr::from_std(&connection_stream.local_addr()?)); 66 | assert_eq!(addr, connection_addr); 67 | Ok(()) 68 | } 69 | -------------------------------------------------------------------------------- /tests/connect.rs: -------------------------------------------------------------------------------- 1 | use nix::sys::socket::{AddressFamily, SockProtocol, SockType, InetAddr, SockFlag}; 2 | use std::{io, net::TcpListener}; 3 | 4 | #[test] 5 | #[ignore] // kernel 5.5 needed for connect 6 | fn connect() -> io::Result<()> { 7 | let listener = TcpListener::bind(("0.0.0.0", 0))?; 8 | listener.set_nonblocking(true)?; 9 | let listener_addr = iou::sqe::SockAddr::new_inet(InetAddr::from_std(&listener.local_addr()?)); 10 | 11 | let socket = nix::sys::socket::socket( 12 | AddressFamily::Inet, 13 | SockType::Stream, 14 | SockFlag::SOCK_NONBLOCK, 15 | SockProtocol::Tcp, 16 | ) 17 | .map_err(|_| io::Error::new(io::ErrorKind::Other, "failed to create socket"))?; 18 | 19 | let mut ring = iou::IoUring::new(1)?; 20 | let mut sqe = ring.prepare_sqe().expect("failed to get sqe"); 21 | unsafe { 22 | sqe.prep_connect(socket, &listener_addr); 23 | sqe.set_user_data(42); 24 | ring.submit_sqes()?; 25 | } 26 | let cqe = ring.wait_for_cqe()?; 27 | let _res = cqe.result()?; 28 | assert_eq!(cqe.user_data(), 42); 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /tests/cqes-iter.rs: -------------------------------------------------------------------------------- 1 | // test CQE iterators 2 | 3 | #[test] 4 | fn cqes_nonblocking() { 5 | let mut io_uring = iou::IoUring::new(8).unwrap(); 6 | 7 | for mut sqe in io_uring.prepare_sqes(8).unwrap() { 8 | unsafe { 9 | sqe.prep_nop(); 10 | sqe.set_user_data(0); 11 | } 12 | } 13 | 14 | io_uring.submit_sqes_and_wait(8).unwrap(); 15 | 16 | assert_eq!(io_uring.cqes().count(), 8); 17 | } 18 | 19 | #[test] 20 | fn cqes_blocking() { 21 | let mut io_uring = iou::IoUring::new(8).unwrap(); 22 | 23 | for mut sqe in io_uring.prepare_sqes(8).unwrap() { 24 | unsafe { 25 | sqe.prep_nop(); 26 | sqe.set_user_data(0); 27 | } 28 | } 29 | 30 | io_uring.submit_sqes().unwrap(); 31 | 32 | assert_eq!(io_uring.cqes_blocking(1).take(8).count(), 8); 33 | } 34 | -------------------------------------------------------------------------------- /tests/exhaust-queue.rs: -------------------------------------------------------------------------------- 1 | // exhaust the SQ/CQ to prove that everything is working properly 2 | 3 | #[test] 4 | fn exhaust_queue_with_prepare_sqe() { 5 | let mut io_uring = iou::IoUring::new(8).unwrap(); 6 | 7 | for counter in 0..64 { 8 | unsafe { 9 | let mut sqe = io_uring.prepare_sqe().unwrap(); 10 | sqe.prep_nop(); 11 | sqe.set_user_data(counter); 12 | io_uring.submit_sqes_and_wait(1).unwrap(); 13 | let cqe = io_uring.peek_for_cqe().unwrap(); 14 | assert_eq!(cqe.user_data(), counter); 15 | } 16 | } 17 | } 18 | 19 | #[test] 20 | fn exhaust_queue_with_prepare_sqes() { 21 | let mut io_uring = iou::IoUring::new(8).unwrap(); 22 | 23 | for base in (0..64).filter(|x| x % 4 == 0) { 24 | unsafe { 25 | let mut counter = base; 26 | let mut sqes = io_uring.prepare_sqes(4).unwrap(); 27 | for mut sqe in sqes.hard_linked() { 28 | sqe.prep_nop(); 29 | sqe.set_user_data(counter); 30 | counter += 1; 31 | } 32 | 33 | io_uring.submit_sqes_and_wait(4).unwrap(); 34 | 35 | for counter in base..counter { 36 | let cqe = io_uring.peek_for_cqe().unwrap(); 37 | assert_eq!(cqe.user_data(), counter); 38 | 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/fileset-placeholder.rs: -------------------------------------------------------------------------------- 1 | use iou::{IoUring, registrar::RegisteredFd}; 2 | use std::fs::File; 3 | use std::io::{IoSlice, Read}; 4 | use std::os::unix::io::AsRawFd; 5 | 6 | const TEXT: &[u8] = b"hello there\n"; 7 | 8 | #[test] 9 | fn main() -> std::io::Result<()> { 10 | let mut ring = IoUring::new(2)?; 11 | let mut registrar = ring.registrar(); 12 | 13 | let reserve_files = [iou::registrar::PLACEHOLDER_FD; 1024]; 14 | let fileset: Vec = registrar.register_files(&reserve_files)?.collect(); 15 | assert!(fileset.iter().all(|fd| fd.is_placeholder())); 16 | 17 | let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); 18 | path.push("props"); 19 | path.push("tmp-fileset-placeholder.txt"); 20 | 21 | // update a random fileset entry with a valid file 22 | let file = std::fs::File::create(&path)?; 23 | let reg_file = registrar.update_registered_files(713, &[file.as_raw_fd()])?.collect::>()[0]; 24 | assert!(!reg_file.is_placeholder()); 25 | 26 | let bufs = &[IoSlice::new(&TEXT)]; 27 | let mut sqe = ring.prepare_sqe().unwrap(); 28 | 29 | unsafe { 30 | sqe.prep_write_vectored(reg_file, bufs, 0); 31 | sqe.set_user_data(0xDEADBEEF); 32 | } 33 | 34 | ring.submit_sqes()?; 35 | let cqe = ring.wait_for_cqe()?; 36 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 37 | 38 | let n = cqe.result()? as usize; 39 | assert!(n == TEXT.len()); 40 | 41 | let mut file = File::open(&path)?; 42 | let mut buf = vec![]; 43 | file.read_to_end(&mut buf)?; 44 | assert_eq!(&TEXT[..n], &buf[..n]); 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /tests/fixed-file-write.rs: -------------------------------------------------------------------------------- 1 | use iou::{IoUring, registrar::RegisteredFd, Registrar}; 2 | use std::fs::{self, File}; 3 | use std::io::{IoSlice, Read}; 4 | use std::os::unix::io::AsRawFd; 5 | use std::path::PathBuf; 6 | 7 | const TEXT: &[u8] = b"hello there"; 8 | 9 | #[test] 10 | fn read_fixed() -> std::io::Result<()> { 11 | let mut ring = IoUring::new(2)?; 12 | 13 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 14 | path.push("props"); 15 | path.push("tmp.txt"); 16 | 17 | let file = File::create(&path)?; 18 | 19 | let fixed_fd = file.as_raw_fd(); 20 | let reg: Registrar = ring.registrar(); 21 | 22 | // register a new file 23 | let fileset: Vec = reg.register_files(&[fixed_fd])?.collect(); 24 | 25 | let bufs = &[IoSlice::new(&TEXT)]; 26 | let reg_file = fileset[0]; 27 | 28 | let mut sqe = ring.prepare_sqe().unwrap(); 29 | 30 | unsafe { 31 | sqe.prep_write_vectored(reg_file, bufs, 0); 32 | sqe.set_user_data(0xDEADBEEF); 33 | } 34 | 35 | ring.submit_sqes()?; 36 | 37 | let cqe = ring.wait_for_cqe()?; 38 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 39 | 40 | let n = cqe.result()? as usize; 41 | assert!(n == TEXT.len()); 42 | 43 | let mut file = File::open(&path)?; 44 | let mut buf = vec![]; 45 | file.read_to_end(&mut buf)?; 46 | assert_eq!(&TEXT[..n], &buf[..n]); 47 | 48 | let _ = fs::remove_file(&path); 49 | 50 | Ok(()) 51 | } 52 | -------------------------------------------------------------------------------- /tests/noop.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | #[test] 4 | fn noop_test() -> io::Result<()> { 5 | // confirm that setup and mmap work 6 | let mut io_uring = iou::IoUring::new(32)?; 7 | 8 | // confirm that submit and enter work 9 | unsafe { 10 | let mut sqe = io_uring.prepare_sqe().unwrap(); 11 | sqe.prep_nop(); 12 | sqe.set_user_data(0xDEADBEEF); 13 | } 14 | io_uring.submit_sqes()?; 15 | 16 | // confirm that cq reading works 17 | { 18 | let cqe = io_uring.wait_for_cqe()?; 19 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 20 | } 21 | 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /tests/poll.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{self, Read, Write}, 3 | os::unix::{io::AsRawFd, net}, 4 | }; 5 | const MESSAGE: &'static [u8] = b"Hello World"; 6 | 7 | #[test] 8 | fn test_poll_add() -> io::Result<()> { 9 | let mut ring = iou::IoUring::new(2)?; 10 | let (mut read, mut write) = net::UnixStream::pair()?; 11 | unsafe { 12 | let mut sqe = ring.prepare_sqe().expect("failed to get sqe"); 13 | sqe.prep_poll_add(read.as_raw_fd(), iou::sqe::PollFlags::POLLIN); 14 | sqe.set_user_data(0xDEADBEEF); 15 | ring.submit_sqes()?; 16 | } 17 | 18 | write.write(MESSAGE)?; 19 | 20 | let cqe = ring.wait_for_cqe()?; 21 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 22 | let mask = unsafe { iou::sqe::PollFlags::from_bits_unchecked(cqe.result()? as _) }; 23 | assert!(mask.contains(iou::sqe::PollFlags::POLLIN)); 24 | let mut buf = [0; MESSAGE.len()]; 25 | read.read(&mut buf)?; 26 | assert_eq!(buf, MESSAGE); 27 | Ok(()) 28 | } 29 | 30 | #[test] 31 | fn test_poll_remove() -> io::Result<()> { 32 | let mut ring = iou::IoUring::new(2)?; 33 | let (read, _write) = net::UnixStream::pair()?; 34 | let uname = nix::sys::utsname::uname(); 35 | let version = semver::Version::parse(uname.release()); 36 | unsafe { 37 | let mut sqe = ring.prepare_sqe().expect("failed to get sqe"); 38 | sqe.prep_poll_add(read.as_raw_fd(), iou::sqe::PollFlags::POLLIN); 39 | sqe.set_user_data(0xDEADBEEF); 40 | ring.submit_sqes()?; 41 | 42 | let mut sqe = ring.prepare_sqe().expect("failed to get sqe"); 43 | sqe.prep_poll_remove(0xDEADBEEF); 44 | sqe.set_user_data(42); 45 | ring.submit_sqes()?; 46 | for _ in 0..2 { 47 | let cqe = ring.wait_for_cqe()?; 48 | let user_data = cqe.user_data(); 49 | if version < semver::Version::parse("5.5.0-0") { 50 | let _ = cqe.result()?; 51 | } else if user_data == 0xDEADBEEF { 52 | let err = cqe 53 | .result() 54 | .expect_err("on kernels >=5.5 error is expected"); 55 | let err_no = nix::errno::Errno::from_i32( 56 | err.raw_os_error() 57 | .expect("on kernels >=5.5 os_error is expected"), 58 | ); 59 | assert_eq!(err_no, nix::errno::Errno::ECANCELED); 60 | } else { 61 | let _ = cqe.result()?; 62 | } 63 | } 64 | Ok(()) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/probe.rs: -------------------------------------------------------------------------------- 1 | use iou::Probe; 2 | use uring_sys::IoRingOp; 3 | 4 | #[test] 5 | fn probe() { 6 | let probe = Probe::new().unwrap(); 7 | assert!(probe.supports(IoRingOp::IORING_OP_NOP)); 8 | } 9 | -------------------------------------------------------------------------------- /tests/read.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{self, IoSliceMut}; 3 | use std::os::unix::io::AsRawFd; 4 | use std::path::PathBuf; 5 | 6 | const TEXT: &[u8] = b"I really wanna stop 7 | But I just gotta taste for it 8 | I feel like I could fly with the ball on the moon 9 | So honey hold my hand you like making me wait for it 10 | I feel like I could die walking up to the room, oh yeah 11 | 12 | Late night watching television 13 | But how we get in this position? 14 | It's way too soon, I know this isn't love 15 | But I need to tell you something 16 | 17 | I really really really really really really like you"; 18 | 19 | #[test] 20 | fn vectored_read_test() -> io::Result<()> { 21 | let mut io_uring = iou::IoUring::new(32)?; 22 | 23 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 24 | path.push("props"); 25 | path.push("text.txt"); 26 | let file = File::open(&path)?; 27 | let mut buf1 = [0; 4096]; 28 | let mut bufs = [IoSliceMut::new(&mut buf1)]; 29 | 30 | unsafe { 31 | let mut sq = io_uring.sq(); 32 | let mut sqe = sq.prepare_sqe().unwrap(); 33 | sqe.prep_read_vectored(file.as_raw_fd(), &mut bufs[..], 0); 34 | sqe.set_user_data(0xDEADBEEF); 35 | sq.submit()?; 36 | } 37 | 38 | let n = { 39 | let mut cq = io_uring.cq(); 40 | let cqe = cq.wait_for_cqe()?; 41 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 42 | cqe.result()? as usize 43 | }; 44 | 45 | assert_eq!(&TEXT[..n], &buf1[..n]); 46 | Ok(()) 47 | } 48 | 49 | #[test] 50 | fn read_test() -> io::Result<()> { 51 | let mut io_uring = iou::IoUring::new(32)?; 52 | 53 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 54 | path.push("props"); 55 | path.push("text.txt"); 56 | let file = File::open(&path)?; 57 | let mut buf = [0; 4096]; 58 | 59 | unsafe { 60 | let mut sq = io_uring.sq(); 61 | let mut sqe = sq.prepare_sqe().unwrap(); 62 | sqe.prep_read(file.as_raw_fd(), &mut buf[..], 0); 63 | sqe.set_user_data(0xDEADBEEF); 64 | sq.submit()?; 65 | } 66 | 67 | let n = { 68 | let mut cq = io_uring.cq(); 69 | let cqe = cq.wait_for_cqe()?; 70 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 71 | cqe.result()? as usize 72 | }; 73 | 74 | assert_eq!(&TEXT[..n], &buf[..n]); 75 | Ok(()) 76 | } 77 | 78 | #[test] 79 | fn read_registered_buf() -> io::Result<()> { 80 | let mut io_uring = iou::IoUring::new(32)?; 81 | let bufs = vec![Box::new([0u8; 4096]) as Box<[u8]>]; 82 | let mut buf: iou::registrar::RegisteredBuf = io_uring.registrar().register_buffers(bufs)?.next().unwrap(); 83 | 84 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 85 | path.push("props"); 86 | path.push("text.txt"); 87 | let file = File::open(&path)?; 88 | 89 | unsafe { 90 | let mut sq = io_uring.sq(); 91 | let mut sqe = sq.prepare_sqe().unwrap(); 92 | sqe.prep_read(file.as_raw_fd(), buf.as_mut(), 0); 93 | sqe.set_user_data(0xDEADBEEF); 94 | assert!(sqe.raw().opcode == uring_sys::IoRingOp::IORING_OP_READ_FIXED as u8); 95 | sq.submit()?; 96 | } 97 | 98 | let n = { 99 | let mut cq = io_uring.cq(); 100 | let cqe = cq.wait_for_cqe()?; 101 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 102 | cqe.result()? as usize 103 | }; 104 | 105 | assert_eq!(&TEXT[..n], &buf.slice_to(n)[..]); 106 | Ok(()) 107 | } 108 | 109 | #[test] 110 | fn read_registered_fd_and_buf() -> io::Result<()> { 111 | use iou::registrar::*; 112 | 113 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 114 | path.push("props"); 115 | path.push("text.txt"); 116 | 117 | let mut io_uring = iou::IoUring::new(32)?; 118 | let bufs = vec![Box::new([0u8; 4096]) as Box<[u8]>]; 119 | let file = File::open(&path)?; 120 | 121 | let mut buf: RegisteredBuf = io_uring.registrar().register_buffers(bufs)?.next().unwrap(); 122 | let fd: RegisteredFd = io_uring.registrar().register_files(&[file.as_raw_fd()])?.next().unwrap(); 123 | 124 | unsafe { 125 | let mut sq = io_uring.sq(); 126 | let mut sqe = sq.prepare_sqe().unwrap(); 127 | sqe.prep_read(fd, buf.as_mut(), 0); 128 | sqe.set_user_data(0xDEADBEEF); 129 | assert!(sqe.raw().opcode == uring_sys::IoRingOp::IORING_OP_READ_FIXED as u8); 130 | assert!(sqe.flags().contains(iou::sqe::SubmissionFlags::FIXED_FILE)); 131 | sq.submit()?; 132 | } 133 | 134 | let n = { 135 | let mut cq = io_uring.cq(); 136 | let cqe = cq.wait_for_cqe()?; 137 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 138 | cqe.result()? as usize 139 | }; 140 | 141 | assert_eq!(&TEXT[..n], &buf.slice_to(n)[..]); 142 | Ok(()) 143 | } 144 | -------------------------------------------------------------------------------- /tests/register-buffers.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn register_buffers_by_val() { 3 | let buf1 = vec![0; 1024].into_boxed_slice(); 4 | let buf2 = vec![0; 1024].into_boxed_slice(); 5 | let ring = iou::IoUring::new(8).unwrap(); 6 | let bufs: Vec<_> = ring.registrar() 7 | .register_buffers(vec![buf1, buf2]) 8 | .unwrap().collect(); 9 | assert_eq!(bufs.len(), 2); 10 | assert_eq!(bufs[0].index(), 0); 11 | assert_eq!(bufs[1].index(), 1); 12 | } 13 | 14 | #[test] 15 | fn register_buffers_by_ref() { 16 | let buf1 = vec![0; 1024]; 17 | let buf2 = vec![0; 1024]; 18 | let ring = iou::IoUring::new(8).unwrap(); 19 | let bufs = &[&buf1[..], &buf2[..]]; 20 | let bufs: Vec<_> = ring.registrar() 21 | .register_buffers_by_ref(bufs) 22 | .unwrap().collect(); 23 | assert_eq!(bufs.len(), 2); 24 | assert_eq!(bufs[0].index(), 0); 25 | assert_eq!(bufs[1].index(), 1); 26 | } 27 | 28 | #[test] 29 | fn register_buffers_by_mut() { 30 | let mut buf1 = vec![0; 1024]; 31 | let mut buf2 = vec![0; 1024]; 32 | let ring = iou::IoUring::new(8).unwrap(); 33 | let bufs = &mut [&mut buf1[..], &mut buf2[..]]; 34 | let bufs: Vec<_> = ring.registrar() 35 | .register_buffers_by_mut(bufs) 36 | .unwrap().collect(); 37 | assert_eq!(bufs.len(), 2); 38 | assert_eq!(bufs[0].index(), 0); 39 | assert_eq!(bufs[1].index(), 1); 40 | } 41 | -------------------------------------------------------------------------------- /tests/write.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{self, File}; 2 | use std::io::{self, Read}; 3 | use std::path::PathBuf; 4 | use std::os::unix::io::AsRawFd; 5 | 6 | const TEXT: &[u8] = b"I really wanna stop 7 | But I just gotta taste for it 8 | I feel like I could fly with the ball on the moon 9 | So honey hold my hand you like making me wait for it 10 | I feel like I could die walking up to the room, oh yeah 11 | 12 | Late night watching television 13 | But how we get in this position? 14 | It's way too soon, I know this isn't love 15 | But I need to tell you something 16 | 17 | I really really really really really really like you"; 18 | 19 | #[test] 20 | fn vectored_write_test() -> io::Result<()> { 21 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 22 | path.push("props"); 23 | path.push("vectored.tmp"); 24 | 25 | let _ = fs::remove_file(&path); 26 | 27 | let n = { 28 | let mut io_uring = iou::IoUring::new(32)?; 29 | let bufs = [io::IoSlice::new(TEXT)]; 30 | 31 | 32 | let file = File::create(&path)?; 33 | unsafe { 34 | let mut sq = io_uring.sq(); 35 | let mut sqe = sq.prepare_sqe().unwrap(); 36 | sqe.prep_write_vectored(file.as_raw_fd(), &bufs, 0); 37 | sqe.set_user_data(0xDEADBEEF); 38 | io_uring.sq().submit()?; 39 | } 40 | 41 | let mut cq = io_uring.cq(); 42 | let cqe = cq.wait_for_cqe()?; 43 | drop(bufs); // hold bufs until after io completes 44 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 45 | cqe.result()? as usize 46 | }; 47 | 48 | let mut file = File::open(&path)?; 49 | let mut buf = vec![]; 50 | file.read_to_end(&mut buf)?; 51 | assert_eq!(&TEXT[..n], &buf[..n]); 52 | let _ = fs::remove_file(&path); 53 | 54 | Ok(()) 55 | } 56 | 57 | #[test] 58 | fn write_test() -> io::Result<()> { 59 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 60 | path.push("props"); 61 | path.push("text.tmp"); 62 | 63 | let _ = fs::remove_file(&path); 64 | 65 | let n = { 66 | let mut io_uring = iou::IoUring::new(32)?; 67 | 68 | let file = File::create(&path)?; 69 | unsafe { 70 | let mut sq = io_uring.sq(); 71 | let mut sqe = sq.prepare_sqe().unwrap(); 72 | sqe.prep_write(file.as_raw_fd(), TEXT, 0); 73 | sqe.set_user_data(0xDEADBEEF); 74 | io_uring.sq().submit()?; 75 | } 76 | 77 | let mut cq = io_uring.cq(); 78 | let cqe = cq.wait_for_cqe()?; 79 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 80 | cqe.result()? as usize 81 | }; 82 | 83 | let mut file = File::open(&path)?; 84 | let mut buf = vec![]; 85 | file.read_to_end(&mut buf)?; 86 | assert_eq!(&TEXT[..n], &buf[..n]); 87 | let _ = fs::remove_file(&path); 88 | 89 | Ok(()) 90 | } 91 | 92 | 93 | #[test] 94 | fn write_registered_buf() -> io::Result<()> { 95 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 96 | path.push("props"); 97 | path.push("write_registered_buf.tmp"); 98 | 99 | let _ = fs::remove_file(&path); 100 | 101 | let mut io_uring = iou::IoUring::new(32)?; 102 | let bufs = vec![Box::new([0u8; 4096]) as Box<[u8]>]; 103 | let mut buf: iou::registrar::RegisteredBuf = io_uring.registrar().register_buffers(bufs)?.next().unwrap(); 104 | 105 | buf.as_mut().slice_to_mut(TEXT.len()).copy_from_slice(TEXT); 106 | 107 | let n = { 108 | let file = File::create(&path)?; 109 | unsafe { 110 | let mut sq = io_uring.sq(); 111 | let mut sqe = sq.prepare_sqe().unwrap(); 112 | sqe.prep_write(file.as_raw_fd(), buf.slice_to(TEXT.len()), 0); 113 | assert!(sqe.raw().opcode == uring_sys::IoRingOp::IORING_OP_WRITE_FIXED as u8); 114 | sqe.set_user_data(0xDEADBEEF); 115 | io_uring.sq().submit()?; 116 | } 117 | 118 | let mut cq = io_uring.cq(); 119 | let cqe = cq.wait_for_cqe()?; 120 | assert_eq!(cqe.user_data(), 0xDEADBEEF); 121 | cqe.result()? as usize 122 | }; 123 | 124 | let mut file = File::open(&path)?; 125 | let mut buf = vec![]; 126 | file.read_to_end(&mut buf)?; 127 | assert_eq!(&TEXT[..n], &buf[..n]); 128 | let _ = fs::remove_file(&path); 129 | 130 | Ok(()) 131 | } 132 | --------------------------------------------------------------------------------