├── .gitignore ├── .gitmodules ├── .rustfmt.toml ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── Cargo.toml ├── HACKING.md ├── LICENSE ├── README.md ├── build.rs └── src ├── clib.rs ├── lib.rs └── nvme.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target/ 3 | **/*.rs.bk 4 | *.swp 5 | Cargo.lock 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "spdk"] 2 | path = spdk 3 | url = https://github.com/spdk/spdk 4 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | write_mode = "overwrite" 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Our goal is to encourage frictionless contributions to the project. In order to 2 | achieve that, we use Unprotocols [C4 process](https://rfc.unprotocols.org/spec:1/C4). 3 | Please read it, it will answer a lot of questions. Our goal is to merge pull requests 4 | as quickly as possible and make new stable releases regularly. 5 | 6 | In a nutshell, this means: 7 | 8 | * We merge pull requests rapidly (try!) 9 | * We are open to diverse ideas 10 | * We prefer code now over consensus later 11 | 12 | An additional commit message kind ("tlog", as in "weblog => blog", "gitlog => 13 | tlog") is encouraged, a commit without files that retains a contextualized 14 | article on the subject. 15 | 16 | The motivation for this is that web is not a reliable place to retain articles 17 | at (hosts go down, content gets deleted, etc.). Nor it's easy to find relevant 18 | pieces with all the noise out there. 19 | 20 | What did the contributor think about when he was developing this or that 21 | part? What train of thought was he on? 22 | 23 | Keeping the articles in the git log allows to retain them forever (for as long 24 | as there's at least one copy of the repository somewhere) and provide 25 | context to those who really want to learn more about the project. 26 | 27 | It is highly recommended to watch [Pieter Hintjens' talk on building open 28 | source communities](https://www.youtube.com/watch?v=uzxcILudFWM) as well as 29 | read his [book on the same 30 | matter](https://www.gitbook.com/book/hintjens/social-architecture/details). 31 | 32 | # Submitting an issue 33 | 34 | According to [development process](https://rfc.unprotocols.org/spec:1/C4#24-development-process), 35 | the issue should describe a documented and provable. What this means is that an 36 | issue should trive to have a clear, understandable problem statement. Just like 37 | a patch, it SHOULD be titled "Problem: ..." and have a detailed description 38 | describing evidence behind it, be it a bug or a feature request, or a longer 39 | term "exploratory" issue. 40 | 41 | # Preparing a patch 42 | 43 | According to [patch requirements](https://rfc.unprotocols.org/spec:1/C4#23-patch-requirements), 44 | the patch should be a minimal and accurate answer to exactly one identified and 45 | agreed problem. A patch commit message must consist of a single short (less 46 | than 50 characters) line stating the problem ("Problem: ...") being solved, 47 | followed by a blank line and then the proposed solution ("Solution: ..."). 48 | 49 | ``` 50 | Problem: short problem statement 51 | 52 | Optional longer explanation of the problem that this patch 53 | addresses, giving necessary details for the reader to be 54 | able to understand it better. 55 | 56 | Solution: explanation of the solution to the problem. Could 57 | be longer than one line. 58 | ``` 59 | 60 | Also, please don't run `rustfmt` (`cargo fmt`) over your patch before committing, unless 61 | you are absolutely sure to include formatting changes that only apply to your actual patch. 62 | Otherwise, it'll make this patch unnecessarily long and might interfere with currently 63 | outstanding PRs or other items in progress. We will run `rustfmt` regularly when nothing 64 | is outstanding to make the code prettier. This will make everybody's life easier! 65 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Yurii Rashkovskii 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spdk" 3 | version = "0.1.0" 4 | authors = ["Yurii Rashkovskii "] 5 | build = "build.rs" 6 | 7 | [lib] 8 | doctest = false 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /HACKING.md: -------------------------------------------------------------------------------- 1 | # How to regenerate bindings 2 | 3 | ``` 4 | bindgen spdk/include/spdk/nvme.h --with-derive-default --whitelist-function "spdk_(env|nvme|dma|mempool).*" \ 5 | --whitelist-type "spdk_(env|nvme|mempool).*" --generate functions,types -- -Ispdk/include > src/clib.rs 6 | ``` 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD LICENSE 2 | 3 | Copyright (c) 2017, Contributors (see CONTRIBUTORS file). 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions 8 | are met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | * Neither the name of Intel Corporation nor the names of its 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PumpkinDB/rust-spdk/34c4e467a16321c8e7f5978a4a78ea7562cf0003/README.md -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::process::{Command, exit}; 2 | use std::path::Path; 3 | use std::io::{stderr, Write}; 4 | 5 | fn exec(command_name: &str, mut cmd: Command) { 6 | match cmd.output() { 7 | Ok(out) => if !out.status.success() { 8 | let _ = writeln!(&mut stderr(), "{} failed:\n {}", 9 | command_name, String::from_utf8(out.stderr).unwrap()); 10 | exit(1); 11 | }, 12 | Err(e) => { 13 | let _ = writeln!(&mut stderr(), "{} exec failed: {:?}", command_name, e); 14 | exit(1); 15 | } 16 | } 17 | } 18 | 19 | fn main() { 20 | let mut make_dpdk = Command::new("make"); 21 | make_dpdk.current_dir(Path::new("spdk/dpdk")) 22 | .arg("install").arg("T=x86_64-native-linuxapp-gcc").arg("EXTRA_CFLAGS=-fPIC"); 23 | exec("dpdk config", make_dpdk); 24 | 25 | let mut config_spdk = Command::new("./configure"); 26 | config_spdk.current_dir(Path::new("spdk")) 27 | .arg("--with-dpdk=dpdk/x86_64-native-linuxapp-gcc"); 28 | exec("spdk config", config_spdk); 29 | 30 | let mut make_spdk = Command::new("make"); 31 | make_spdk.current_dir(Path::new("spdk")); 32 | exec("spdk make", make_spdk); 33 | 34 | println!("cargo:rustc-link-lib=static=spdk_env_dpdk"); 35 | println!("cargo:rustc-link-lib=static=spdk_log"); 36 | println!("cargo:rustc-link-lib=static=spdk_util"); 37 | println!("cargo:rustc-link-lib=static=spdk_nvme"); 38 | println!("cargo:rustc-link-search=spdk/build/lib"); 39 | println!("cargo:rustc-link-lib=spdk/dpdk"); 40 | println!("cargo:rustc-link-search=spdk/dpdk/x86_64-native-linuxapp-gcc/lib"); 41 | } 42 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Contributors (see CONTRIBUTORS file). 2 | // All rights reserved. 3 | // 4 | // This source code is licensed under the BSD-style license found in the 5 | // LICENSE file in the root directory of this source tree. 6 | 7 | #![feature(untagged_unions)] // for clib 8 | #![cfg_attr(test, feature(try_from))] 9 | #![cfg_attr(test, feature(test))] 10 | 11 | #[cfg(test)] 12 | extern crate test; 13 | 14 | use std::ptr::null_mut; 15 | 16 | #[allow(dead_code,non_camel_case_types,non_snake_case)] 17 | mod clib; 18 | 19 | pub mod nvme; 20 | 21 | use self::clib::*; 22 | use std::marker::PhantomData; 23 | 24 | #[derive(Debug)] 25 | pub struct DMA<'a>(*mut ::std::os::raw::c_void, usize, PhantomData<&'a ()>); 26 | 27 | impl<'a> DMA<'a> { 28 | pub fn alloc(size: usize, align: usize) -> Self { 29 | DMA(unsafe { spdk_dma_malloc(size, align, null_mut()) }, size, PhantomData) 30 | } 31 | 32 | pub fn alloc_zeroed(size: usize, align: usize) -> Self { 33 | DMA(unsafe { spdk_dma_zmalloc(size, align, null_mut()) }, size, PhantomData) 34 | } 35 | 36 | pub fn as_slice(&self) -> &'a [u8] { 37 | unsafe { ::std::slice::from_raw_parts(self.0 as *mut _ as *const u8, self.1) } 38 | } 39 | 40 | pub fn as_slice_mut(&self) -> &'a mut [u8] { 41 | unsafe { ::std::slice::from_raw_parts_mut(self.0 as *mut _ as *mut u8, self.1) } 42 | } 43 | 44 | } 45 | 46 | impl<'a> Drop for DMA<'a> { 47 | fn drop(&mut self) { 48 | unsafe { spdk_dma_free(self.0) } 49 | } 50 | } 51 | 52 | pub struct EnvOpts(spdk_env_opts); 53 | 54 | impl EnvOpts { 55 | pub fn new() -> Self { 56 | let mut opts: spdk_env_opts = Default::default(); 57 | unsafe { 58 | spdk_env_opts_init(&mut opts as *mut spdk_env_opts); 59 | } 60 | EnvOpts(opts) 61 | } 62 | } 63 | 64 | pub fn init_env(opts: &EnvOpts) { 65 | unsafe { 66 | spdk_env_init(&opts.0 as *const spdk_env_opts); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/nvme.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Contributors (see CONTRIBUTORS file). 2 | // All rights reserved. 3 | // 4 | // This source code is licensed under the BSD-style license found in the 5 | // LICENSE file in the root directory of this source tree. 6 | 7 | use std; 8 | 9 | use std::ffi::{CStr, CString}; 10 | use std::ptr::null; 11 | 12 | use super::clib::*; 13 | 14 | pub const IO_FLAG_PRCHK_REF_TAG: u32 = 1u32 << 26; 15 | pub const IO_FLAG_PRCHK_APP_TAG: u32 = 1u32 << 27; 16 | pub const IO_FLAG_PRCHK_GUARD: u32 = 1u32 << 28; 17 | pub const IO_FLAG_PRACT: u32 = 1u32 << 29; 18 | pub const IO_FLAG_FORCE_UNIT_ACCESS: u32 = 1u32 << 30; 19 | pub const IO_FLAG_LIMITED_RETRY: u32 = 1u32 << 31; 20 | 21 | #[derive(Debug)] 22 | pub struct CompletionQueueEntry(*const spdk_nvme_cpl); 23 | 24 | pub trait CommandCallback { 25 | fn callback(&mut self, cpl: CompletionQueueEntry); 26 | } 27 | 28 | pub use super::clib::spdk_nvme_transport_type as TransportType; 29 | 30 | #[derive(Debug)] 31 | pub struct TransportIdentifier(*const spdk_nvme_transport_id); 32 | 33 | pub struct OwnedTransportIdentifier(spdk_nvme_transport_id); 34 | 35 | impl OwnedTransportIdentifier { 36 | pub fn from_str(str: &str) -> Result { 37 | let mut tid : spdk_nvme_transport_id = Default::default(); 38 | let cstr = CString::new(str).unwrap(); 39 | unsafe { 40 | if spdk_nvme_transport_id_parse(&mut tid as *mut spdk_nvme_transport_id, cstr.as_ptr()) == 0 { 41 | Ok(OwnedTransportIdentifier(tid)) 42 | } else { 43 | Err(()) 44 | } 45 | } 46 | } 47 | } 48 | 49 | impl TransportIdentifier { 50 | pub fn transport_type(&self) -> TransportType { 51 | unsafe { (*self.0).trtype } 52 | } 53 | pub fn address(&self) -> &str { 54 | unsafe { CStr::from_ptr(&(*self.0).traddr as *const i8).to_str().unwrap() } 55 | } 56 | } 57 | 58 | pub trait UnderlyingTransportIdentifier { 59 | fn transport_id(&self) -> *const spdk_nvme_transport_id; 60 | } 61 | 62 | impl<'a> UnderlyingTransportIdentifier for &'a OwnedTransportIdentifier { 63 | fn transport_id(&self) -> *const spdk_nvme_transport_id { 64 | &self.0 as *const spdk_nvme_transport_id 65 | } 66 | } 67 | 68 | impl UnderlyingTransportIdentifier for TransportIdentifier { 69 | fn transport_id(&self) -> *const spdk_nvme_transport_id { 70 | self.0 71 | } 72 | } 73 | 74 | impl UnderlyingTransportIdentifier for () { 75 | fn transport_id(&self) -> *const spdk_nvme_transport_id { 76 | null() 77 | } 78 | } 79 | 80 | #[derive(Debug)] 81 | pub struct ControllerOptions(*const spdk_nvme_ctrlr_opts); 82 | #[derive(Debug)] 83 | pub struct ControllerMutableOptions(*mut spdk_nvme_ctrlr_opts); 84 | 85 | #[derive(Debug)] 86 | pub struct QueuePair(*mut spdk_nvme_qpair); 87 | 88 | impl QueuePair { 89 | #[inline] 90 | pub fn process_completions(&self, max: u32) -> i32 { 91 | unsafe { 92 | spdk_nvme_qpair_process_completions(self.0, max) 93 | } 94 | } 95 | } 96 | 97 | impl Drop for QueuePair { 98 | fn drop(&mut self) { 99 | unsafe { spdk_nvme_ctrlr_free_io_qpair(self.0); } 100 | } 101 | } 102 | 103 | 104 | #[derive(Debug)] 105 | pub struct Namespace(*mut spdk_nvme_ns); 106 | 107 | macro_rules! ns_data { 108 | ($name: ident, $field: ident) => { 109 | pub fn $name(&self) -> u16 { 110 | unsafe { 111 | let data = spdk_nvme_ns_get_data(self.0); 112 | (*data).$field 113 | } 114 | } 115 | }; 116 | } 117 | 118 | impl Namespace { 119 | pub fn is_active(&self) -> bool { 120 | unsafe { spdk_nvme_ns_is_active(self.0) } 121 | } 122 | 123 | pub fn id(&self) -> u32 { 124 | unsafe { spdk_nvme_ns_get_id(self.0) } 125 | } 126 | 127 | pub fn size(&self) -> u64 { 128 | unsafe { spdk_nvme_ns_get_size(self.0) } 129 | } 130 | 131 | pub fn sector_size(&self) -> u32 { 132 | unsafe { spdk_nvme_ns_get_sector_size(self.0) } 133 | } 134 | 135 | ns_data!(atomic_write_unit_normal, nawun); 136 | ns_data!(atomic_write_unit_power_failure, nawupf); 137 | ns_data!(atomic_boundary_size_normal, nabsn); 138 | 139 | unsafe extern "C" fn cmd_cb(cb_ctx: *mut ::std::os::raw::c_void, 140 | cpl: *const spdk_nvme_cpl) { 141 | let cb = cb_ctx as *mut _ as *mut P; 142 | (&mut *cb).callback(CompletionQueueEntry(cpl)); 143 | drop(cb); 144 | } 145 | 146 | pub fn write(&self, qpair: &QueuePair, data: &[u8], 147 | lba: u64, lba_count: u32, callback: C, 148 | flags: u32) -> Result<(), ()> { 149 | let cb = Box::new(callback); 150 | let code = 151 | unsafe { spdk_nvme_ns_cmd_write(self.0, qpair.0, data as *const _ as *mut ::std::os::raw::c_void, 152 | lba, lba_count, 153 | Some(Namespace::cmd_cb::), 154 | &*cb as *const _ as *mut ::std::os::raw::c_void, 155 | flags) }; 156 | // crossing the FFI boundary 157 | std::mem::forget(cb); 158 | if code == 0 { 159 | Ok(()) 160 | } else { 161 | Err(()) 162 | } 163 | } 164 | 165 | pub fn read(&self, qpair: &QueuePair, data: &mut [u8], 166 | lba: u64, lba_count: u32, callback: C, 167 | flags: u32) { 168 | let cb = Box::new(callback); 169 | unsafe { spdk_nvme_ns_cmd_read(self.0, qpair.0, data as *const _ as *mut ::std::os::raw::c_void, 170 | lba, lba_count, 171 | Some(Namespace::cmd_cb::), 172 | &*cb as *const _ as *mut ::std::os::raw::c_void, 173 | flags); } 174 | // crossing the FFI boundary 175 | std::mem::forget(cb); 176 | } 177 | } 178 | 179 | #[derive(Debug)] 180 | pub struct Controller(*mut spdk_nvme_ctrlr); 181 | 182 | pub use super::clib::{spdk_nvme_qprio as QueuePriority, spdk_nvme_io_qpair_opts as QueueOptions}; 183 | 184 | macro_rules! ctrlr_data { 185 | ($name: ident, $field: ident) => { 186 | pub fn $name(&self) -> u16 { 187 | unsafe { 188 | let data = spdk_nvme_ctrlr_get_data(self.0); 189 | (*data).$field 190 | } 191 | } 192 | }; 193 | } 194 | 195 | impl Controller { 196 | pub fn alloc_io_queue_pair(&self, opts: Option) -> Result { 197 | let (options, size) = match opts { 198 | None => (::std::ptr::null(), 0), 199 | Some(value) => (&value as *const super::clib::spdk_nvme_io_qpair_opts, ::std::mem::size_of::()) 200 | }; 201 | let qpair = unsafe { spdk_nvme_ctrlr_alloc_io_qpair(self.0, options, size) }; 202 | if qpair.is_null() { 203 | Err(()) 204 | } else { 205 | Ok(QueuePair(qpair)) 206 | } 207 | } 208 | 209 | pub fn namespaces(&self) -> Vec { 210 | let num = unsafe { spdk_nvme_ctrlr_get_num_ns(self.0) }; 211 | let mut result = Vec::with_capacity(num as usize); 212 | for i in 0..num { 213 | result.push(Namespace(unsafe { spdk_nvme_ctrlr_get_ns(self.0, i + 1) })); 214 | } 215 | result 216 | } 217 | 218 | pub fn detach(self) { 219 | unsafe { spdk_nvme_detach(self.0); } 220 | } 221 | 222 | 223 | ctrlr_data!(pci_vendor_id, vid); 224 | ctrlr_data!(atomic_write_unit_normal, awun); 225 | } 226 | 227 | #[allow(unused_variables)] 228 | pub trait ProbeCallback { 229 | fn probe(&mut self, transport_id: TransportIdentifier, opts: ControllerMutableOptions) -> bool; 230 | fn attach(&mut self, transport_id: TransportIdentifier, ctrlr: Controller, opts: ControllerOptions) {} 231 | } 232 | 233 | 234 | pub fn probe(transport_id: T, probe: P) -> Result<(), ()> { 235 | 236 | unsafe extern "C" fn probe_cb(cb_ctx: *mut ::std::os::raw::c_void, 237 | trid: *const spdk_nvme_transport_id, 238 | opts: *mut spdk_nvme_ctrlr_opts) 239 | -> bool { 240 | let cb = cb_ctx as *mut _ as *mut P; 241 | (&mut *cb).probe(TransportIdentifier(trid), ControllerMutableOptions(opts)) 242 | } 243 | 244 | unsafe extern "C" fn attach_cb(cb_ctx: *mut ::std::os::raw::c_void, 245 | trid: *const spdk_nvme_transport_id, 246 | ctrlr: *mut spdk_nvme_ctrlr, 247 | opts: *const spdk_nvme_ctrlr_opts) { 248 | let cb = cb_ctx as *mut _ as *mut P; 249 | (&mut *cb).attach(TransportIdentifier(trid), Controller(ctrlr), ControllerOptions(opts)) 250 | } 251 | 252 | unsafe { 253 | if spdk_nvme_probe(transport_id.transport_id(), 254 | &probe as *const _ as *mut ::std::os::raw::c_void, 255 | Some(probe_cb::

), 256 | Some(attach_cb::

), 257 | None) == 0 { 258 | Ok(()) 259 | } else { 260 | Err(()) 261 | } 262 | } 263 | } 264 | 265 | 266 | 267 | #[cfg(test)] 268 | #[allow(unused_variables)] 269 | mod tests { 270 | 271 | use std::convert::{TryFrom, TryInto}; 272 | use super::*; 273 | use super::super::*; 274 | 275 | impl<'a> CommandCallback for &'a std::sync::atomic::AtomicUsize { 276 | fn callback(&mut self, cpl: CompletionQueueEntry) { 277 | self.fetch_add(1, std::sync::atomic::Ordering::SeqCst); 278 | } 279 | } 280 | 281 | struct Uninitialized { 282 | ctrlr: Option 283 | } 284 | 285 | struct Initialized(Controller); 286 | 287 | use test::Bencher; 288 | 289 | impl Initialized { 290 | pub fn test(&self, b: &mut Bencher) { 291 | use std::rc::Rc; 292 | let ref ctrlr = self.0; 293 | let qpair = Rc::new(ctrlr.alloc_io_queue_pair(QueuePriority::SPDK_NVME_QPRIO_URGENT).unwrap()); 294 | let buf = Rc::new(DMA::alloc_zeroed(4096 * 1024 * 2, 512)); 295 | (buf.as_slice_mut()[0..4]).copy_from_slice("test".as_bytes()); 296 | let rbuf = Rc::new(DMA::alloc_zeroed(4096 * 1024 * 2, 512)); 297 | 298 | let namespaces = ctrlr.namespaces(); 299 | 300 | for ns in namespaces { 301 | let buf_ = buf.clone(); 302 | let rbuf_ = rbuf.clone(); 303 | let qpair_ = qpair.clone(); 304 | let slice = buf_.as_slice(); 305 | let mut rslice = rbuf_.as_slice_mut(); 306 | 307 | let sector_sz = ns.sector_size(); 308 | let lba_count = slice.len() as u32 / sector_sz; 309 | println!("sector_sz {} lba count {}", sector_sz, lba_count); 310 | 311 | b.iter(move || { 312 | let mut ctr = std::sync::atomic::AtomicUsize::new(0); 313 | ns.write(&qpair_, slice, 314 | 0, lba_count, 315 | &ctr, 0).expect("start write did not succeed"); 316 | ns.read(&qpair_, &mut rslice, 317 | 0, lba_count, 318 | &ctr, 0); 319 | while *ctr.get_mut() < 2 { 320 | qpair_.process_completions(0); 321 | } 322 | }); 323 | } 324 | } 325 | pub fn cleanup(self) { 326 | self.0.detach() 327 | } 328 | 329 | } 330 | 331 | impl Uninitialized { 332 | pub fn new() -> Self { 333 | Uninitialized { ctrlr: None } 334 | } 335 | } 336 | 337 | impl TryFrom for Initialized { 338 | type Error = (); 339 | 340 | fn try_from(value: Uninitialized) -> Result { 341 | match value.ctrlr { 342 | Some(c) => Ok(Initialized(c)), 343 | None => Err(()) 344 | } 345 | } 346 | 347 | } 348 | 349 | 350 | impl<'a> ProbeCallback for &'a mut Uninitialized { 351 | fn probe(&mut self, transport_id: TransportIdentifier, opts: ControllerMutableOptions) -> bool { 352 | println!("Attaching {:?} {}", 353 | transport_id.transport_type(), 354 | transport_id.address()); 355 | true 356 | } 357 | 358 | fn attach(&mut self, transport_id: TransportIdentifier, ctrlr: Controller, opts: ControllerOptions) { 359 | println!("Attached {:?}", ctrlr); 360 | self.ctrlr = Some(ctrlr); 361 | } 362 | } 363 | 364 | 365 | 366 | #[bench] 367 | fn it_works(b: &mut Bencher) { 368 | use std::env; 369 | match env::var("nvme_pci") { 370 | Ok(addr) => { 371 | let opts = EnvOpts::new(); 372 | init_env(&opts); 373 | let mut t = Uninitialized::new(); 374 | println!("Probing..."); 375 | let tr = OwnedTransportIdentifier::from_str(format!("trtype:PCIe traddr:{}", addr).as_str()) 376 | .expect("can't parse PCIe address"); 377 | probe(&tr, &mut t).expect("failed probing NVMe controllers"); 378 | let i : Initialized = t.try_into().expect("can't attach an NVMe controller"); 379 | println!("Initialized."); 380 | i.test(b); 381 | i.cleanup(); 382 | }, 383 | Err(_) => println!("skipping the test") 384 | } 385 | } 386 | } 387 | --------------------------------------------------------------------------------