Here it is at last...

It might be overengineered, I'm waiting for the SCSI experts to tell me about that. :)

Paolo
Virtio SCSI Controller Device Spec
==================================

The virtio controller device groups together one or more simple virtual
devices (ie. disk), and allows communicating to these devices using the
SCSI protocol.  A controller device represents a SCSI host with many
targets attached.

The virtio controller services two kinds of requests:

- command requests for a logical unit;

- task management functions related to a logical unit, target or
command.

The controller is also able to send out notifications about added
and removed devices.

v4:
    First public version

Configuration
-------------

Subsystem Device ID
    TBD

Virtqueues
    0..n-1:one requestq per target
    n:control transmitq
    n+1:control receiveq

Feature bits
    VIRTIO_SCSI_F_INOUT - Whether a single request can include both
        read-only and write-only data buffers.

Device configuration layout
    struct virtio_scsi_config {
        u32 num_targets;
    }

    num_targets is the number of targets, and the id of the
    virtqueue used for the control receiveq.

Device initialization
---------------------

The initialization routine should first of all discover the controller's
control virtqueues.

The driver should then place at least a buffer in the control receiveq.
Buffers returned by the device on the control receiveq may be referred
to as "events" in the rest of the document.

The driver can immediately issue requests (for example, INQUIRY or
REPORT LUNS) or task management functions (for example, I_T RESET).

Device operation: request queue
-------------------------------

The driver queues requests to the virtqueue, and they are used by the device
(not necessarily in order).

Requests have the following format:

    struct virtio_scsi_req
    {
        u32 type;
        ...
        u8 response;
    }

    #define VIRTIO_SCSI_T_BARRIER         0x80000000

    The type identifies the remaining fields.  The value
    VIRTIO_SCSI_T_BARRIER can be ORed in the type as well.  This bit
    indicates that this request acts as a barrier and that all preceding
    requests must be complete before this one, and all following requests
    must not be started until this is complete.  Note that a barrier
    does not flush caches in the underlying backend device in host,
    and thus does not serve as data consistency guarantee.  The driver
    must send a SYNCHRONIZE CACHE command to flush the host cache.

    Valid response values are defined separately for each command.

- Task management function

    #define VIRTIO_SCSI_T_TMF                      0

    #define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET       1
    #define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET      4
    #define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET   5
    #define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET       7

    #define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_DETACH  (1 << 24)

    struct virtio_scsi_req_tmf
    {
        u32 subtype;
        u8 lun[8];
        u8 additional[];
        u8 response;
    }

    /* command-specific response values */
    #define VIRTIO_SCSI_S_FUNCTION_COMPLETE        0
    #define VIRTIO_SCSI_S_NO_TARGET                1
    #define VIRTIO_SCSI_S_TARGET_FAILURE           2
    #define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED       3
    #define VIRTIO_SCSI_S_FUNCTION_REJECTED        4
    #define VIRTIO_SCSI_S_INCORRECT_LUN            5

    The type is VIRTIO_SCSI_T_LUN_INFO, possibly with the
    VIRTIO_SCSI_T_BARRIER bit ORed in.

    The subtype and lun field are filled in by the driver, the additional
    and response field is filled in by the device.  Unknown LUNs are
    ignored; also, the lun field is ignored for the I_T NEXUS RESET
    command.

    Task management functions accepting an I_T_L_Q nexus (ABORT TASK,
    QUERY TASK) are only accessible through the control transmitq.
    Task management functions not in the above list are not accessible
    in this version of the specification.  Future versions may allow
    access to them through additional features.

    VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_DETACH asks the device to make the
    logical unit (and the target as well if this is the last logical
    unit) disappear.  It takes an I_T_L nexus.  This non-standard TMF
    should be used in response to a host request to shutdown a target
    or LUN, after having placed the LUN in a clean state.

    The outcome of the task management function is written by the device
    in the response field.  A value of VIRTIO_SCSI_S_NO_TARGET means
    that (even though the virtqueue exists) there is no target with this
    number.  Other return values map 1-to-1 with those defined in SAM.

