Commit bf6e3cc4 authored by Anthony Liguori's avatar Anthony Liguori
Browse files

Merge remote-tracking branch 'bonzini/scsi-next' into staging



# By Paolo Bonzini (4) and Peter Lieven (1)
# Via Paolo Bonzini
* bonzini/scsi-next:
  help: add id suboption to -iscsi
  scsi-disk: fix WRITE SAME with large non-zero payload
  block/iscsi: introduce bdrv_co_{readv, writev, flush_to_disk}
  scsi-disk: fix VERIFY emulation
  scsi-bus: fix transfer length and direction for VERIFY command

Message-id: 1386594157-17535-1-git-send-email-pbonzini@redhat.com
Signed-off-by: default avatarAnthony Liguori <aliguori@amazon.com>
parents 47acdd63 2fe3798c
Loading
Loading
Loading
Loading
+105 −296
Original line number Diff line number Diff line
@@ -239,44 +239,6 @@ iscsi_process_write(void *arg)
    iscsi_set_events(iscsilun);
}

static int
iscsi_aio_writev_acb(IscsiAIOCB *acb);

static void
iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status,
                     void *command_data, void *opaque)
{
    IscsiAIOCB *acb = opaque;

    trace_iscsi_aio_write16_cb(iscsi, status, acb, acb->canceled);

    g_free(acb->buf);
    acb->buf = NULL;

    if (acb->canceled != 0) {
        return;
    }

    acb->status = 0;
    if (status != 0) {
        if (status == SCSI_STATUS_CHECK_CONDITION
            && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
            && acb->retries-- > 0) {
            scsi_free_scsi_task(acb->task);
            acb->task = NULL;
            if (iscsi_aio_writev_acb(acb) == 0) {
                iscsi_set_events(acb->iscsilun);
                return;
            }
        }
        error_report("Failed to write16 data to iSCSI lun. %s",
                     iscsi_get_error(iscsi));
        acb->status = -EIO;
    }

    iscsi_schedule_bh(acb);
}

static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun)
{
    return sector * iscsilun->block_size / BDRV_SECTOR_SIZE;
@@ -301,324 +263,172 @@ static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors,
    return 1;
}

static int
iscsi_aio_writev_acb(IscsiAIOCB *acb)
static int coroutine_fn iscsi_co_writev(BlockDriverState *bs,
                                        int64_t sector_num, int nb_sectors,
                                        QEMUIOVector *iov)
{
    struct iscsi_context *iscsi = acb->iscsilun->iscsi;
    size_t size;
    uint32_t num_sectors;
    IscsiLun *iscsilun = bs->opaque;
    struct IscsiTask iTask;
    uint64_t lba;
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
    struct iscsi_data data;
#endif
    int ret;

    acb->canceled   = 0;
    acb->bh         = NULL;
    acb->status     = -EINPROGRESS;
    acb->buf        = NULL;
    uint32_t num_sectors;
    uint8_t *data = NULL;
    uint8_t *buf = NULL;

    /* this will allow us to get rid of 'buf' completely */
    size = acb->nb_sectors * BDRV_SECTOR_SIZE;
    if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
        return -EINVAL;
    }

    lba = sector_qemu2lun(sector_num, iscsilun);
    num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
    data.size = MIN(size, acb->qiov->size);

    /* if the iovec only contains one buffer we can pass it directly */
    if (acb->qiov->niov == 1) {
        data.data = acb->qiov->iov[0].iov_base;
    if (iov->niov == 1) {
        data = iov->iov[0].iov_base;
    } else {
        acb->buf = g_malloc(data.size);
        qemu_iovec_to_buf(acb->qiov, 0, acb->buf, data.size);
        data.data = acb->buf;
        size_t size = MIN(nb_sectors * BDRV_SECTOR_SIZE, iov->size);
        buf = g_malloc(size);
        qemu_iovec_to_buf(iov, 0, buf, size);
        data = buf;
    }
#endif

    acb->task = malloc(sizeof(struct scsi_task));
    if (acb->task == NULL) {
        error_report("iSCSI: Failed to allocate task for scsi WRITE16 "
                     "command. %s", iscsi_get_error(iscsi));
        return -1;
    iscsi_co_init_iscsitask(iscsilun, &iTask);
retry:
    iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba,
                                    data, num_sectors * iscsilun->block_size,
                                    iscsilun->block_size, 0, 0, 0, 0, 0,
                                    iscsi_co_generic_cb, &iTask);
    if (iTask.task == NULL) {
        g_free(buf);
        return -EIO;
    }
    memset(acb->task, 0, sizeof(struct scsi_task));

    acb->task->xfer_dir = SCSI_XFER_WRITE;
    acb->task->cdb_size = 16;
    acb->task->cdb[0] = 0x8a;
    lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
    *(uint32_t *)&acb->task->cdb[2]  = htonl(lba >> 32);
    *(uint32_t *)&acb->task->cdb[6]  = htonl(lba & 0xffffffff);
    num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
    *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
    acb->task->expxferlen = size;

#if defined(LIBISCSI_FEATURE_IOVECTOR)
    ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
                                   iscsi_aio_write16_cb,
                                   NULL,
                                   acb);
