[pbs-devel] [PATCH proxmox-backup 1/4] tape: add functions to parse drive device activity

Dominik Csapak d.csapak at proxmox.com
Mon May 13 12:49:23 CEST 2024


we use the VHF part from the DT Device Activity page for that.
This is intended to query the drive for it's current state and activity.

Currently only the activity is parsed and used.

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 pbs-api-types/src/tape/drive.rs           | 65 +++++++++++++++
 pbs-tape/src/sg_tape.rs                   |  3 +
 pbs-tape/src/sg_tape/device_status.rs     | 99 +++++++++++++++++++++++
 pbs-tape/src/sg_tape/volume_statistics.rs |  8 +-
 4 files changed, 171 insertions(+), 4 deletions(-)
 create mode 100644 pbs-tape/src/sg_tape/device_status.rs

diff --git a/pbs-api-types/src/tape/drive.rs b/pbs-api-types/src/tape/drive.rs
index 626c5d9c3..de569980a 100644
--- a/pbs-api-types/src/tape/drive.rs
+++ b/pbs-api-types/src/tape/drive.rs
@@ -276,3 +276,68 @@ pub struct Lp17VolumeStatistics {
     /// Volume serial number
     pub serial: String,
 }
+
+/// The DT Device Activity from DT Device Status LP page
+#[api]
+#[derive(Copy, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum DeviceActivity {
+    /// No activity
+    NoActivity,
+    /// Cleaning
+    Cleaning,
+    /// Loading
+    Loading,
+    /// Unloading
+    Unloading,
+    /// Other unspecified activity
+    Other,
+    /// Reading
+    Reading,
+    /// Writing
+    Writing,
+    /// Locating
+    Locating,
+    /// Rewinding
+    Rewinding,
+    /// Erasing
+    Erasing,
+    /// Formatting
+    Formatting,
+    /// Calibrating
+    Calibrating,
+    /// Other (DT)
+    OtherDT,
+    /// Updating microcode
+    MicrocodeUpdate,
+    /// Reading encrypted data
+    ReadingEncrypted,
+    /// Writing encrypted data
+    WritingEncrypted,
+}
+
+impl TryFrom<u8> for DeviceActivity {
+    type Error = Error;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        Ok(match value {
+            0x00 => DeviceActivity::NoActivity,
+            0x01 => DeviceActivity::Cleaning,
+            0x02 => DeviceActivity::Loading,
+            0x03 => DeviceActivity::Unloading,
+            0x04 => DeviceActivity::Other,
+            0x05 => DeviceActivity::Reading,
+            0x06 => DeviceActivity::Writing,
+            0x07 => DeviceActivity::Locating,
+            0x08 => DeviceActivity::Rewinding,
+            0x09 => DeviceActivity::Erasing,
+            0x0A => DeviceActivity::Formatting,
+            0x0B => DeviceActivity::Calibrating,
+            0x0C => DeviceActivity::OtherDT,
+            0x0D => DeviceActivity::MicrocodeUpdate,
+            0x0E => DeviceActivity::ReadingEncrypted,
+            0x0F => DeviceActivity::WritingEncrypted,
+            other => bail!("invalid DT device activity value: {:x}", other),
+        })
+    }
+}
diff --git a/pbs-tape/src/sg_tape.rs b/pbs-tape/src/sg_tape.rs
index 146d05507..85be6b071 100644
--- a/pbs-tape/src/sg_tape.rs
+++ b/pbs-tape/src/sg_tape.rs
@@ -15,6 +15,9 @@ mod volume_statistics;
 use proxmox_uuid::Uuid;
 pub use volume_statistics::*;
 
+mod device_status;
+pub use device_status::*;
+
 mod tape_alert_flags;
 pub use tape_alert_flags::*;
 
