qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
To: Fam Zheng <famz@redhat.com>
Cc: pbonzini@redhat.com, qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH v2 1/1] hw/scsi: support SCSI-2 passthrough without PI
Date: Tue, 27 Mar 2018 18:18:04 -0300	[thread overview]
Message-ID: <daff504f-51a0-d5bb-b965-bd3959711857@linux.vnet.ibm.com> (raw)
In-Reply-To: <20180327172159.GK31422@lemon.usersys.redhat.com>



On 03/27/2018 02:21 PM, Fam Zheng wrote:
> On Tue, 03/13 13:43, Daniel Henrique Barboza wrote:
>> QEMU SCSI code makes assumptions about how the PROTECT and BYTCHK
>> works in the protocol, denying support for PI (Protection
>> Information) in case the guest OS requests it. However, in SCSI versions 2
>> and older, there is no PI concept in the protocol.
>>
>> This means that when dealing with such devices:
>>
>> - there is no PROTECT bit in byte 5 of the standard INQUIRY response. The
>> whole byte is marked as "Reserved";
>>
>> - there is no RDPROTECT in byte 2 of READ. We have 'Logical Unit Number'
>> in this field instead;
>>
>> - there is no VRPROTECT in byte 2 of VERIFY. We have 'Logical Unit Number'
>> in this field instead. This also means that the BYTCHK bit in this case
>> is not related to PI.
>>
>> Since QEMU does not consider these changes, a SCSI passthrough using
>> a SCSI-2 device will not work. It will mistake these fields with
>> PI information and return Illegal Request SCSI SENSE thinking
>> that the driver is asking for PI support.
>>
>> This patch fixes it by adding a new attribute called 'scsi_version'
>> that is read from the standard INQUIRY response of passthrough
>> devices. This allows for a version verification before applying
>> conditions related to PI that doesn't apply for older versions.
>>
>> Reported-by: Dac Nguyen <dacng@us.ibm.com>
>> Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
>> ---
>>
>> Changes in v2:
>> - removed "scsi_version" as a property
>> - scsi_version is now initialized with -1 in scsi_realize (that is
>> used by scsi_hd_realize, scsi_cd_realize, scsi_disk_realize and
>> scsi_block_realize) and scsi_generic_realize
>>
>>
>>   hw/scsi/scsi-disk.c    | 14 +++++++++++---
>>   hw/scsi/scsi-generic.c | 42 +++++++++++++++++++++++++++++++-----------
>>   include/hw/scsi/scsi.h |  1 +
>>   3 files changed, 43 insertions(+), 14 deletions(-)
>>
>> diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
>> index 49d2559d93..80b1eb92ae 100644
>> --- a/hw/scsi/scsi-disk.c
>> +++ b/hw/scsi/scsi-disk.c
>> @@ -2176,7 +2176,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
>>       case READ_12:
>>       case READ_16:
>>           DPRINTF("Read (sector %" PRId64 ", count %u)\n", r->req.cmd.lba, len);
>> -        if (r->req.cmd.buf[1] & 0xe0) {
>> +        if ((r->req.cmd.buf[1] & 0xe0) && (s->qdev.scsi_version > 2)) {
>>               goto illegal_request;
>>           }
>>           if (!check_lba_range(s, r->req.cmd.lba, len)) {
>> @@ -2206,8 +2206,12 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
>>           /* We get here only for BYTCHK == 0x01 and only for scsi-block.
>>            * As far as DMA is concerned, we can treat it the same as a write;
>>            * scsi_block_do_sgio will send VERIFY commands.
>> +         *
>> +         * For scsi versions 2 and older, the BYTCHK isn't related
>> +         * to VRPROTECT (in fact, there is no VRPROTECT). Skip
>> +         * this check in these versions.
>>            */
>> -        if (r->req.cmd.buf[1] & 0xe0) {
>> +        if ((r->req.cmd.buf[1] & 0xe0) && (s->qdev.scsi_version > 2)) {
>>               goto illegal_request;
>>           }
>>           if (!check_lba_range(s, r->req.cmd.lba, len)) {
>> @@ -2383,6 +2387,8 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
>>           return;
>>       }
>>   
>> +    dev->scsi_version = -1;
>> +
>>       if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) &&
>>               !(s->features & (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS))) {
>>           blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_removable_block_ops, s);
>> @@ -2796,6 +2802,8 @@ static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
>>   static int32_t scsi_block_dma_command(SCSIRequest *req, uint8_t *buf)
>>   {
>>       SCSIBlockReq *r = (SCSIBlockReq *)req;
>> +    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
>> +
>>       r->cmd = req->cmd.buf[0];
>>       switch (r->cmd >> 5) {
>>       case 0:
>> @@ -2821,7 +2829,7 @@ static int32_t scsi_block_dma_command(SCSIRequest *req, uint8_t *buf)
>>           abort();
>>       }
>>   
>> -    if (r->cdb1 & 0xe0) {
>> +    if ((r->cdb1 & 0xe0) && (s->qdev.scsi_version > 2)) {
>>           /* Protection information is not supported.  */
>>           scsi_check_condition(&r->req, SENSE_CODE(INVALID_FIELD));
>>           return 0;
>> diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
>> index 7414fe2d67..5cc5598983 100644
>> --- a/hw/scsi/scsi-generic.c
>> +++ b/hw/scsi/scsi-generic.c
>> @@ -194,17 +194,35 @@ static void scsi_read_complete(void * opaque, int ret)
>>               r->buf[3] |= 0x80;
>>           }
>>       }
>> -    if (s->type == TYPE_DISK &&
>> -        r->req.cmd.buf[0] == INQUIRY &&
>> -        r->req.cmd.buf[2] == 0xb0) {
>> -        uint32_t max_transfer =
>> -            blk_get_max_transfer(s->conf.blk) / s->blocksize;
>> -
>> -        assert(max_transfer);
>> -        stl_be_p(&r->buf[8], max_transfer);
>> -        /* Also take care of the opt xfer len. */
>> -        stl_be_p(&r->buf[12],
>> -                 MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
>> +    if (r->req.cmd.buf[0] == INQUIRY) {
>> +        /*
>> +         *  EVPD set to zero returns the standard INQUIRY data.
>> +         *
>> +         *  Check if scsi_version is unset (-1) to avoid re-defining it
>> +         *  each time an INQUIRY with standard data is received.
> Though I think re-defining it each time cannot hurt: it makes sure we always
> have the same view as the guest, even if the backend has odd behavior - response
> changes at second INQUIRY, e.g. after guest reboots.  Maybe we could reset
> scsi_version to -1 in scsi_disk_reset and scsi_generic_reset?
You're right. It is a fairly easy change to make and it will make the code
more resilient to odd behaviors after a guest reboot.

I've sent a v3 with this suggestion implemented.


Thanks,

Daniel


>
> Either way,
>
> Reviewed-by: Fam Zheng <famz@redhat.com>
>
>> +         *
>> +         *  On SCSI-2 and older, first 3 bits of byte 2 is the
>> +         *  ANSI-approved version, while on later versions the
>> +         *  whole byte 2 contains the version. Check if we're dealing
>> +         *  with a newer version and, in that case, assign the
>> +         *  whole byte.
>> +         */
>> +        if ((s->scsi_version == -1) && ((r->req.cmd.buf[1] & 0x01) == 0)) {
>> +            s->scsi_version = r->buf[2] & 0x07;
>> +            if (s->scsi_version > 2) {
>> +                s->scsi_version = r->buf[2];
>> +            }
>> +        }
>> +        if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) {
>> +            uint32_t max_transfer =
>> +                blk_get_max_transfer(s->conf.blk) / s->blocksize;
>> +
>> +            assert(max_transfer);
>> +            stl_be_p(&r->buf[8], max_transfer);
>> +            /* Also take care of the opt xfer len. */
>> +            stl_be_p(&r->buf[12],
>> +                     MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
>> +        }
>>       }
>>       scsi_req_data(&r->req, len);
>>       scsi_req_unref(&r->req);
>> @@ -525,6 +543,8 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp)
>>       s->type = scsiid.scsi_type;
>>       DPRINTF("device type %d\n", s->type);
>>   
>> +    s->scsi_version = -1;
>> +
>>       switch (s->type) {
>>       case TYPE_TAPE:
>>           s->blocksize = get_stream_blocksize(s->conf.blk);
>> diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
>> index 7ecaddac9d..a698c7f60c 100644
>> --- a/include/hw/scsi/scsi.h
>> +++ b/include/hw/scsi/scsi.h
>> @@ -85,6 +85,7 @@ struct SCSIDevice
>>       uint64_t max_lba;
>>       uint64_t wwn;
>>       uint64_t port_wwn;
>> +    int scsi_version;
>>   };
>>   
>>   extern const VMStateDescription vmstate_scsi_device;
>> -- 
>> 2.14.3
>>

      reply	other threads:[~2018-03-27 21:18 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-03-13 16:43 [Qemu-devel] [PATCH v2 1/1] hw/scsi: support SCSI-2 passthrough without PI Daniel Henrique Barboza
2018-03-21 17:40 ` Daniel Henrique Barboza
2018-03-27 17:21 ` Fam Zheng
2018-03-27 21:18   ` Daniel Henrique Barboza [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=daff504f-51a0-d5bb-b965-bd3959711857@linux.vnet.ibm.com \
    --to=danielhb@linux.vnet.ibm.com \
    --cc=famz@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).