#else
    ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
                                   iscsi_aio_write16_cb,
                                   &data,
                                   acb);
    scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
                          iov->niov);
#endif
    if (ret != 0) {
        scsi_free_scsi_task(acb->task);
        g_free(acb->buf);
        return -1;
    while (!iTask.complete) {
        iscsi_set_events(iscsilun);
        qemu_coroutine_yield();
    }

#if defined(LIBISCSI_FEATURE_IOVECTOR)
    scsi_task_set_iov_out(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov);
#endif

    return 0;
    if (iTask.task != NULL) {
        scsi_free_scsi_task(iTask.task);
        iTask.task = NULL;
    }

static BlockDriverAIOCB *
iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
                 QEMUIOVector *qiov, int nb_sectors,
                 BlockDriverCompletionFunc *cb,
                 void *opaque)
{
    IscsiLun *iscsilun = bs->opaque;
    IscsiAIOCB *acb;

    if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
        return NULL;
    if (iTask.do_retry) {
        goto retry;
    }

    acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
    trace_iscsi_aio_writev(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);

    acb->iscsilun    = iscsilun;
    acb->qiov        = qiov;
    acb->nb_sectors  = nb_sectors;
    acb->sector_num  = sector_num;
    acb->retries     = ISCSI_CMD_RETRIES;
    g_free(buf);

    if (iscsi_aio_writev_acb(acb) != 0) {
        qemu_aio_release(acb);
        return NULL;
    if (iTask.status != SCSI_STATUS_GOOD) {
        return -EIO;
    }

    iscsi_set_events(iscsilun);
    return &acb->common;
    return 0;
}

static int
iscsi_aio_readv_acb(IscsiAIOCB *acb);

static void
iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status,
                    void *command_data, void *opaque)
static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
                                       int64_t sector_num, int nb_sectors,
                                       QEMUIOVector *iov)
{
    IscsiAIOCB *acb = opaque;

    trace_iscsi_aio_read16_cb(iscsi, status, acb, acb->canceled);

    if (acb->canceled != 0) {
        return;
    }

    acb->status = 0;
    if (status != 0) {
        if (status == SCSI_STATUS_CHECK_CONDITION
            && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
            && acb->retries-- > 0) {
            scsi_free_scsi_task(acb->task);
            acb->task = NULL;
            if (iscsi_aio_readv_acb(acb) == 0) {
                iscsi_set_events(acb->iscsilun);
                return;
            }
        }
        error_report("Failed to read16 data from iSCSI lun. %s",
                     iscsi_get_error(iscsi));
        acb->status = -EIO;
    }

    iscsi_schedule_bh(acb);
}