diff --git a/pbs-tape/src/sg_tape/device_status.rs b/pbs-tape/src/sg_tape/device_status.rs
new file mode 100644
index 000000000..353ba0a7b
--- /dev/null
+++ b/pbs-tape/src/sg_tape/device_status.rs
@@ -0,0 +1,99 @@
+use std::os::fd::AsRawFd;
+
+use anyhow::{bail, format_err, Error};
+
+use pbs_api_types::DeviceActivity;
+use proxmox_io::ReadExt;
+
+use super::LpParameterHeader;
+use crate::sgutils2::SgRaw;
+
+/// SCSI command to query volume statistics
+///
+/// CDB: LOG SENSE / LP11h DT Device Activity
+///
+/// Only returns the Device Activity result from the VHF data
+pub fn read_device_activity<F: AsRawFd>(file: &mut F) -> Result<DeviceActivity, Error> {
+    let data = sg_read_dt_device_status(file)?;
+
+    decode_dt_device_status(&data)
+        .map_err(|err| format_err!("decode dt device status failed - {}", err))
+}
+
+#[allow(clippy::vec_init_then_push)]
+fn sg_read_dt_device_status<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> {
+    let alloc_len: u16 = 8192;
+    let mut sg_raw = SgRaw::new(file, alloc_len as usize)?;
+
+    let mut cmd = Vec::new();
+    cmd.push(0x4D); // LOG SENSE
+    cmd.push(0);
+    cmd.push((1 << 6) | 0x11); // DT Device Status log page
+    cmd.push(0); // Subpage 0
+    cmd.push(0);
+    cmd.push(0);
+    cmd.push(0);
+    cmd.extend(alloc_len.to_be_bytes()); // alloc len
+    cmd.push(0u8); // control byte
+
+    sg_raw.set_timeout(1); // use short timeout
+    sg_raw
+        .do_command(&cmd)
+        .map_err(|err| format_err!("read tape dt device status failed - {}", err))
+        .map(|v| v.to_vec())
+}
+
+fn decode_dt_device_status(data: &[u8]) -> Result<DeviceActivity, Error> {
+    if !((data[0] & 0x7f) == 0x11 && data[1] == 0) {
+        bail!("invalid response");
+    }
+
+    let mut reader = &data[2..];
+
+    let page_len: u16 = unsafe { reader.read_be_value()? };
+
+    let page_len = page_len as usize;
+
+    if (page_len + 4) > data.len() {
+        bail!("invalid page length");
+    } else {
+        // Note: Quantum hh7 returns the allocation_length instead of real data_len
+        reader = &data[4..page_len + 4];
+    }
+
+    let mut page_valid = false;
+
+    let mut activity = DeviceActivity::Other;
+
+    loop {
+        if reader.is_empty() {
+            break;
+        }
+        let head: LpParameterHeader = unsafe { reader.read_be_value()? };
+
+        match head.parameter_code {
+            0x0000 => {
+                let vhf_descriptor = reader.read_exact_allocated(head.parameter_len as usize)?;
+
+                if vhf_descriptor.len() != 4 {
+                    bail!("invalid VHF data descriptor");
+                }
+
+                activity = vhf_descriptor[2].try_into()?;
+
+                if vhf_descriptor[0] & 0x01 == 1 {
+                    page_valid = true;
+                }
+            }
+            _ => {
+                reader.read_exact_allocated(head.parameter_len as usize)?;
+            }
+        }
+    }
+
+    if !page_valid {
+        bail!("missing page-valid parameter");
+    }
+
+    Ok(activity)
+}
diff --git a/pbs-tape/src/sg_tape/volume_statistics.rs b/pbs-tape/src/sg_tape/volume_statistics.rs
index f27a682c1..d3815db09 100644
--- a/pbs-tape/src/sg_tape/volume_statistics.rs
+++ b/pbs-tape/src/sg_tape/volume_statistics.rs
@@ -46,10 +46,10 @@ fn sg_read_volume_statistics<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error>
 
 #[repr(C, packed)]
 #[derive(Endian)]
-struct LpParameterHeader {
-    parameter_code: u16,
-    control: u8,
-    parameter_len: u8,
+pub(crate) struct LpParameterHeader {
+    pub parameter_code: u16,
+    pub control: u8,
+    pub parameter_len: u8,
 }
 
 fn decode_volume_statistics(data: &[u8]) -> Result<Lp17VolumeStatistics, Error> {
-- 
2.39.2





More information about the pbs-devel mailing list