Skip to main content

Subsystem

Struct Subsystem 

Source
pub struct Subsystem<'r> { /* private fields */ }
Expand description

An NVMe subsystem.

A subsystem groups one or more controllers that share a common identity (NQN, serial, model). For typical single-controller PCIe SSDs the subsystem has exactly one controller; Fabrics and multipath setups can have several.

Implementations§

Source§

impl<'r> Subsystem<'r>

Source

pub fn name(&self) -> Result<&'r str>

The kernel-assigned subsystem name, e.g. nvme-subsys0.

Source

pub fn nqn(&self) -> Result<&'r str>

The subsystem NVMe Qualified Name (NQN).

Source

pub fn subsystem_type(&self) -> Result<&'r str>

The subsystem type (e.g. nvm, discovery).

Source

pub fn serial(&self) -> Result<&'r str>

The subsystem-level serial number, when reported.

Only present when built against a libnvme that exposes nvme_subsystem_get_serial. Older releases (notably the libnvme 1.8 shipped in Ubuntu 24.04) do not have this symbol; on those builds the method is compiled out.

Source

pub fn model(&self) -> Result<&'r str>

The subsystem-level model string, when reported.

Only present when built against a libnvme that exposes nvme_subsystem_get_model.

Source

pub fn firmware(&self) -> Result<&'r str>

The subsystem-level firmware revision, when reported.

Only present when built against a libnvme that exposes nvme_subsystem_get_fw_rev.

Source

pub fn iopolicy(&self) -> Result<&'r str>

ANA / multipath I/O policy for this subsystem, e.g. numa, round-robin.

Only present when built against a libnvme that exposes nvme_subsystem_get_iopolicy.

Source

pub fn application(&self) -> Result<&'r str>

Application-set tag for this subsystem (used by tools like nvme-cli for grouping). Empty when unset.

Only present when built against a libnvme that exposes nvme_subsystem_get_application.

Source

pub fn controllers(&self) -> Controllers<'r>

Iterate over the controllers in this subsystem.