static int
iscsi_aio_readv_acb(IscsiAIOCB *acb)
{
    struct iscsi_context *iscsi = acb->iscsilun->iscsi;
    size_t size;
    IscsiLun *iscsilun = bs->opaque;
    struct IscsiTask iTask;
    uint64_t lba;
    uint32_t num_sectors;
    int ret;
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
    int i;
#endif

    acb->canceled    = 0;
    acb->bh          = NULL;
    acb->status      = -EINPROGRESS;
    acb->buf         = NULL;

    size = acb->nb_sectors * BDRV_SECTOR_SIZE;

    acb->task = malloc(sizeof(struct scsi_task));
    if (acb->task == NULL) {
        error_report("iSCSI: Failed to allocate task for scsi READ16 "
                     "command. %s", iscsi_get_error(iscsi));
        return -1;
    if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
        return -EINVAL;
    }
    memset(acb->task, 0, sizeof(struct scsi_task));

    acb->task->xfer_dir = SCSI_XFER_READ;
    acb->task->expxferlen = size;
    lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
    num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
    lba = sector_qemu2lun(sector_num, iscsilun);
    num_sectors = sector_qemu2lun(nb_sectors, iscsilun);

    switch (acb->iscsilun->type) {
    iscsi_co_init_iscsitask(iscsilun, &iTask);
retry:
    switch (iscsilun->type) {
    case TYPE_DISK:
        acb->task->cdb_size = 16;
        acb->task->cdb[0]  = 0x88;
        *(uint32_t *)&acb->task->cdb[2]  = htonl(lba >> 32);
        *(uint32_t *)&acb->task->cdb[6]  = htonl(lba & 0xffffffff);
        *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
        iTask.task = iscsi_read16_task(iscsilun->iscsi, iscsilun->lun, lba,
                                       num_sectors * iscsilun->block_size,
                                       iscsilun->block_size, 0, 0, 0, 0, 0,
                                       iscsi_co_generic_cb, &iTask);
        break;
    default:
        acb->task->cdb_size = 10;
        acb->task->cdb[0]  = 0x28;
        *(uint32_t *)&acb->task->cdb[2] = htonl(lba);
        *(uint16_t *)&acb->task->cdb[7] = htons(num_sectors);
        iTask.task = iscsi_read10_task(iscsilun->iscsi, iscsilun->lun, lba,
                                       num_sectors * iscsilun->block_size,
                                       iscsilun->block_size, 0, 0, 0, 0, 0,
                                       iscsi_co_generic_cb, &iTask);
        break;
    }

    ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
                                   iscsi_aio_read16_cb,
                                   NULL,
                                   acb);
    if (ret != 0) {
        scsi_free_scsi_task(acb->task);
        return -1;
    if (iTask.task == NULL) {
        return -EIO;
    }

#if defined(LIBISCSI_FEATURE_IOVECTOR)
    scsi_task_set_iov_in(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov);
    scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
#else
    for (i = 0; i < acb->qiov->niov; i++) {
        scsi_task_add_data_in_buffer(acb->task,
                acb->qiov->iov[i].iov_len,
                acb->qiov->iov[i].iov_base);
    for (i = 0; i < iov->niov; i++) {
        scsi_task_add_data_in_buffer(iTask.task,
                                     iov->iov[i].iov_len,
                                     iov->iov[i].iov_base);
    }
#endif
    return 0;
}

static BlockDriverAIOCB *
iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
                QEMUIOVector *qiov, int nb_sectors,
                BlockDriverCompletionFunc *cb,
                void *opaque)
{
    IscsiLun *iscsilun = bs->opaque;
    IscsiAIOCB *acb;

    if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
        return NULL;
    }

    acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
    trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);

    acb->nb_sectors  = nb_sectors;
    acb->sector_num  = sector_num;
    acb->iscsilun    = iscsilun;
    acb->qiov        = qiov;
    acb->retries     = ISCSI_CMD_RETRIES;

    if (iscsi_aio_readv_acb(acb) != 0) {
        qemu_aio_release(acb);
        return NULL;
    }

    while (!iTask.complete) {
        iscsi_set_events(iscsilun);
    return &acb->common;
}

static int
iscsi_aio_flush_acb(IscsiAIOCB *acb);

