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
345 let (request_length, header_size, is_spbit_set) = if is_mode_select_10 {
346 let cdb = scsi::ModeSelect10::read_from_prefix(&request.cdb[..])
347 .unwrap()
348 .0; (
350 cdb.parameter_list_length.get() as usize,
351 MODE_PARAMETER_HEADER10_SIZE,
352 cdb.flags.spbit(),
353 )
354 } else {
355 let cdb = scsi::ModeSelect::read_from_prefix(&request.cdb[..])
356 .unwrap()
357 .0; (
359 cdb.parameter_list_length as usize,
360 MODE_PARAMETER_HEADER_SIZE,
361 cdb.flags.spbit(),
362 )
363 };
364
365 if request_length == 0 {
366 return Ok(0);
367 }
368
369 let min = header_size + MODE_CACHING_PAGE_SIZE;
371 if request_length != external_data.len() || request_length < min {
372 tracelimit::error_ratelimited!(
373 request_length,
374 external_data = external_data.len(),
375 min,
376 "invalid parameter list length"
377 );
378 return Err(ScsiError::IllegalRequest(
379 AdditionalSenseCode::PARAMETER_LIST_LENGTH,
380 ));
381 }
382
383 if is_spbit_set {
385 tracing::debug!("doesn't support saving pages");
386 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
387 }
388
389 let mut buffer: Vec<u8> = vec![0; request_length];
390 external_data
391 .reader()
392 .read(&mut buffer)
393 .map_err(ScsiError::MemoryAccess)?;
394
395 let block_descriptor_length = if is_mode_select_10 {
396 let temp10 = scsi::ModeParameterHeader10::read_from_prefix(
397 &buffer[..MODE_PARAMETER_HEADER10_SIZE],
398 )
399 .unwrap()
400 .0; usize::from(temp10.block_descriptor_length)
402 } else {
403 let temp =
404 scsi::ModeParameterHeader::read_from_prefix(&buffer[..MODE_PARAMETER_HEADER_SIZE])
405 .unwrap()
406 .0; temp.block_descriptor_length as usize
408 };
409
410 let skipped = header_size + block_descriptor_length;
412 let min = skipped + MODE_CACHING_PAGE_SIZE;
413 if request_length < min {
414 tracelimit::error_ratelimited!(request_length, min, "invalid parameter list length");
415 return Err(ScsiError::IllegalRequest(
416 AdditionalSenseCode::PARAMETER_LIST_LENGTH,
417 ));
418 }
419
420 let page = scsi::ModeCachingPage::read_from_prefix(
422 &buffer[skipped..skipped + MODE_CACHING_PAGE_SIZE],
423 )
424 .unwrap()
425 .0; if page.page_code != scsi::MODE_PAGE_CACHING
427 || (page.page_length as usize) < MODE_CACHING_PAGE_SIZE
428 || ((page.flags & scsi::MODE_CACHING_WRITE_CACHE_ENABLE == 0)
429 && self.scsi_parameters.write_cache_enabled)
430 || ((page.flags & scsi::MODE_CACHING_WRITE_CACHE_ENABLE != 0)
431 && !self.scsi_parameters.write_cache_enabled)
432 {
433 tracing::debug!(
446 page_code = page.page_code,
447 page_length = page.page_length,
448 flags = page.flags,
449 write_cache_enabled = self.scsi_parameters.write_cache_enabled,
450 "invalid parameter list"
451 );
452 return Err(ScsiError::IllegalRequest(
453 AdditionalSenseCode::INVALID_FIELD_PARAMETER_LIST,
454 ));
455 }
456
457 Ok(request_length)
458 }
459
460 fn handle_mode_sense(
461 &self,
462 external_data: &RequestBuffers<'_>,
463 request: &Request,
464 ) -> Result<usize, ScsiError> {
465 if external_data.is_empty() {
466 return Ok(0);
467 }
468
469 let is_mode_sense_10 = request.scsiop() == ScsiOp::MODE_SENSE10;
470
471 let (allocation_length, page_code, page_control, header_size) = if is_mode_sense_10 {
472 let cdb = scsi::ModeSense10::read_from_prefix(&request.cdb[..])
473 .unwrap()
474 .0; (
476 cdb.allocation_length.get() as usize,
477 cdb.flags2.page_code(),
478 cdb.flags2.pc() << 6,
479 MODE_PARAMETER_HEADER10_SIZE,
480 )
481 } else {
482 let cdb = scsi::ModeSense::read_from_prefix(&request.cdb[..])
483 .unwrap()
484 .0; (
486 cdb.allocation_length as usize,
487 cdb.flags2.page_code(),
488 cdb.flags2.pc() << 6,
489 MODE_PARAMETER_HEADER_SIZE,
490 )
491 };
492
493 if allocation_length == 0 {
495 return Ok(0);
496 }
497
498 if allocation_length > external_data.len() || allocation_length < header_size {
501 tracelimit::error_ratelimited!(
502 allocation_length,
503 external_data = external_data.len(),
504 header_size,
505 "invalid cdb"
506 );
507 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
508 }
509
510 if page_control == scsi::MODE_CONTROL_SAVED_VALUES
511 || (page_code != scsi::MODE_PAGE_CACHING && page_code != scsi::MODE_PAGE_ALL)
512 {
513 return Err(ScsiError::UnsupportedModePageCode(page_control, page_code));
514 }
515
516 let mut dsp = 0;
517 if self.disk.is_read_only() {
518 dsp |= scsi::MODE_DSP_WRITE_PROTECT;
519 }
520
521 if self.scsi_parameters.support_fua {
522 dsp |= scsi::MODE_DSP_FUA_SUPPORTED;
523 }
524
525 let temp;
526 let temp10;
527 let header = if is_mode_sense_10 {
528 temp10 = scsi::ModeParameterHeader10 {
529 mode_data_length: MODE_DATA_LENGTH10.into(),
530 device_specific_parameter: dsp,
531 ..FromZeros::new_zeroed()
532 };
533 temp10.as_bytes()
534 } else {
535 temp = scsi::ModeParameterHeader {
536 mode_data_length: MODE_DATA_LENGTH,
537 device_specific_parameter: dsp,
538 ..FromZeros::new_zeroed()
539 };
540 temp.as_bytes()
541 };
542
543 let mut page = scsi::ModeCachingPage {
544 page_code: scsi::MODE_PAGE_CACHING,
545 page_length: (MODE_CACHING_PAGE_SIZE - 2) as u8,
546 ..FromZeros::new_zeroed()
547 };
548
549 if (page_control == scsi::MODE_CONTROL_CURRENT_VALUES
550 || page_control == scsi::MODE_CONTROL_DEFAULT_VALUES)
551 && external_data.len() - header_size >= scsi::WRITE_CACHE_ENABLE_BYTE_OFFSET
552 {
553 if self.scsi_parameters.write_cache_enabled {
554 page.flags |= scsi::MODE_CACHING_WRITE_CACHE_ENABLE;
555 }
556 }
557
558 let mut data = [0; MODE_PARAMETER_HEADER10_SIZE + MODE_CACHING_PAGE_SIZE];
560 data[..header_size].copy_from_slice(header);
561 data[header_size..header_size + MODE_CACHING_PAGE_SIZE].copy_from_slice(page.as_bytes());
562 let tx = std::cmp::min(allocation_length, header_size + MODE_CACHING_PAGE_SIZE);
563 external_data
564 .writer()
565 .write(&data[..tx])
566 .map_err(ScsiError::MemoryAccess)?;
567
568 Ok(tx)
569 }
570
571 fn handle_service_action_in16(
572 &self,
573 external_data: &RequestBuffers<'_>,
574 request: &Request,
575 sector_count: u64,
576 ) -> Result<usize, ScsiError> {
577 let cdb = scsi::ServiceActionIn16::read_from_prefix(&request.cdb[..])
578 .unwrap()
579 .0; match cdb.service_action & 0x1f {
581 scsi::SERVICE_ACTION_READ_CAPACITY16 => {
582 let min = size_of::<scsi::ReadCapacityDataEx>();
583 if external_data.len() < min {
584 tracelimit::error_ratelimited!(len = external_data.len(), min, "data overrun");
585 return Err(ScsiError::DataOverrun);
586 }
587
588 let mut data = scsi::ReadCapacity16Data {
589 ex: scsi::ReadCapacityDataEx {
590 logical_block_address: (sector_count - 1).into(),
593 bytes_per_block: (1u32 << self.sector_shift).into(),
594 },
595 exponents: self.physical_extra_shift,
596 ..FromZeros::new_zeroed()
597 };
598
599 if self.scsi_parameters.support_unmap {
600 data.lowest_aligned_block_msb |= scsi::READ_CAPACITY16_LBPME;
604 }
605
606 let tx = std::cmp::min(external_data.len(), size_of::<scsi::ReadCapacity16Data>());
607 external_data
608 .writer()
609 .write(&data.as_bytes()[..tx])
610 .map_err(ScsiError::MemoryAccess)?;
611
612 Ok(tx)
613 }
614 scsi::SERVICE_ACTION_GET_LBA_STATUS => {
615 if !self.scsi_parameters.support_get_lba_status {
616 tracing::debug!("doesn't support get lba status");
617 Err(ScsiError::IllegalRequest(
618 AdditionalSenseCode::ILLEGAL_COMMAND,
619 ))
620 } else {
621 self.handle_get_lba_status(external_data, request, sector_count)
622 }
623 }
624 _ => Err(ScsiError::UnsupportedServiceAction(cdb.service_action)),
625 }
626 }
627
628 fn handle_read_capacity(
629 &self,
630 external_data: &RequestBuffers<'_>,
631 sector_count: u64,
632 ) -> Result<usize, ScsiError> {
633 let tx = size_of::<scsi::ReadCapacityData>();
634 if external_data.len() < tx {
635 tracelimit::error_ratelimited!(len = external_data.len(), tx, "data overrun");
636 return Err(ScsiError::DataOverrun);
637 }
638
639 let last_lba = std::cmp::min(sector_count - 1, u32::MAX.into());
644 let data = scsi::ReadCapacityData {
645 logical_block_address: (last_lba as u32).into(),
646 bytes_per_block: (1u32 << self.sector_shift).into(),
647 };
648
649 external_data
650 .writer()
651 .write(data.as_bytes())
652 .map_err(ScsiError::MemoryAccess)?;
653
654 Ok(tx)
655 }
656
657 fn handle_verify_validation(
658 &self,
659 request: &Request,
660 sector_count: u64,
661 ) -> Result<usize, ScsiError> {
662 let op = request.scsiop();
663 tracing::debug!("handle_verify_validation");
664 let (start_lba, lba_count) = match op {
665 ScsiOp::VERIFY | ScsiOp::WRITE_VERIFY => {
666 let cdb = scsi::Cdb10::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
668 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
669 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
670 }
671 (
672 cdb.logical_block.get() as u64,
673 cdb.transfer_blocks.get() as u64,
674 )
675 }
676 ScsiOp::VERIFY12 | ScsiOp::WRITE_VERIFY12 => {
677 let cdb = scsi::Cdb12::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
679 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
680 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
681 }
682 (
683 cdb.logical_block.get() as u64,
684 cdb.transfer_blocks.get() as u64,
685 )
686 }
687 ScsiOp::VERIFY16 | ScsiOp::WRITE_VERIFY16 => {
688 let cdb = scsi::Cdb16::read_from_prefix(&request.cdb[..]).unwrap().0; (cdb.logical_block.get(), cdb.transfer_blocks.get() as u64)
690 }
691 _ => unreachable!(),
692 };
693
694 if !validate_lba_range(sector_count, start_lba, lba_count) {
695 return Err(ScsiError::IllegalRequest(
697 AdditionalSenseCode::ILLEGAL_BLOCK,
698 ));
699 }
700
701 Ok(0)
702 }
703
704 fn handle_send_diagnostic_validation(&self, request: &Request) -> Result<usize, ScsiError> {
705 tracing::debug!("handle_send_diagnostic_validation");
706 let cdb = scsi::SendDiagnostic::read_from_prefix(&request.cdb[..])
707 .unwrap()
708 .0; if cdb.flags.self_test_code() == 0
710 && !cdb.flags.page_format()
711 && cdb.parameter_list_length.get() == 0
712 {
713 Ok(0)
714 } else {
715 Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB))
716 }
717 }
718
719 fn handle_control_cdb(
720 &self,
721 external_data: &RequestBuffers<'_>,
722 request: &Request,
723 sector_count: u64,
724 ) -> Result<usize, ScsiError> {
725 let op = request.scsiop();
726 match op {
727 ScsiOp::INQUIRY => self.handle_inquiry(external_data, request, sector_count),
728 ScsiOp::REQUEST_SENSE => self.handle_request_sense(external_data, request, false),
729 ScsiOp::MODE_SENSE | ScsiOp::MODE_SENSE10 => {
730 self.handle_mode_sense(external_data, request)
731 }
732 ScsiOp::TEST_UNIT_READY
733 | ScsiOp::FORMAT_UNIT
734 | ScsiOp::RESERVE_UNIT
735 | ScsiOp::RELEASE_UNIT
736 | ScsiOp::MEDIUM_REMOVAL => Ok(0),
737 ScsiOp::SEND_DIAGNOSTIC => self.handle_send_diagnostic_validation(request),
738 ScsiOp::READ_CAPACITY => self.handle_read_capacity(external_data, sector_count),
739 ScsiOp::SERVICE_ACTION_IN16 => {
741 self.handle_service_action_in16(external_data, request, sector_count)
742 }
743 ScsiOp::MODE_SELECT | ScsiOp::MODE_SELECT10 => {
744 self.handle_mode_select(external_data, request)
745 }
746 ScsiOp::VERIFY
747 | ScsiOp::VERIFY12
748 | ScsiOp::VERIFY16
749 | ScsiOp::WRITE_VERIFY
750 | ScsiOp::WRITE_VERIFY12
751 | ScsiOp::WRITE_VERIFY16 => self.handle_verify_validation(request, sector_count),
752 _ => {
753 tracing::debug!(?op, "illegal command");
754 Err(ScsiError::IllegalRequest(
755 AdditionalSenseCode::ILLEGAL_COMMAND,
756 ))
757 }
758 }
759 }
760
761 fn validate_data_cdb(
762 &self,
763 external_data: &RequestBuffers<'_>,
764 request: &Request,
765 sector_count: u64,
766 ) -> Result<RequestParameters, ScsiError> {
767 let cdb = scsi::Cdb10::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
769 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
770 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
771 }
772 let len = cdb.transfer_blocks.get() as u64;
773 let offset = cdb.logical_block.get() as u64;
774 let sector_shift = self.sector_shift;
775 let max = external_data.len() >> sector_shift;
776 if len == 0 || len as usize > max {
777 tracelimit::error_ratelimited!(len, max, "illegal block");
778 return Err(ScsiError::IllegalRequest(
779 AdditionalSenseCode::ILLEGAL_BLOCK,
780 ));
781 }
782
783 if sector_count <= offset || sector_count - offset < len {
784 tracelimit::error_ratelimited!(sector_count, offset, len, "illegal block");
785 return Err(ScsiError::IllegalRequest(
786 AdditionalSenseCode::ILLEGAL_BLOCK,
787 ));
788 }
789
790 let fua = cdb.flags.fua();
791 let tx = (len as usize) << sector_shift;
792 Ok(RequestParameters { tx, offset, fua })
793 }
794
795 fn validate_data_cdb6_read_write(
796 &self,
797 external_data: &RequestBuffers<'_>,
798 request: &Request,
799 sector_count: u64,
800 ) -> Result<RequestParameters, ScsiError> {
801 let cdb = scsi::Cdb6ReadWrite::read_from_prefix(&request.cdb[..])
802 .unwrap()
803 .0; let len = cdb.transfer_blocks as u64;
805 let offset = u32::from_be_bytes([
806 0,
807 cdb.logical_block[0],
808 cdb.logical_block[1],
809 cdb.logical_block[2],
810 ]) as u64;
811 let sector_shift = self.sector_shift;
812 let max = external_data.len() >> sector_shift;
813 if len == 0 || len as usize > max {
814 tracelimit::error_ratelimited!(len, max, "illegal block");
815 return Err(ScsiError::IllegalRequest(
816 AdditionalSenseCode::ILLEGAL_BLOCK,
817 ));
818 }
819
820 if sector_count <= offset || sector_count - offset < len {
821 tracelimit::error_ratelimited!(sector_count, offset, len, "illegal block");
822 return Err(ScsiError::IllegalRequest(
823 AdditionalSenseCode::ILLEGAL_BLOCK,
824 ));
825 }
826
827 let tx = (len as usize) << sector_shift;
828 Ok(RequestParameters {
829 tx,
830 offset,
831 fua: false,
832 })
833 }
834
835 fn validate_data_cdb12(
836 &self,
837 external_data: &RequestBuffers<'_>,
838 request: &Request,
839 sector_count: u64,
840 ) -> Result<RequestParameters, ScsiError> {
841 let cdb = scsi::Cdb12::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
843 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
844 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
845 }
846 let len = cdb.transfer_blocks.get() as u64;
847 let offset = cdb.logical_block.get() as u64;
848 let max = external_data.len() >> self.sector_shift;
849 if len == 0 || len as usize > max {
850 tracelimit::error_ratelimited!(len, max, "illegal block");
851 return Err(ScsiError::IllegalRequest(
852 AdditionalSenseCode::ILLEGAL_BLOCK,
853 ));
854 }
855
856 if sector_count <= offset || sector_count - offset < len {
857 tracelimit::error_ratelimited!(sector_count, offset, len, "illegal block");
858 return Err(ScsiError::IllegalRequest(
859 AdditionalSenseCode::ILLEGAL_BLOCK,
860 ));
861 }
862
863 let fua = cdb.flags.fua();
864 let tx = (len as usize) << self.sector_shift;
865 Ok(RequestParameters { tx, offset, fua })
866 }
867
868 fn validate_data_cdb16(
869 &self,
870 external_data: &RequestBuffers<'_>,
871 request: &Request,
872 sector_count: u64,
873 ) -> Result<RequestParameters, ScsiError> {
874 let cdb = scsi::Cdb16::read_from_prefix(&request.cdb[..]).unwrap().0; let len = cdb.transfer_blocks.get() as u64;
876 let offset = cdb.logical_block.get();
877 let sector_shift = self.sector_shift;
878 let max = external_data.len() >> sector_shift;
879 if len == 0 || len as usize > max {
880 tracelimit::error_ratelimited!(len, max, "illegal block");
881 return Err(ScsiError::IllegalRequest(
882 AdditionalSenseCode::ILLEGAL_BLOCK,
883 ));
884 }
885
886 if sector_count <= offset || sector_count - offset < len {
887 tracelimit::error_ratelimited!(sector_count, offset, len, "illegal block");
888 return Err(ScsiError::IllegalRequest(
889 AdditionalSenseCode::ILLEGAL_BLOCK,
890 ));
891 }
892
893 let fua = cdb.flags.fua();
894 let tx = (len as usize) << sector_shift;
895 Ok(RequestParameters { tx, offset, fua })
896 }
897
898 fn validate_write_same(
899 &self,
900 external_data: &RequestBuffers<'_>,
901 request: &Request,
902 sector_count: u64,
903 ) -> Result<WriteSameParameters, ScsiError> {
904 let op = request.scsiop();
905 let mut p = match op {
906 ScsiOp::WRITE_SAME => {
907 let cdb = scsi::Cdb10::read_from_prefix(&request.cdb[..]).unwrap().0; if cdb.flags.relative_address() {
909 tracing::debug!(flags = ?cdb.flags, "doesn't support relative address");
910 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
911 }
912 WriteSameParameters {
913 start_lba: cdb.logical_block.get() as u64,
914 lba_count: cdb.transfer_blocks.get() as usize,
915 fua: cdb.flags.fua(),
916 sector_size: 0,
917 tx: 0,
918 }
919 }
920 ScsiOp::WRITE_SAME16 => {
921 let cdb = scsi::Cdb16::read_from_prefix(&request.cdb[..]).unwrap().0; WriteSameParameters {
923 start_lba: cdb.logical_block.get(),
924 lba_count: cdb.transfer_blocks.get() as usize,
925 fua: cdb.flags.fua(),
926 sector_size: 0,
927 tx: 0,
928 }
929 }
930 _ => unreachable!(),
931 };
932
933 if !validate_lba_range(sector_count, p.start_lba, p.lba_count.try_into().unwrap()) {
934 return Err(ScsiError::IllegalRequest(
936 AdditionalSenseCode::ILLEGAL_BLOCK,
937 ));
938 }
939
940 if self.disk.is_read_only() {
941 return Err(ScsiError::WriteProtected);
942 }
943
944 p.tx = p.lba_count << self.sector_shift;
946 if p.tx > VHDMP_MAX_WRITE_SAME_LENGTH_BYTES.try_into().unwrap()
947 || p.tx > self.scsi_parameters.maximum_transfer_length
948 {
949 tracelimit::error_ratelimited!(p.tx, "transfer length too big");
950 return Err(ScsiError::IllegalRequest(
951 AdditionalSenseCode::ILLEGAL_BLOCK,
952 ));
953 }
954
955 p.sector_size = self.sector_size.try_into().unwrap();
957 let external_data_len = external_data.len();
958 if p.lba_count > 0 && external_data_len < p.sector_size {
959 tracelimit::error_ratelimited!(external_data_len, "provided transfer length too small");
960 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
961 }
962
963 Ok(p)
964 }
965
966 fn process_result(&self, result: Result<usize, ScsiError>, op: ScsiOp) -> ScsiResult {
967 let result = result.map_err(|err| {
968 match err {
969 ScsiError::UnsupportedModePageCode(..)
970 | ScsiError::UnsupportedServiceAction(_)
971 | ScsiError::UnsupportedVpdPageCode(_) => tracing::debug!(disk = ?self.scsi_parameters.disk_id, error = err.as_error(), ?op, "scsi_error"),
972 | ScsiError::IllegalRequest(_) => tracing::debug!(disk = ?self.scsi_parameters.disk_id, error = err.as_error(), ?op, "scsi_error"),
973 _ => tracelimit::warn_ratelimited!(disk = ?self.scsi_parameters.disk_id, error = err.as_error(), ?op, "scsi_error"),
974 }
975 err
976 });
977
978 let result = match result {
979 Ok(tx) => ScsiResult {
980 scsi_status: ScsiStatus::GOOD,
981 srb_status: SrbStatus::SUCCESS,
982 tx,
983 sense_data: None,
984 },
985 Err(err) => {
986 match err {
987 ScsiError::MemoryAccess(_)
988 | ScsiError::UnsupportedModePageCode(..)
989 | ScsiError::UnsupportedServiceAction(_)
990 | ScsiError::UnsupportedVpdPageCode(_)
991 | ScsiError::SrbError
992 | ScsiError::Disk(DiskError::InvalidInput)
993 | ScsiError::Disk(DiskError::MemoryAccess(_)) => ScsiResult {
994 scsi_status: ScsiStatus::CHECK_CONDITION,
995 srb_status: SrbStatus::INVALID_REQUEST,
996 tx: 0,
997 sense_data: Some(illegal_request_sense(AdditionalSenseCode::INVALID_CDB)),
998 },
999 ScsiError::IllegalRequest(sense_code) => ScsiResult {
1000 scsi_status: ScsiStatus::CHECK_CONDITION,
1001 srb_status: SrbStatus::INVALID_REQUEST,
1002 tx: 0,
1003 sense_data: Some(illegal_request_sense(sense_code)),
1004 },
1005 ScsiError::DataOverrun => ScsiResult {
1006 scsi_status: ScsiStatus::CHECK_CONDITION,
1007 srb_status: SrbStatus::DATA_OVERRUN,
1008 tx: 0,
1009 sense_data: Some(illegal_request_sense(AdditionalSenseCode::INVALID_CDB)),
1010 },
1011 ScsiError::UnitAttention => ScsiResult {
1012 scsi_status: ScsiStatus::CHECK_CONDITION,
1013 srb_status: SrbStatus::ERROR,
1014 tx: 0,
1015 sense_data: Some(scsi::SenseData::new(
1016 SenseKey::UNIT_ATTENTION,
1017 AdditionalSenseCode::PARAMETERS_CHANGED,
1018 scsi::SCSI_SENSEQ_CAPACITY_DATA_CHANGED,
1019 )),
1020 },
1021 ScsiError::WriteProtected | ScsiError::Disk(DiskError::ReadOnly) => {
1022 ScsiResult {
1023 scsi_status: ScsiStatus::CHECK_CONDITION,
1024 srb_status: SrbStatus::ERROR,
1025 tx: 0,
1026 sense_data: Some(scsi::SenseData::new(
1027 SenseKey::DATA_PROTECT,
1028 AdditionalSenseCode::WRITE_PROTECT,
1029 0,
1030 )),
1031 }
1032 }
1033 ScsiError::Disk(err) => {
1034 match err {
1035 DiskError::AbortDueToPreemptAndAbort => ScsiResult {
1036 scsi_status: ScsiStatus::TASK_ABORTED,
1037 srb_status: SrbStatus::ABORTED,
1038 tx: 0,
1039 sense_data: Some(scsi::SenseData::new(
1040 SenseKey::ABORTED_COMMAND,
1041 AdditionalSenseCode::NO_SENSE,
1042 0,
1043 )),
1044 },
1045 DiskError::IllegalBlock => ScsiResult {
1046 scsi_status: ScsiStatus::CHECK_CONDITION,
1047 srb_status: SrbStatus::ERROR,
1048 tx: 0,
1049 sense_data: Some(scsi::SenseData::new(
1050 SenseKey::ILLEGAL_REQUEST,
1051 AdditionalSenseCode::ILLEGAL_BLOCK,
1052 0,
1053 )),
1054 },
1055 DiskError::Io(_) => ScsiResult {
1056 scsi_status: ScsiStatus::CHECK_CONDITION,
1057 srb_status: SrbStatus::ERROR,
1058 tx: 0,
1059 sense_data: Some(scsi::SenseData::new(
1060 SenseKey::MEDIUM_ERROR,
1061 AdditionalSenseCode::NO_SENSE,
1062 0,
1063 )),
1064 },
1065 DiskError::MediumError(_, details) => {
1066 let (sense_code, qualifier) = match details {
1067 disk_backend::MediumErrorDetails::ApplicationTagCheckFailed => {
1068 (
1069 AdditionalSenseCode::UNRECOVERED_ERROR,
1070 scsi::SCSI_SENSEQ_LOGICAL_BLOCK_TAG_CHECK_FAILED,
1071 )
1072 }
1073 disk_backend::MediumErrorDetails::GuardCheckFailed => (
1074 AdditionalSenseCode::CRC_OR_ECC_ERROR,
1075 scsi::SCSI_SENSEQ_LOGICAL_BLOCK_GUARD_CHECK_FAILED,
1076 ),
1077 disk_backend::MediumErrorDetails::ReferenceTagCheckFailed => (
1078 AdditionalSenseCode::CRC_OR_ECC_ERROR,
1079 scsi::SCSI_SENSEQ_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED,
1080 ),
1081 disk_backend::MediumErrorDetails::UnrecoveredReadError => {
1082 (AdditionalSenseCode::UNRECOVERED_ERROR, 0)
1083 }
1084 disk_backend::MediumErrorDetails::WriteFault => {
1085 (AdditionalSenseCode::WRITE, 0)
1086 }
1087 };
1088 ScsiResult {
1089 scsi_status: ScsiStatus::CHECK_CONDITION,
1090 srb_status: SrbStatus::ERROR,
1091 tx: 0,
1092 sense_data: Some(scsi::SenseData::new(
1093 SenseKey::MEDIUM_ERROR,
1094 sense_code,
1095 qualifier,
1096 )),
1097 }
1098 }
1099 DiskError::ReservationConflict => ScsiResult {
1100 scsi_status: ScsiStatus::RESERVATION_CONFLICT,
1101 srb_status: SrbStatus::ERROR,
1102 tx: 0,
1103 sense_data: None,
1104 },
1105 DiskError::UnsupportedEject => ScsiResult {
1106 scsi_status: ScsiStatus::CHECK_CONDITION,
1107 srb_status: SrbStatus::INVALID_REQUEST,
1108 tx: 0,
1109 sense_data: Some(illegal_request_sense(
1110 AdditionalSenseCode::ILLEGAL_COMMAND,
1111 )),
1112 },
1113 DiskError::InvalidInput
1114 | DiskError::MemoryAccess(_)
1115 | DiskError::ReadOnly => unreachable!(), }
1117 }
1118 }
1119 }
1120 };
1121
1122 self.sense_data.set(result.sense_data.as_ref());
1123 if op == ScsiOp::PERSISTENT_RESERVE_OUT && result.scsi_status != ScsiStatus::GOOD {
1124 tracing::warn!(scsi_result = ?result, "PERSISTENT_RESERVE_OUT failed.");
1125 } else {
1126 tracing::trace!(scsi_result = ?result, ?op, "process_result completed.");
1127 }
1128
1129 result
1130 }
1131
1132 fn get_and_update_sector_count(&self, op: ScsiOp) -> Result<u64, u64> {
1140 let current = self.last_sector_count.load(Ordering::Relaxed);
1141 let sector_count = self.disk.sector_count();
1142 if sector_count == current || op == ScsiOp::INQUIRY {
1144 return Ok(sector_count);
1145 }
1146 tracing::info!(
1147 sector_count,
1148 old_sector_count = current,
1149 "updating sector count"
1150 );
1151 if self
1152 .last_sector_count
1153 .compare_exchange(current, sector_count, Ordering::SeqCst, Ordering::SeqCst)
1154 .is_err()
1155 {
1156 return Ok(sector_count);
1158 }
1159 Err(sector_count)
1160 }
1161}
1162
1163impl SimpleScsiDisk {
1164 async fn handle_data_cdb(
1165 &self,
1166 external_data: &RequestBuffers<'_>,
1167 request: &Request,
1168 sector_count: u64,
1169 ) -> Result<usize, ScsiError> {
1170 let op = request.scsiop();
1171 let is_read;
1172 let p = match op {
1173 ScsiOp::READ | ScsiOp::WRITE => {
1174 is_read = op == ScsiOp::READ;
1175 self.validate_data_cdb(external_data, request, sector_count)?
1176 }
1177 ScsiOp::READ6 | ScsiOp::WRITE6 => {
1178 is_read = op == ScsiOp::READ6;
1179 self.validate_data_cdb6_read_write(external_data, request, sector_count)?
1180 }
1181 ScsiOp::READ12 | ScsiOp::WRITE12 => {
1182 is_read = op == ScsiOp::READ12;
1183 self.validate_data_cdb12(external_data, request, sector_count)?
1184 }
1185 ScsiOp::READ16 | ScsiOp::WRITE16 => {
1186 is_read = op == ScsiOp::READ16;
1187 self.validate_data_cdb16(external_data, request, sector_count)?
1188 }
1189 _ => unreachable!(),
1190 };
1191
1192 let external_data = external_data.subrange(0, p.tx);
1194
1195 Ok(if is_read {
1196 self.disk
1197 .read_vectored(&external_data, p.offset)
1198 .await
1199 .map_err(ScsiError::Disk)?;
1200
1201 p.tx
1202 } else {
1203 if self.disk.is_read_only() {
1204 return Err(ScsiError::WriteProtected);
1205 }
1206
1207 self.disk
1208 .write_vectored(&external_data, p.offset, p.fua)
1209 .await
1210 .map_err(ScsiError::Disk)?;
1211
1212 p.tx
1213 })
1214 }
1215
1216 async fn handle_synchronize_cache(&self) -> Result<usize, ScsiError> {
1217 self.disk.sync_cache().await.map_err(ScsiError::Disk)?;
1218 Ok(0)
1219 }
1220
1221 async fn handle_write_same(
1222 &self,
1223 external_data: &RequestBuffers<'_>,
1224 request: &Request,
1225 sector_count: u64,
1226 ) -> Result<usize, ScsiError> {
1227 let p = self.validate_write_same(external_data, request, sector_count)?;
1228 if p.tx > 0 {
1229 let external_data = external_data.subrange(0, p.sector_size);
1231 for offset in p.start_lba..p.start_lba + (p.lba_count as u64) {
1233 self.disk
1234 .write_vectored(&external_data, offset, p.fua)
1235 .await
1236 .map_err(ScsiError::Disk)?;
1237 }
1238 }
1239
1240 Ok(p.tx)
1241 }
1242
1243 async fn handle_start_stop(&self, request: &Request) -> Result<usize, ScsiError> {
1244 let cdb = scsi::StartStop::read_from_prefix(&request.cdb[..])
1245 .unwrap()
1246 .0; if cdb.immediate & scsi::IMMEDIATE_BIT != 0 {
1248 tracing::debug!("immediate bit is not supported");
1249 return Err(ScsiError::IllegalRequest(AdditionalSenseCode::INVALID_CDB));
1250 }
1251
1252 if cdb.flag.start() {
1253 return Ok(0);
1254 };
1255
1256 self.disk.sync_cache().await.map_err(ScsiError::Disk)?;
1257 Ok(0)
1258 }
1259}
1260
1261impl AsyncScsiDisk for SimpleScsiDisk {
1262 fn execute_scsi<'a>(
1263 &'a self,
1264 external_data: &'a RequestBuffers<'a>,
1265 request: &'a Request,
1266 ) -> StackFuture<'a, ScsiResult, { ASYNC_SCSI_DISK_STACK_SIZE }> {
1267 StackFuture::from(async move {
1268 let op = request.scsiop();
1269
1270 let sector_count = match self.get_and_update_sector_count(op) {
1271 Ok(c) => c,
1272 Err(_) => {
1273 let result = match op {
1275 ScsiOp::REQUEST_SENSE => {
1276 self.handle_request_sense(external_data, request, true)
1277 }
1278 _ => Err(ScsiError::UnitAttention),
1279 };
1280 return self.process_result(result, op);
1281 }
1282 };
1283
1284 let result = match op {
1285 ScsiOp::WRITE
1286 | ScsiOp::WRITE6
1287 | ScsiOp::WRITE12
1288 | ScsiOp::WRITE16
1289 | ScsiOp::READ
1290 | ScsiOp::READ6
1291 | ScsiOp::READ12
1292 | ScsiOp::READ16 => {
1293 self.handle_data_cdb(external_data, request, sector_count)
1294 .instrument(tracing::trace_span!("handle_data_cdb_async", ?op,))
1295 .await
1296 }
1297 ScsiOp::WRITE_SAME | ScsiOp::WRITE_SAME16 => {
1298 self.handle_write_same(external_data, request, sector_count)
1299 .instrument(tracing::trace_span!("handle_write_same_async"))
1300 .await
1301 }
1302 ScsiOp::SYNCHRONIZE_CACHE | ScsiOp::SYNCHRONIZE_CACHE16 => {
1303 self.handle_synchronize_cache()
1304 .instrument(tracing::trace_span!("handle_synchronize_cache_async", ?op,))
1305 .await
1306 }
1307 ScsiOp::START_STOP_UNIT => {
1308 self.handle_start_stop(request)
1309 .instrument(tracing::trace_span!("handle_start_stop_async",))
1310 .await
1311 }
1312 ScsiOp::UNMAP => {
1313 self.handle_unmap(external_data, request, sector_count)
1314 .instrument(tracing::debug_span!("handle_unmap_async"))
1315 .await
1316 }
1317 ScsiOp::PERSISTENT_RESERVE_IN | ScsiOp::PERSISTENT_RESERVE_OUT => {
1318 self.handle_persistent_reserve(external_data, request)
1319 .instrument(tracing::trace_span!("handle_persistent_reserve_async", ?op,))
1320 .await
1321 }
1322 _ => {
1323 let _span = tracing::trace_span!("handle_control_cdb", ?op,).entered();
1324 self.handle_control_cdb(external_data, request, sector_count)
1325 }
1326 };
1327
1328 self.process_result(result, op)
1329 })
1330 }
1331}
1332
1333impl Inspect for SimpleScsiDisk {
1334 fn inspect(&self, req: inspect::Request<'_>) {
1335 let mut resp = req.respond();
1336 resp.binary("disk_id", self.scsi_parameters.disk_id)
1337 .field("logical_sector_size", self.sector_size)
1338 .field(
1339 "physical_sector_size",
1340 1usize << self.sector_shift << self.physical_extra_shift,
1341 )
1342 .field(
1343 "sector_count",
1344 self.last_sector_count.load(Ordering::Relaxed),
1345 )
1346 .field("scsi_parameters", &self.scsi_parameters)
1347 .field("pr", self.support_pr)
1348 .field("backend", &self.disk);
1349 }
1350}
1351
1352#[derive(Default, Debug)]
1353struct SenseDataSlot {
1354 is_valid: AtomicBool,
1355 data: Mutex<Option<scsi::SenseData>>,
1356}
1357
1358impl SenseDataSlot {
1359 fn set(&self, sense_data: Option<&scsi::SenseData>) {
1361 match sense_data {
1362 None => {
1363 if self.is_valid.load(Ordering::Relaxed) {
1369 self.is_valid.store(false, Ordering::Relaxed)
1370 }
1371 }
1372 Some(sense_data) => {
1373 *self.data.lock() = Some(*sense_data);
1374 self.is_valid.store(true, Ordering::Release);
1375 }
1376 }
1377 }
1378
1379 fn get(&self) -> Option<scsi::SenseData> {
1381 if self.is_valid.load(Ordering::Relaxed) {
1382 *self.data.lock()
1385 } else {
1386 None
1387 }
1388 }
1389
1390 pub(crate) fn take(&self) -> Option<scsi::SenseData> {
1392 if self.is_valid.swap(false, Ordering::Acquire) {
1393 self.data.lock().take()
1396 } else {
1397 None
1398 }
1399 }
1400}