- SCSI command

    #define VIRTIO_SCSI_T_CMD                       1

    struct virtio_scsi_req_cmd {
        u32 type;
        u32 ioprio;
        u8 lun[8];
        u64 id;
        u32 num_dataout, num_datain;
        char cdb[];
        char data[][num_dataout+num_datain];
        u8 sense[];
        u32 sense_len;
        u32 residual;
        u8 status;
        u8 response;
    };

    /* command-specific response values */
    #define VIRTIO_SCSI_S_OK              0
    #define VIRTIO_SCSI_S_NO_TARGET       1
    #define VIRTIO_SCSI_S_UNDERRUN        6
    #define VIRTIO_SCSI_S_ABORTED         7
    #define VIRTIO_SCSI_S_FAILURE         8

    The type field must be VIRTIO_SCSI_T_CMD, possibly with the
    VIRTIO_SCSI_T_BARRIER bit ORed in.  The ioprio field will indicate the
    priority of this request, with higher values corresponding to higher
    priorities.  The lun field addresses a logical unit in the target.
    The id field is the command identifier as defined in SAM.  All
    of these fields are always read-only.

    The cdb, data and sense fields must reside in separate buffers.
    The cdb field is always read-only.  The data buffers may be either
    read-only or write-only, depending on the request, with the read-only
    buffers coming first.  The sense buffer is always write-only.

    The request shall have num_dataout read-only data buffers and
    num_datain write-only data buffers.  One of these two values must be
    zero if the VIRTIO_SCSI_F_INOUT has not been negotiated.

    Remaining fields are filled in by the device.  The sense_len field
    indicates the number of bytes actually written to the sense buffer,
    while the residual field indicates the residual size, calculated as
    data_length - number_of_transferred_bytes.

    The status byte is written by the device to be the SCSI status code.

    The response byte is written by the device to be one of the following:

    - VIRTIO_SCSI_S_OK when the request was completed and the status byte
      is filled with a SCSI status code (not necessarily "GOOD").

    - VIRTIO_SCSI_S_NO_TARGET is returned if there is no target with this
      number.

    - VIRTIO_SCSI_S_UNDERRUN if the content of the CDB requires transferring
      more data than is available in the data buffers.

    - VIRTIO_SCSI_S_ABORTED if the request was cancelled due to a LUN reset,
      target reset or another task management function.

    - VIRTIO_SCSI_S_FAILURE for other host or guest error.

- Asynchronous notification query

    #define VIRTIO_SCSI_T_AN_QUERY                    2

    struct virtio_scsi_an_subscribe {
        u8  lun[8];
        u32 event_requested;
        u32 event_actual;
    }

    #define VIRTIO_SCSI_EVT_ASYNC_MEDIA_CHANGE        16

    By sending this command, the driver asks the controller which
    events it can report, as described in Annex A of the SCSI MMC
    specification.  The driver writes the events it is interested in
    into the event_requested; the device responds by writing the events
    that it supports into event_actual.

    The lun and event_requested fields are written by the driver.
    The event_actual and response fields are written by the device.

    Valid values of the response byte are VIRTIO_SCSI_S_OK,
    VIRTIO_SCSI_S_NO_TARGET, VIRTIO_SCSI_S_FAILURE (with the same meaning
    as above).

- Asynchronous notification subscription

    #define VIRTIO_SCSI_T_AN_SUBSCRIBE                3

    struct virtio_scsi_an_subscribe {
        u8  lun[8];
        u32 event_requested;
        u32 event_actual;
    }

    #define VIRTIO_SCSI_EVT_ASYNC_MEDIA_CHANGE        16

    By sending this command, the driver asks the controller to report
    events as described in Annex A of the SCSI MMC specification.  The
    driver writes the events it is interested in into the event_requested;
    the device responds by writing the events that it supports into
    event_actual.

    The lun and event_requested fields are written by the driver.
    The event_actual and response fields are written by the device.

    Valid values of the response byte are VIRTIO_SCSI_S_OK,
    VIRTIO_SCSI_S_NO_TARGET, VIRTIO_SCSI_S_FAILURE (with the same meaning
    as above).

Device operation: control transmitq
-----------------------------------

Currently, the control transmitq is only used to send out-of-band task
management functions.  This allows the driver to abort tasks even when
a target's virtqueue is full.  Note that, in contrast, sending TMF on
the request virtqueues allows setting the VIRTIO_SCSI_T_BARRIER bit;
no similar functionality is provided by the control transmitq.

Requests have the following format:

    struct virtio_scsi_ctrl
    {
        u32 type;
        ...
        u8 response;
    }

    The type identifies the remaining fields.

The following commands are defined:

- Task management function

    #define VIRTIO_SCSI_T_TMF                      0

    #define VIRTIO_SCSI_T_TMF_ABORT_TASK           0
    #define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET       1
    #define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET      4
    #define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET   5
    #define VIRTIO_SCSI_T_TMF_QUERY_TASK           6
    #define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET       7

    #define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_DETACH  (1 << 24)

    struct virtio_scsi_ctrl_tmf
    {
        u32 subtype;
        u32 target;
        u8 lun[8];
        u64 id;
        u8 additional[];
        u8 response;
    }

    /* command-specific response values */
    #define VIRTIO_SCSI_S_FUNCTION_COMPLETE        0
    #define VIRTIO_SCSI_S_NO_TARGET                1
    #define VIRTIO_SCSI_S_FAILURE                  2
    #define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED       3
    #define VIRTIO_SCSI_S_FUNCTION_REJECTED        4
    #define VIRTIO_SCSI_S_INCORRECT_LUN            5

    The type is VIRTIO_SCSI_T_TMF.  All fields but the last one are
    filled by the driver, the response field is filled in by the device.
    The id command must match the id in a SCSI command.  Irrelevant fields
    for the requested TMF are ignored.

    VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_DETACH asks the device to make the
    logical unit (and the target as well if this is the last logical
    unit) disappear.  It takes an I_T_L nexus.  This non-standard TMF
    should be used in response to a host request to shutdown a target
    or LUN, after having placed the LUN in a clean state.

    The outcome of the task management function is written by the device
    in the response field.  VIRTIO_SCSI_S_NO_TARGET is returned if
    there is no target with this number.  Other return values map 1-to-1
    with those defined in SAM.

Device operation: control receiveq
----------------------------------

The control receiveq is used by the device to report information on
devices that are attached to the controller.  The driver should always
leave a few (?) buffers ready in the control receiveq.  The device may
end up dropping events if it finds no buffer ready.

Buffers are placed in the control receiveq and filled by the device when
interesting events occur.  Events have the following format:

    #define VIRTIO_SCSI_T_EVENTS_MISSED   0x80000000

    struct virtio_scsi_ctrl_recv {
        u32 event;
        u8 event-specific-data[];
        u8 event-specific-data-ret[];
    }

If bit 31 is set in the event, the device failed to report an event due
to missing buffers.  In this case, the driver should poll the logical
units for unit attention conditions, and/or do whatever form of bus scan
is appropriate for the guest operating system.

The following events are defined:

- Transport reset

    #define VIRTIO_SCSI_T_TRANSPORT_RESET  0

    struct virtio_scsi_reset {
        u32 target;
        u8  lun[8];
        u32 reason;
    }

    #define VIRTIO_SCSI_EVT_RESET_HARD         1
    #define VIRTIO_SCSI_EVT_RESET_RESCAN       2
    #define VIRTIO_SCSI_EVT_RESET_SHUTDOWN     4
    #define VIRTIO_SCSI_EVT_RESET_REMOVED      8

    By sending this event, the controller signals that a logical unit
    on a target has been reset, including the case of a new device
    appearing or disappearing on the bus.

    The device fills in all three fields.  By convention, a LUN field
    referring to a well-known LUN means that the event affects all LUNs
    enumerated by the target.

    The reason value is one of the four #define values appearing above.
    VIRTIO_SCSI_EVT_RESET_REMOVED is used if the target or logical unit
    is no longer able to receive commands.  VIRTIO_SCSI_EVT_RESET_HARD
    is used if the logical unit has been reset, but is still present.
    VIRTIO_SCSI_EVT_RESET_RESCAN is used if a target or logical unit has
    just appeared on the controller.  VIRTIO_SCSI_EVT_RESET_SHUTDOWN
    is used when the host wants to initiate a graceful shutdown of a
    logical unit.

    Events should also be reported via sense codes or response codes,
    with the exception of newly appeared targets:

    - VIRTIO_SCSI_EVT_RESET_HARD
      sense UNIT ATTENTION
      asc POWER ON, RESET OR BUS DEVICE RESET OCCURRED

    - VIRTIO_SCSI_EVT_RESET_RESCAN
      sense UNIT ATTENTION
      asc REPORTED LUNS DATA HAS CHANGED

    - VIRTIO_SCSI_EVT_RESET_SHUTDOWN
      sense UNIT ATTENTION
      asc TARGET OPERATING CONDITIONS HAVE CHANGED
      ascq 0x80 (vendor specific)

    - VIRTIO_SCSI_EVT_RESET_REMOVED
      sense ILLEGAL REQUEST
      asc LOGICAL UNIT NOT SUPPORTED

    However, in general events should be more easily handled by the
    driver than sense codes.

- Asynchronous notification

    #define VIRTIO_SCSI_T_MEDIA_CHANGE     1

    struct virtio_scsi_reset {
        u32 target;
        u8  lun[8];
        u32 event;
    }

    #define VIRTIO_SCSI_EVT_ASYNC_MEDIA_CHANGE        1

    By sending this event, the controller signals that an event was
    fired from a physical interface.  The device only sends events
    that the driver has subscribed to via the "Asynchronous notification
    subscription" command.

    All fields are written by the device.

Reply via email to