mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-11 03:24:58 -06:00
Merge remote-tracking branch 'remotes/bonzini/scsi-next' into staging
* remotes/bonzini/scsi-next:
virtio-scsi: define dummy handle_output for vhost-scsi vqs
block/iscsi: drop obsolete pointers from iscsi_co_writev
block/iscsi: fix init value for iTask->retries
block/iscsi: bump libiscsi requirement to 1.9.0
virtio-scsi: add support for the any_layout feature
virtio-scsi: introduce virtio_scsi_complete_cmd_req
virtio-scsi: prepare sense data handling for any_layout
virtio-scsi: add extra argument and return type to qemu_sgl_concat
virtio-scsi: add target swap for VirtIOSCSICtrlTMFReq fields
virtio-scsi: start preparing for any_layout
util: add return value to qemu_iovec_concat_iov
megasas: use PCI DMA API
scsi: Print command name in debug
scsi-disk: fix bug in scsi_block_new_request() introduced by commit 137745c
scsi-disk.c: Fix compilation with -DDEBUG_SCSI
block/iscsi: use 16 byte CDBs only when necessary
block/iscsi: fix potential segfault on early callback
block/iscsi: handle BUSY condition
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
6baa963f4d
13 changed files with 340 additions and 299 deletions
165
block/iscsi.c
165
block/iscsi.c
|
@ -26,6 +26,7 @@
|
||||||
#include "config-host.h"
|
#include "config-host.h"
|
||||||
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
#include <math.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
|
@ -64,6 +65,7 @@ typedef struct IscsiLun {
|
||||||
unsigned char *zeroblock;
|
unsigned char *zeroblock;
|
||||||
unsigned long *allocationmap;
|
unsigned long *allocationmap;
|
||||||
int cluster_sectors;
|
int cluster_sectors;
|
||||||
|
bool use_16_for_rw;
|
||||||
} IscsiLun;
|
} IscsiLun;
|
||||||
|
|
||||||
typedef struct IscsiTask {
|
typedef struct IscsiTask {
|
||||||
|
@ -75,6 +77,7 @@ typedef struct IscsiTask {
|
||||||
Coroutine *co;
|
Coroutine *co;
|
||||||
QEMUBH *bh;
|
QEMUBH *bh;
|
||||||
IscsiLun *iscsilun;
|
IscsiLun *iscsilun;
|
||||||
|
QEMUTimer retry_timer;
|
||||||
} IscsiTask;
|
} IscsiTask;
|
||||||
|
|
||||||
typedef struct IscsiAIOCB {
|
typedef struct IscsiAIOCB {
|
||||||
|
@ -86,7 +89,6 @@ typedef struct IscsiAIOCB {
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
int status;
|
int status;
|
||||||
int canceled;
|
int canceled;
|
||||||
int retries;
|
|
||||||
int64_t sector_num;
|
int64_t sector_num;
|
||||||
int nb_sectors;
|
int nb_sectors;
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
@ -96,7 +98,8 @@ typedef struct IscsiAIOCB {
|
||||||
|
|
||||||
#define NOP_INTERVAL 5000
|
#define NOP_INTERVAL 5000
|
||||||
#define MAX_NOP_FAILURES 3
|
#define MAX_NOP_FAILURES 3
|
||||||
#define ISCSI_CMD_RETRIES 5
|
#define ISCSI_CMD_RETRIES ARRAY_SIZE(iscsi_retry_times)
|
||||||
|
static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048};
|
||||||
|
|
||||||
/* this threshhold is a trade-off knob to choose between
|
/* this threshhold is a trade-off knob to choose between
|
||||||
* the potential additional overhead of an extra GET_LBA_STATUS request
|
* the potential additional overhead of an extra GET_LBA_STATUS request
|
||||||
|
@ -142,10 +145,25 @@ iscsi_schedule_bh(IscsiAIOCB *acb)
|
||||||
static void iscsi_co_generic_bh_cb(void *opaque)
|
static void iscsi_co_generic_bh_cb(void *opaque)
|
||||||
{
|
{
|
||||||
struct IscsiTask *iTask = opaque;
|
struct IscsiTask *iTask = opaque;
|
||||||
|
iTask->complete = 1;
|
||||||
qemu_bh_delete(iTask->bh);
|
qemu_bh_delete(iTask->bh);
|
||||||
qemu_coroutine_enter(iTask->co, NULL);
|
qemu_coroutine_enter(iTask->co, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void iscsi_retry_timer_expired(void *opaque)
|
||||||
|
{
|
||||||
|
struct IscsiTask *iTask = opaque;
|
||||||
|
iTask->complete = 1;
|
||||||
|
if (iTask->co) {
|
||||||
|
qemu_coroutine_enter(iTask->co, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned exp_random(double mean)
|
||||||
|
{
|
||||||
|
return -mean * log((double)rand() / RAND_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
||||||
void *command_data, void *opaque)
|
void *command_data, void *opaque)
|
||||||
|
@ -153,19 +171,34 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
||||||
struct IscsiTask *iTask = opaque;
|
struct IscsiTask *iTask = opaque;
|
||||||
struct scsi_task *task = command_data;
|
struct scsi_task *task = command_data;
|
||||||
|
|
||||||
iTask->complete = 1;
|
|
||||||
iTask->status = status;
|
iTask->status = status;
|
||||||
iTask->do_retry = 0;
|
iTask->do_retry = 0;
|
||||||
iTask->task = task;
|
iTask->task = task;
|
||||||
|
|
||||||
if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION
|
if (status != SCSI_STATUS_GOOD) {
|
||||||
|
if (iTask->retries++ < ISCSI_CMD_RETRIES) {
|
||||||
|
if (status == SCSI_STATUS_CHECK_CONDITION
|
||||||
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
|
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
|
||||||
error_report("iSCSI CheckCondition: %s", iscsi_get_error(iscsi));
|
error_report("iSCSI CheckCondition: %s",
|
||||||
|
iscsi_get_error(iscsi));
|
||||||
iTask->do_retry = 1;
|
iTask->do_retry = 1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if (status == SCSI_STATUS_BUSY) {
|
||||||
if (status != SCSI_STATUS_GOOD) {
|
unsigned retry_time =
|
||||||
|
exp_random(iscsi_retry_times[iTask->retries - 1]);
|
||||||
|
error_report("iSCSI Busy (retry #%u in %u ms): %s",
|
||||||
|
iTask->retries, retry_time,
|
||||||
|
iscsi_get_error(iscsi));
|
||||||
|
aio_timer_init(iTask->iscsilun->aio_context,
|
||||||
|
&iTask->retry_timer, QEMU_CLOCK_REALTIME,
|
||||||
|
SCALE_MS, iscsi_retry_timer_expired, iTask);
|
||||||
|
timer_mod(&iTask->retry_timer,
|
||||||
|
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + retry_time);
|
||||||
|
iTask->do_retry = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
error_report("iSCSI Failure: %s", iscsi_get_error(iscsi));
|
error_report("iSCSI Failure: %s", iscsi_get_error(iscsi));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,6 +207,8 @@ out:
|
||||||
iTask->bh = aio_bh_new(iTask->iscsilun->aio_context,
|
iTask->bh = aio_bh_new(iTask->iscsilun->aio_context,
|
||||||
iscsi_co_generic_bh_cb, iTask);
|
iscsi_co_generic_bh_cb, iTask);
|
||||||
qemu_bh_schedule(iTask->bh);
|
qemu_bh_schedule(iTask->bh);
|
||||||
|
} else {
|
||||||
|
iTask->complete = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +216,6 @@ static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask)
|
||||||
{
|
{
|
||||||
*iTask = (struct IscsiTask) {
|
*iTask = (struct IscsiTask) {
|
||||||
.co = qemu_coroutine_self(),
|
.co = qemu_coroutine_self(),
|
||||||
.retries = ISCSI_CMD_RETRIES,
|
|
||||||
.iscsilun = iscsilun,
|
.iscsilun = iscsilun,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -325,8 +359,6 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs,
|
||||||
struct IscsiTask iTask;
|
struct IscsiTask iTask;
|
||||||
uint64_t lba;
|
uint64_t lba;
|
||||||
uint32_t num_sectors;
|
uint32_t num_sectors;
|
||||||
uint8_t *data = NULL;
|
|
||||||
uint8_t *buf = NULL;
|
|
||||||
|
|
||||||
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -334,31 +366,24 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs,
|
||||||
|
|
||||||
lba = sector_qemu2lun(sector_num, iscsilun);
|
lba = sector_qemu2lun(sector_num, iscsilun);
|
||||||
num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
|
num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
|
||||||
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
/* if the iovec only contains one buffer we can pass it directly */
|
|
||||||
if (iov->niov == 1) {
|
|
||||||
data = iov->iov[0].iov_base;
|
|
||||||
} else {
|
|
||||||
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
|
|
||||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||||
retry:
|
retry:
|
||||||
|
if (iscsilun->use_16_for_rw) {
|
||||||
iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba,
|
iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba,
|
||||||
data, num_sectors * iscsilun->block_size,
|
NULL, num_sectors * iscsilun->block_size,
|
||||||
iscsilun->block_size, 0, 0, 0, 0, 0,
|
iscsilun->block_size, 0, 0, 0, 0, 0,
|
||||||
iscsi_co_generic_cb, &iTask);
|
iscsi_co_generic_cb, &iTask);
|
||||||
|
} else {
|
||||||
|
iTask.task = iscsi_write10_task(iscsilun->iscsi, iscsilun->lun, lba,
|
||||||
|
NULL, num_sectors * iscsilun->block_size,
|
||||||
|
iscsilun->block_size, 0, 0, 0, 0, 0,
|
||||||
|
iscsi_co_generic_cb, &iTask);
|
||||||
|
}
|
||||||
if (iTask.task == NULL) {
|
if (iTask.task == NULL) {
|
||||||
g_free(buf);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
||||||
iov->niov);
|
iov->niov);
|
||||||
#endif
|
|
||||||
while (!iTask.complete) {
|
while (!iTask.complete) {
|
||||||
iscsi_set_events(iscsilun);
|
iscsi_set_events(iscsilun);
|
||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
|
@ -374,8 +399,6 @@ retry:
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(buf);
|
|
||||||
|
|
||||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -386,7 +409,6 @@ retry:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun,
|
static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun,
|
||||||
int64_t sector_num, int nb_sectors)
|
int64_t sector_num, int nb_sectors)
|
||||||
{
|
{
|
||||||
|
@ -496,9 +518,6 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* LIBISCSI_FEATURE_IOVECTOR */
|
|
||||||
|
|
||||||
|
|
||||||
static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors,
|
int64_t sector_num, int nb_sectors,
|
||||||
QEMUIOVector *iov)
|
QEMUIOVector *iov)
|
||||||
|
@ -507,15 +526,11 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
||||||
struct IscsiTask iTask;
|
struct IscsiTask iTask;
|
||||||
uint64_t lba;
|
uint64_t lba;
|
||||||
uint32_t num_sectors;
|
uint32_t num_sectors;
|
||||||
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
int i;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES &&
|
if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES &&
|
||||||
!iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
|
!iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
|
||||||
int64_t ret;
|
int64_t ret;
|
||||||
|
@ -529,42 +544,28 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
lba = sector_qemu2lun(sector_num, iscsilun);
|
lba = sector_qemu2lun(sector_num, iscsilun);
|
||||||
num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
|
num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
|
||||||
|
|
||||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||||
retry:
|
retry:
|
||||||
switch (iscsilun->type) {
|
if (iscsilun->use_16_for_rw) {
|
||||||
case TYPE_DISK:
|
|
||||||
iTask.task = iscsi_read16_task(iscsilun->iscsi, iscsilun->lun, lba,
|
iTask.task = iscsi_read16_task(iscsilun->iscsi, iscsilun->lun, lba,
|
||||||
num_sectors * iscsilun->block_size,
|
num_sectors * iscsilun->block_size,
|
||||||
iscsilun->block_size, 0, 0, 0, 0, 0,
|
iscsilun->block_size, 0, 0, 0, 0, 0,
|
||||||
iscsi_co_generic_cb, &iTask);
|
iscsi_co_generic_cb, &iTask);
|
||||||
break;
|
} else {
|
||||||
default:
|
|
||||||
iTask.task = iscsi_read10_task(iscsilun->iscsi, iscsilun->lun, lba,
|
iTask.task = iscsi_read10_task(iscsilun->iscsi, iscsilun->lun, lba,
|
||||||
num_sectors * iscsilun->block_size,
|
num_sectors * iscsilun->block_size,
|
||||||
iscsilun->block_size,
|
iscsilun->block_size,
|
||||||
#if !defined(CONFIG_LIBISCSI_1_4) /* API change from 1.4.0 to 1.5.0 */
|
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
#endif
|
|
||||||
iscsi_co_generic_cb, &iTask);
|
iscsi_co_generic_cb, &iTask);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (iTask.task == NULL) {
|
if (iTask.task == NULL) {
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
||||||
#else
|
|
||||||
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
|
|
||||||
|
|
||||||
while (!iTask.complete) {
|
while (!iTask.complete) {
|
||||||
iscsi_set_events(iscsilun);
|
iscsi_set_events(iscsilun);
|
||||||
|
@ -719,18 +720,9 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||||
data.data = acb->ioh->dxferp;
|
data.data = acb->ioh->dxferp;
|
||||||
data.size = acb->ioh->dxfer_len;
|
data.size = acb->ioh->dxfer_len;
|
||||||
} else {
|
} else {
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
scsi_task_set_iov_out(acb->task,
|
scsi_task_set_iov_out(acb->task,
|
||||||
(struct scsi_iovec *) acb->ioh->dxferp,
|
(struct scsi_iovec *) acb->ioh->dxferp,
|
||||||
acb->ioh->iovec_count);
|
acb->ioh->iovec_count);
|
||||||
#else
|
|
||||||
struct iovec *iov = (struct iovec *)acb->ioh->dxferp;
|
|
||||||
|
|
||||||
acb->buf = g_malloc(acb->ioh->dxfer_len);
|
|
||||||
data.data = acb->buf;
|
|
||||||
data.size = iov_to_buf(iov, acb->ioh->iovec_count, 0,
|
|
||||||
acb->buf, acb->ioh->dxfer_len);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,20 +742,9 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||||
acb->ioh->dxfer_len,
|
acb->ioh->dxfer_len,
|
||||||
acb->ioh->dxferp);
|
acb->ioh->dxferp);
|
||||||
} else {
|
} else {
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
scsi_task_set_iov_in(acb->task,
|
scsi_task_set_iov_in(acb->task,
|
||||||
(struct scsi_iovec *) acb->ioh->dxferp,
|
(struct scsi_iovec *) acb->ioh->dxferp,
|
||||||
acb->ioh->iovec_count);
|
acb->ioh->iovec_count);
|
||||||
#else
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < acb->ioh->iovec_count; i++) {
|
|
||||||
struct iovec *iov = (struct iovec *)acb->ioh->dxferp;
|
|
||||||
|
|
||||||
scsi_task_add_data_in_buffer(acb->task,
|
|
||||||
iov[i].iov_len,
|
|
||||||
iov[i].iov_base);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,7 +753,6 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||||
return &acb->common;
|
return &acb->common;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void ioctl_cb(void *opaque, int status)
|
static void ioctl_cb(void *opaque, int status)
|
||||||
{
|
{
|
||||||
int *p_status = opaque;
|
int *p_status = opaque;
|
||||||
|
@ -877,8 +857,6 @@ retry:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED)
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
||||||
int nb_sectors, BdrvRequestFlags flags)
|
int nb_sectors, BdrvRequestFlags flags)
|
||||||
|
@ -887,15 +865,23 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
||||||
struct IscsiTask iTask;
|
struct IscsiTask iTask;
|
||||||
uint64_t lba;
|
uint64_t lba;
|
||||||
uint32_t nb_blocks;
|
uint32_t nb_blocks;
|
||||||
|
bool use_16_for_ws = iscsilun->use_16_for_rw;
|
||||||
|
|
||||||
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) {
|
if (flags & BDRV_REQ_MAY_UNMAP) {
|
||||||
/* WRITE SAME with UNMAP is not supported by the target,
|
if (!use_16_for_ws && !iscsilun->lbp.lbpws10) {
|
||||||
* fall back and try WRITE SAME without UNMAP */
|
/* WRITESAME10 with UNMAP is unsupported try WRITESAME16 */
|
||||||
|
use_16_for_ws = true;
|
||||||
|
}
|
||||||
|
if (use_16_for_ws && !iscsilun->lbp.lbpws) {
|
||||||
|
/* WRITESAME16 with UNMAP is not supported by the target,
|
||||||
|
* fall back and try WRITESAME10/16 without UNMAP */
|
||||||
flags &= ~BDRV_REQ_MAY_UNMAP;
|
flags &= ~BDRV_REQ_MAY_UNMAP;
|
||||||
|
use_16_for_ws = iscsilun->use_16_for_rw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) {
|
if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) {
|
||||||
|
@ -912,10 +898,18 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
|
||||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||||
retry:
|
retry:
|
||||||
if (iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba,
|
if (use_16_for_ws) {
|
||||||
|
iTask.task = iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba,
|
||||||
iscsilun->zeroblock, iscsilun->block_size,
|
iscsilun->zeroblock, iscsilun->block_size,
|
||||||
nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP),
|
nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP),
|
||||||
0, 0, iscsi_co_generic_cb, &iTask) == NULL) {
|
0, 0, iscsi_co_generic_cb, &iTask);
|
||||||
|
} else {
|
||||||
|
iTask.task = iscsi_writesame10_task(iscsilun->iscsi, iscsilun->lun, lba,
|
||||||
|
iscsilun->zeroblock, iscsilun->block_size,
|
||||||
|
nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP),
|
||||||
|
0, 0, iscsi_co_generic_cb, &iTask);
|
||||||
|
}
|
||||||
|
if (iTask.task == NULL) {
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -957,8 +951,6 @@ retry:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED */
|
|
||||||
|
|
||||||
static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -1068,7 +1060,6 @@ static char *parse_initiator_name(const char *target)
|
||||||
return iscsi_name;
|
return iscsi_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
|
||||||
static void iscsi_nop_timed_event(void *opaque)
|
static void iscsi_nop_timed_event(void *opaque)
|
||||||
{
|
{
|
||||||
IscsiLun *iscsilun = opaque;
|
IscsiLun *iscsilun = opaque;
|
||||||
|
@ -1086,7 +1077,6 @@ static void iscsi_nop_timed_event(void *opaque)
|
||||||
timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
|
timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
|
||||||
iscsi_set_events(iscsilun);
|
iscsi_set_events(iscsilun);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
|
static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -1113,6 +1103,7 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
|
||||||
iscsilun->num_blocks = rc16->returned_lba + 1;
|
iscsilun->num_blocks = rc16->returned_lba + 1;
|
||||||
iscsilun->lbpme = rc16->lbpme;
|
iscsilun->lbpme = rc16->lbpme;
|
||||||
iscsilun->lbprz = rc16->lbprz;
|
iscsilun->lbprz = rc16->lbprz;
|
||||||
|
iscsilun->use_16_for_rw = (rc16->returned_lba > 0xffffffff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1224,14 +1215,12 @@ static void iscsi_attach_aio_context(BlockDriverState *bs,
|
||||||
iscsilun->aio_context = new_context;
|
iscsilun->aio_context = new_context;
|
||||||
iscsi_set_events(iscsilun);
|
iscsi_set_events(iscsilun);
|
||||||
|
|
||||||
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
|
||||||
/* Set up a timer for sending out iSCSI NOPs */
|
/* Set up a timer for sending out iSCSI NOPs */
|
||||||
iscsilun->nop_timer = aio_timer_new(iscsilun->aio_context,
|
iscsilun->nop_timer = aio_timer_new(iscsilun->aio_context,
|
||||||
QEMU_CLOCK_REALTIME, SCALE_MS,
|
QEMU_CLOCK_REALTIME, SCALE_MS,
|
||||||
iscsi_nop_timed_event, iscsilun);
|
iscsi_nop_timed_event, iscsilun);
|
||||||
timer_mod(iscsilun->nop_timer,
|
timer_mod(iscsilun->nop_timer,
|
||||||
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
|
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1423,13 +1412,11 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) {
|
iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) {
|
||||||
iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran *
|
iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran *
|
||||||
iscsilun->block_size) >> BDRV_SECTOR_BITS;
|
iscsilun->block_size) >> BDRV_SECTOR_BITS;
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
if (iscsilun->lbprz && !(bs->open_flags & BDRV_O_NOCACHE)) {
|
if (iscsilun->lbprz && !(bs->open_flags & BDRV_O_NOCACHE)) {
|
||||||
iscsilun->allocationmap =
|
iscsilun->allocationmap =
|
||||||
bitmap_new(DIV_ROUND_UP(bs->total_sectors,
|
bitmap_new(DIV_ROUND_UP(bs->total_sectors,
|
||||||
iscsilun->cluster_sectors));
|
iscsilun->cluster_sectors));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -1614,13 +1601,9 @@ static BlockDriver bdrv_iscsi = {
|
||||||
.bdrv_truncate = iscsi_truncate,
|
.bdrv_truncate = iscsi_truncate,
|
||||||
.bdrv_refresh_limits = iscsi_refresh_limits,
|
.bdrv_refresh_limits = iscsi_refresh_limits,
|
||||||
|
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
.bdrv_co_get_block_status = iscsi_co_get_block_status,
|
.bdrv_co_get_block_status = iscsi_co_get_block_status,
|
||||||
#endif
|
|
||||||
.bdrv_co_discard = iscsi_co_discard,
|
.bdrv_co_discard = iscsi_co_discard,
|
||||||
#if defined(SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED)
|
|
||||||
.bdrv_co_write_zeroes = iscsi_co_write_zeroes,
|
.bdrv_co_write_zeroes = iscsi_co_write_zeroes,
|
||||||
#endif
|
|
||||||
.bdrv_co_readv = iscsi_co_readv,
|
.bdrv_co_readv = iscsi_co_readv,
|
||||||
.bdrv_co_writev = iscsi_co_writev,
|
.bdrv_co_writev = iscsi_co_writev,
|
||||||
.bdrv_co_flush_to_disk = iscsi_co_flush,
|
.bdrv_co_flush_to_disk = iscsi_co_flush,
|
||||||
|
|
39
configure
vendored
39
configure
vendored
|
@ -3405,46 +3405,20 @@ if compile_prog "" "" ; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# Do we have libiscsi
|
# Do we have libiscsi >= 1.9.0
|
||||||
# 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
|
if test "$libiscsi" != "no" ; then
|
||||||
cat > $TMPC << EOF
|
if $pkg_config --atleast-version=1.9.0 libiscsi; then
|
||||||
#include <stdio.h>
|
|
||||||
#include <iscsi/iscsi.h>
|
|
||||||
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"
|
libiscsi="yes"
|
||||||
libiscsi_cflags=$($pkg_config --cflags libiscsi)
|
libiscsi_cflags=$($pkg_config --cflags libiscsi)
|
||||||
libiscsi_libs=$($pkg_config --libs libiscsi)
|
libiscsi_libs=$($pkg_config --libs libiscsi)
|
||||||
elif compile_prog "" "-liscsi" ; then
|
|
||||||
libiscsi="yes"
|
|
||||||
libiscsi_libs="-liscsi"
|
|
||||||
else
|
else
|
||||||
if test "$libiscsi" = "yes" ; then
|
if test "$libiscsi" = "yes" ; then
|
||||||
feature_not_found "libiscsi" "Install libiscsi devel"
|
feature_not_found "libiscsi" "Install libiscsi >= 1.9.0"
|
||||||
fi
|
fi
|
||||||
libiscsi="no"
|
libiscsi="no"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# We also need to know the API version because there was an
|
|
||||||
# API change from 1.4.0 to 1.5.0.
|
|
||||||
if test "$libiscsi" = "yes"; then
|
|
||||||
cat >$TMPC <<EOF
|
|
||||||
#include <iscsi/iscsi.h>
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
iscsi_read10_task(0, 0, 0, 0, 0, 0, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
if compile_prog "" "-liscsi"; then
|
|
||||||
libiscsi_version="1.4.0"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# Do we need libm
|
# Do we need libm
|
||||||
cat > $TMPC << EOF
|
cat > $TMPC << EOF
|
||||||
|
@ -4218,11 +4192,7 @@ echo "nss used $smartcard_nss"
|
||||||
echo "libusb $libusb"
|
echo "libusb $libusb"
|
||||||
echo "usb net redir $usb_redir"
|
echo "usb net redir $usb_redir"
|
||||||
echo "GLX support $glx"
|
echo "GLX support $glx"
|
||||||
if test "$libiscsi_version" = "1.4.0"; then
|
|
||||||
echo "libiscsi support $libiscsi (1.4.0)"
|
|
||||||
else
|
|
||||||
echo "libiscsi support $libiscsi"
|
echo "libiscsi support $libiscsi"
|
||||||
fi
|
|
||||||
echo "libnfs support $libnfs"
|
echo "libnfs support $libnfs"
|
||||||
echo "build guest agent $guest_agent"
|
echo "build guest agent $guest_agent"
|
||||||
echo "QGA VSS support $guest_agent_with_vss"
|
echo "QGA VSS support $guest_agent_with_vss"
|
||||||
|
@ -4579,9 +4549,6 @@ fi
|
||||||
|
|
||||||
if test "$libiscsi" = "yes" ; then
|
if test "$libiscsi" = "yes" ; then
|
||||||
echo "CONFIG_LIBISCSI=m" >> $config_host_mak
|
echo "CONFIG_LIBISCSI=m" >> $config_host_mak
|
||||||
if test "$libiscsi_version" = "1.4.0"; then
|
|
||||||
echo "CONFIG_LIBISCSI_1_4=y" >> $config_host_mak
|
|
||||||
fi
|
|
||||||
echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak
|
echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak
|
||||||
echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak
|
echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -294,6 +294,7 @@ static void megasas_unmap_sgl(MegasasCmd *cmd)
|
||||||
static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr,
|
static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr,
|
||||||
uint8_t sense_len)
|
uint8_t sense_len)
|
||||||
{
|
{
|
||||||
|
PCIDevice *pcid = PCI_DEVICE(cmd->state);
|
||||||
uint32_t pa_hi = 0, pa_lo;
|
uint32_t pa_hi = 0, pa_lo;
|
||||||
hwaddr pa;
|
hwaddr pa;
|
||||||
|
|
||||||
|
@ -306,7 +307,7 @@ static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr,
|
||||||
pa_hi = le32_to_cpu(cmd->frame->pass.sense_addr_hi);
|
pa_hi = le32_to_cpu(cmd->frame->pass.sense_addr_hi);
|
||||||
}
|
}
|
||||||
pa = ((uint64_t) pa_hi << 32) | pa_lo;
|
pa = ((uint64_t) pa_hi << 32) | pa_lo;
|
||||||
cpu_physical_memory_write(pa, sense_ptr, sense_len);
|
pci_dma_write(pcid, pa, sense_ptr, sense_len);
|
||||||
cmd->frame->header.sense_len = sense_len;
|
cmd->frame->header.sense_len = sense_len;
|
||||||
}
|
}
|
||||||
return sense_len;
|
return sense_len;
|
||||||
|
@ -472,6 +473,7 @@ static MegasasCmd *megasas_next_frame(MegasasState *s,
|
||||||
static MegasasCmd *megasas_enqueue_frame(MegasasState *s,
|
static MegasasCmd *megasas_enqueue_frame(MegasasState *s,
|
||||||
hwaddr frame, uint64_t context, int count)
|
hwaddr frame, uint64_t context, int count)
|
||||||
{
|
{
|
||||||
|
PCIDevice *pcid = PCI_DEVICE(s);
|
||||||
MegasasCmd *cmd = NULL;
|
MegasasCmd *cmd = NULL;
|
||||||
int frame_size = MFI_FRAME_SIZE * 16;
|
int frame_size = MFI_FRAME_SIZE * 16;
|
||||||
hwaddr frame_size_p = frame_size;
|
hwaddr frame_size_p = frame_size;
|
||||||
|
@ -484,11 +486,11 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s,
|
||||||
if (!cmd->pa) {
|
if (!cmd->pa) {
|
||||||
cmd->pa = frame;
|
cmd->pa = frame;
|
||||||
/* Map all possible frames */
|
/* Map all possible frames */
|
||||||
cmd->frame = cpu_physical_memory_map(frame, &frame_size_p, 0);
|
cmd->frame = pci_dma_map(pcid, frame, &frame_size_p, 0);
|
||||||
if (frame_size_p != frame_size) {
|
if (frame_size_p != frame_size) {
|
||||||
trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame);
|
trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame);
|
||||||
if (cmd->frame) {
|
if (cmd->frame) {
|
||||||
cpu_physical_memory_unmap(cmd->frame, frame_size_p, 0, 0);
|
pci_dma_unmap(pcid, cmd->frame, frame_size_p, 0, 0);
|
||||||
cmd->frame = NULL;
|
cmd->frame = NULL;
|
||||||
cmd->pa = 0;
|
cmd->pa = 0;
|
||||||
}
|
}
|
||||||
|
@ -561,13 +563,14 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context)
|
||||||
|
|
||||||
static void megasas_reset_frames(MegasasState *s)
|
static void megasas_reset_frames(MegasasState *s)
|
||||||
{
|
{
|
||||||
|
PCIDevice *pcid = PCI_DEVICE(s);
|
||||||
int i;
|
int i;
|
||||||
MegasasCmd *cmd;
|
MegasasCmd *cmd;
|
||||||
|
|
||||||
for (i = 0; i < s->fw_cmds; i++) {
|
for (i = 0; i < s->fw_cmds; i++) {
|
||||||
cmd = &s->frames[i];
|
cmd = &s->frames[i];
|
||||||
if (cmd->pa) {
|
if (cmd->pa) {
|
||||||
cpu_physical_memory_unmap(cmd->frame, cmd->pa_size, 0, 0);
|
pci_dma_unmap(pcid, cmd->frame, cmd->pa_size, 0, 0);
|
||||||
cmd->frame = NULL;
|
cmd->frame = NULL;
|
||||||
cmd->pa = 0;
|
cmd->pa = 0;
|
||||||
}
|
}
|
||||||
|
@ -584,6 +587,7 @@ static void megasas_abort_command(MegasasCmd *cmd)
|
||||||
|
|
||||||
static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd)
|
static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd)
|
||||||
{
|
{
|
||||||
|
PCIDevice *pcid = PCI_DEVICE(s);
|
||||||
uint32_t pa_hi, pa_lo;
|
uint32_t pa_hi, pa_lo;
|
||||||
hwaddr iq_pa, initq_size;
|
hwaddr iq_pa, initq_size;
|
||||||
struct mfi_init_qinfo *initq;
|
struct mfi_init_qinfo *initq;
|
||||||
|
@ -595,7 +599,7 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd)
|
||||||
iq_pa = (((uint64_t) pa_hi << 32) | pa_lo);
|
iq_pa = (((uint64_t) pa_hi << 32) | pa_lo);
|
||||||
trace_megasas_init_firmware((uint64_t)iq_pa);
|
trace_megasas_init_firmware((uint64_t)iq_pa);
|
||||||
initq_size = sizeof(*initq);
|
initq_size = sizeof(*initq);
|
||||||
initq = cpu_physical_memory_map(iq_pa, &initq_size, 0);
|
initq = pci_dma_map(pcid, iq_pa, &initq_size, 0);
|
||||||
if (!initq || initq_size != sizeof(*initq)) {
|
if (!initq || initq_size != sizeof(*initq)) {
|
||||||
trace_megasas_initq_map_failed(cmd->index);
|
trace_megasas_initq_map_failed(cmd->index);
|
||||||
s->event_count++;
|
s->event_count++;
|
||||||
|
@ -631,7 +635,7 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd)
|
||||||
s->fw_state = MFI_FWSTATE_OPERATIONAL;
|
s->fw_state = MFI_FWSTATE_OPERATIONAL;
|
||||||
out:
|
out:
|
||||||
if (initq) {
|
if (initq) {
|
||||||
cpu_physical_memory_unmap(initq, initq_size, 0, 0);
|
pci_dma_unmap(pcid, initq, initq_size, 0, 0);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1429,7 +1429,7 @@ int scsi_build_sense(uint8_t *in_buf, int in_len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *scsi_command_name(uint8_t cmd)
|
const char *scsi_command_name(uint8_t cmd)
|
||||||
{
|
{
|
||||||
static const char *names[] = {
|
static const char *names[] = {
|
||||||
[ TEST_UNIT_READY ] = "TEST_UNIT_READY",
|
[ TEST_UNIT_READY ] = "TEST_UNIT_READY",
|
||||||
|
@ -1545,6 +1545,8 @@ static const char *scsi_command_name(uint8_t cmd)
|
||||||
[ SET_READ_AHEAD ] = "SET_READ_AHEAD",
|
[ SET_READ_AHEAD ] = "SET_READ_AHEAD",
|
||||||
[ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE",
|
[ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE",
|
||||||
[ MECHANISM_STATUS ] = "MECHANISM_STATUS",
|
[ MECHANISM_STATUS ] = "MECHANISM_STATUS",
|
||||||
|
[ GET_EVENT_STATUS_NOTIFICATION ] = "GET_EVENT_STATUS_NOTIFICATION",
|
||||||
|
[ READ_DISC_INFORMATION ] = "READ_DISC_INFORMATION",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL)
|
if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL)
|
||||||
|
|
|
@ -2015,7 +2015,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
|
||||||
case VERIFY_10:
|
case VERIFY_10:
|
||||||
case VERIFY_12:
|
case VERIFY_12:
|
||||||
case VERIFY_16:
|
case VERIFY_16:
|
||||||
DPRINTF("Verify (bytchk %lu)\n", (r->req.buf[1] >> 1) & 3);
|
DPRINTF("Verify (bytchk %d)\n", (req->cmd.buf[1] >> 1) & 3);
|
||||||
if (req->cmd.buf[1] & 6) {
|
if (req->cmd.buf[1] & 6) {
|
||||||
goto illegal_request;
|
goto illegal_request;
|
||||||
}
|
}
|
||||||
|
@ -2027,7 +2027,8 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
|
||||||
(long)r->req.cmd.xfer);
|
(long)r->req.cmd.xfer);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
|
DPRINTF("Unknown SCSI command (%2.2x=%s)\n", buf[0],
|
||||||
|
scsi_command_name(buf[0]));
|
||||||
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
|
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2526,7 +2527,7 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
|
||||||
* ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without
|
* ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without
|
||||||
* O_DIRECT everything must go through SG_IO.
|
* O_DIRECT everything must go through SG_IO.
|
||||||
*/
|
*/
|
||||||
if (bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE) {
|
if (!(bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -799,8 +799,9 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
|
||||||
req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, req);
|
req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, req);
|
||||||
n = scsi_req_enqueue(req->sreq);
|
n = scsi_req_enqueue(req->sreq);
|
||||||
|
|
||||||
DPRINTF("VSCSI: Queued command tag 0x%x CMD 0x%x LUN %d ret: %d\n",
|
DPRINTF("VSCSI: Queued command tag 0x%x CMD 0x%x=%s LUN %d ret: %d\n",
|
||||||
req->qtag, srp->cmd.cdb[0], lun, n);
|
req->qtag, srp->cmd.cdb[0], scsi_command_name(srp->cmd.cdb[0]),
|
||||||
|
lun, n);
|
||||||
|
|
||||||
if (n) {
|
if (n) {
|
||||||
/* Transfer direction must be set before preprocessing the
|
/* Transfer direction must be set before preprocessing the
|
||||||
|
|
|
@ -196,6 +196,10 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static void vhost_scsi_realize(DeviceState *dev, Error **errp)
|
static void vhost_scsi_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
|
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
|
||||||
|
@ -217,7 +221,9 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtio_scsi_common_realize(dev, &err);
|
virtio_scsi_common_realize(dev, &err, vhost_dummy_handle_output,
|
||||||
|
vhost_dummy_handle_output,
|
||||||
|
vhost_dummy_handle_output);
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "hw/virtio/virtio-scsi.h"
|
#include "hw/virtio/virtio-scsi.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/iov.h"
|
||||||
#include <hw/scsi/scsi.h>
|
#include <hw/scsi/scsi.h>
|
||||||
#include <block/scsi.h>
|
#include <block/scsi.h>
|
||||||
#include <hw/virtio/virtio-bus.h>
|
#include <hw/virtio/virtio-bus.h>
|
||||||
|
@ -25,21 +26,28 @@ typedef struct VirtIOSCSIReq {
|
||||||
VirtQueueElement elem;
|
VirtQueueElement elem;
|
||||||
QEMUSGList qsgl;
|
QEMUSGList qsgl;
|
||||||
SCSIRequest *sreq;
|
SCSIRequest *sreq;
|
||||||
|
size_t resp_size;
|
||||||
|
enum SCSIXferMode mode;
|
||||||
|
QEMUIOVector resp_iov;
|
||||||
union {
|
union {
|
||||||
char *buf;
|
VirtIOSCSICmdResp cmd;
|
||||||
VirtIOSCSICmdReq *cmd;
|
VirtIOSCSICtrlTMFResp tmf;
|
||||||
VirtIOSCSICtrlTMFReq *tmf;
|
VirtIOSCSICtrlANResp an;
|
||||||
VirtIOSCSICtrlANReq *an;
|
VirtIOSCSIEvent event;
|
||||||
} req;
|
|
||||||
union {
|
|
||||||
char *buf;
|
|
||||||
VirtIOSCSICmdResp *cmd;
|
|
||||||
VirtIOSCSICtrlTMFResp *tmf;
|
|
||||||
VirtIOSCSICtrlANResp *an;
|
|
||||||
VirtIOSCSIEvent *event;
|
|
||||||
} resp;
|
} resp;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
VirtIOSCSICmdReq cmd;
|
||||||
|
uint8_t cdb[];
|
||||||
|
} QEMU_PACKED;
|
||||||
|
VirtIOSCSICtrlTMFReq tmf;
|
||||||
|
VirtIOSCSICtrlANReq an;
|
||||||
|
} req;
|
||||||
} VirtIOSCSIReq;
|
} VirtIOSCSIReq;
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(offsetof(VirtIOSCSIReq, req.cdb) !=
|
||||||
|
offsetof(VirtIOSCSIReq, req.cmd) + sizeof(VirtIOSCSICmdReq));
|
||||||
|
|
||||||
static inline int virtio_scsi_get_lun(uint8_t *lun)
|
static inline int virtio_scsi_get_lun(uint8_t *lun)
|
||||||
{
|
{
|
||||||
return ((lun[2] << 8) | lun[3]) & 0x3FFF;
|
return ((lun[2] << 8) | lun[3]) & 0x3FFF;
|
||||||
|
@ -56,18 +64,41 @@ static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun)
|
||||||
return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
|
return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq)
|
||||||
|
{
|
||||||
|
VirtIOSCSIReq *req;
|
||||||
|
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||||
|
|
||||||
|
req = g_malloc0(sizeof(*req) + vs->cdb_size);
|
||||||
|
|
||||||
|
req->vq = vq;
|
||||||
|
req->dev = s;
|
||||||
|
req->sreq = NULL;
|
||||||
|
qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory);
|
||||||
|
qemu_iovec_init(&req->resp_iov, 1);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_scsi_free_req(VirtIOSCSIReq *req)
|
||||||
|
{
|
||||||
|
qemu_iovec_destroy(&req->resp_iov);
|
||||||
|
qemu_sglist_destroy(&req->qsgl);
|
||||||
|
g_free(req);
|
||||||
|
}
|
||||||
|
|
||||||
static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
|
static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
|
||||||
{
|
{
|
||||||
VirtIOSCSI *s = req->dev;
|
VirtIOSCSI *s = req->dev;
|
||||||
VirtQueue *vq = req->vq;
|
VirtQueue *vq = req->vq;
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||||
virtqueue_push(vq, &req->elem, req->qsgl.size + req->elem.in_sg[0].iov_len);
|
|
||||||
qemu_sglist_destroy(&req->qsgl);
|
qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size);
|
||||||
|
virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size);
|
||||||
if (req->sreq) {
|
if (req->sreq) {
|
||||||
req->sreq->hba_private = NULL;
|
req->sreq->hba_private = NULL;
|
||||||
scsi_req_unref(req->sreq);
|
scsi_req_unref(req->sreq);
|
||||||
}
|
}
|
||||||
g_free(req);
|
virtio_scsi_free_req(req);
|
||||||
virtio_notify(vdev, vq);
|
virtio_notify(vdev, vq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,50 +108,73 @@ static void virtio_scsi_bad_req(void)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qemu_sgl_init_external(VirtIOSCSIReq *req, struct iovec *sg,
|
static size_t qemu_sgl_concat(VirtIOSCSIReq *req, struct iovec *iov,
|
||||||
hwaddr *addr, int num)
|
hwaddr *addr, int num, size_t skip)
|
||||||
{
|
{
|
||||||
QEMUSGList *qsgl = &req->qsgl;
|
QEMUSGList *qsgl = &req->qsgl;
|
||||||
|
size_t copied = 0;
|
||||||
|
|
||||||
qemu_sglist_init(qsgl, DEVICE(req->dev), num, &address_space_memory);
|
while (num) {
|
||||||
while (num--) {
|
if (skip >= iov->iov_len) {
|
||||||
qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len);
|
skip -= iov->iov_len;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void virtio_scsi_parse_req(VirtIOSCSI *s, VirtQueue *vq,
|
|
||||||
VirtIOSCSIReq *req)
|
|
||||||
{
|
|
||||||
assert(req->elem.in_num);
|
|
||||||
req->vq = vq;
|
|
||||||
req->dev = s;
|
|
||||||
req->sreq = NULL;
|
|
||||||
if (req->elem.out_num) {
|
|
||||||
req->req.buf = req->elem.out_sg[0].iov_base;
|
|
||||||
}
|
|
||||||
req->resp.buf = req->elem.in_sg[0].iov_base;
|
|
||||||
|
|
||||||
if (req->elem.out_num > 1) {
|
|
||||||
qemu_sgl_init_external(req, &req->elem.out_sg[1],
|
|
||||||
&req->elem.out_addr[1],
|
|
||||||
req->elem.out_num - 1);
|
|
||||||
} else {
|
} else {
|
||||||
qemu_sgl_init_external(req, &req->elem.in_sg[1],
|
qemu_sglist_add(qsgl, *addr + skip, iov->iov_len - skip);
|
||||||
&req->elem.in_addr[1],
|
copied += iov->iov_len - skip;
|
||||||
req->elem.in_num - 1);
|
skip = 0;
|
||||||
}
|
}
|
||||||
|
iov++;
|
||||||
|
addr++;
|
||||||
|
num--;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(skip == 0);
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_scsi_parse_req(VirtIOSCSIReq *req,
|
||||||
|
unsigned req_size, unsigned resp_size)
|
||||||
|
{
|
||||||
|
size_t in_size, out_size;
|
||||||
|
|
||||||
|
if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0,
|
||||||
|
&req->req, req_size) < req_size) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qemu_iovec_concat_iov(&req->resp_iov,
|
||||||
|
req->elem.in_sg, req->elem.in_num, 0,
|
||||||
|
resp_size) < resp_size) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
req->resp_size = resp_size;
|
||||||
|
|
||||||
|
out_size = qemu_sgl_concat(req, req->elem.out_sg,
|
||||||
|
&req->elem.out_addr[0], req->elem.out_num,
|
||||||
|
req_size);
|
||||||
|
in_size = qemu_sgl_concat(req, req->elem.in_sg,
|
||||||
|
&req->elem.in_addr[0], req->elem.in_num,
|
||||||
|
resp_size);
|
||||||
|
|
||||||
|
if (out_size && in_size) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_size) {
|
||||||
|
req->mode = SCSI_XFER_TO_DEV;
|
||||||
|
} else if (in_size) {
|
||||||
|
req->mode = SCSI_XFER_FROM_DEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
|
static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
|
||||||
{
|
{
|
||||||
VirtIOSCSIReq *req;
|
VirtIOSCSIReq *req = virtio_scsi_init_req(s, vq);
|
||||||
req = g_malloc(sizeof(*req));
|
|
||||||
if (!virtqueue_pop(vq, &req->elem)) {
|
if (!virtqueue_pop(vq, &req->elem)) {
|
||||||
g_free(req);
|
virtio_scsi_free_req(req);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtio_scsi_parse_req(s, vq, req);
|
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,9 +197,9 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
|
||||||
VirtIOSCSIReq *req;
|
VirtIOSCSIReq *req;
|
||||||
uint32_t n;
|
uint32_t n;
|
||||||
|
|
||||||
req = g_malloc(sizeof(*req));
|
|
||||||
qemu_get_be32s(f, &n);
|
qemu_get_be32s(f, &n);
|
||||||
assert(n < vs->conf.num_queues);
|
assert(n < vs->conf.num_queues);
|
||||||
|
req = virtio_scsi_init_req(s, vs->cmd_vqs[n]);
|
||||||
qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
|
qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
|
||||||
/* TODO: add a way for SCSIBusInfo's load_request to fail,
|
/* TODO: add a way for SCSIBusInfo's load_request to fail,
|
||||||
* and fail migration instead of asserting here.
|
* and fail migration instead of asserting here.
|
||||||
|
@ -156,41 +210,44 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
|
||||||
#endif
|
#endif
|
||||||
assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg));
|
assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg));
|
||||||
assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg));
|
assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg));
|
||||||
virtio_scsi_parse_req(s, vs->cmd_vqs[n], req);
|
|
||||||
|
if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
|
||||||
|
sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) {
|
||||||
|
error_report("invalid SCSI request migration data");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
scsi_req_ref(sreq);
|
scsi_req_ref(sreq);
|
||||||
req->sreq = sreq;
|
req->sreq = sreq;
|
||||||
if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
|
if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
|
||||||
int req_mode =
|
assert(req->sreq->cmd.mode == req->mode);
|
||||||
(req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
|
|
||||||
|
|
||||||
assert(req->sreq->cmd.mode == req_mode);
|
|
||||||
}
|
}
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
{
|
{
|
||||||
SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf->lun);
|
SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf.lun);
|
||||||
SCSIRequest *r, *next;
|
SCSIRequest *r, *next;
|
||||||
BusChild *kid;
|
BusChild *kid;
|
||||||
int target;
|
int target;
|
||||||
|
|
||||||
/* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */
|
/* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */
|
||||||
req->resp.tmf->response = VIRTIO_SCSI_S_OK;
|
req->resp.tmf.response = VIRTIO_SCSI_S_OK;
|
||||||
|
|
||||||
switch (req->req.tmf->subtype) {
|
tswap32s(&req->req.tmf.subtype);
|
||||||
|
switch (req->req.tmf.subtype) {
|
||||||
case VIRTIO_SCSI_T_TMF_ABORT_TASK:
|
case VIRTIO_SCSI_T_TMF_ABORT_TASK:
|
||||||
case VIRTIO_SCSI_T_TMF_QUERY_TASK:
|
case VIRTIO_SCSI_T_TMF_QUERY_TASK:
|
||||||
if (!d) {
|
if (!d) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
|
if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) {
|
||||||
goto incorrect_lun;
|
goto incorrect_lun;
|
||||||
}
|
}
|
||||||
QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
|
QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
|
||||||
VirtIOSCSIReq *cmd_req = r->hba_private;
|
VirtIOSCSIReq *cmd_req = r->hba_private;
|
||||||
if (cmd_req && cmd_req->req.cmd->tag == req->req.tmf->tag) {
|
if (cmd_req && cmd_req->req.cmd.tag == req->req.tmf.tag) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,11 +257,11 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
* check for it in the loop above.
|
* check for it in the loop above.
|
||||||
*/
|
*/
|
||||||
assert(r->hba_private);
|
assert(r->hba_private);
|
||||||
if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) {
|
if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) {
|
||||||
/* "If the specified command is present in the task set, then
|
/* "If the specified command is present in the task set, then
|
||||||
* return a service response set to FUNCTION SUCCEEDED".
|
* return a service response set to FUNCTION SUCCEEDED".
|
||||||
*/
|
*/
|
||||||
req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
|
req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
|
||||||
} else {
|
} else {
|
||||||
scsi_req_cancel(r);
|
scsi_req_cancel(r);
|
||||||
}
|
}
|
||||||
|
@ -215,7 +272,7 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
if (!d) {
|
if (!d) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
|
if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) {
|
||||||
goto incorrect_lun;
|
goto incorrect_lun;
|
||||||
}
|
}
|
||||||
s->resetting++;
|
s->resetting++;
|
||||||
|
@ -229,16 +286,16 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
if (!d) {
|
if (!d) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
|
if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) {
|
||||||
goto incorrect_lun;
|
goto incorrect_lun;
|
||||||
}
|
}
|
||||||
QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
|
QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
|
||||||
if (r->hba_private) {
|
if (r->hba_private) {
|
||||||
if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) {
|
if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) {
|
||||||
/* "If there is any command present in the task set, then
|
/* "If there is any command present in the task set, then
|
||||||
* return a service response set to FUNCTION SUCCEEDED".
|
* return a service response set to FUNCTION SUCCEEDED".
|
||||||
*/
|
*/
|
||||||
req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
|
req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
scsi_req_cancel(r);
|
scsi_req_cancel(r);
|
||||||
|
@ -248,7 +305,7 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
|
case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
|
||||||
target = req->req.tmf->lun[1];
|
target = req->req.tmf.lun[1];
|
||||||
s->resetting++;
|
s->resetting++;
|
||||||
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
|
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
|
||||||
d = DO_UPCAST(SCSIDevice, qdev, kid->child);
|
d = DO_UPCAST(SCSIDevice, qdev, kid->child);
|
||||||
|
@ -261,18 +318,18 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
|
|
||||||
case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
|
case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
|
||||||
default:
|
default:
|
||||||
req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_REJECTED;
|
req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
incorrect_lun:
|
incorrect_lun:
|
||||||
req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN;
|
req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET;
|
req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
|
static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
|
||||||
|
@ -281,57 +338,70 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
|
||||||
VirtIOSCSIReq *req;
|
VirtIOSCSIReq *req;
|
||||||
|
|
||||||
while ((req = virtio_scsi_pop_req(s, vq))) {
|
while ((req = virtio_scsi_pop_req(s, vq))) {
|
||||||
int out_size, in_size;
|
int type;
|
||||||
if (req->elem.out_num < 1 || req->elem.in_num < 1) {
|
|
||||||
|
if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0,
|
||||||
|
&type, sizeof(type)) < sizeof(type)) {
|
||||||
virtio_scsi_bad_req();
|
virtio_scsi_bad_req();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_size = req->elem.out_sg[0].iov_len;
|
tswap32s(&req->req.tmf.type);
|
||||||
in_size = req->elem.in_sg[0].iov_len;
|
if (req->req.tmf.type == VIRTIO_SCSI_T_TMF) {
|
||||||
if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) {
|
if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq),
|
||||||
if (out_size < sizeof(VirtIOSCSICtrlTMFReq) ||
|
sizeof(VirtIOSCSICtrlTMFResp)) < 0) {
|
||||||
in_size < sizeof(VirtIOSCSICtrlTMFResp)) {
|
|
||||||
virtio_scsi_bad_req();
|
virtio_scsi_bad_req();
|
||||||
}
|
} else {
|
||||||
virtio_scsi_do_tmf(s, req);
|
virtio_scsi_do_tmf(s, req);
|
||||||
|
|
||||||
} else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY ||
|
|
||||||
req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) {
|
|
||||||
if (out_size < sizeof(VirtIOSCSICtrlANReq) ||
|
|
||||||
in_size < sizeof(VirtIOSCSICtrlANResp)) {
|
|
||||||
virtio_scsi_bad_req();
|
|
||||||
}
|
}
|
||||||
req->resp.an->event_actual = 0;
|
|
||||||
req->resp.an->response = VIRTIO_SCSI_S_OK;
|
} else if (req->req.tmf.type == VIRTIO_SCSI_T_AN_QUERY ||
|
||||||
|
req->req.tmf.type == VIRTIO_SCSI_T_AN_SUBSCRIBE) {
|
||||||
|
if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlANReq),
|
||||||
|
sizeof(VirtIOSCSICtrlANResp)) < 0) {
|
||||||
|
virtio_scsi_bad_req();
|
||||||
|
} else {
|
||||||
|
req->resp.an.event_actual = 0;
|
||||||
|
req->resp.an.response = VIRTIO_SCSI_S_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
virtio_scsi_complete_req(req);
|
virtio_scsi_complete_req(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req)
|
||||||
|
{
|
||||||
|
/* Sense data is not in req->resp and is copied separately
|
||||||
|
* in virtio_scsi_command_complete.
|
||||||
|
*/
|
||||||
|
req->resp_size = sizeof(VirtIOSCSICmdResp);
|
||||||
|
virtio_scsi_complete_req(req);
|
||||||
|
}
|
||||||
|
|
||||||
static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
|
static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
|
||||||
size_t resid)
|
size_t resid)
|
||||||
{
|
{
|
||||||
VirtIOSCSIReq *req = r->hba_private;
|
VirtIOSCSIReq *req = r->hba_private;
|
||||||
VirtIOSCSI *s = req->dev;
|
uint8_t sense[SCSI_SENSE_BUF_SIZE];
|
||||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
|
||||||
uint32_t sense_len;
|
uint32_t sense_len;
|
||||||
|
|
||||||
if (r->io_canceled) {
|
if (r->io_canceled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
req->resp.cmd->response = VIRTIO_SCSI_S_OK;
|
req->resp.cmd.response = VIRTIO_SCSI_S_OK;
|
||||||
req->resp.cmd->status = status;
|
req->resp.cmd.status = status;
|
||||||
if (req->resp.cmd->status == GOOD) {
|
if (req->resp.cmd.status == GOOD) {
|
||||||
req->resp.cmd->resid = tswap32(resid);
|
req->resp.cmd.resid = tswap32(resid);
|
||||||
} else {
|
} else {
|
||||||
req->resp.cmd->resid = 0;
|
req->resp.cmd.resid = 0;
|
||||||
sense_len = scsi_req_get_sense(r, req->resp.cmd->sense,
|
sense_len = scsi_req_get_sense(r, sense, sizeof(sense));
|
||||||
vs->sense_size);
|
sense_len = MIN(sense_len, req->resp_iov.size - sizeof(req->resp.cmd));
|
||||||
req->resp.cmd->sense_len = tswap32(sense_len);
|
qemu_iovec_from_buf(&req->resp_iov, sizeof(req->resp.cmd),
|
||||||
|
&req->resp, sense_len);
|
||||||
|
req->resp.cmd.sense_len = tswap32(sense_len);
|
||||||
}
|
}
|
||||||
virtio_scsi_complete_req(req);
|
virtio_scsi_complete_cmd_req(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r)
|
static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r)
|
||||||
|
@ -349,17 +419,17 @@ static void virtio_scsi_request_cancelled(SCSIRequest *r)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (req->dev->resetting) {
|
if (req->dev->resetting) {
|
||||||
req->resp.cmd->response = VIRTIO_SCSI_S_RESET;
|
req->resp.cmd.response = VIRTIO_SCSI_S_RESET;
|
||||||
} else {
|
} else {
|
||||||
req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED;
|
req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED;
|
||||||
}
|
}
|
||||||
virtio_scsi_complete_req(req);
|
virtio_scsi_complete_cmd_req(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
|
static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
|
||||||
{
|
{
|
||||||
req->resp.cmd->response = VIRTIO_SCSI_S_FAILURE;
|
req->resp.cmd.response = VIRTIO_SCSI_S_FAILURE;
|
||||||
virtio_scsi_complete_req(req);
|
virtio_scsi_complete_cmd_req(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
|
static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
|
||||||
|
@ -373,44 +443,36 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
|
||||||
|
|
||||||
while ((req = virtio_scsi_pop_req(s, vq))) {
|
while ((req = virtio_scsi_pop_req(s, vq))) {
|
||||||
SCSIDevice *d;
|
SCSIDevice *d;
|
||||||
int out_size, in_size;
|
int rc;
|
||||||
if (req->elem.out_num < 1 || req->elem.in_num < 1) {
|
|
||||||
virtio_scsi_bad_req();
|
|
||||||
}
|
|
||||||
|
|
||||||
out_size = req->elem.out_sg[0].iov_len;
|
rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
|
||||||
in_size = req->elem.in_sg[0].iov_len;
|
sizeof(VirtIOSCSICmdResp) + vs->sense_size);
|
||||||
if (out_size < sizeof(VirtIOSCSICmdReq) + vs->cdb_size ||
|
if (rc < 0) {
|
||||||
in_size < sizeof(VirtIOSCSICmdResp) + vs->sense_size) {
|
if (rc == -ENOTSUP) {
|
||||||
virtio_scsi_bad_req();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req->elem.out_num > 1 && req->elem.in_num > 1) {
|
|
||||||
virtio_scsi_fail_cmd_req(req);
|
virtio_scsi_fail_cmd_req(req);
|
||||||
|
} else {
|
||||||
|
virtio_scsi_bad_req();
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
d = virtio_scsi_device_find(s, req->req.cmd->lun);
|
d = virtio_scsi_device_find(s, req->req.cmd.lun);
|
||||||
if (!d) {
|
if (!d) {
|
||||||
req->resp.cmd->response = VIRTIO_SCSI_S_BAD_TARGET;
|
req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
|
||||||
virtio_scsi_complete_req(req);
|
virtio_scsi_complete_cmd_req(req);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
req->sreq = scsi_req_new(d, req->req.cmd->tag,
|
req->sreq = scsi_req_new(d, req->req.cmd.tag,
|
||||||
virtio_scsi_get_lun(req->req.cmd->lun),
|
virtio_scsi_get_lun(req->req.cmd.lun),
|
||||||
req->req.cmd->cdb, req);
|
req->req.cdb, req);
|
||||||
|
|
||||||
if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
|
if (req->sreq->cmd.mode != SCSI_XFER_NONE
|
||||||
int req_mode =
|
&& (req->sreq->cmd.mode != req->mode ||
|
||||||
(req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
|
req->sreq->cmd.xfer > req->qsgl.size)) {
|
||||||
|
req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN;
|
||||||
if (req->sreq->cmd.mode != req_mode ||
|
virtio_scsi_complete_cmd_req(req);
|
||||||
req->sreq->cmd.xfer > req->qsgl.size) {
|
|
||||||
req->resp.cmd->response = VIRTIO_SCSI_S_OVERRUN;
|
|
||||||
virtio_scsi_complete_req(req);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
n = scsi_req_enqueue(req->sreq);
|
n = scsi_req_enqueue(req->sreq);
|
||||||
if (n) {
|
if (n) {
|
||||||
|
@ -513,7 +575,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req->elem.out_num || req->elem.in_num != 1) {
|
if (req->elem.out_num) {
|
||||||
virtio_scsi_bad_req();
|
virtio_scsi_bad_req();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,12 +584,12 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||||
s->events_dropped = false;
|
s->events_dropped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
in_size = req->elem.in_sg[0].iov_len;
|
in_size = iov_size(req->elem.in_sg, req->elem.in_num);
|
||||||
if (in_size < sizeof(VirtIOSCSIEvent)) {
|
if (in_size < sizeof(VirtIOSCSIEvent)) {
|
||||||
virtio_scsi_bad_req();
|
virtio_scsi_bad_req();
|
||||||
}
|
}
|
||||||
|
|
||||||
evt = req->resp.event;
|
evt = &req->resp.event;
|
||||||
memset(evt, 0, sizeof(VirtIOSCSIEvent));
|
memset(evt, 0, sizeof(VirtIOSCSIEvent));
|
||||||
evt->event = event;
|
evt->event = event;
|
||||||
evt->reason = reason;
|
evt->reason = reason;
|
||||||
|
@ -605,7 +667,9 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = {
|
||||||
.load_request = virtio_scsi_load_request,
|
.load_request = virtio_scsi_load_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
void virtio_scsi_common_realize(DeviceState *dev, Error **errp)
|
void virtio_scsi_common_realize(DeviceState *dev, Error **errp,
|
||||||
|
HandleOutput ctrl, HandleOutput evt,
|
||||||
|
HandleOutput cmd)
|
||||||
{
|
{
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev);
|
VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev);
|
||||||
|
@ -619,12 +683,12 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp)
|
||||||
s->cdb_size = VIRTIO_SCSI_CDB_SIZE;
|
s->cdb_size = VIRTIO_SCSI_CDB_SIZE;
|
||||||
|
|
||||||
s->ctrl_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE,
|
s->ctrl_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE,
|
||||||
virtio_scsi_handle_ctrl);
|
ctrl);
|
||||||
s->event_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE,
|
s->event_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE,
|
||||||
virtio_scsi_handle_event);
|
evt);
|
||||||
for (i = 0; i < s->conf.num_queues; i++) {
|
for (i = 0; i < s->conf.num_queues; i++) {
|
||||||
s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE,
|
s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE,
|
||||||
virtio_scsi_handle_cmd);
|
cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,7 +699,9 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp)
|
||||||
static int virtio_scsi_id;
|
static int virtio_scsi_id;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
virtio_scsi_common_realize(dev, &err);
|
virtio_scsi_common_realize(dev, &err, virtio_scsi_handle_ctrl,
|
||||||
|
virtio_scsi_handle_event,
|
||||||
|
virtio_scsi_handle_cmd);
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -143,6 +143,8 @@
|
||||||
#define READ_CD 0xbe
|
#define READ_CD 0xbe
|
||||||
#define SEND_DVD_STRUCTURE 0xbf
|
#define SEND_DVD_STRUCTURE 0xbf
|
||||||
|
|
||||||
|
const char *scsi_command_name(uint8_t cmd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SERVICE ACTION IN subcodes
|
* SERVICE ACTION IN subcodes
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -268,6 +268,10 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
|
||||||
|
|
||||||
#define PC_COMPAT_2_0 \
|
#define PC_COMPAT_2_0 \
|
||||||
{\
|
{\
|
||||||
|
.driver = "virtio-scsi-pci",\
|
||||||
|
.property = "any_layout",\
|
||||||
|
.value = "off",\
|
||||||
|
},{\
|
||||||
.driver = "apic",\
|
.driver = "apic",\
|
||||||
.property = "version",\
|
.property = "version",\
|
||||||
.value = stringify(0x11),\
|
.value = stringify(0x11),\
|
||||||
|
|
|
@ -84,14 +84,13 @@
|
||||||
#define VIRTIO_SCSI_EVT_RESET_RESCAN 1
|
#define VIRTIO_SCSI_EVT_RESET_RESCAN 1
|
||||||
#define VIRTIO_SCSI_EVT_RESET_REMOVED 2
|
#define VIRTIO_SCSI_EVT_RESET_REMOVED 2
|
||||||
|
|
||||||
/* SCSI command request, followed by data-out */
|
/* SCSI command request, followed by CDB and data-out */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t lun[8]; /* Logical Unit Number */
|
uint8_t lun[8]; /* Logical Unit Number */
|
||||||
uint64_t tag; /* Command identifier */
|
uint64_t tag; /* Command identifier */
|
||||||
uint8_t task_attr; /* Task attribute */
|
uint8_t task_attr; /* Task attribute */
|
||||||
uint8_t prio;
|
uint8_t prio;
|
||||||
uint8_t crn;
|
uint8_t crn;
|
||||||
uint8_t cdb[];
|
|
||||||
} QEMU_PACKED VirtIOSCSICmdReq;
|
} QEMU_PACKED VirtIOSCSICmdReq;
|
||||||
|
|
||||||
/* Response, followed by sense data and data-in */
|
/* Response, followed by sense data and data-in */
|
||||||
|
@ -101,7 +100,6 @@ typedef struct {
|
||||||
uint16_t status_qualifier; /* Status qualifier */
|
uint16_t status_qualifier; /* Status qualifier */
|
||||||
uint8_t status; /* Command completion status */
|
uint8_t status; /* Command completion status */
|
||||||
uint8_t response; /* Response values */
|
uint8_t response; /* Response values */
|
||||||
uint8_t sense[];
|
|
||||||
} QEMU_PACKED VirtIOSCSICmdResp;
|
} QEMU_PACKED VirtIOSCSICmdResp;
|
||||||
|
|
||||||
/* Task Management Request */
|
/* Task Management Request */
|
||||||
|
@ -186,7 +184,12 @@ typedef struct {
|
||||||
DEFINE_PROP_BIT("param_change", _state, _feature_field, \
|
DEFINE_PROP_BIT("param_change", _state, _feature_field, \
|
||||||
VIRTIO_SCSI_F_CHANGE, true)
|
VIRTIO_SCSI_F_CHANGE, true)
|
||||||
|
|
||||||
void virtio_scsi_common_realize(DeviceState *dev, Error **errp);
|
typedef void (*HandleOutput)(VirtIODevice *, VirtQueue *);
|
||||||
|
|
||||||
|
void virtio_scsi_common_realize(DeviceState *dev, Error **errp,
|
||||||
|
HandleOutput ctrl, HandleOutput evt,
|
||||||
|
HandleOutput cmd);
|
||||||
|
|
||||||
void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp);
|
void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp);
|
||||||
|
|
||||||
#endif /* _QEMU_VIRTIO_SCSI_H */
|
#endif /* _QEMU_VIRTIO_SCSI_H */
|
||||||
|
|
|
@ -315,7 +315,7 @@ void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov);
|
||||||
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
|
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
|
||||||
void qemu_iovec_concat(QEMUIOVector *dst,
|
void qemu_iovec_concat(QEMUIOVector *dst,
|
||||||
QEMUIOVector *src, size_t soffset, size_t sbytes);
|
QEMUIOVector *src, size_t soffset, size_t sbytes);
|
||||||
void qemu_iovec_concat_iov(QEMUIOVector *dst,
|
size_t qemu_iovec_concat_iov(QEMUIOVector *dst,
|
||||||
struct iovec *src_iov, unsigned int src_cnt,
|
struct iovec *src_iov, unsigned int src_cnt,
|
||||||
size_t soffset, size_t sbytes);
|
size_t soffset, size_t sbytes);
|
||||||
bool qemu_iovec_is_zero(QEMUIOVector *qiov);
|
bool qemu_iovec_is_zero(QEMUIOVector *qiov);
|
||||||
|
|
|
@ -295,7 +295,7 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
|
||||||
* of src".
|
* of src".
|
||||||
* Only vector pointers are processed, not the actual data buffers.
|
* Only vector pointers are processed, not the actual data buffers.
|
||||||
*/
|
*/
|
||||||
void qemu_iovec_concat_iov(QEMUIOVector *dst,
|
size_t qemu_iovec_concat_iov(QEMUIOVector *dst,
|
||||||
struct iovec *src_iov, unsigned int src_cnt,
|
struct iovec *src_iov, unsigned int src_cnt,
|
||||||
size_t soffset, size_t sbytes)
|
size_t soffset, size_t sbytes)
|
||||||
{
|
{
|
||||||
|
@ -303,7 +303,7 @@ void qemu_iovec_concat_iov(QEMUIOVector *dst,
|
||||||
size_t done;
|
size_t done;
|
||||||
|
|
||||||
if (!sbytes) {
|
if (!sbytes) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
assert(dst->nalloc != -1);
|
assert(dst->nalloc != -1);
|
||||||
for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) {
|
for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) {
|
||||||
|
@ -317,6 +317,8 @@ void qemu_iovec_concat_iov(QEMUIOVector *dst,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(soffset == 0); /* offset beyond end of src */
|
assert(soffset == 0); /* offset beyond end of src */
|
||||||
|
|
||||||
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue