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