static void
iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
                     void *command_data, void *opaque)
{
    IscsiAIOCB *acb = opaque;

    if (acb->canceled != 0) {
        return;
        qemu_coroutine_yield();
    }

    acb->status = 0;
    if (status != 0) {
        if (status == SCSI_STATUS_CHECK_CONDITION
            && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
            && acb->retries-- > 0) {
            scsi_free_scsi_task(acb->task);
            acb->task = NULL;
            if (iscsi_aio_flush_acb(acb) == 0) {
                iscsi_set_events(acb->iscsilun);
                return;
            }
        }
        error_report("Failed to sync10 data on iSCSI lun. %s",
                     iscsi_get_error(iscsi));
        acb->status = -EIO;
    if (iTask.task != NULL) {
        scsi_free_scsi_task(iTask.task);
        iTask.task = NULL;
    }

    iscsi_schedule_bh(acb);
    if (iTask.do_retry) {
        goto retry;
    }

static int
iscsi_aio_flush_acb(IscsiAIOCB *acb)
{
    struct iscsi_context *iscsi = acb->iscsilun->iscsi;

    acb->canceled   = 0;
    acb->bh         = NULL;
    acb->status     = -EINPROGRESS;
    acb->buf        = NULL;

    acb->task = iscsi_synchronizecache10_task(iscsi, acb->iscsilun->lun,
                                         0, 0, 0, 0,
                                         iscsi_synccache10_cb,
                                         acb);
    if (acb->task == NULL) {
        error_report("iSCSI: Failed to send synchronizecache10 command. %s",
                     iscsi_get_error(iscsi));
        return -1;
    if (iTask.status != SCSI_STATUS_GOOD) {
        return -EIO;
    }

    return 0;
}

static BlockDriverAIOCB *
iscsi_aio_flush(BlockDriverState *bs,
                BlockDriverCompletionFunc *cb, void *opaque)
static int coroutine_fn iscsi_co_flush(BlockDriverState *bs)
{
    IscsiLun *iscsilun = bs->opaque;
    struct IscsiTask iTask;

    IscsiAIOCB *acb;
    iscsi_co_init_iscsitask(iscsilun, &iTask);

    acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
retry:
    if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0,
                                      0, iscsi_co_generic_cb, &iTask) == NULL) {
        return -EIO;
    }

    acb->iscsilun    = iscsilun;
    acb->retries     = ISCSI_CMD_RETRIES;
    while (!iTask.complete) {
        iscsi_set_events(iscsilun);
        qemu_coroutine_yield();
    }

    if (iscsi_aio_flush_acb(acb) != 0) {
        qemu_aio_release(acb);
        return NULL;
    if (iTask.task != NULL) {
        scsi_free_scsi_task(iTask.task);
        iTask.task = NULL;
    }

    iscsi_set_events(iscsilun);
    if (iTask.do_retry) {
        goto retry;
    }

    return &acb->common;
    if (iTask.status != SCSI_STATUS_GOOD) {
        return -EIO;
    }

    return 0;
}

#ifdef __linux__
@@ -1624,10 +1434,9 @@ static BlockDriver bdrv_iscsi = {
#if defined(SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED)
    .bdrv_co_write_zeroes = iscsi_co_write_zeroes,
#endif

    .bdrv_aio_readv  = iscsi_aio_readv,
    .bdrv_aio_writev = iscsi_aio_writev,
    .bdrv_aio_flush  = iscsi_aio_flush,
    .bdrv_co_readv         = iscsi_co_readv,
    .bdrv_co_writev        = iscsi_co_writev,
    .bdrv_co_flush_to_disk = iscsi_co_flush,

#ifdef __linux__
    .bdrv_ioctl       = iscsi_ioctl,
+3 −3
Original line number Diff line number Diff line
@@ -3053,13 +3053,13 @@ fi

##########################################
# Do we have libiscsi
# We check for iscsi_unmap_sync() to make sure we have a
# recent enough version of libiscsi.
# We check for iscsi_write16_sync() to make sure we have a
# at least version 1.4.0 of libiscsi.
if test "$libiscsi" != "no" ; then
  cat > $TMPC << EOF
#include <stdio.h>
#include <iscsi/iscsi.h>
int main(void) { iscsi_unmap_sync(NULL,0,0,0,NULL,0); return 0; }
int main(void) { iscsi_write16_sync(NULL,0,0,NULL,0,0,0,0,0,0,0); return 0; }
EOF
  if $pkg_config --atleast-version=1.7.0 libiscsi; then
    libiscsi="yes"
+13 −1
Original line number Diff line number Diff line
@@ -886,7 +886,6 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
    case RELEASE:
    case ERASE:
    case ALLOW_MEDIUM_REMOVAL:
    case VERIFY_10:
    case SEEK_10:
    case SYNCHRONIZE_CACHE:
    case SYNCHRONIZE_CACHE_16:
@@ -903,6 +902,16 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
    case ALLOW_OVERWRITE:
        cmd->xfer = 0;
        break;
    case VERIFY_10:
    case VERIFY_12:
    case VERIFY_16:
        if ((buf[1] & 2) == 0) {
            cmd->xfer = 0;
        } else if ((buf[1] & 4) == 1) {
            cmd->xfer = 1;
        }
        cmd->xfer *= dev->blocksize;
        break;
    case MODE_SENSE:
        break;
    case WRITE_SAME_10:
@@ -1100,6 +1109,9 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd)
    case WRITE_VERIFY_12:
    case WRITE_16:
    case WRITE_VERIFY_16:
    case VERIFY_10:
    case VERIFY_12:
    case VERIFY_16:
    case COPY:
    case COPY_VERIFY:
    case COMPARE:
+21 −8
Original line number Diff line number Diff line
@@ -1626,7 +1626,7 @@ static void scsi_write_same_complete(void *opaque, int ret)
        bdrv_acct_start(s->qdev.conf.bs, &r->acct, data->iov.iov_len, BDRV_ACCT_WRITE);
        r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, data->sector,
                                       &data->qiov, data->iov.iov_len / 512,
                                       scsi_write_same_complete, r);
                                       scsi_write_same_complete, data);
        return;
    }

