disk_backend/pr.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Persistent reservation support.
5
6use crate::DiskError;
7use inspect::Inspect;
8
9/// Trait implemented by disks that support SCSI-style persistent reservations.
10#[async_trait::async_trait]
11pub trait PersistentReservation: Sync {
12 /// Returns the disk's capabilities.
13 fn capabilities(&self) -> ReservationCapabilities;
14
15 /// Returns a report of the current registration and reservation state.
16 async fn report(&self) -> Result<ReservationReport, DiskError>;
17
18 /// Updates the registration for this client.
19 ///
20 /// If the current key does not match `old_key`, then fails with
21 /// [`DiskError::ReservationConflict`]. If `old_key` is `None`, then update
22 /// the registration regardless.
23 ///
24 /// If `new_key` is 0, then remove the registration.
25 ///
26 /// `ptpl` provides an optional new state for "persist through power loss".
27 async fn register(
28 &self,
29 current_key: Option<u64>,
30 new_key: u64,
31 ptpl: Option<bool>,
32 ) -> Result<(), DiskError>;
33
34 /// Creates a reservation for this client with type `reservation_type`.
35 ///
36 /// Fails with [`DiskError::ReservationConflict`] if there is a key mismatch.
37 async fn reserve(&self, key: u64, reservation_type: ReservationType) -> Result<(), DiskError>;
38
39 /// Releases the reservation for this client with type `reservation_type`.
40 ///
41 /// Fails with [`DiskError::ReservationConflict`] if there is a key or type
42 /// mismatch.
43 async fn release(&self, key: u64, reservation_type: ReservationType) -> Result<(), DiskError>;
44
45 /// Clears any reservation and registration for this client.
46 ///
47 /// Fails with [`DiskError::ReservationConflict`] if there is a key mismatch.
48 async fn clear(&self, key: u64) -> Result<(), DiskError>;
49
50 /// Preempts an existing reservation. See the SCSI spec for the precise
51 /// behavior of this.
52 async fn preempt(
53 &self,
54 current_key: u64,
55 preempt_key: u64,
56 reservation_type: ReservationType,
57 abort: bool,
58 ) -> Result<(), DiskError>;
59}
60
61/// Capabilities returned by [`PersistentReservation::capabilities`].
62///
63/// These bits correspond to values in [`ReservationType`].
64#[expect(missing_docs)] // TODO
65pub struct ReservationCapabilities {
66 pub write_exclusive: bool,
67 pub exclusive_access: bool,
68 pub write_exclusive_registrants_only: bool,
69 pub exclusive_access_registrants_only: bool,
70 pub write_exclusive_all_registrants: bool,
71 pub exclusive_access_all_registrants: bool,
72 pub persist_through_power_loss: bool,
73}
74
75/// The reservation type.
76///
77/// These are defined in the SCSI spec.
78#[derive(Debug, Copy, Clone, PartialEq, Eq, Inspect)]
79#[expect(missing_docs)] // TODO
80pub enum ReservationType {
81 WriteExclusive,
82 ExclusiveAccess,
83 WriteExclusiveRegistrantsOnly,
84 ExclusiveAccessRegistrantsOnly,
85 WriteExclusiveAllRegistrants,
86 ExclusiveAccessAllRegistrants,
87}
88
89/// A registered controller.
90#[derive(Debug, Clone)]
91pub struct RegisteredController {
92 /// The registration key.
93 pub key: u64,
94 /// The host ID of the client.
95 pub host_id: Vec<u8>,
96 /// The controller ID within the host.
97 pub controller_id: u16,
98 /// If true, the controller holds the reservation.
99 pub holds_reservation: bool,
100}
101
102/// The report returned by [`PersistentReservation::report`].
103#[derive(Debug, Clone)]
104pub struct ReservationReport {
105 /// A counter that increases every time a registration changes.
106 pub generation: u32,
107 /// The current reservation type for the disk.
108 pub reservation_type: Option<ReservationType>,
109 /// The persist through power loss state.
110 pub persist_through_power_loss: bool,
111 /// The registered controllers.
112 pub controllers: Vec<RegisteredController>,
113}