diff --git a/Cargo.lock b/Cargo.lock index db25e9b..762c6e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,33 +19,33 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cast" @@ -55,9 +55,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "ciborium" @@ -88,18 +88,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstyle", "clap_lex", @@ -107,9 +107,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "criterion" @@ -174,9 +174,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "either" @@ -186,12 +186,12 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -202,21 +202,21 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", + "r-efi", "wasi", - "windows-targets", ] [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "io-uring-bench" @@ -288,27 +288,26 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.170" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "linux-raw-sys" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" +version = "0.10.0" +source = "git+https://github.com/silvanshade/linux-raw-sys?branch=io_uring-zcrx#0829463bdee7317ab5c506a691c0d4bceea27521" [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "num-traits" @@ -321,9 +320,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" @@ -361,22 +360,28 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rayon" version = "1.10.0" @@ -428,15 +433,14 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +version = "1.0.6" +source = "git+https://github.com/silvanshade/rustix?branch=linux-6.15-io_uring#1b1eb8ae79570981bd2cb2028abdfe71006febc2" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -446,6 +450,7 @@ dependencies = [ "anyhow", "bitflags", "libc", + "linux-raw-sys", "rustix", "slab", "socket2", @@ -453,9 +458,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -506,18 +511,15 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -525,9 +527,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -536,11 +538,10 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.18.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", "fastrand", "getrandom", "once_cell", @@ -576,9 +577,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -666,7 +667,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -675,7 +676,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", ] [[package]] @@ -684,14 +694,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -700,53 +726,101 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index e085ac2..49993a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ members = [ "io-uring-test", "io-uring-bench" ] [dependencies] bitflags = { version = "2.4.0", default-features = false } -rustix = { version = "1.0.2", default-features = false, features = ["io_uring", "mm", "thread"] } +linux-raw-sys = { version = "0.10.0", default-features = false } +rustix = { version = "1.0.2", default-features = false, features = ["io_uring", "mm", "process", "thread"] } [dev-dependencies] libc = "0.2.98" @@ -27,3 +28,7 @@ anyhow = "1" rustix = "1.0.2" socket2 = "0.5" slab = "0.4" + +[patch.crates-io] +linux-raw-sys = { git = "https://github.com/silvanshade/linux-raw-sys", branch = "io_uring-zcrx" } +rustix = { git = "https://github.com/silvanshade/rustix", branch = "linux-6.15-io_uring" } diff --git a/io-uring-test/Cargo.toml b/io-uring-test/Cargo.toml index e8a7bb3..6d9a3ef 100644 --- a/io-uring-test/Cargo.toml +++ b/io-uring-test/Cargo.toml @@ -10,7 +10,7 @@ publish = false [dependencies] io-uring = { path = "..", package = "rustix-uring" } libc = { version = "0.2", features = [ "extra_traits" ] } -rustix = { version = "1.0.2", features = ["fs"] } +rustix = { version = "1.0.2", features = ["fs", "pipe"] } anyhow = "1" tempfile = "3" once_cell = "1" diff --git a/io-uring-test/src/main.rs b/io-uring-test/src/main.rs index f3051a9..799049e 100644 --- a/io-uring-test/src/main.rs +++ b/io-uring-test/src/main.rs @@ -89,8 +89,16 @@ fn test( tests::cancel::test_async_cancel_fd(&mut ring, &test)?; tests::cancel::test_async_cancel_fd_all(&mut ring, &test)?; + // epoll + tests::epoll::test_ready(&mut ring, &test)?; + tests::epoll::test_not_ready(&mut ring, &test)?; + tests::epoll::test_delete(&mut ring, &test)?; + tests::epoll::test_remove(&mut ring, &test)?; + tests::epoll::test_race(&mut ring, &test)?; + // fs tests::fs::test_file_write_read(&mut ring, &test)?; + tests::fs::test_pipe_read_multishot(&mut ring, &test)?; tests::fs::test_file_writev_readv(&mut ring, &test)?; tests::fs::test_file_cur_pos(&mut ring, &test)?; tests::fs::test_file_fsync(&mut ring, &test)?; @@ -106,6 +114,9 @@ fn test( tests::fs::test_file_splice(&mut ring, &test)?; tests::fs::test_ftruncate(&mut ring, &test)?; tests::fs::test_fixed_fd_install(&mut ring, &test)?; + tests::fs::test_get_set_xattr(&mut ring, &test)?; + tests::fs::test_f_get_set_xattr(&mut ring, &test)?; + tests::fs::test_pipe_fixed_writev_readv(&mut ring, &test)?; // timeout tests::timeout::test_timeout(&mut ring, &test)?; @@ -139,6 +150,7 @@ fn test( tests::net::test_tcp_shutdown(&mut ring, &test)?; tests::net::test_socket(&mut ring, &test)?; + tests::net::test_socket_bind_listen(&mut ring, &test)?; tests::net::test_udp_recvmsg_multishot(&mut ring, &test)?; tests::net::test_udp_recvmsg_multishot_trunc(&mut ring, &test)?; tests::net::test_udp_send_with_dest(&mut ring, &test)?; @@ -155,6 +167,9 @@ fn test( tests::futex::test_futex_wake(&mut ring, &test)?; tests::futex::test_futex_waitv(&mut ring, &test)?; + // wait + tests::waitid::test_waitid(&mut ring, &test)?; + // regression test tests::regression::test_issue154(&mut ring, &test)?; diff --git a/io-uring-test/src/tests/epoll.rs b/io-uring-test/src/tests/epoll.rs new file mode 100644 index 0000000..7c7b176 --- /dev/null +++ b/io-uring-test/src/tests/epoll.rs @@ -0,0 +1,278 @@ +use crate::Test; +use ::core::{mem::MaybeUninit, time::Duration}; +use ::rustix::{event::epoll, fd::OwnedFd, io, pipe}; +use ::std::{ + os::fd::{AsFd, BorrowedFd}, + thread, +}; +use io_uring::{cqueue, opcode, squeue, types, IoUring}; +use std::os::unix::io::AsRawFd; + +// Tests translated from liburing/test/epwait.c. + +#[derive(Debug)] +struct RxTxPipe { + rx: OwnedFd, + tx: OwnedFd, +} + +pub fn test_ready( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::EpollWait::CODE); + ); + + println!("test ready"); + + const NPIPES: usize = 2; + let (efd, pipes, mut events) = init::()?; + + for pipe in &pipes { + let tx = pipe.tx.as_fd(); + io::write(tx, b"foo")?; + } + + let sqe = opcode::EpollWait::new(types::Fd(efd.as_raw_fd()), events.as_mut_ptr(), NPIPES as _) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + cqe.result()?; + } + + let mut tmp = [0u8; 16]; + + for event in &events { + let fd = unsafe { BorrowedFd::borrow_raw(event.data.u64() as _) }; + io::read(fd, &mut tmp)?; + } + + Ok(()) +} + +pub fn test_not_ready( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::EpollWait::CODE); + ); + + println!("test not ready"); + + const NPIPES: usize = 2; + let (efd, pipes, mut events) = init::()?; + + let sqe = opcode::EpollWait::new(types::Fd(efd.as_raw_fd()), events.as_mut_ptr(), NPIPES as _) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + + for pipe in &pipes { + thread::sleep(Duration::from_micros(10000)); + let tx = pipe.tx.as_fd(); + io::write(tx, b"foo")?; + } + + let mut nr = 0; + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + nr = cqe.result()?; + assert!(nr.cast_signed() >= 0); + } + + let mut tmp = [0u8; 16]; + + for event in events.iter().take(nr as _) { + let fd = unsafe { BorrowedFd::borrow_raw(event.data.u64() as _) }; + io::read(fd, &mut tmp)?; + } + + Ok(()) +} + +pub fn test_delete( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::EpollWait::CODE); + ); + + println!("test delete"); + + const NPIPES: usize = 2; + let (efd, pipes, mut events) = init::()?; + + let sqe = opcode::EpollWait::new(types::Fd(efd.as_raw_fd()), events.as_mut_ptr(), NPIPES as _) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + + epoll::delete(efd.as_fd(), pipes[0].rx.as_fd())?; + + let mut tmp = [0u8; 16]; + + for pipe in &pipes { + io::write(pipe.tx.as_fd(), &tmp)?; + } + + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + cqe.result()?; + } + + for pipe in &pipes { + io::read(pipe.rx.as_fd(), &mut tmp)?; + } + + let data = epoll::EventData::new_u64(pipes[0].rx.as_raw_fd().cast_unsigned().into()); + let flags = epoll::EventFlags::IN; + epoll::add(efd, pipes[0].rx.as_fd(), data, flags)?; + + Ok(()) +} + +pub fn test_remove( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::EpollWait::CODE); + ); + + println!("test remove"); + + const NPIPES: usize = 2; + let (efd, pipes, mut events) = init::()?; + + let sqe = opcode::EpollWait::new(types::Fd(efd.as_raw_fd()), events.as_mut_ptr(), NPIPES as _) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + + drop(efd); + + thread::sleep(Duration::from_micros(10000)); + for pipe in &pipes { + io::write(pipe.tx.as_fd(), b"foo")?; + } + + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + let err = cqe.result().unwrap_err(); + assert!([io::Errno::AGAIN, io::Errno::BADF].contains(&err)); + } + + Ok(()) +} + +pub fn test_race( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::EpollWait::CODE); + ); + + println!("test race"); + + const LOOPS: usize = 500; + const NPIPES: usize = 8; + + fn prune(events: &[epoll::Event], nr: usize) -> anyhow::Result<()> { + let mut tmp = [0u8; 32]; + + for event in events.iter().take(nr) { + let fd = unsafe { BorrowedFd::borrow_raw(event.data.u64() as _) }; + io::read(fd, &mut tmp)?; + } + + Ok(()) + } + + thread::scope(|scope| -> anyhow::Result<()> { + let (efd, pipes, mut events) = init::()?; + + let handle = scope.spawn(move || -> anyhow::Result<()> { + for _ in 0..LOOPS { + thread::sleep(Duration::from_micros(150)); + for pipe in &pipes { + io::write(pipe.tx.as_fd(), b"foo")?; + } + } + Ok(()) + }); + + for _ in 0..LOOPS { + let sqe = opcode::EpollWait::new( + types::Fd(efd.as_raw_fd()), + events.as_mut_ptr(), + NPIPES as _, + ) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + ring.submit_and_wait(1)?; + let cqe = ring + .completion() + .next() + .map(Into::::into) + .unwrap(); + assert_eq!(cqe.user_data_u64(), 0x666); + let nr = cqe.result()?; + prune(&events, nr as _)?; + thread::sleep(Duration::from_micros(100)); + } + + handle.join().unwrap()?; + + Ok(()) + })?; + + Ok(()) +} + +fn init( +) -> anyhow::Result<(OwnedFd, [RxTxPipe; NPIPES], [epoll::Event; NPIPES])> { + let pipes: [RxTxPipe; NPIPES] = { + let mut pipes: [MaybeUninit; NPIPES] = [const { MaybeUninit::uninit() }; NPIPES]; + for pipe in &mut pipes { + let (rx, tx) = pipe::pipe()?; + pipe.write(RxTxPipe { rx, tx }); + } + unsafe { ::core::mem::transmute_copy(&pipes) } + }; + + let efd = epoll::create(epoll::CreateFlags::empty())?; + + for pipe in &pipes { + let efd = efd.as_fd(); + let rx = pipe.rx.as_fd(); + let data = epoll::EventData::new_u64(rx.as_raw_fd().cast_unsigned().into()); + let flags = epoll::EventFlags::IN; + epoll::add(efd, rx, data, flags)?; + } + + let events: [epoll::Event; NPIPES] = unsafe { ::core::mem::zeroed() }; + + Ok((efd, pipes, events)) +} diff --git a/io-uring-test/src/tests/fs.rs b/io-uring-test/src/tests/fs.rs index c159eb9..0ec379b 100644 --- a/io-uring-test/src/tests/fs.rs +++ b/io-uring-test/src/tests/fs.rs @@ -1,5 +1,8 @@ +use crate::tests::register_buf_ring; use crate::utils; use crate::Test; +use ::rustix::io_uring::iovec; +use ::std::collections::BTreeSet; use io_uring::Errno; use io_uring::{cqueue, opcode, squeue, types, IoUring}; use std::ffi::CString; @@ -28,6 +31,215 @@ pub fn test_file_write_read( Ok(()) } +pub fn test_pipe_read_multishot( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::Write::CODE); + test.probe.is_supported(opcode::ReadMulti::CODE); + ); + + println!("test pipe_read_multishot"); + + let (rx, tx) = ::rustix::pipe::pipe()?; + + const BYTES0: &[u8] = "The quick brown fox jumps over the lazy dog.".as_bytes(); + const BYTES1: &[u8] = "我能吞下玻璃而不伤身体。".as_bytes(); + + let buf_ring = register_buf_ring::Builder::new(0xcafe) + .ring_entries(2) + .buf_len(BYTES0.len().max(BYTES1.len())) + .build()?; + buf_ring.rc.register(ring)?; + + let mut got_writes; + let mut got_reads; + let mut got_bufs: BTreeSet; + + // Prepare multishot read + + let sqe_read = opcode::ReadMulti::new(types::Fd(rx.as_raw_fd()), 0, 0xcafe) + .build() + .user_data(0x777) + .into(); + unsafe { ring.submission().push(&sqe_read) }?; + + // Write BYTES0 + + let sqe_write0 = opcode::Write::new( + types::Fd(tx.as_raw_fd()), + BYTES0.as_ptr(), + BYTES0.len() as _, + ) + .build() + .user_data(0x555) + .flags(squeue::Flags::IO_LINK) + .into(); + unsafe { ring.submission().push(&sqe_write0) }?; + ring.submit_and_wait(1)?; + + // Process one write/read pair. Fills the first buffer in the ring. + + got_writes = 0; + got_reads = 0; + got_bufs = BTreeSet::new(); + for cqe in ring.completion().map(Into::::into) { + let len = cqe.result()?; + match cqe.user_data_u64() { + 0x555 => { + assert_eq!(BYTES0.len(), len as _); + got_writes += 1; + } + 0x777 => { + let bufs = buf_ring.rc.get_bufs(&buf_ring, len, cqe.flags()); + assert_eq!(1, bufs.len()); + assert_eq!(Some(0), cqueue::buffer_select(cqe.flags())); + assert_eq!(BYTES0.len(), len as _); + assert_eq!(BYTES0, bufs[0].as_slice()); + assert!(cqueue::more(cqe.flags())); + got_reads += 1; + got_bufs.insert(0); + } + _ => {} + } + } + assert_eq!(got_writes, 1); + assert_eq!(got_reads, 1); + assert_eq!(got_bufs, BTreeSet::from([0])); + + // Write BYTES1 + + let sqe_write1 = opcode::Write::new( + types::Fd(tx.as_raw_fd()), + BYTES1.as_ptr(), + BYTES1.len() as _, + ) + .build() + .user_data(0x666) + .flags(squeue::Flags::IO_LINK) + .into(); + unsafe { ring.submission().push(&sqe_write1) }?; + ring.submit_and_wait(1)?; + + // Process one write/read pair. Fills the first buffer in the ring. + + got_writes = 0; + got_reads = 0; + got_bufs = BTreeSet::new(); + for cqe in ring.completion().map(Into::::into) { + let len = cqe.result()?; + match cqe.user_data_u64() { + 0x666 => { + assert_eq!(BYTES1.len(), len as _); + got_writes += 1; + } + 0x777 => { + let bufs = buf_ring.rc.get_bufs(&buf_ring, len, cqe.flags()); + assert_eq!(1, bufs.len()); + assert_eq!(Some(1), cqueue::buffer_select(cqe.flags())); + assert_eq!(BYTES1.len(), len as _); + assert_eq!(BYTES1, bufs[0].as_slice()); + assert!(cqueue::more(cqe.flags())); + got_reads += 1; + got_bufs.insert(1); + } + _ => {} + } + } + assert_eq!(got_writes, 1); + assert_eq!(got_reads, 1); + assert_eq!(got_bufs, BTreeSet::from([1])); + + // Write BYTES0 and BYTES1 + + let sqe_write0 = opcode::Write::new( + types::Fd(tx.as_raw_fd()), + BYTES0.as_ptr(), + BYTES0.len() as _, + ) + .build() + .flags(squeue::Flags::IO_LINK) + .user_data(0x555) + .into(); + let sqe_write1 = opcode::Write::new( + types::Fd(tx.as_raw_fd()), + BYTES1.as_ptr(), + BYTES1.len() as _, + ) + .build() + .flags(squeue::Flags::IO_LINK) + .user_data(0x666) + .into(); + unsafe { ring.submission().push_multiple(&[sqe_write0, sqe_write1]) }?; + ring.submit_and_wait(1)?; + + // Process two write/read pairs. Fills the first and second buffer in the ring. + + got_writes = 0; + got_reads = 0; + got_bufs = BTreeSet::new(); + for cqe in ring.completion().map(Into::::into) { + let len = cqe.result()?; + match cqe.user_data_u64() { + 0x555 => { + assert_eq!(BYTES0.len(), len as _); + assert_eq!(got_writes, 0); + got_writes += 1; + } + 0x666 => { + assert_eq!(BYTES1.len(), len as _); + assert_eq!(got_writes, 1); + got_writes += 1; + } + 0x777 => { + let bufs = buf_ring.rc.get_bufs(&buf_ring, len, cqe.flags()); + assert_eq!(1, bufs.len()); + match cqueue::buffer_select(cqe.flags()) { + Some(idx @ 0) => { + assert_eq!(BYTES0.len(), len as _); + assert_eq!(BYTES0, bufs[0].as_slice()); + assert_eq!(got_reads, 0); + assert_eq!(got_bufs, BTreeSet::from([])); + got_bufs.insert(idx); + } + Some(idx @ 1) => { + assert_eq!(BYTES1.len(), len as _); + assert_eq!(BYTES1, bufs[0].as_slice()); + assert_eq!(got_reads, 1); + assert_eq!(got_bufs, BTreeSet::from([0])); + got_bufs.insert(idx); + } + _ => unreachable!(), + } + assert!(cqueue::more(cqe.flags())); + got_reads += 1; + } + _ => {} + } + } + assert_eq!(got_writes, 2); + assert_eq!(got_reads, 2); + assert_eq!(got_bufs, BTreeSet::from([0, 1])); + + // Close the pipe writer fd to observe termination of the multi-read. + + drop(tx); + + ring.submit_and_wait(0)?; + let mut completions = ring.completion().map(Into::::into); + assert_eq!(1, completions.len()); + + let cqe = completions.next().unwrap(); + let len = cqe.result()?; + assert_eq!(0, len); + assert_eq!(0x777, cqe.user_data_u64()); + assert!(!cqueue::more(cqe.flags())); + + Ok(()) +} + pub fn test_file_writev_readv( ring: &mut IoUring, test: &Test, @@ -48,6 +260,111 @@ pub fn test_file_writev_readv( Ok(()) } +pub fn test_pipe_fixed_writev_readv( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::WritevFixed::CODE); + test.probe.is_supported(opcode::ReadvFixed::CODE); + ); + + println!("test pipe_fixed_writev_readv"); + + ring.submitter().unregister_buffers()?; + + let (rx, tx) = ::rustix::pipe::pipe()?; + + const BYTES0: &[u8] = "The quick brown fox jumps over the lazy dog.".as_bytes(); + const BYTES1: &[u8] = "我能吞下玻璃而不伤身体。".as_bytes(); + + let mut src = Vec::with_capacity(BYTES0.len() + BYTES1.len()); + src.extend_from_slice(BYTES0); + src.extend_from_slice(BYTES1); + + let mut dst = vec![0u8; BYTES0.len() + BYTES1.len()]; + + unsafe { + ring.submitter().register_buffers(&[ + iovec { + iov_base: src.as_mut_ptr() as *mut _, + iov_len: src.len(), + }, + iovec { + iov_base: dst.as_mut_ptr() as *mut _, + iov_len: dst.len(), + }, + ])?; + } + + let src_parts = [ + libc::iovec { + iov_base: src.as_ptr() as *mut _, + iov_len: BYTES0.len(), + }, + libc::iovec { + iov_base: unsafe { src.as_ptr().add(BYTES0.len()) } as *mut _, + iov_len: BYTES1.len(), + }, + ]; + unsafe { + ring.submission().push( + &opcode::WritevFixed::new( + types::Fd(tx.as_raw_fd()), + src_parts.as_ptr().cast(), + src_parts.len() as u32, + 0, + ) + .build() + .user_data(0x666) + .into(), + )?; + } + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into) { + assert_eq!(cqe.user_data_u64(), 0x666); + let len = cqe.result()?; + assert_eq!(len, src.len() as _); + } + + let dst_parts = [ + libc::iovec { + iov_base: dst.as_ptr() as *mut _, + iov_len: BYTES0.len(), + }, + libc::iovec { + iov_base: unsafe { dst.as_ptr().add(BYTES0.len()) } as *mut _, + iov_len: BYTES1.len(), + }, + ]; + unsafe { + ring.submission().push( + &opcode::ReadvFixed::new( + types::Fd(rx.as_raw_fd()), + dst_parts.as_ptr().cast(), + dst_parts.len() as u32, + 1, + ) + .build() + .user_data(0x666) + .into(), + )?; + } + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into) { + assert_eq!(cqe.user_data_u64(), 0x666); + let len = cqe.result()?; + assert_eq!(len, src.len() as _); + } + + ring.submitter().unregister_buffers()?; + + assert_eq!(src, dst); + + Ok(()) +} + pub fn test_file_fsync( ring: &mut IoUring, test: &Test, @@ -1012,3 +1329,148 @@ pub fn test_fixed_fd_install( Ok(()) } + +pub fn test_get_set_xattr( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::GetXattr::CODE); + test.probe.is_supported(opcode::SetXattr::CODE); + ); + + println!("test get_set_xattr"); + + let dir = tempfile::tempdir()?; + let file_path = dir.path().join("test-file"); + fs::write(&file_path, b"test content")?; + + let file_path_cstr = CString::new(file_path.as_os_str().as_bytes())?; + + let attr_name = CString::new("user.test_attr")?; + let attr_value = CString::new("test_value")?; + let mut buffer = vec![0u8; 128]; + + // Set extended attribute + let setxattr_e = opcode::SetXattr::new( + attr_name.as_ptr(), + attr_value.as_ptr().cast(), + file_path_cstr.as_ptr().cast(), + attr_value.as_bytes().len() as u32, + ) + .flags(types::XattrFlags::empty()) + .build() + .user_data(0x01) + .into(); + + unsafe { + ring.submission().push(&setxattr_e).expect("queue is full"); + } + + ring.submit_and_wait(1)?; + + let cqes: Vec = ring.completion().map(Into::into).collect(); + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data().u64_(), 0x01); + assert_eq!(cqes[0].result(), Ok(0)); + + // Get extended attribute + let getxattr_e = opcode::GetXattr::new( + attr_name.as_ptr(), + buffer.as_mut_ptr().cast(), + file_path_cstr.as_ptr().cast(), + buffer.len() as u32, + ) + .build() + .user_data(0x02) + .into(); + + unsafe { + ring.submission().push(&getxattr_e).expect("queue is full"); + } + + ring.submit_and_wait(1)?; + + let cqes: Vec = ring.completion().map(Into::into).collect(); + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data().u64_(), 0x02); + let len = cqes[0].result().unwrap() as usize; + assert_eq!(len, attr_value.as_bytes().len()); + + let retrieved_value = CString::new(&buffer[..len])?; + assert_eq!(retrieved_value, attr_value); + + Ok(()) +} + +pub fn test_f_get_set_xattr( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::FGetXattr::CODE); + test.probe.is_supported(opcode::FSetXattr::CODE); + ); + + println!("test f_get_set_xattr"); + + let file = tempfile::tempfile()?; + let fd = types::Fd(file.as_raw_fd()); + + let attr_name = CString::new("user.test_attr")?; + let attr_value = CString::new("test_value")?; + let mut buffer = vec![0u8; 128]; + + // Set extended attribute on file descriptor + let fsetxattr_e = opcode::FSetXattr::new( + fd, + attr_name.as_ptr(), + attr_value.as_ptr().cast(), + attr_value.as_bytes().len() as u32, + ) + .flags(types::XattrFlags::empty()) + .build() + .user_data(0x01) + .into(); + + unsafe { + ring.submission().push(&fsetxattr_e).expect("queue is full"); + } + + ring.submit_and_wait(1)?; + + let cqes: Vec = ring.completion().map(Into::into).collect(); + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data().u64_(), 0x01); + assert_eq!(cqes[0].result(), Ok(0)); + + // Get extended attribute from file descriptor + let fgetxattr_e = opcode::FGetXattr::new( + fd, + attr_name.as_ptr(), + buffer.as_mut_ptr().cast(), + buffer.len() as u32, + ) + .build() + .user_data(0x02) + .into(); + + unsafe { + ring.submission().push(&fgetxattr_e).expect("queue is full"); + } + + ring.submit_and_wait(1)?; + + let cqes: Vec = ring.completion().map(Into::into).collect(); + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data().u64_(), 0x02); + let len = cqes[0].result().unwrap() as usize; + assert_eq!(len, attr_value.as_bytes().len()); + + let retrieved_value = CString::new(&buffer[..len])?; + assert_eq!(retrieved_value, attr_value); + + Ok(()) +} diff --git a/io-uring-test/src/tests/mod.rs b/io-uring-test/src/tests/mod.rs index f380cb8..638d5ab 100644 --- a/io-uring-test/src/tests/mod.rs +++ b/io-uring-test/src/tests/mod.rs @@ -1,4 +1,5 @@ pub mod cancel; +pub mod epoll; pub mod fs; pub mod futex; pub mod net; @@ -10,3 +11,4 @@ pub mod register_buffers; pub mod register_sync_cancel; pub mod regression; pub mod timeout; +pub mod waitid; diff --git a/io-uring-test/src/tests/net.rs b/io-uring-test/src/tests/net.rs index 3488a36..3ad04e9 100644 --- a/io-uring-test/src/tests/net.rs +++ b/io-uring-test/src/tests/net.rs @@ -160,11 +160,7 @@ pub fn test_tcp_send_bundle( unsafe { let mut queue = ring.submission(); - let send_e = send_e - .build() - .user_data(0x01) - .flags(squeue::Flags::IO_LINK) - .into(); + let send_e = send_e.build().user_data(0x01).into(); queue.push(&send_e).expect("queue is full"); } @@ -1498,11 +1494,206 @@ pub fn test_socket( let cqes: Vec = ring.completion().map(Into::into).collect(); assert_eq!(cqes.len(), 1); assert_eq!(cqes[0].user_data().u64_(), 42); - assert!(cqes[0].result().unwrap() as i32 != plain_fd); + + let fd = cqes[0].result().unwrap() as i32; + assert!(fd != plain_fd); + assert_eq!(cqes[0].flags(), cqueue::Flags::empty()); + + let io_uring_socket = unsafe { Socket::from_raw_fd(fd) }; + + // Try a setsockopt. + { + let mut optval: libc::c_int = 0; + let mut optval_size: libc::socklen_t = std::mem::size_of_val(&optval) as libc::socklen_t; + // Get value before. + let ret = unsafe { + libc::getsockopt( + io_uring_socket.as_raw_fd(), + libc::SOL_SOCKET, + libc::SO_REUSEADDR, + &mut optval as *mut _ as *mut libc::c_void, + &mut optval_size as *mut _ as *mut libc::socklen_t, + ) + }; + assert_eq!(ret, 0); + assert_eq!(optval, 0); + + // Set value. + optval = 1; + let op = io_uring::opcode::SetSockOpt::new( + io_uring::types::Fd(io_uring_socket.as_raw_fd()), + libc::SOL_SOCKET as u32, + libc::SO_REUSEADDR as u32, + &optval as *const _ as *const libc::c_void, + std::mem::size_of_val(&optval) as libc::socklen_t, + ) + .build() + .user_data(1234); + unsafe { + ring.submission().push(&op.into()).expect("queue is full"); + } + ring.submit_and_wait(1)?; + let cqes: Vec = ring.completion().map(Into::into).collect(); + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data().u64_(), 1234); + assert_eq!(cqes[0].result(), Ok(0)); + assert_eq!(cqes[0].flags(), cqueue::Flags::empty()); + + // Check value actually set. + optval = 0; + let ret = unsafe { + libc::getsockopt( + io_uring_socket.as_raw_fd(), + libc::SOL_SOCKET, + libc::SO_REUSEADDR, + &mut optval as *mut _ as *mut libc::c_void, + &mut optval_size as *mut _ as *mut libc::socklen_t, + ) + }; + assert_eq!(ret, 0); + assert_eq!(optval, 1); + } + + // Close both sockets, to avoid leaking FDs. + drop(plain_socket); + drop(io_uring_socket); + + // Cleanup all fixed files (if any), then reserve slot 0. + let _ = ring.submitter().unregister_files(); + ring.submitter().register_files_sparse(1).unwrap(); + + let fixed_socket_op = opcode::Socket::new( + Domain::IPV4.into(), + Type::DGRAM.into(), + Protocol::UDP.into(), + ); + let dest_slot = types::DestinationSlot::try_from_slot_target(0).unwrap(); + unsafe { + ring.submission() + .push( + &fixed_socket_op + .file_index(Some(dest_slot)) + .build() + .user_data(55) + .into(), + ) + .expect("queue is full"); + } + ring.submit_and_wait(1)?; + + let cqes: Vec = ring.completion().map(Into::into).collect(); + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data().u64_(), 55); + assert_eq!(cqes[0].result(), Ok(0)); + assert_eq!(cqes[0].flags(), cqueue::Flags::empty()); + + // If the fixed-socket operation worked properly, this must not fail. + ring.submitter().unregister_files().unwrap(); + + Ok(()) +} + +pub fn test_socket_bind_listen( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + use socket2::{Domain, Protocol, Socket, Type}; + + require!( + test; + test.probe.is_supported(opcode::Socket::CODE); + test.probe.is_supported(opcode::Bind::CODE); + test.probe.is_supported(opcode::Listen::CODE); + ); + + println!("test socket_bind_listen"); + + // Open a TCP socket through old-style `socket(2)` syscall. + // This is used both as a kernel sanity check, and for comparing the returned io-uring FD. + let plain_socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap(); + let plain_fd = plain_socket.as_raw_fd(); + + let socket_fd_op = opcode::Socket::new( + Domain::IPV4.into(), + Type::STREAM.into(), + Protocol::TCP.into(), + ); + unsafe { + ring.submission() + .push(&socket_fd_op.build().user_data(42).into()) + .expect("queue is full"); + } + ring.submit_and_wait(1)?; + + let cqes: Vec = ring.completion().map(Into::into).collect(); + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data().u64_(), 42); + + let fd = cqes[0].result().unwrap() as i32; + + let io_uring_socket = unsafe { Socket::from_raw_fd(fd) }; + assert!(io_uring_socket.as_raw_fd() != plain_fd); assert_eq!(cqes[0].flags(), cqueue::Flags::empty()); + // Try to bind. + { + let server_addr: std::net::SocketAddr = "127.0.0.1:0".parse().unwrap(); + let server_addr: socket2::SockAddr = server_addr.into(); + let op = io_uring::opcode::Bind::new( + io_uring::types::Fd(io_uring_socket.as_raw_fd()), + server_addr.as_ptr() as *const _, + server_addr.len(), + ) + .build() + .user_data(2345); + unsafe { + ring.submission().push(&op.into()).expect("queue is full"); + } + ring.submit_and_wait(1)?; + let cqes: Vec = ring.completion().map(Into::into).collect(); + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data().u64_(), 2345); + assert_eq!(cqes[0].result(), Ok(0)); + assert_eq!(cqes[0].flags(), cqueue::Flags::empty()); + + assert_eq!( + io_uring_socket + .local_addr() + .expect("no local addr") + .as_socket_ipv4() + .expect("no IPv4 address") + .ip(), + server_addr.as_socket_ipv4().unwrap().ip() + ); + } + + // Try to listen. + { + let op = + io_uring::opcode::Listen::new(io_uring::types::Fd(io_uring_socket.as_raw_fd()), 128) + .build() + .user_data(3456); + unsafe { + ring.submission().push(&op.into()).expect("queue is full"); + } + ring.submit_and_wait(1)?; + let cqes: Vec = ring.completion().map(Into::into).collect(); + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data().u64_(), 3456); + assert_eq!(cqes[0].result(), Ok(0)); + assert_eq!(cqes[0].flags(), cqueue::Flags::empty()); + + // Ensure the socket is actually in the listening state. + _ = TcpStream::connect( + io_uring_socket + .local_addr() + .unwrap() + .as_socket_ipv4() + .unwrap(), + )?; + } + // Close both sockets, to avoid leaking FDs. - let io_uring_socket = unsafe { Socket::from_raw_fd(cqes[0].result().unwrap() as i32) }; drop(plain_socket); drop(io_uring_socket); diff --git a/io-uring-test/src/tests/waitid.rs b/io-uring-test/src/tests/waitid.rs new file mode 100644 index 0000000..47e62db --- /dev/null +++ b/io-uring-test/src/tests/waitid.rs @@ -0,0 +1,138 @@ +use ::core::mem::MaybeUninit; +use ::std::process::{Command, Stdio}; + +use ::io_uring::{cqueue, opcode, squeue, types, IoUring}; +use ::rustix::process; + +use crate::Test; + +pub fn test_waitid( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require!( + test; + test.probe.is_supported(opcode::Waitid::CODE); + ); + + println!("test waitid"); + + #[allow(clippy::zombie_processes)] + let child = Command::new("yes") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .expect("failed to execute child"); + + let pid = process::Pid::from_child(&child); + let pgid = process::getpgid(Some(pid)).unwrap(); + + let mut status = MaybeUninit::::uninit(); + let status = unsafe { status.assume_init_mut() }; + + // Test waiting for the process by pid. + + unsafe { ::libc::kill(child.id() as _, ::libc::SIGSTOP) }; + let sqe = opcode::Waitid::new(types::WaitId::Pid(pid), status) + .options(types::WaitIdOptions::STOPPED) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + cqe.result()?; + } + #[cfg(not(any(target_os = "fuchsia", target_os = "netbsd")))] + assert_eq!(status.stopping_signal(), Some(::libc::SIGSTOP as _)); + assert_eq!(status.raw_signo(), ::libc::SIGCHLD); + assert_eq!(status.raw_errno(), 0); + assert_eq!(status.raw_code(), ::libc::CLD_STOPPED); + + unsafe { ::libc::kill(child.id() as _, ::libc::SIGCONT) }; + let sqe = opcode::Waitid::new(types::WaitId::Pid(pid), status) + .options(types::WaitIdOptions::CONTINUED) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + cqe.result()?; + } + assert!(status.continued()); + + // Now do the same thing with the pgid. + + unsafe { ::libc::kill(child.id() as _, ::libc::SIGSTOP) }; + + let sqe = opcode::Waitid::new(types::WaitId::Pgid(Some(pgid)), status) + .options(types::WaitIdOptions::STOPPED) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + cqe.result()?; + } + #[cfg(not(any(target_os = "fuchsia", target_os = "netbsd")))] + assert_eq!(status.stopping_signal(), Some(::libc::SIGSTOP as _)); + assert_eq!(status.raw_signo(), ::libc::SIGCHLD); + assert_eq!(status.raw_errno(), 0); + assert_eq!(status.raw_code(), ::libc::CLD_STOPPED); + + unsafe { ::libc::kill(child.id() as _, ::libc::SIGCONT) }; + + let sqe = opcode::Waitid::new(types::WaitId::Pgid(Some(pgid)), status) + .options(types::WaitIdOptions::CONTINUED) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + cqe.result()?; + } + assert!(status.continued()); + + // Finish + + unsafe { ::libc::kill(child.id() as _, ::libc::SIGKILL) }; + + let sqe = opcode::Waitid::new(types::WaitId::Pid(pid), status) + .options(types::WaitIdOptions::EXITED | types::WaitIdOptions::NOWAIT) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + cqe.result()?; + } + assert!(status.killed()); + #[cfg(not(any(target_os = "fuchsia", target_os = "netbsd")))] + assert_eq!(status.terminating_signal(), Some(::libc::SIGKILL as _)); + + let sqe = opcode::Waitid::new(types::WaitId::Pid(pid), status) + .options(types::WaitIdOptions::EXITED) + .build() + .user_data(0x666) + .into(); + unsafe { ring.submission().push(&sqe) }?; + ring.submit_and_wait(1)?; + for cqe in ring.completion().map(Into::::into).take(1) { + assert_eq!(cqe.user_data_u64(), 0x666); + cqe.result()?; + } + assert!(status.killed()); + #[cfg(not(any(target_os = "fuchsia", target_os = "netbsd")))] + assert_eq!(status.terminating_signal(), Some(::libc::SIGKILL as _)); + + Ok(()) +} diff --git a/src/opcode.rs b/src/opcode.rs index a0247cd..3fc06b7 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -5,6 +5,7 @@ use core::convert::TryInto; use core::mem; +use ::rustix::io_uring::IoringSqeFlags; use rustix::fd::RawFd; use crate::squeue::Entry; @@ -642,6 +643,35 @@ opcode! { } } +opcode! { + /// Set a socket option. + pub struct SetSockOpt { + fd: { impl sealed::UseFixed }, + level: { u32 }, + optname: { u32 }, + optval: { *const core::ffi::c_void }, + optlen: { u32 }, + ;; + cmd_op: sys::IoringSocketOp = sys::IoringSocketOp::default() + } + + pub const CODE = sys::IoringOp::UringCmd; + + pub fn build(self) -> Entry { + let SetSockOpt { fd, level, optname, optval, optlen, cmd_op } = self; + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.off_or_addr2.cmd_op.cmd_op = sys::IoringSocketOp::Setsockopt as _; + sqe.addr_or_splice_off_in.sockopt_level_optname.level = level; + sqe.addr_or_splice_off_in.sockopt_level_optname.optname = optname; + sqe.op_flags.socket_op = cmd_op; + sqe.splice_fd_in_or_file_index_or_addr_len.optlen = optlen; + sqe.addr3_or_cmd.optval.ptr = optval.cast_mut(); + Entry(sqe) + } +} + opcode! { /// Attempt to cancel an already issued request. pub struct AsyncCancel { @@ -1436,6 +1466,114 @@ opcode! { } } +// === 5.17 === + +opcode! { + /// Get extended attribute, equivalent to `getxattr(2)`. + pub struct GetXattr { + name: { *const sys::c_char }, + value: { *mut core::ffi::c_void }, + path: { *const sys::c_char }, + len: { u32 }, + ;; + } + + pub const CODE = sys::IoringOp::Getxattr; + + pub fn build(self) -> Entry { + let GetXattr { name, value, path, len } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + sqe.addr_or_splice_off_in.addr.ptr = name.cast_mut().cast(); + sqe.len.len = len; + sqe.off_or_addr2.addr2.ptr = value; + sqe.addr3_or_cmd.path.ptr = path.cast_mut().cast(); + sqe.op_flags.xattr_flags = sys::XattrFlags::empty(); + Entry(sqe) + } +} + +opcode! { + /// Set extended attribute, equivalent to `setxattr(2)`. + pub struct SetXattr { + name: { *const sys::c_char }, + value: { *const core::ffi::c_void }, + path: { *const sys::c_char }, + len: { u32 }, + ;; + flags: sys::XattrFlags = sys::XattrFlags::empty() + } + + pub const CODE = sys::IoringOp::Setxattr; + + pub fn build(self) -> Entry { + let SetXattr { name, value, path, flags, len } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + sqe.addr_or_splice_off_in.addr.ptr = name.cast_mut().cast(); + sqe.len.len = len; + sqe.off_or_addr2.addr2.ptr = value.cast_mut(); + sqe.addr3_or_cmd.path.ptr = path.cast_mut().cast(); + sqe.op_flags.xattr_flags = flags; + Entry(sqe) + } +} + +opcode! { + /// Get extended attribute from a file descriptor, equivalent to `fgetxattr(2)`. + pub struct FGetXattr { + fd: { impl sealed::UseFixed }, + name: { *const sys::c_char }, + value: { *mut core::ffi::c_void }, + len: { u32 }, + ;; + } + + pub const CODE = sys::IoringOp::Fgetxattr; + + pub fn build(self) -> Entry { + let FGetXattr { fd, name, value, len } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.addr_or_splice_off_in.addr.ptr = name.cast_mut().cast(); + sqe.len.len = len; + sqe.off_or_addr2.addr2.ptr = value; + sqe.op_flags.xattr_flags = sys::XattrFlags::empty(); + Entry(sqe) + } +} + +opcode! { + /// Set extended attribute on a file descriptor, equivalent to `fsetxattr(2)`. + pub struct FSetXattr { + fd: { impl sealed::UseFixed }, + name: { *const sys::c_char }, + value: { *const core::ffi::c_void }, + len: { u32 }, + ;; + flags: sys::XattrFlags = sys::XattrFlags::empty() + } + + pub const CODE = sys::IoringOp::Fsetxattr; + + pub fn build(self) -> Entry { + let FSetXattr { fd, name, value, flags, len } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.addr_or_splice_off_in.addr.ptr = name.cast_mut().cast(); + sqe.len.len = len; + sqe.off_or_addr2.addr2.ptr = value.cast_mut(); + sqe.op_flags.xattr_flags = flags; + Entry(sqe) + } +} + // === 5.18 === opcode! { @@ -1752,6 +1890,65 @@ opcode! { // === 6.7 === +opcode! { + /// Issue the equivalent of `pread(2)` with multi-shot semantics. + pub struct ReadMulti { + fd: { impl sealed::UseFixed }, + len: { u32 }, + buf_group: { u16 }, + ;; + offset: u64 = 0, + } + + pub const CODE = sys::IoringOp::ReadMultishot; + + pub fn build(self) -> Entry { + let Self { fd, len, buf_group, offset } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.off_or_addr2.off = offset; + sqe.len.len = len; + sqe.buf.buf_group = buf_group; + sqe.flags = IoringSqeFlags::BUFFER_SELECT; + Entry(sqe) + } +} + +opcode! { + /// Issue the equivalent of a waitid(2) system call. + pub struct Waitid { + id: { types::WaitId }, + infop: { *mut types::WaitIdStatus }, + ;; + options: types::WaitIdOptions = types::WaitIdOptions::empty() + } + + pub const CODE = sys::IoringOp::Waitid; + + pub fn build(self) -> Entry { + use ::linux_raw_sys::general::{P_ALL, P_PGID, P_PID, P_PIDFD}; + + let Self { id, infop, options } = self; + + let (idtype, id) = match id { + types::WaitId::All => (P_ALL, 0), + types::WaitId::Pid(pid) => (P_PID, types::Pid::as_raw(Some(pid)) as _), + types::WaitId::Pgid(pgid) => (P_PGID, types::Pid::as_raw(pgid)) as _, + types::WaitId::PidFd(fd) => (P_PIDFD, fd.0), + }; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + sqe.fd = id; + sqe.len.len = idtype; + sqe.splice_fd_in_or_file_index_or_addr_len.file_index = options.bits(); + sqe.off_or_addr2.addr2.ptr = infop.cast(); + Entry(sqe) + } +} + opcode! { /// Wait on a futex, like but not equivalant to `futex(2)`'s `FUTEX_WAIT_BITSET`. /// @@ -2003,3 +2200,163 @@ opcode! { Entry(sqe) } } + +// === 6.11 === + +opcode! { + /// Bind a socket, equivalent to `bind(2)`. + pub struct Bind { + fd: { impl sealed::UseFixed }, + addr: { *const sys::SocketAddrOpaque }, + addrlen: { sys::SocketAddrLen } + ;; + } + + pub const CODE = sys::IoringOp::Bind; + + pub fn build(self) -> Entry { + let Bind { fd, addr, addrlen } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.addr_or_splice_off_in.addr.ptr = addr.cast_mut().cast(); + sqe.off_or_addr2.off = u64::from(addrlen); + Entry(sqe) + } +} + +opcode! { + /// Listen on a socket, equivalent to `listen(2)`. + pub struct Listen { + fd: { impl sealed::UseFixed }, + backlog: { i32 }, + ;; + } + + pub const CODE = sys::IoringOp::Listen; + + pub fn build(self) -> Entry { + let Listen { fd, backlog } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.len.len = backlog as _; + Entry(sqe) + } +} + +// === 6.15 === + +opcode! { + /// Issue the zerocopy equivalent of a `recv(2)` system call. + pub struct RecvZc { + fd: { impl sealed::UseFixed }, + len: { u32 }, + ;; + ifq: u32 = 0, + ioprio: sys::IoringRecvFlags = sys::IoringRecvFlags::empty(), + } + + pub const CODE = sys::IoringOp::RecvZc; + + pub fn build(self) -> Entry { + let Self { fd, len, ifq, ioprio } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.len.len = len; + sqe.ioprio.recv_flags = ioprio | sys::IoringRecvFlags::MULTISHOT; + sqe.splice_fd_in_or_file_index_or_addr_len.zcrx_ifq_idx = ifq; + Entry(sqe) + } +} + +opcode! { + /// Issue the equivalent of a `epoll_wait(2)` system call. + pub struct EpollWait { + fd: { impl sealed::UseFixed }, + events: { *mut ::rustix::event::epoll::Event }, + max_events: { u32 }, + ;; + flags: u32 = 0, + } + + pub const CODE = sys::IoringOp::EpollWait; + + pub fn build(self) -> Entry { + let Self { fd, events, max_events, flags } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.addr_or_splice_off_in.addr.ptr = events.cast(); + sqe.len.len = max_events; + sqe.op_flags.poll32_events = flags; + Entry(sqe) + } +} + +opcode! { + /// Vectored read into a fixed buffer, equivalent to `preadv2(2)`. + pub struct ReadvFixed { + fd: { impl sealed::UseFixed }, + iovec: { *const types::iovec }, + len: { u32 }, + buf_index: { u16 }, + ;; + ioprio: u16 = 0, + offset: u64 = 0, + rw_flags: types::RwFlags = types::RwFlags::empty(), + } + + pub const CODE = sys::IoringOp::ReadvFixed; + + pub fn build(self) -> Entry { + let Self { fd, iovec, len, buf_index, offset, ioprio, rw_flags } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.off_or_addr2.off = offset as _; + sqe.addr_or_splice_off_in.addr.ptr = iovec as _; + sqe.len.len = len; + sqe.buf.buf_index = buf_index; + sqe.ioprio.ioprio = ioprio; + sqe.op_flags.rw_flags = rw_flags; + Entry(sqe) + } +} + +opcode! { + /// Vectored write from a fixed buffer, equivalent to `pwritev2(2)`. + pub struct WritevFixed { + fd: { impl sealed::UseFixed }, + iovec: { *const sys::iovec }, + len: { u32 }, + buf_index: { u16 }, + ;; + ioprio: u16 = 0, + offset: u64 = 0, + rw_flags: types::RwFlags = types::RwFlags::empty() + } + + pub const CODE = sys::IoringOp::WritevFixed; + + pub fn build(self) -> Entry { + let Self { fd, iovec, len, buf_index, offset, ioprio, rw_flags } = self; + + let mut sqe = sqe_zeroed(); + sqe.opcode = Self::CODE; + assign_fd!(sqe.fd = fd); + sqe.off_or_addr2.off = offset as _; + sqe.addr_or_splice_off_in.addr.ptr = iovec as _; + sqe.len.len = len; + sqe.buf.buf_index = buf_index; + sqe.ioprio.ioprio = ioprio; + sqe.op_flags.rw_flags = rw_flags; + Entry(sqe) + } +} diff --git a/src/submit.rs b/src/submit.rs index b5ec272..4a60823 100644 --- a/src/submit.rs +++ b/src/submit.rs @@ -646,4 +646,12 @@ impl<'a> Submitter<'a> { 1, ) } + + /// Register a netdev hw rx queue for zerocopy. + /// + /// Available since 6.15. + pub fn register_ifq(&self, reg: &types::io_uring_zcrx_ifq_reg) -> io::Result<()> { + let ptr = ::core::ptr::from_ref(reg).cast(); + execute(self.fd, sys::IoringRegisterOp::RegisterZcrxIfq, ptr, 1) + } } diff --git a/src/types.rs b/src/types.rs index a94a940..9abb267 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,7 @@ //! Common Linux types not provided by libc. +pub use linux_raw_sys::io_uring::io_uring_zcrx_ifq_reg; + pub(crate) mod sealed { use super::{Fd, Fixed}; use rustix::fd::RawFd; @@ -48,10 +50,11 @@ use core::marker::PhantomData; use core::num::NonZeroU32; use rustix::fd::RawFd; +pub use ::rustix::process::{Pid, WaitIdOptions, WaitIdStatus}; pub use sys::ReadWriteFlags as RwFlags; pub use sys::{ iovec, Advice, AtFlags, EpollEvent, Mode, MsgHdr, OFlags, RenameFlags, ResolveFlags, - SocketAddrLen, SocketAddrOpaque, Statx, StatxFlags, + SocketAddrLen, SocketAddrOpaque, Statx, StatxFlags, XattrFlags, }; /// A file descriptor that has not been registered with io_uring. @@ -676,6 +679,29 @@ impl FutexWaitV { } } +#[non_exhaustive] +pub enum WaitId { + All, + Pid(self::Pid), + Pgid(Option), + PidFd(self::Fd), +} + +impl<'a> From<::rustix::process::WaitId<'a>> for self::WaitId { + fn from(value: ::rustix::process::WaitId<'a>) -> Self { + use ::rustix::fd::AsRawFd as _; + match value { + ::rustix::process::WaitId::All => self::WaitId::All, + ::rustix::process::WaitId::Pid(pid) => self::WaitId::Pid(pid), + ::rustix::process::WaitId::Pgid(pid) => self::WaitId::Pgid(pid), + ::rustix::process::WaitId::PidFd(borrowed_fd) => { + self::WaitId::PidFd(self::Fd(borrowed_fd.as_raw_fd())) + } + _ => unreachable!(), + } + } +} + #[cfg(test)] mod tests { use core::time::Duration;