@@ -1720,10 +1720,19 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req)
        scsi_disk_emulate_unmap(r, r->iov.iov_base);
        break;

    case VERIFY_10:
    case VERIFY_12:
    case VERIFY_16:
        if (r->req.status == -1) {
            scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
        }
        break;

    case WRITE_SAME_10:
    case WRITE_SAME_16:
        scsi_disk_emulate_write_same(r, r->iov.iov_base);
        break;

    default:
        abort();
    }
@@ -1964,6 +1973,14 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
    case UNMAP:
        DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer);
        break;
    case VERIFY_10:
    case VERIFY_12:
    case VERIFY_16:
        DPRINTF("Verify (bytchk %lu)\n", (r->req.buf[1] >> 1) & 3);
        if (req->cmd.buf[1] & 6) {
            goto illegal_request;
        }
        break;
    case WRITE_SAME_10:
    case WRITE_SAME_16:
        DPRINTF("WRITE SAME %d (len %lu)\n",
@@ -2044,10 +2061,6 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
            scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
            return 0;
        }
        /* fallthrough */
    case VERIFY_10:
    case VERIFY_12:
    case VERIFY_16:
        DPRINTF("Write %s(sector %" PRId64 ", count %u)\n",
                (command & 0xe) == 0xe ? "And Verify " : "",
                r->req.cmd.lba, len);
@@ -2315,14 +2328,14 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = {
    [UNMAP]                           = &scsi_disk_emulate_reqops,
    [WRITE_SAME_10]                   = &scsi_disk_emulate_reqops,
    [WRITE_SAME_16]                   = &scsi_disk_emulate_reqops,
    [VERIFY_10]                       = &scsi_disk_emulate_reqops,
    [VERIFY_12]                       = &scsi_disk_emulate_reqops,
    [VERIFY_16]                       = &scsi_disk_emulate_reqops,

    [READ_6]                          = &scsi_disk_dma_reqops,
    [READ_10]                         = &scsi_disk_dma_reqops,
    [READ_12]                         = &scsi_disk_dma_reqops,
    [READ_16]                         = &scsi_disk_dma_reqops,
    [VERIFY_10]                       = &scsi_disk_dma_reqops,
    [VERIFY_12]                       = &scsi_disk_dma_reqops,
    [VERIFY_16]                       = &scsi_disk_dma_reqops,
    [WRITE_6]                         = &scsi_disk_dma_reqops,
    [WRITE_10]                        = &scsi_disk_dma_reqops,
    [WRITE_12]                        = &scsi_disk_dma_reqops,
+1 −1
Original line number Diff line number Diff line
@@ -2096,7 +2096,7 @@ ETEXI
DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi,
    "-iscsi [user=user][,password=password]\n"
    "       [,header-digest=CRC32C|CR32C-NONE|NONE-CRC32C|NONE\n"
    "       [,initiator-name=iqn]\n"
    "       [,initiator-name=initiator-iqn][,id=target-iqn]\n"
    "                iSCSI session parameters\n", QEMU_ARCH_ALL)
STEXI