Examples found in repository?
examples/fw_info.rs (line 14)
9fn main() -> Result<(), Box<dyn std::error::Error>> {
10    let root = Root::scan()?;
11    let mut any = false;
12    for host in root.hosts() {
13        for subsys in host.subsystems() {
14            for ctrl in subsys.controllers() {
15                any = true;
16                print_fw(&ctrl)?;
17            }
18        }
19    }
20    if !any {
21        println!("(no NVMe controllers found)");
22    }
23    Ok(())
24}
More examples
Hide additional examples
examples/smart_log.rs (line 10)
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6    let root = Root::scan()?;
7    let mut any = false;
8    for host in root.hosts() {
9        for subsys in host.subsystems() {
10            for ctrl in subsys.controllers() {
11                any = true;
12                print_smart(&ctrl)?;
13            }
14        }
15    }
16    if !any {
17        println!("(no NVMe controllers found)");
18    }
19    Ok(())
20}
examples/id_ctrl.rs (line 10)
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6    let root = Root::scan()?;
7    let mut any = false;
8    for host in root.hosts() {
9        for subsys in host.subsystems() {
10            for ctrl in subsys.controllers() {
11                any = true;
12                print_controller(&ctrl)?;
13            }
14        }
15    }
16    if !any {
17        println!("(no NVMe controllers found)");
18    }
19    Ok(())
20}
examples/list_nvme.rs (line 11)
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6    let root = Root::scan()?;
7
8    let mut rows: Vec<Row> = Vec::new();
9    for host in root.hosts() {
10        for subsys in host.subsystems() {
11            for ctrl in subsys.controllers() {
12                let mut had_namespace = false;
13                for ns in ctrl.namespaces() {
14                    rows.push(Row::from(&ctrl, Some(&ns)));
15                    had_namespace = true;
16                }
17                if !had_namespace {
18                    rows.push(Row::from(&ctrl, None));
19                }
20            }
21        }
22    }
23
24    if rows.is_empty() {
25        println!("(no NVMe devices found)");
26        return Ok(());
27    }
28
29    print_table(&rows);
30    Ok(())
31}
examples/format_smoke.rs (line 26)
21fn main() -> Result<(), Box<dyn std::error::Error>> {
22    let root = Root::scan()?;
23    let mut formatted = 0;
24    for host in root.hosts() {
25        for subsys in host.subsystems() {
26            for ctrl in subsys.controllers() {
27                let model = ctrl.model().unwrap_or("?").trim();
28                if model != QEMU_MODEL {
29                    println!(
30                        "skipping {}: model {model:?} != {QEMU_MODEL:?} (refusing to format real hardware)",
31                        ctrl.name()?
32                    );
33                    continue;
34                }
35                for ns in ctrl.namespaces() {
36                    let nsid = ns.nsid();
37                    let lba_size_before = ns.lba_size();
38                    let lba_count_before = ns.lba_count();
39                    println!(
40                        "about to format {}: NSID={nsid}, {lba_count_before} blocks × {lba_size_before} B",
41                        ns.name()?
42                    );
43                    ns.format()
44                        .lba_format(0)
45                        .secure_erase(SecureErase::None)
46                        .execute()?;
47                    println!("  -> format succeeded");
48                    formatted += 1;
49                }
50            }
51        }
52    }
53    if formatted == 0 {
54        println!("(no QEMU virtual NVMe controllers found)");
55    } else {
56        println!("formatted {formatted} namespace(s)");
57    }
58    Ok(())
59}
examples/io_smoke.rs (line 25)
19fn main() -> Result<(), Box<dyn std::error::Error>> {
20    let root = Root::scan()?;
21    let mut exercised = 0;
22
23    for host in root.hosts() {
24        for subsys in host.subsystems() {
25            for ctrl in subsys.controllers() {
26                let model = ctrl.model().unwrap_or("?").trim();
27                if model != QEMU_MODEL {
28                    println!(
29                        "skipping {}: model {model:?} != {QEMU_MODEL:?} (refusing on real hardware)",
30                        ctrl.name()?
31                    );
32                    continue;
33                }
34
35                for ns in ctrl.namespaces() {
36                    let lba_size = ns.lba_size();
37                    println!(
38                        "exercising {} (NSID {}, {} B LBAs)",
39                        ns.name()?,
40                        ns.nsid(),
41                        lba_size
42                    );
43
44                    // ---- Write one LBA with a recognizable pattern -----
45                    let mut pattern = vec![0u8; lba_size as usize];
46                    for (i, b) in pattern.iter_mut().enumerate() {
47                        *b = (i & 0xFF) as u8;
48                    }
49                    ns.write(0, 1, &pattern).fua().execute()?;
50                    println!("  write ok");
51
52                    // ---- Read it back ---------------------------------
53                    let got = ns.read_to_vec(0, 1)?;
54                    assert_eq!(got, pattern, "read did not return the bytes we wrote");
55                    println!("  read ok (round-trip matches)");
56
57                    // ---- Compare (should succeed) ---------------------
58                    ns.compare(0, 1, &pattern).execute()?;
59                    println!("  compare ok");
60
61                    // ---- Verify (controller-side integrity check) -----
62                    ns.verify(0, 1).execute()?;
63                    println!("  verify ok");
64
65                    // ---- Write zeroes over the LBA --------------------
66                    ns.write_zeroes(0, 1).execute()?;
67                    let zeroed = ns.read_to_vec(0, 1)?;
68                    assert!(zeroed.iter().all(|&b| b == 0), "expected all-zero LBA");
69                    println!("  write_zeroes ok");
70
71                    // ---- DSM deallocate (TRIM) ------------------------
72                    ns.dsm(DsmAttr::DEALLOCATE)
73                        .ranges(&[DsmRange::new(0, 1)])
74                        .execute()?;
75                    println!("  dsm(deallocate) ok");
76
77                    // ---- Flush ----------------------------------------
78                    ns.flush()?;
79                    println!("  flush ok");
80
81                    exercised += 1;
82                }
83            }
84        }
85    }
86
87    if exercised == 0 {
88        println!("(no QEMU virtual NVMe controllers found)");
89    } else {
90        println!("\nall I/O commands exercised on {exercised} namespace(s)");
91    }
92    Ok(())
93}

Trait Implementations§

Source§

impl Debug for Subsystem<'_>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'r> Freeze for Subsystem<'r>

§

impl<'r> RefUnwindSafe for Subsystem<'r>

§

impl<'r> !Send for Subsystem<'r>

§

impl<'r> !Sync for Subsystem<'r>

§

impl<'r> Unpin for Subsystem<'r>

§

impl<'r> UnsafeUnpin for Subsystem<'r>

§

impl<'r> UnwindSafe for Subsystem<'r>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.