1#![expect(missing_docs)]
30#![forbid(unsafe_code)]
31
32pub mod atapi_scsi;
33mod getlbastatus;
34mod inquiry;
35mod reservation;
36pub mod resolver;
37pub mod scsidvd;
38mod unmap;
39
40#[cfg(test)]
41mod tests;
42
43pub use inquiry::INQUIRY_DATA_TEMPLATE;
44
45use disk_backend::Disk;
46use disk_backend::DiskError;
47use disk_backend::UnmapBehavior;
48use guestmem::AccessError;
49use guestmem::MemoryRead;
50use guestmem::MemoryWrite;
51use guid::Guid;
52use inspect::Inspect;
53use parking_lot::Mutex;
54use scsi::AdditionalSenseCode;
55use scsi::ScsiOp;
56use scsi::ScsiStatus;
57use scsi::SenseKey;
58use scsi::srb::SrbStatus;
59use scsi_buffers::RequestBuffers;
60use scsi_core::ASYNC_SCSI_DISK_STACK_SIZE;
61use scsi_core::AsyncScsiDisk;
62use scsi_core::Request;
63use scsi_core::ScsiResult;
64use scsi_core::ScsiSaveRestore;
65use scsi_core::save_restore::SavedSenseData;
66use scsi_core::save_restore::ScsiDiskSavedState;
67use scsi_core::save_restore::ScsiSavedState;
68use scsi_defs as scsi;
69use scsidisk_resources::DiskIdentity;
70use scsidisk_resources::DiskParameters;
71use stackfuture::StackFuture;
72use std::fmt::Debug;
73use std::sync::atomic::AtomicBool;
74use std::sync::atomic::AtomicU64;
75use std::sync::atomic::Ordering;
76use thiserror::Error;
77use tracing::Instrument;
78use tracing_helpers::ErrorValueExt;
79use unmap::validate_lba_range;
80use vmcore::save_restore::RestoreError;
81use vmcore::save_restore::SaveError;
82use zerocopy::FromBytes;
83use zerocopy::FromZeros;
84use zerocopy::IntoBytes;
85
86const UNMAP_RANGE_DESCRIPTOR_COUNT_MAX: u16 = 4096;
87const VHDMP_MAX_WRITE_SAME_LENGTH_BYTES: u64 = 8 * 1024 * 1024; impl ScsiSaveRestore for SimpleScsiDisk {
90 fn save(&self) -> Result<Option<ScsiSavedState>, SaveError> {
91 let sense = self.sense_data.get();
92 let sense_data = sense.map(|sense| SavedSenseData {
93 sense_key: sense.header.sense_key.0,
94 additional_sense_code: sense.additional_sense_code.0,
95 additional_sense_code_qualifier: sense.additional_sense_code_qualifier,
96 });
97 Ok(Some(ScsiSavedState::ScsiDisk(ScsiDiskSavedState {
98 sector_count: self.last_sector_count.load(Ordering::Relaxed),
99 sense_data,
100 })))
101 }
102
103 fn restore(&self, state: &ScsiSavedState) -> Result<(), RestoreError> {
104 if let ScsiSavedState::ScsiDisk(disk_state) = state {
105 let ScsiDiskSavedState {
106 sector_count,
107 sense_data,
108 } = *disk_state;
109
110 self.sense_data.set(
112 sense_data
113 .map(|sense| {
114 scsi::SenseData::new(
115 SenseKey(sense.sense_key),
116 AdditionalSenseCode(sense.additional_sense_code),
117 sense.additional_sense_code_qualifier,
118 )
119 })
120 .as_ref(),
121 );
122
123 self.last_sector_count
124 .store(sector_count, Ordering::Relaxed);
125 Ok(())
126 } else {
127 Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
128 "saved state didn't match expected format ScsiDiskSavedState"
129 )))
130 }
131 }
132}
133
134pub struct SimpleScsiDisk {
135 disk: Disk,
136 sector_shift: u8,
137 physical_extra_shift: u8,
138 sector_size: u32,
139 sense_data: SenseDataSlot,
140 scsi_parameters: ScsiParameters,
141 support_pr: bool,
142 last_sector_count: AtomicU64,
143}
144
145#[derive(Debug, Clone, Inspect)]
146struct ScsiParameters {
147 disk_id: [u8; 16],
148 physical_sector_size: u32,
149 support_fua: bool,
150 write_cache_enabled: bool,
151 support_odx: bool,
152 support_unmap: bool,
153 support_get_lba_status: bool,
154 maximum_transfer_length: usize,
155 identity: DiskIdentity,
156 serial_number: Vec<u8>,
157 medium_rotation_rate: u16,
158 optimal_unmap_sectors: u32,
159}
160
161impl SimpleScsiDisk {
162 pub fn new(disk: Disk, disk_parameters: DiskParameters) -> Self {
163 let sector_size = disk.sector_size();
164 let sector_shift = sector_size.trailing_zeros() as u8;
165 let mut sector_count = disk.sector_count();
166
167 if let Some(size) = disk_parameters.scsi_disk_size_in_bytes {
169 sector_count = sector_count.min(size >> sector_shift);
170 }
171
172 let scsi_parameters = {
175 let DiskParameters {
176 disk_id,
177 identity,
178 serial_number,
179 medium_rotation_rate,
180 physical_sector_size,
181 fua,
182 write_cache,
183 scsi_disk_size_in_bytes: _,
184 odx,
185 unmap,
186 max_transfer_length,
187 optimal_unmap_sectors,
188 get_lba_status,
189 } = disk_parameters;
190
191 fn nonzero_id(id: [u8; 16]) -> Option<[u8; 16]> {
192 if id == [0; 16] { None } else { Some(id) }
193 }
194
195 let disk_id = disk_id
198 .and_then(nonzero_id)
199 .or_else(|| disk.disk_id().and_then(nonzero_id))
200 .unwrap_or_else(|| Guid::new_random().into());
201
202 ScsiParameters {
203 disk_id,
204 physical_sector_size: physical_sector_size
205 .unwrap_or_else(|| disk.physical_sector_size()),
206 support_fua: fua.unwrap_or_else(|| disk.is_fua_respected()),
207 write_cache_enabled: write_cache.unwrap_or(true),
208 support_odx: odx.unwrap_or(false),
209 support_get_lba_status: get_lba_status,
210 support_unmap: unmap.unwrap_or(disk.unmap_behavior() != UnmapBehavior::Ignored),
211 maximum_transfer_length: max_transfer_length.unwrap_or(8 * 1024 * 1024),
212 identity: identity.unwrap_or_else(DiskIdentity::msft),
213 serial_number,
214 medium_rotation_rate: medium_rotation_rate.unwrap_or(1), optimal_unmap_sectors: optimal_unmap_sectors.unwrap_or(1),
216 }
217 };
218
219 let physical_extra_shift =
220 scsi_parameters.physical_sector_size.trailing_zeros() as u8 - sector_shift;
221 let support_pr = disk.pr().is_some();
222
223 SimpleScsiDisk {
224 disk,
225 sector_shift,
226 physical_extra_shift,
227 sector_size,
228 sense_data: Default::default(),
229 scsi_parameters,
230 support_pr,
231 last_sector_count: AtomicU64::new(sector_count),
232 }
233 }
234}
235
236#[derive(Error, Debug)]
237enum ScsiError {
238 #[error("memory access error")]
239 MemoryAccess(#[source] AccessError),
240 #[error("illegal request, asc: {0:?}")]
241 IllegalRequest(AdditionalSenseCode),
242 #[error("data overrun")]
243 DataOverrun,
244 #[error("srb generic error")]
245 SrbError,
246 #[error("device is write protected")]
247 WriteProtected,
248 #[error("disk io error")]
249 Disk(#[source] DiskError),
250 #[error("pending unit attention")]
251 UnitAttention,
252 #[error("unsupported mode page code: page control {0} page code {1}")]
253 UnsupportedModePageCode(u8, u8),
254 #[error("unsupported vpd page code: {0}")]
255 UnsupportedVpdPageCode(u8),
256 #[error("unsupported service action: {0}")]
257 UnsupportedServiceAction(u8),
258}
259
260struct RequestParameters {
261 tx: usize,
262 offset: u64,
263 fua: bool,
264}
265
266struct WriteSameParameters {
267 lba_count: usize,
268 start_lba: u64,
269 fua: bool,
270 sector_size: usize,
271 tx: usize,
272}
273
274const MODE_CACHING_PAGE_SIZE: usize = size_of::<scsi::ModeCachingPage>();
275const MODE_PARAMETER_HEADER_SIZE: usize = size_of::<scsi::ModeParameterHeader>();
276const MODE_PARAMETER_HEADER10_SIZE: usize = size_of::<scsi::ModeParameterHeader10>();
277const MODE_DATA_LENGTH10: u16 = (MODE_PARAMETER_HEADER10_SIZE + MODE_CACHING_PAGE_SIZE - 2) as u16;
278const MODE_DATA_LENGTH: u8 = (MODE_PARAMETER_HEADER_SIZE + MODE_CACHING_PAGE_SIZE - 1) as u8;
279
280pub fn illegal_request_sense(sense_code: AdditionalSenseCode) -> scsi::SenseData {
281 match sense_code {
282 AdditionalSenseCode::ILLEGAL_COMMAND
283 | AdditionalSenseCode::INVALID_CDB
284 | AdditionalSenseCode::NO_SENSE
285 | AdditionalSenseCode::INVALID_FIELD_PARAMETER_LIST
286 | AdditionalSenseCode::PARAMETER_LIST_LENGTH
287 | AdditionalSenseCode::ILLEGAL_BLOCK => {
288 scsi::SenseData::new(SenseKey::ILLEGAL_REQUEST, sense_code, 0)
289 }
290 _ => unreachable!(),
291 }
292}
293
294impl SimpleScsiDisk {
295 fn handle_request_sense(
296 &self,
297 external_data: &RequestBuffers<'_>,
298 request: &Request,
299 unit_attention: bool,
300 ) -> Result<usize, ScsiError> {
301 let cdb = scsi::CdbInquiry::read_from_prefix(&request.cdb[..])
302 .unwrap()
303 .0; let allocation_length = cdb.allocation_length.get() as usize;
305
306 let min = size_of::<scsi::SenseDataHeader>();
307 if allocation_length < min || allocation_length > external_data.len() {
308 tracelimit::error_ratelimited!(
309 allocation_length,
310 min,
311 external_data_len = external_data.len(),
312 "srb error"
313 );
314 return Err(ScsiError::SrbError);
315 }
316
317 let sense = if unit_attention {
318 scsi::SenseData::new(
319 SenseKey::UNIT_ATTENTION,
320 AdditionalSenseCode::PARAMETERS_CHANGED,
321 scsi::SCSI_SENSEQ_CAPACITY_DATA_CHANGED,
322 )
323 } else {
324 self.sense_data.take().unwrap_or_else(|| {
325 scsi::SenseData::new(SenseKey::NO_SENSE, AdditionalSenseCode::NO_SENSE, 0x00)
326 })
327 };
328
329 let tx = std::cmp::min(allocation_length, size_of::<scsi::SenseData>());
330 external_data
331 .writer()
332 .write(&sense.as_bytes()[..tx])
333 .map_err(ScsiError::MemoryAccess)?;
334
335 Ok(tx)
336 }
337
338 fn handle_mode_select(
339 &self,
340 external_data: &RequestBuffers<'_>,
341 request: &Request,
342 ) -> Result<usize, ScsiError> {
343 let is_mode_select_10 = request.scsiop() == ScsiOp::MODE_SELECT10;
344 let request_length;
345 let header_size;
346 let is_spbit_set;
347 if is_mode_select_10 {
348 let cdb = scsi::ModeSelect10::read_from_prefix(&request.cdb[..])
349 .unwrap()
350 .0; request_length = cdb.parameter_list_length.get() as usize;
352 header_size = MODE_PARAMETER_HEADER10_SIZE;
353 is_spbit_set = cdb.flags.spbit();
354 } else {
355 let cdb = scsi::ModeSelect::read_from_prefix(&request.cdb[..])
356 .unwrap()
357 .0; request_length = cdb.parameter_list_length as usize;
359 header_size = MODE_PARAMETER_HEADER_SIZE;
360 is_spbit_set = cdb.flags.spbit();
361 }
362
363 if request_length == 0 {
364 return Ok(0);
365 }
366
367 let min = header_size + MODE_CACHING_PAGE_SIZE;
369 if request_length != external_data.len() || request_length < min {
370 tracelimit::error_ratelimited!(
371 request_length,
372 external_data = external_data.len(),
373 min,
374 "invalid parameter list length"
375 );
376 return Err(ScsiError::IllegalRequest(
377 AdditionalSenseCode::PARAMETER_LIST_LENGTH,
378 ));
379 }
380
381 if is_spbit_set {
383 tracing::debug!("doesn't support saving pages");
384 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
385 }
386
387 let mut buffer: Vec<u8> = vec![0; request_length];
388 external_data
389 .reader()
390 .read(&mut buffer)
391 .map_err(ScsiError::MemoryAccess)?;
392
393 let block_descriptor_length = if is_mode_select_10 {
394 let temp10 = scsi::ModeParameterHeader10::read_from_prefix(
395 &buffer[..MODE_PARAMETER_HEADER10_SIZE],
396 )
397 .unwrap()
398 .0; usize::from(temp10.block_descriptor_length)
400 } else {
401 let temp =
402 scsi::ModeParameterHeader::read_from_prefix(&buffer[..MODE_PARAMETER_HEADER_SIZE])
403 .unwrap()
404 .0; temp.block_descriptor_length as usize
406 };
407
408 let skipped = header_size + block_descriptor_length;
410 let min = skipped + MODE_CACHING_PAGE_SIZE;
411 if request_length < min {
412 tracelimit::error_ratelimited!(request_length, min, "invalid parameter list length");
413 return Err(ScsiError::IllegalRequest(
414 AdditionalSenseCode::PARAMETER_LIST_LENGTH,
415 ));
416 }
417
418 let page = scsi::ModeCachingPage::read_from_prefix(
420 &buffer[skipped..skipped + MODE_CACHING_PAGE_SIZE],
421 )
422 .unwrap()
423 .0; if page.page_code != scsi::MODE_PAGE_CACHING
425 || (page.page_length as usize) < MODE_CACHING_PAGE_SIZE
426 || ((page.flags & scsi::MODE_CACHING_WRITE_CACHE_ENABLE == 0)
427 && self.scsi_parameters.write_cache_enabled)
428 || ((page.flags & scsi::MODE_CACHING_WRITE_CACHE_ENABLE != 0)
429 && !self.scsi_parameters.write_cache_enabled)
430 {
431 tracing::debug!(
444 page_code = page.page_code,
445 page_length = page.page_length,
446 flags = page.flags,
447 write_cache_enabled = self.scsi_parameters.write_cache_enabled,
448 "invalid parameter list"
449 );
450 return Err(ScsiError::IllegalRequest(
451 AdditionalSenseCode::INVALID_FIELD_PARAMETER_LIST,
452 ));
453 }
454
455 Ok(request_length)
456 }
457
458 fn handle_mode_sense(
459 &self,
460 external_data: &RequestBuffers<'_>,
461 request: &Request,
462 ) -> Result<usize, ScsiError> {
463 if external_data.is_empty() {
464 return Ok(0);
465 }
466
467 let is_mode_sense_10 = request.scsiop() == ScsiOp::MODE_SENSE10;
468 let page_code;
469 let page_control;
470 let allocation_length;
471 let header_size;
472 if is_mode_sense_10 {
473 let cdb = scsi::ModeSense10::read_from_prefix(&request.cdb[..])
474 .unwrap()
475 .0; allocation_length = cdb.allocation_length.get() as usize;
477 page_code = cdb.flags2.page_code();
478 page_control = cdb.flags2.pc() << 6;
479 header_size = MODE_PARAMETER_HEADER10_SIZE;
480 } else {
481 let cdb = scsi::ModeSense::read_from_prefix(&request.cdb[..])
482 .unwrap()
483 .0; allocation_length = cdb.allocation_length as usize;
485 page_code = cdb.flags2.page_code();
486 page_control = cdb.flags2.pc() << 6;
487 header_size = MODE_PARAMETER_HEADER_SIZE;
488 }
489
490 if allocation_length == 0 {
492 return Ok(0);
493 }
494
495 if allocation_length > external_data.len() || allocation_length < header_size {
498 tracelimit::error_ratelimited!(
499 allocation_length,
500 external_data = external_data.len(),
501 header_size,
502 "invalid cdb"
503 );
504 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
505 }
506
507 if page_control == scsi::MODE_CONTROL_SAVED_VALUES
508 || (page_code != scsi::MODE_PAGE_CACHING && page_code != scsi::MODE_PAGE_ALL)
509 {
510 return Err(ScsiError::UnsupportedModePageCode(page_control, page_code));
511 }
512
513 let mut dsp = 0;
514 if self.disk.is_read_only() {
515 dsp |= scsi::MODE_DSP_WRITE_PROTECT;
516 }
517
518 if self.scsi_parameters.support_fua {
519 dsp |= scsi::MODE_DSP_FUA_SUPPORTED;
520 }
521
522 let temp;
523 let temp10;
524 let header = if is_mode_sense_10 {
525 temp10 = scsi::ModeParameterHeader10 {
526 mode_data_length: MODE_DATA_LENGTH10.into(),
527 device_specific_parameter: dsp,
528 ..FromZeros::new_zeroed()
529 };
530 temp10.as_bytes()
531 } else {
532 temp = scsi::ModeParameterHeader {
533 mode_data_length: MODE_DATA_LENGTH,
534 device_specific_parameter: dsp,
535 ..FromZeros::new_zeroed()
536 };
537 temp.as_bytes()
538 };
539
540 let mut page = scsi::ModeCachingPage {
541 page_code: scsi::MODE_PAGE_CACHING,
542 page_length: (MODE_CACHING_PAGE_SIZE - 2) as u8,
543 ..FromZeros::new_zeroed()
544 };
545
546 if (page_control == scsi::MODE_CONTROL_CURRENT_VALUES
547 || page_control == scsi::MODE_CONTROL_DEFAULT_VALUES)
548 && external_data.len() - header_size >= scsi::WRITE_CACHE_ENABLE_BYTE_OFFSET
549 {
550 if self.scsi_parameters.write_cache_enabled {
551 page.flags |= scsi::MODE_CACHING_WRITE_CACHE_ENABLE;
552 }
553 }
554
555 let mut data = [0; MODE_PARAMETER_HEADER10_SIZE + MODE_CACHING_PAGE_SIZE];
557 data[..header_size].copy_from_slice(header);
558 data[header_size..header_size + MODE_CACHING_PAGE_SIZE].copy_from_slice(page.as_bytes());
559 let tx = std::cmp::min(allocation_length, header_size + MODE_CACHING_PAGE_SIZE);
560 external_data
561 .writer()
562 .write(&data[..tx])
563 .map_err(ScsiError::MemoryAccess)?;
564
565 Ok(tx)
566 }
567
568 fn handle_service_action_in16(
569 &self,
570 external_data: &RequestBuffers<'_>,
571 request: &Request,
572 sector_count: u64,
573 ) -> Result<usize, ScsiError> {
574 let cdb = scsi::ServiceActionIn16::read_from_prefix(&request.cdb[..])
575 .unwrap()
576 .0; match cdb.service_action & 0x1f {
578 scsi::SERVICE_ACTION_READ_CAPACITY16 => {
579 let min = size_of::<scsi::ReadCapacityDataEx>();
580 if external_data.len() < min {
581 tracelimit::error_ratelimited!(len = external_data.len(), min, "data overrun");
582 return Err(ScsiError::DataOverrun);
583 }
584
585 let mut data = scsi::ReadCapacity16Data {
586 ex: scsi::ReadCapacityDataEx {
587 logical_block_address: (sector_count - 1).into(),
590 bytes_per_block: (1u32 << self.sector_shift).into(),
591 },
592 exponents: self.physical_extra_shift,
593 ..FromZeros::new_zeroed()
594 };
595
596 if self.scsi_parameters.support_unmap {
597 data.lowest_aligned_block_msb |= scsi::READ_CAPACITY16_LBPME;
601 }
602
603 let tx = std::cmp::min(external_data.len(), size_of::<scsi::ReadCapacity16Data>());
604 external_data
605 .writer()
606 .write(&data.as_bytes()[..tx])
607 .map_err(ScsiError::MemoryAccess)?;
608
609 Ok(tx)
610 }
611 scsi::SERVICE_ACTION_GET_LBA_STATUS => {
612 if !self.scsi_parameters.support_get_lba_status {
613 tracing::debug!("doesn't support get lba status");
614 Err(ScsiError::IllegalRequest(
615 AdditionalSenseCode::ILLEGAL_COMMAND,
616 ))
617 } else {
618 self.handle_get_lba_status(external_data, request, sector_count)
619 }
620 }
621 _ => Err(ScsiError::UnsupportedServiceAction(cdb.service_action)),
622 }
623 }
624
625 fn handle_read_capacity(
626 &self,
627 external_data: &RequestBuffers<'_>,
628 sector_count: u64,
629 ) -> Result<usize, ScsiError> {
630 let tx = size_of::<scsi::ReadCapacityData>();
631 if external_data.len() < tx {
632 tracelimit::error_ratelimited!(len = external_data.len(), tx, "data overrun");
633 return Err(ScsiError::DataOverrun);
634 }
635
636 let last_lba = std::cmp::min(sector_count - 1, u32::MAX.into());
641 let data = scsi::ReadCapacityData {
642 logical_block_address: (last_lba as u32).into(),
643 bytes_per_block: (1u32 << self.sector_shift).into(),
644 };
645
646 external_data
647 .writer()
648 .write(data.as_bytes())
649 .map_err(ScsiError::MemoryAccess)?;
650
651 Ok(tx)
652 }
653
654 fn handle_verify_validation(
655 &self,
656 request: &Request,
657 sector_count: u64,
658 ) -> Result<usize, ScsiError> {
659 let op = request.scsiop();
660 tracing::debug!("handle_verify_validation");
661 let (start_lba, lba_count) = match op {
662 ScsiOp::VERIFY | ScsiOp::WRITE_VERIFY => {
663 let cdb = scsi::Cdb10::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
665 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
666 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
667 }
668 (
669 cdb.logical_block.get() as u64,
670 cdb.transfer_blocks.get() as u64,
671 )
672 }
673 ScsiOp::VERIFY12 | ScsiOp::WRITE_VERIFY12 => {
674 let cdb = scsi::Cdb12::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
676 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
677 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
678 }
679 (
680 cdb.logical_block.get() as u64,
681 cdb.transfer_blocks.get() as u64,
682 )
683 }
684 ScsiOp::VERIFY16 | ScsiOp::WRITE_VERIFY16 => {
685 let cdb = scsi::Cdb16::read_from_prefix(&request.cdb[..]).unwrap().0; (cdb.logical_block.get(), cdb.transfer_blocks.get() as u64)
687 }
688 _ => unreachable!(),
689 };
690
691 if !validate_lba_range(sector_count, start_lba, lba_count) {
692 return Err(ScsiError::IllegalRequest(
694 AdditionalSenseCode::ILLEGAL_BLOCK,
695 ));
696 }
697
698 Ok(0)
699 }
700
701 fn handle_send_diagnostic_validation(&self, request: &Request) -> Result<usize, ScsiError> {
702 tracing::debug!("handle_send_diagnostic_validation");
703 let cdb = scsi::SendDiagnostic::read_from_prefix(&request.cdb[..])
704 .unwrap()
705 .0; if cdb.flags.self_test_code() == 0
707 && !cdb.flags.page_format()
708 && cdb.parameter_list_length.get() == 0
709 {
710 Ok(0)
711 } else {
712 Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB))
713 }
714 }
715
716 fn handle_control_cdb(
717 &self,
718 external_data: &RequestBuffers<'_>,
719 request: &Request,
720 sector_count: u64,
721 ) -> Result<usize, ScsiError> {
722 let op = request.scsiop();
723 match op {
724 ScsiOp::INQUIRY => self.handle_inquiry(external_data, request, sector_count),
725 ScsiOp::REQUEST_SENSE => self.handle_request_sense(external_data, request, false),
726 ScsiOp::MODE_SENSE | ScsiOp::MODE_SENSE10 => {
727 self.handle_mode_sense(external_data, request)
728 }
729 ScsiOp::TEST_UNIT_READY
730 | ScsiOp::FORMAT_UNIT
731 | ScsiOp::RESERVE_UNIT
732 | ScsiOp::RELEASE_UNIT
733 | ScsiOp::MEDIUM_REMOVAL => Ok(0),
734 ScsiOp::SEND_DIAGNOSTIC => self.handle_send_diagnostic_validation(request),
735 ScsiOp::READ_CAPACITY => self.handle_read_capacity(external_data, sector_count),
736 ScsiOp::SERVICE_ACTION_IN16 => {
738 self.handle_service_action_in16(external_data, request, sector_count)
739 }
740 ScsiOp::MODE_SELECT | ScsiOp::MODE_SELECT10 => {
741 self.handle_mode_select(external_data, request)
742 }
743 ScsiOp::VERIFY
744 | ScsiOp::VERIFY12
745 | ScsiOp::VERIFY16
746 | ScsiOp::WRITE_VERIFY
747 | ScsiOp::WRITE_VERIFY12
748 | ScsiOp::WRITE_VERIFY16 => self.handle_verify_validation(request, sector_count),
749 _ => {
750 tracing::debug!(?op, "illegal command");
751 Err(ScsiError::IllegalRequest(
752 AdditionalSenseCode::ILLEGAL_COMMAND,
753 ))
754 }
755 }
756 }
757
758 fn validate_data_cdb(
759 &self,
760 external_data: &RequestBuffers<'_>,
761 request: &Request,
762 sector_count: u64,
763 ) -> Result<RequestParameters, ScsiError> {
764 let cdb = scsi::Cdb10::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
766 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
767 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
768 }
769 let len = cdb.transfer_blocks.get() as u64;
770 let offset = cdb.logical_block.get() as u64;
771 let sector_shift = self.sector_shift;
772 let max = external_data.len() >> sector_shift;
773 if len == 0 || len as usize > max {
774 tracelimit::error_ratelimited!(len, max, "illegal block");
775 return Err(ScsiError::IllegalRequest(
776 AdditionalSenseCode::ILLEGAL_BLOCK,
777 ));
778 }
779
780 if sector_count <= offset || sector_count - offset < len {
781 tracelimit::error_ratelimited!(sector_count, offset, len, "illegal block");
782 return Err(ScsiError::IllegalRequest(
783 AdditionalSenseCode::ILLEGAL_BLOCK,
784 ));
785 }
786
787 let fua = cdb.flags.fua();
788 let tx = (len as usize) << sector_shift;
789 Ok(RequestParameters { tx, offset, fua })
790 }
791
792 fn validate_data_cdb6_read_write(
793 &self,
794 external_data: &RequestBuffers<'_>,
795 request: &Request,
796 sector_count: u64,
797 ) -> Result<RequestParameters, ScsiError> {
798 let cdb = scsi::Cdb6ReadWrite::read_from_prefix(&request.cdb[..])
799 .unwrap()
800 .0; let len = cdb.transfer_blocks as u64;
802 let offset = u32::from_be_bytes([
803 0,
804 cdb.logical_block[0],
805 cdb.logical_block[1],
806 cdb.logical_block[2],
807 ]) as u64;
808 let sector_shift = self.sector_shift;
809 let max = external_data.len() >> sector_shift;
810 if len == 0 || len as usize > max {
811 tracelimit::error_ratelimited!(len, max, "illegal block");
812 return Err(ScsiError::IllegalRequest(
813 AdditionalSenseCode::ILLEGAL_BLOCK,
814 ));
815 }
816
817 if sector_count <= offset || sector_count - offset < len {
818 tracelimit::error_ratelimited!(sector_count, offset, len, "illegal block");
819 return Err(ScsiError::IllegalRequest(
820 AdditionalSenseCode::ILLEGAL_BLOCK,
821 ));
822 }
823
824 let tx = (len as usize) << sector_shift;
825 Ok(RequestParameters {
826 tx,
827 offset,
828 fua: false,
829 })
830 }
831
832 fn validate_data_cdb12(
833 &self,
834 external_data: &RequestBuffers<'_>,
835 request: &Request,
836 sector_count: u64,
837 ) -> Result<RequestParameters, ScsiError> {
838 let cdb = scsi::Cdb12::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
840 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
841 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
842 }
843 let len = cdb.transfer_blocks.get() as u64;
844 let offset = cdb.logical_block.get() as u64;
845 let max = external_data.len() >> self.sector_shift;
846 if len == 0 || len as usize > max {
847 tracelimit::error_ratelimited!(len, max, "illegal block");
848 return Err(ScsiError::IllegalRequest(
849 AdditionalSenseCode::ILLEGAL_BLOCK,
850 ));
851 }
852
853 if sector_count <= offset || sector_count - offset < len {
854 tracelimit::error_ratelimited!(sector_count, offset, len, "illegal block");
855 return Err(ScsiError::IllegalRequest(
856 AdditionalSenseCode::ILLEGAL_BLOCK,
857 ));
858 }
859
860 let fua = cdb.flags.fua();
861 let tx = (len as usize) << self.sector_shift;
862 Ok(RequestParameters { tx, offset, fua })
863 }
864
865 fn validate_data_cdb16(
866 &self,
867 external_data: &RequestBuffers<'_>,
868 request: &Request,
869 sector_count: u64,
870 ) -> Result<RequestParameters, ScsiError> {
871 let cdb = scsi::Cdb16::read_from_prefix(&request.cdb[..]).unwrap().0; let len = cdb.transfer_blocks.get() as u64;
873 let offset = cdb.logical_block.get();
874 let sector_shift = self.sector_shift;
875 let max = external_data.len() >> sector_shift;
876 if len == 0 || len as usize > max {
877 tracelimit::error_ratelimited!(len, max, "illegal block");
878 return Err(ScsiError::IllegalRequest(
879 AdditionalSenseCode::ILLEGAL_BLOCK,
880 ));
881 }
882
883 if sector_count <= offset || sector_count - offset < len {
884 tracelimit::error_ratelimited!(sector_count, offset, len, "illegal block");
885 return Err(ScsiError::IllegalRequest(
886 AdditionalSenseCode::ILLEGAL_BLOCK,
887 ));
888 }
889
890 let fua = cdb.flags.fua();
891 let tx = (len as usize) << sector_shift;
892 Ok(RequestParameters { tx, offset, fua })
893 }
894
895 fn validate_write_same(
896 &self,
897 external_data: &RequestBuffers<'_>,
898 request: &Request,
899 sector_count: u64,
900 ) -> Result<WriteSameParameters, ScsiError> {
901 let op = request.scsiop();
902 let mut p = match op {
903 ScsiOp::WRITE_SAME => {
904 let cdb = scsi::Cdb10::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
906 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
907 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
908 }
909 WriteSameParameters {
910 start_lba: cdb.logical_block.get() as u64,
911 lba_count: cdb.transfer_blocks.get() as usize,
912 fua: cdb.flags.fua(),
913 sector_size: 0,
914 tx: 0,
915 }
916 }
917 ScsiOp::WRITE_SAME16 => {
918 let cdb = scsi::Cdb16::read_from_prefix(&request.cdb[..]).unwrap().0; WriteSameParameters {
920 start_lba: cdb.logical_block.get(),
921 lba_count: cdb.transfer_blocks.get() as usize,
922 fua: cdb.flags.fua(),
923 sector_size: 0,
924 tx: 0,
925 }
926 }
927 _ => unreachable!(),
928 };
929
930 if !validate_lba_range(sector_count, p.start_lba, p.lba_count.try_into().unwrap()) {
931 return Err(ScsiError::IllegalRequest(
933 AdditionalSenseCode::ILLEGAL_BLOCK,
934 ));
935 }
936
937 if self.disk.is_read_only() {
938 return Err(ScsiError::WriteProtected);
939 }
940
941 p.tx = p.lba_count << self.sector_shift;
943 if p.tx > VHDMP_MAX_WRITE_SAME_LENGTH_BYTES.try_into().unwrap()
944 || p.tx > self.scsi_parameters.maximum_transfer_length
945 {
946 tracelimit::error_ratelimited!(p.tx, "transfer length too big");
947 return Err(ScsiError::IllegalRequest(
948 AdditionalSenseCode::ILLEGAL_BLOCK,
949 ));
950 }
951
952 p.sector_size = self.sector_size.try_into().unwrap();
954 let external_data_len = external_data.len();
955 if p.lba_count > 0 && external_data_len < p.sector_size {
956 tracelimit::error_ratelimited!(external_data_len, "provided transfer length too small");
957 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
958 }
959
960 Ok(p)
961 }
962
963 fn process_result(&self, result: Result<usize, ScsiError>, op: ScsiOp) -> ScsiResult {
964 let result = result.map_err(|err| {
965 match err {
966 ScsiError::UnsupportedModePageCode(..)
967 | ScsiError::UnsupportedServiceAction(_)
968 | ScsiError::UnsupportedVpdPageCode(_) => tracing::debug!(disk = ?self.scsi_parameters.disk_id, error = err.as_error(), ?op, "scsi_error"),
969 | ScsiError::IllegalRequest(_) => tracing::debug!(disk = ?self.scsi_parameters.disk_id, error = err.as_error(), ?op, "scsi_error"),
970 _ => tracelimit::warn_ratelimited!(disk = ?self.scsi_parameters.disk_id, error = err.as_error(), ?op, "scsi_error"),
971 }
972 err
973 });
974
975 let result = match result {
976 Ok(tx) => ScsiResult {
977 scsi_status: ScsiStatus::GOOD,
978 srb_status: SrbStatus::SUCCESS,
979 tx,
980 sense_data: None,
981 },
982 Err(err) => {
983 match err {
984 ScsiError::MemoryAccess(_)
985 | ScsiError::UnsupportedModePageCode(..)
986 | ScsiError::UnsupportedServiceAction(_)
987 | ScsiError::UnsupportedVpdPageCode(_)
988 | ScsiError::SrbError
989 | ScsiError::Disk(DiskError::InvalidInput)
990 | ScsiError::Disk(DiskError::MemoryAccess(_)) => ScsiResult {
991 scsi_status: ScsiStatus::CHECK_CONDITION,
992 srb_status: SrbStatus::INVALID_REQUEST,
993 tx: 0,
994 sense_data: Some(illegal_request_sense(AdditionalSenseCode::INVALID_CDB)),
995 },
996 ScsiError::IllegalRequest(sense_code) => ScsiResult {
997 scsi_status: ScsiStatus::CHECK_CONDITION,
998 srb_status: SrbStatus::INVALID_REQUEST,
999 tx: 0,
1000 sense_data: Some(illegal_request_sense(sense_code)),
1001 },
1002 ScsiError::DataOverrun => ScsiResult {
1003 scsi_status: ScsiStatus::CHECK_CONDITION,
1004 srb_status: SrbStatus::DATA_OVERRUN,
1005 tx: 0,
1006 sense_data: Some(illegal_request_sense(AdditionalSenseCode::INVALID_CDB)),
1007 },
1008 ScsiError::UnitAttention => ScsiResult {
1009 scsi_status: ScsiStatus::CHECK_CONDITION,
1010 srb_status: SrbStatus::ERROR,
1011 tx: 0,
1012 sense_data: Some(scsi::SenseData::new(
1013 SenseKey::UNIT_ATTENTION,
1014 AdditionalSenseCode::PARAMETERS_CHANGED,
1015 scsi::SCSI_SENSEQ_CAPACITY_DATA_CHANGED,
1016 )),
1017 },
1018 ScsiError::WriteProtected | ScsiError::Disk(DiskError::ReadOnly) => {
1019 ScsiResult {
1020 scsi_status: ScsiStatus::CHECK_CONDITION,
1021 srb_status: SrbStatus::ERROR,
1022 tx: 0,
1023 sense_data: Some(scsi::SenseData::new(
1024 SenseKey::DATA_PROTECT,
1025 AdditionalSenseCode::WRITE_PROTECT,
1026 0,
1027 )),
1028 }
1029 }
1030 ScsiError::Disk(err) => {
1031 match err {
1032 DiskError::AbortDueToPreemptAndAbort => ScsiResult {
1033 scsi_status: ScsiStatus::TASK_ABORTED,
1034 srb_status: SrbStatus::ABORTED,
1035 tx: 0,
1036 sense_data: Some(scsi::SenseData::new(
1037 SenseKey::ABORTED_COMMAND,
1038 AdditionalSenseCode::NO_SENSE,
1039 0,
1040 )),
1041 },
1042 DiskError::IllegalBlock => ScsiResult {
1043 scsi_status: ScsiStatus::CHECK_CONDITION,
1044 srb_status: SrbStatus::ERROR,
1045 tx: 0,
1046 sense_data: Some(scsi::SenseData::new(
1047 SenseKey::ILLEGAL_REQUEST,
1048 AdditionalSenseCode::ILLEGAL_BLOCK,
1049 0,
1050 )),
1051 },
1052 DiskError::Io(_) => ScsiResult {
1053 scsi_status: ScsiStatus::CHECK_CONDITION,
1054 srb_status: SrbStatus::ERROR,
1055 tx: 0,
1056 sense_data: Some(scsi::SenseData::new(
1057 SenseKey::MEDIUM_ERROR,
1058 AdditionalSenseCode::NO_SENSE,
1059 0,
1060 )),
1061 },
1062 DiskError::MediumError(_, details) => {
1063 let (sense_code, qualifier) = match details {
1064 disk_backend::MediumErrorDetails::ApplicationTagCheckFailed => {
1065 (
1066 AdditionalSenseCode::UNRECOVERED_ERROR,
1067 scsi::SCSI_SENSEQ_LOGICAL_BLOCK_TAG_CHECK_FAILED,
1068 )
1069 }
1070 disk_backend::MediumErrorDetails::GuardCheckFailed => (
1071 AdditionalSenseCode::CRC_OR_ECC_ERROR,
1072 scsi::SCSI_SENSEQ_LOGICAL_BLOCK_GUARD_CHECK_FAILED,
1073 ),
1074 disk_backend::MediumErrorDetails::ReferenceTagCheckFailed => (
1075 AdditionalSenseCode::CRC_OR_ECC_ERROR,
1076 scsi::SCSI_SENSEQ_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED,
1077 ),
1078 disk_backend::MediumErrorDetails::UnrecoveredReadError => {
1079 (AdditionalSenseCode::UNRECOVERED_ERROR, 0)
1080 }
1081 disk_backend::MediumErrorDetails::WriteFault => {
1082 (AdditionalSenseCode::WRITE, 0)
1083 }
1084 };
1085 ScsiResult {
1086 scsi_status: ScsiStatus::CHECK_CONDITION,
1087 srb_status: SrbStatus::ERROR,
1088 tx: 0,
1089 sense_data: Some(scsi::SenseData::new(
1090 SenseKey::MEDIUM_ERROR,
1091 sense_code,
1092 qualifier,
1093 )),
1094 }
1095 }
1096 DiskError::ReservationConflict => ScsiResult {
1097 scsi_status: ScsiStatus::RESERVATION_CONFLICT,
1098 srb_status: SrbStatus::ERROR,
1099 tx: 0,
1100 sense_data: None,
1101 },
1102 DiskError::UnsupportedEject => ScsiResult {
1103 scsi_status: ScsiStatus::CHECK_CONDITION,
1104 srb_status: SrbStatus::INVALID_REQUEST,
1105 tx: 0,
1106 sense_data: Some(illegal_request_sense(
1107 AdditionalSenseCode::ILLEGAL_COMMAND,
1108 )),
1109 },
1110 DiskError::InvalidInput
1111 | DiskError::MemoryAccess(_)
1112 | DiskError::ReadOnly => unreachable!(), }
1114 }
1115 }
1116 }
1117 };
1118
1119 self.sense_data.set(result.sense_data.as_ref());
1120 if op == ScsiOp::PERSISTENT_RESERVE_OUT && result.scsi_status != ScsiStatus::GOOD {
1121 tracing::warn!(scsi_result = ?result, "PERSISTENT_RESERVE_OUT failed.");
1122 } else {
1123 tracing::trace!(scsi_result = ?result, ?op, "process_result completed.");
1124 }
1125
1126 result
1127 }
1128
1129 fn get_and_update_sector_count(&self, op: ScsiOp) -> Result<u64, u64> {
1137 let current = self.last_sector_count.load(Ordering::Relaxed);
1138 let sector_count = self.disk.sector_count();
1139 if sector_count == current || op == ScsiOp::INQUIRY {
1141 return Ok(sector_count);
1142 }
1143 tracing::info!(
1144 sector_count,
1145 old_sector_count = current,
1146 "updating sector count"
1147 );
1148 if self
1149 .last_sector_count
1150 .compare_exchange(current, sector_count, Ordering::SeqCst, Ordering::SeqCst)
1151 .is_err()
1152 {
1153 return Ok(sector_count);
1155 }
1156 Err(sector_count)
1157 }
1158}
1159
1160impl SimpleScsiDisk {
1161 async fn handle_data_cdb(
1162 &self,
1163 external_data: &RequestBuffers<'_>,
1164 request: &Request,
1165 sector_count: u64,
1166 ) -> Result<usize, ScsiError> {
1167 let op = request.scsiop();
1168 let is_read;
1169 let p = match op {
1170 ScsiOp::READ | ScsiOp::WRITE => {
1171 is_read = op == ScsiOp::READ;
1172 self.validate_data_cdb(external_data, request, sector_count)?
1173 }
1174 ScsiOp::READ6 | ScsiOp::WRITE6 => {
1175 is_read = op == ScsiOp::READ6;
1176 self.validate_data_cdb6_read_write(external_data, request, sector_count)?
1177 }
1178 ScsiOp::READ12 | ScsiOp::WRITE12 => {
1179 is_read = op == ScsiOp::READ12;
1180 self.validate_data_cdb12(external_data, request, sector_count)?
1181 }
1182 ScsiOp::READ16 | ScsiOp::WRITE16 => {
1183 is_read = op == ScsiOp::READ16;
1184 self.validate_data_cdb16(external_data, request, sector_count)?
1185 }
1186 _ => unreachable!(),
1187 };
1188
1189 let external_data = external_data.subrange(0, p.tx);
1191
1192 Ok(if is_read {
1193 self.disk
1194 .read_vectored(&external_data, p.offset)
1195 .await
1196 .map_err(ScsiError::Disk)?;
1197
1198 p.tx
1199 } else {
1200 if self.disk.is_read_only() {
1201 return Err(ScsiError::WriteProtected);
1202 }
1203
1204 self.disk
1205 .write_vectored(&external_data, p.offset, p.fua)
1206 .await
1207 .map_err(ScsiError::Disk)?;
1208
1209 p.tx
1210 })
1211 }
1212
1213 async fn handle_synchronize_cache(&self) -> Result<usize, ScsiError> {
1214 self.disk.sync_cache().await.map_err(ScsiError::Disk)?;
1215 Ok(0)
1216 }
1217
1218 async fn handle_write_same(
1219 &self,
1220 external_data: &RequestBuffers<'_>,
1221 request: &Request,
1222 sector_count: u64,
1223 ) -> Result<usize, ScsiError> {
1224 let p = self.validate_write_same(external_data, request, sector_count)?;
1225 if p.tx > 0 {
1226 let external_data = external_data.subrange(0, p.sector_size);
1228 for offset in p.start_lba..p.start_lba + (p.lba_count as u64) {
1230 self.disk
1231 .write_vectored(&external_data, offset, p.fua)
1232 .await
1233 .map_err(ScsiError::Disk)?;
1234 }
1235 }
1236
1237 Ok(p.tx)
1238 }
1239
1240 async fn handle_start_stop(&self, request: &Request) -> Result<usize, ScsiError> {
1241 let cdb = scsi::StartStop::read_from_prefix(&request.cdb[..])
1242 .unwrap()
1243 .0; if cdb.immediate & scsi::IMMEDIATE_BIT != 0 {
1245 tracing::debug!("immediate bit is not supported");
1246 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
1247 }
1248
1249 if cdb.flag.start() {
1250 return Ok(0);
1251 };
1252
1253 self.disk.sync_cache().await.map_err(ScsiError::Disk)?;
1254 Ok(0)
1255 }
1256}
1257
1258impl AsyncScsiDisk for SimpleScsiDisk {
1259 fn execute_scsi<'a>(
1260 &'a self,
1261 external_data: &'a RequestBuffers<'a>,
1262 request: &'a Request,
1263 ) -> StackFuture<'a, ScsiResult, { ASYNC_SCSI_DISK_STACK_SIZE }> {
1264 StackFuture::from(async move {
1265 let op = request.scsiop();
1266
1267 let sector_count = match self.get_and_update_sector_count(op) {
1268 Ok(c) => c,
1269 Err(_) => {
1270 let result = match op {
1272 ScsiOp::REQUEST_SENSE => {
1273 self.handle_request_sense(external_data, request, true)
1274 }
1275 _ => Err(ScsiError::UnitAttention),
1276 };
1277 return self.process_result(result, op);
1278 }
1279 };
1280
1281 let result = match op {
1282 ScsiOp::WRITE
1283 | ScsiOp::WRITE6
1284 | ScsiOp::WRITE12
1285 | ScsiOp::WRITE16
1286 | ScsiOp::READ
1287 | ScsiOp::READ6
1288 | ScsiOp::READ12
1289 | ScsiOp::READ16 => {
1290 self.handle_data_cdb(external_data, request, sector_count)
1291 .instrument(tracing::trace_span!("handle_data_cdb_async", ?op,))
1292 .await
1293 }
1294 ScsiOp::WRITE_SAME | ScsiOp::WRITE_SAME16 => {
1295 self.handle_write_same(external_data, request, sector_count)
1296 .instrument(tracing::trace_span!("handle_write_same_async"))
1297 .await
1298 }
1299 ScsiOp::SYNCHRONIZE_CACHE | ScsiOp::SYNCHRONIZE_CACHE16 => {
1300 self.handle_synchronize_cache()
1301 .instrument(tracing::trace_span!("handle_synchronize_cache_async", ?op,))
1302 .await
1303 }
1304 ScsiOp::START_STOP_UNIT => {
1305 self.handle_start_stop(request)
1306 .instrument(tracing::trace_span!("handle_start_stop_async",))
1307 .await
1308 }
1309 ScsiOp::UNMAP => {
1310 self.handle_unmap(external_data, request, sector_count)
1311 .instrument(tracing::debug_span!("handle_unmap_async"))
1312 .await
1313 }
1314 ScsiOp::PERSISTENT_RESERVE_IN | ScsiOp::PERSISTENT_RESERVE_OUT => {
1315 self.handle_persistent_reserve(external_data, request)
1316 .instrument(tracing::trace_span!("handle_persistent_reserve_async", ?op,))
1317 .await
1318 }
1319 _ => {
1320 let _span = tracing::trace_span!("handle_control_cdb", ?op,).entered();
1321 self.handle_control_cdb(external_data, request, sector_count)
1322 }
1323 };
1324
1325 self.process_result(result, op)
1326 })
1327 }
1328}
1329
1330impl Inspect for SimpleScsiDisk {
1331 fn inspect(&self, req: inspect::Request<'_>) {
1332 let mut resp = req.respond();
1333 resp.binary("disk_id", self.scsi_parameters.disk_id)
1334 .field("logical_sector_size", self.sector_size)
1335 .field(
1336 "physical_sector_size",
1337 1usize << self.sector_shift << self.physical_extra_shift,
1338 )
1339 .field(
1340 "sector_count",
1341 self.last_sector_count.load(Ordering::Relaxed),
1342 )
1343 .field("scsi_parameters", &self.scsi_parameters)
1344 .field("pr", self.support_pr)
1345 .field("backend", &self.disk);
1346 }
1347}
1348
1349#[derive(Default, Debug)]
1350struct SenseDataSlot {
1351 is_valid: AtomicBool,
1352 data: Mutex<Option<scsi::SenseData>>,
1353}
1354
1355impl SenseDataSlot {
1356 fn set(&self, sense_data: Option<&scsi::SenseData>) {
1358 match sense_data {
1359 None => {
1360 if self.is_valid.load(Ordering::Relaxed) {
1366 self.is_valid.store(false, Ordering::Relaxed)
1367 }
1368 }
1369 Some(sense_data) => {
1370 *self.data.lock() = Some(*sense_data);
1371 self.is_valid.store(true, Ordering::Release);
1372 }
1373 }
1374 }
1375
1376 fn get(&self) -> Option<scsi::SenseData> {
1378 if self.is_valid.load(Ordering::Relaxed) {
1379 *self.data.lock()
1382 } else {
1383 None
1384 }
1385 }
1386
1387 pub(crate) fn take(&self) -> Option<scsi::SenseData> {
1389 if self.is_valid.swap(false, Ordering::Acquire) {
1390 self.data.lock().take()
1393 } else {
1394 None
1395 }
1396 }
1397}