tape: add functions to parse drive device activity

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@proxmox.com>
This commit is contained in:
Dominik Csapak 2024-05-13 12:49:23 +02:00 committed by Dietmar Maurer
parent 4b21a00744
commit 3f1a084b90
4 changed files with 171 additions and 4 deletions

View File

@ -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),
})
}
}

View File

@ -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::*;

View File

@ -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)
}

View File

@ -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> {