nvme_common/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Conversion routines between [`nvme_spec`] and [`disk_backend`] types,
5//! primarily for persistent reservation mapping between NVMe and SCSI PR
6//! models.
7
8#![forbid(unsafe_code)]
9
10use disk_backend::pr;
11use nvme_spec::nvm;
12use thiserror::Error;
13
14/// Converts a `disk_backend` reservation type to an NVMe reservation type.
15pub fn to_nvme_reservation_type(reservation_type: pr::ReservationType) -> nvm::ReservationType {
16    match reservation_type {
17        pr::ReservationType::WriteExclusive => nvm::ReservationType::WRITE_EXCLUSIVE,
18        pr::ReservationType::ExclusiveAccess => nvm::ReservationType::EXCLUSIVE_ACCESS,
19        pr::ReservationType::WriteExclusiveRegistrantsOnly => {
20            nvm::ReservationType::WRITE_EXCLUSIVE_REGISTRANTS_ONLY
21        }
22        pr::ReservationType::ExclusiveAccessRegistrantsOnly => {
23            nvm::ReservationType::EXCLUSIVE_ACCESS_REGISTRANTS_ONLY
24        }
25        pr::ReservationType::WriteExclusiveAllRegistrants => {
26            nvm::ReservationType::WRITE_EXCLUSIVE_ALL_REGISTRANTS
27        }
28        pr::ReservationType::ExclusiveAccessAllRegistrants => {
29            nvm::ReservationType::EXCLUSIVE_ACCESS_ALL_REGISTRANTS
30        }
31    }
32}
33
34/// Error returned by [`from_nvme_reservation_type`].
35#[derive(Debug, Error)]
36#[error("invalid nvme reservation type: {0:#x?}")]
37pub struct InvalidReservationType(nvm::ReservationType);
38
39/// Converts a `disk_backend` reservation type from an NVMe reservation type.
40pub fn from_nvme_reservation_type(
41    nvme_type: nvm::ReservationType,
42) -> Result<pr::ReservationType, InvalidReservationType> {
43    let reservation_type = match nvme_type {
44        nvm::ReservationType::WRITE_EXCLUSIVE => pr::ReservationType::WriteExclusive,
45        nvm::ReservationType::EXCLUSIVE_ACCESS => pr::ReservationType::ExclusiveAccess,
46        nvm::ReservationType::WRITE_EXCLUSIVE_REGISTRANTS_ONLY => {
47            pr::ReservationType::WriteExclusiveRegistrantsOnly
48        }
49        nvm::ReservationType::EXCLUSIVE_ACCESS_REGISTRANTS_ONLY => {
50            pr::ReservationType::ExclusiveAccessRegistrantsOnly
51        }
52        nvm::ReservationType::WRITE_EXCLUSIVE_ALL_REGISTRANTS => {
53            pr::ReservationType::WriteExclusiveAllRegistrants
54        }
55        nvm::ReservationType::EXCLUSIVE_ACCESS_ALL_REGISTRANTS => {
56            pr::ReservationType::ExclusiveAccessAllRegistrants
57        }
58        _ => return Err(InvalidReservationType(nvme_type)),
59    };
60    Ok(reservation_type)
61}
62
63/// Builds `disk_backend` reservation capabilities from NVMe reservation capabilities.
64pub fn from_nvme_reservation_capabilities(
65    rescap: nvm::ReservationCapabilities,
66) -> pr::ReservationCapabilities {
67    pr::ReservationCapabilities {
68        write_exclusive: rescap.write_exclusive(),
69        exclusive_access: rescap.exclusive_access(),
70        write_exclusive_registrants_only: rescap.write_exclusive_registrants_only(),
71        exclusive_access_registrants_only: rescap.exclusive_access_registrants_only(),
72        write_exclusive_all_registrants: rescap.write_exclusive_all_registrants(),
73        exclusive_access_all_registrants: rescap.exclusive_access_all_registrants(),
74        persist_through_power_loss: rescap.persist_through_power_loss(),
75    }
76}
77
78/// Parses an NVMe reservation report into a `disk_backend` reservation report.
79pub fn from_nvme_reservation_report(
80    report_header: &nvm::ReservationReport,
81    controllers: &[nvm::RegisteredControllerExtended],
82) -> Result<pr::ReservationReport, InvalidReservationType> {
83    let reservation_type = if report_header.rtype.0 != 0 {
84        Some(from_nvme_reservation_type(report_header.rtype)?)
85    } else {
86        None
87    };
88
89    let controllers = controllers
90        .iter()
91        .map(|controller| pr::RegisteredController {
92            key: controller.rkey,
93            holds_reservation: controller.rcsts.holds_reservation(),
94            controller_id: controller.cntlid,
95            host_id: controller.hostid.to_vec(),
96        })
97        .collect();
98
99    let report = pr::ReservationReport {
100        generation: report_header.generation,
101        reservation_type,
102        controllers,
103        persist_through_power_loss: report_header.ptpls != 0,
104    };
105
106    Ok(report)
107}