Skip to content

scsi: drop the initiator's state when the nexus is lost - #4258

Open
Robert Nowotny (bitranox) wants to merge 1 commit into
microsoft:mainfrom
bitranox:scsi-clear-nexus-state
Open

scsi: drop the initiator's state when the nexus is lost#4258
Robert Nowotny (bitranox) wants to merge 1 commit into
microsoft:mainfrom
bitranox:scsi-clear-nexus-state

Conversation

@bitranox

Copy link
Copy Markdown
Contributor

State scoped to the I_T nexus outlives it. PREVENT ALLOW MEDIUM REMOVAL, pending sense data and a queued medium-change event all live on the device, and storvsp's close() never reaches the SCSI layer, so they carry into whatever initiator comes next: a reopened channel, or a new guest after a machine reset, which closes every open channel on the way down.

What that costs depends on the command. A new initiator's first REQUEST SENSE can be answered with the previous initiator's sense, and a medium-change event queued for an initiator that is gone is delivered to the new one; neither needs the new initiator to do anything to invite it. The tray lock is the same defect but easier to mask, and measuring it says so: a guest that locks the door itself before ejecting, as the Linux sr driver does while probing, never observes the stale bit. One that ejects without having locked is refused with MEDIUM_REMOVAL_PREVENTED by a decision it never made.

AsyncScsiDisk gains clear_nexus_state, defaulting to nothing so a device holding no nexus-scoped state needs no change. SimpleScsiDvd clears the prevent/persistent pair, the pending medium event and its sense slot; SimpleScsiDisk clears its sense slot; AtapiScsiDisk clears its own slot in front of the device's and forwards. storvsp calls it when the primary channel closes, beside the protocol reset that is already there for the same reason.

Deliberately not reset: the tray and the media, so an ejected tray stays ejected as it would on real hardware; persistent reservations, which a bus reset is not supposed to clear; and the capacity latch on SimpleScsiDisk, since re-arming it would raise a spurious unit attention at the next initiator's first command.

Two tests, both verified to fail against the unfixed code. The first locks the tray, requires the eject to be refused, clears the nexus and requires the same command to succeed; the refusal is the control, without which a pass would only show that ejecting works at all. The second checks that sense clears while the tray state survives, and its command order is load-bearing: a successful command empties the sense slot on its way out, so written with the eject last it passed against a clear_nexus_state mutated to do nothing.

State scoped to the I_T nexus outlives it. PREVENT ALLOW MEDIUM REMOVAL,
pending sense data and a queued medium-change event all live on the device,
and storvsp's close() never reaches the SCSI layer, so they carry into
whatever initiator comes next: a reopened channel, or a new guest after a
machine reset, which closes every open channel on the way down.

What that costs depends on the command. A new initiator's first REQUEST
SENSE can be answered with the previous initiator's sense, and a
medium-change event queued for an initiator that is gone is delivered to
the new one; neither needs the new initiator to do anything to invite it.
The tray lock is the same defect but easier to mask, and measuring it says
so: a guest that locks the door itself before ejecting, as the Linux sr
driver does while probing, never observes the stale bit. One that ejects
without having locked is refused with MEDIUM_REMOVAL_PREVENTED by a
decision it never made.

AsyncScsiDisk gains clear_nexus_state, defaulting to nothing so a device
holding no nexus-scoped state needs no change. SimpleScsiDvd clears the
prevent/persistent pair, the pending medium event and its sense slot;
SimpleScsiDisk clears its sense slot; AtapiScsiDisk clears its own slot
in front of the device's and forwards. storvsp calls it when the primary
channel closes, beside the protocol reset that is already there for the
same reason.

What deliberately does NOT reset: the tray and the media. An ejected tray
stays ejected, as it would on real hardware. Nor do persistent
reservations, which a bus reset is not supposed to clear. The capacity
latch on SimpleScsiDisk stays too, since re-arming it would raise a
spurious unit attention at the next initiator's first command.

Two tests, both verified to fail against the unfixed code. The first locks
the tray, requires the eject to be refused, clears the nexus and requires
the same command to succeed - the refusal is the control, without which a
pass would only show that ejecting works. The second checks sense clears
while the tray state survives, and its command ORDER is load-bearing: a
successful command empties the sense slot on its way out, so written with
the eject last it passed against a clear_nexus_state mutated to do nothing.
@bitranox
Robert Nowotny (bitranox) requested a review from a team as a code owner August 14, 2026 18:57
Copilot AI lite review requested due to automatic review settings August 14, 2026 18:57
@bitranox
Robert Nowotny (bitranox) requested a review from a team as a code owner August 14, 2026 18:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a SCSI correctness issue where initiator-scoped state (I_T nexus state) could outlive the initiator in the storvsp transport, causing the next initiator/guest to inherit stale sense data, queued medium-change events, and a PREVENT ALLOW MEDIUM REMOVAL tray lock.

Changes:

  • Add AsyncScsiDisk::clear_nexus_state() (default no-op) and implement it for key SCSI device types to clear initiator-scoped state.
  • Invoke nexus-state clearing from storvsp when the primary channel closes (the point where the I_T nexus is effectively lost).
  • Add regression tests validating that tray lock and sense data do not survive nexus loss while device-scoped tray/media state does.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
vm/devices/storage/storvsp/src/lib.rs Clears per-initiator device state when the primary channel closes (nexus loss).
vm/devices/storage/scsidisk/src/scsidvd/mod.rs Implements nexus-state clearing for DVD and adds regression tests for tray-lock and sense behavior.
vm/devices/storage/scsidisk/src/lib.rs Implements nexus-state clearing for disks (clears pending sense, preserves capacity latch behavior).
vm/devices/storage/scsidisk/src/atapi_scsi.rs Clears both wrapper and inner sense slots on nexus loss for ATAPI wrapper devices.
vm/devices/storage/scsi_core/src/lib.rs Extends AsyncScsiDisk with a default clear_nexus_state() API and documentation.
Suppressed comments (1)

vm/devices/storage/scsidisk/src/scsidvd/mod.rs:2688

  • size_of isn’t in scope here; this will fail to compile unless it’s fully-qualified or imported in this module. Using core::mem::size_of keeps it self-contained.
        data[..size_of::<scsi::StartStop>()].copy_from_slice(cdb.as_bytes());

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread vm/devices/storage/scsidisk/src/scsidvd/mod.rs
@github-actions

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants