2 use std
::os
::unix
::io
::AsRawFd
;
4 use anyhow
::{bail, format_err, Error}
;
5 use endian_trait
::Endian
;
7 use proxmox_io
::ReadExt
;
9 use pbs_api_types
::Lp17VolumeStatistics
;
11 use crate::sgutils2
::SgRaw
;
13 /// SCSI command to query volume statistics
15 /// CDB: LOG SENSE / LP17h Volume Statistics
17 /// The Volume Statistics log page is included in Ultrium 5 and later
19 pub fn read_volume_statistics
<F
: AsRawFd
>(file
: &mut F
) -> Result
<Lp17VolumeStatistics
, Error
> {
21 let data
= sg_read_volume_statistics(file
)?
;
23 decode_volume_statistics(&data
)
26 fn sg_read_volume_statistics
<F
: AsRawFd
>(file
: &mut F
) -> Result
<Vec
<u8>, Error
> {
28 let alloc_len
: u16 = 8192;
29 let mut sg_raw
= SgRaw
::new(file
, alloc_len
as usize)?
;
31 let mut cmd
= Vec
::new();
32 cmd
.push(0x4D); // LOG SENSE
34 cmd
.push((1<<6) | 0x17); // Volume Statistics log page
35 cmd
.push(0); // Subpage 0
39 cmd
.extend(&alloc_len
.to_be_bytes()); // alloc len
40 cmd
.push(0u8); // control byte
42 sg_raw
.do_command(&cmd
)
43 .map_err(|err
| format_err
!("read tape volume statistics failed - {}", err
))
49 struct LpParameterHeader
{
55 fn decode_volume_statistics(data
: &[u8]) -> Result
<Lp17VolumeStatistics
, Error
> {
58 let read_be_counter
= |reader
: &mut &[u8], len
: u8| {
59 let len
= len
as usize;
60 if len
== 0 || len
> 8 {
61 bail
!("invalid conter size '{}'", len
);
63 let mut buffer
= [0u8; 8];
64 reader
.read_exact(&mut buffer
[..len
])?
;
69 .fold(0, |value
, curr
| (value
<< 8) | *curr
as u64);
74 proxmox_lang
::try_block
!({
75 if !((data
[0] & 0x7f) == 0x17 && data
[1] == 0) {
76 bail
!("invalid response");
79 let mut reader
= &data
[2..];
81 let page_len
: u16 = unsafe { reader.read_be_value()? }
;
83 let page_len
= page_len
as usize;
85 if (page_len
+ 4) > data
.len() {
86 bail
!("invalid page length");
88 // Note: Quantum hh7 returns the allocation_length instead of real data_len
89 reader
= &data
[4..page_len
+4];
92 let mut stat
= Lp17VolumeStatistics
::default();
93 let mut page_valid
= false;
96 if reader
.is_empty() {
99 let head
: LpParameterHeader
= unsafe { reader.read_be_value()? }
;
101 match head
.parameter_code
{
103 let value
: u64 = read_be_counter(&mut reader
, head
.parameter_len
)?
;
105 bail
!("page-valid flag not set");
111 read_be_counter(&mut reader
, head
.parameter_len
)?
;
114 stat
.volume_datasets_written
=
115 read_be_counter(&mut reader
, head
.parameter_len
)?
;
118 stat
.volume_recovered_write_data_errors
=
119 read_be_counter(&mut reader
, head
.parameter_len
)?
;
122 stat
.volume_unrecovered_write_data_errors
=
123 read_be_counter(&mut reader
, head
.parameter_len
)?
;
126 stat
.volume_write_servo_errors
=
127 read_be_counter(&mut reader
, head
.parameter_len
)?
;
130 stat
.volume_unrecovered_write_servo_errors
=
131 read_be_counter(&mut reader
, head
.parameter_len
)?
;
134 stat
.volume_datasets_read
=
135 read_be_counter(&mut reader
, head
.parameter_len
)?
;
138 stat
.volume_recovered_read_errors
=
139 read_be_counter(&mut reader
, head
.parameter_len
)?
;
142 stat
.volume_unrecovered_read_errors
=
143 read_be_counter(&mut reader
, head
.parameter_len
)?
;
146 stat
.last_mount_unrecovered_write_errors
=
147 read_be_counter(&mut reader
, head
.parameter_len
)?
;
150 stat
.last_mount_unrecovered_read_errors
=
151 read_be_counter(&mut reader
, head
.parameter_len
)?
;
154 stat
.last_mount_bytes_written
=
155 read_be_counter(&mut reader
, head
.parameter_len
)?
* 1_000_000;
158 stat
.last_mount_bytes_read
=
159 read_be_counter(&mut reader
, head
.parameter_len
)?
* 1_000_000;
162 stat
.lifetime_bytes_written
=
163 read_be_counter(&mut reader
, head
.parameter_len
)?
* 1_000_000;
166 stat
.lifetime_bytes_read
=
167 read_be_counter(&mut reader
, head
.parameter_len
)?
* 1_000_000;
170 stat
.last_load_write_compression_ratio
=
171 read_be_counter(&mut reader
, head
.parameter_len
)?
;
174 stat
.last_load_read_compression_ratio
=
175 read_be_counter(&mut reader
, head
.parameter_len
)?
;
178 stat
.medium_mount_time
=
179 read_be_counter(&mut reader
, head
.parameter_len
)?
;
182 stat
.medium_ready_time
=
183 read_be_counter(&mut reader
, head
.parameter_len
)?
;
186 stat
.total_native_capacity
=
187 read_be_counter(&mut reader
, head
.parameter_len
)?
* 1_000_000;
190 stat
.total_used_native_capacity
=
191 read_be_counter(&mut reader
, head
.parameter_len
)?
* 1_000_000;
194 let data
= reader
.read_exact_allocated(head
.parameter_len
as usize)?
;
195 stat
.serial
= String
::from_utf8_lossy(&data
).to_string();
198 let value
= read_be_counter(&mut reader
, head
.parameter_len
)?
;
200 stat
.write_protect
= true;
204 let value
= read_be_counter(&mut reader
, head
.parameter_len
)?
;
210 stat
.beginning_of_medium_passes
=
211 read_be_counter(&mut reader
, head
.parameter_len
)?
;
214 stat
.middle_of_tape_passes
=
215 read_be_counter(&mut reader
, head
.parameter_len
)?
;
218 reader
.read_exact_allocated(head
.parameter_len
as usize)?
;
224 bail
!("missing page-valid parameter");
229 }).map_err(|err
| format_err
!("decode volume statistics failed - {}", err
))