2011-10-25 12:24:24 +04:00
|
|
|
/*
|
|
|
|
* QEMU Block driver for iSCSI images
|
|
|
|
*
|
|
|
|
* Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
2016-07-18 11:52:20 +03:00
|
|
|
* Copyright (c) 2012-2016 Peter Lieven <pl@kamp.de>
|
2011-10-25 12:24:24 +04:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2016-01-18 21:01:42 +03:00
|
|
|
#include "qemu/osdep.h"
|
2011-10-25 12:24:24 +04:00
|
|
|
|
|
|
|
#include <poll.h>
|
2014-05-31 01:36:47 +04:00
|
|
|
#include <math.h>
|
2012-05-22 14:10:05 +04:00
|
|
|
#include <arpa/inet.h>
|
2011-10-25 12:24:24 +04:00
|
|
|
#include "qemu-common.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/config-file.h"
|
|
|
|
#include "qemu/error-report.h"
|
2014-04-28 15:11:32 +04:00
|
|
|
#include "qemu/bitops.h"
|
|
|
|
#include "qemu/bitmap.h"
|
2012-12-17 21:19:44 +04:00
|
|
|
#include "block/block_int.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "block/scsi.h"
|
2013-06-23 19:07:08 +04:00
|
|
|
#include "qemu/iov.h"
|
2013-08-02 19:02:01 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
|
|
|
#include "qmp-commands.h"
|
2015-03-17 20:29:20 +03:00
|
|
|
#include "qapi/qmp/qstring.h"
|
2016-01-21 17:19:21 +03:00
|
|
|
#include "crypto/secret.h"
|
2011-10-25 12:24:24 +04:00
|
|
|
|
|
|
|
#include <iscsi/iscsi.h>
|
|
|
|
#include <iscsi/scsi-lowlevel.h>
|
|
|
|
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
#ifdef __linux__
|
|
|
|
#include <scsi/sg.h>
|
|
|
|
#endif
|
2011-10-25 12:24:24 +04:00
|
|
|
|
|
|
|
typedef struct IscsiLun {
|
|
|
|
struct iscsi_context *iscsi;
|
2014-05-08 18:34:42 +04:00
|
|
|
AioContext *aio_context;
|
2011-10-25 12:24:24 +04:00
|
|
|
int lun;
|
2012-05-25 15:59:01 +04:00
|
|
|
enum scsi_inquiry_peripheral_device_type type;
|
2011-10-25 12:24:24 +04:00
|
|
|
int block_size;
|
2012-05-26 11:41:13 +04:00
|
|
|
uint64_t num_blocks;
|
2012-05-22 13:56:36 +04:00
|
|
|
int events;
|
2012-12-06 13:46:47 +04:00
|
|
|
QEMUTimer *nop_timer;
|
2015-04-07 23:08:15 +03:00
|
|
|
QEMUTimer *event_timer;
|
2013-07-19 11:19:39 +04:00
|
|
|
struct scsi_inquiry_logical_block_provisioning lbp;
|
|
|
|
struct scsi_inquiry_block_limits bl;
|
2013-10-24 14:07:02 +04:00
|
|
|
unsigned char *zeroblock;
|
2016-07-18 11:52:20 +03:00
|
|
|
/* The allocmap tracks which clusters (pages) on the iSCSI target are
|
|
|
|
* allocated and which are not. In case a target returns zeros for
|
|
|
|
* unallocated pages (iscsilun->lprz) we can directly return zeros instead
|
|
|
|
* of reading zeros over the wire if a read request falls within an
|
|
|
|
* unallocated block. As there are 3 possible states we need 2 bitmaps to
|
|
|
|
* track. allocmap_valid keeps track if QEMU's information about a page is
|
|
|
|
* valid. allocmap tracks if a page is allocated or not. In case QEMU has no
|
|
|
|
* valid information about a page the corresponding allocmap entry should be
|
|
|
|
* switched to unallocated as well to force a new lookup of the allocation
|
|
|
|
* status as lookups are generally skipped if a page is suspect to be
|
|
|
|
* allocated. If a iSCSI target is opened with cache.direct = on the
|
|
|
|
* allocmap_valid does not exist turning all cached information invalid so
|
|
|
|
* that a fresh lookup is made for any page even if allocmap entry returns
|
|
|
|
* it's unallocated. */
|
|
|
|
unsigned long *allocmap;
|
|
|
|
unsigned long *allocmap_valid;
|
|
|
|
long allocmap_size;
|
2014-04-28 15:11:32 +04:00
|
|
|
int cluster_sectors;
|
2014-06-04 17:47:39 +04:00
|
|
|
bool use_16_for_rw;
|
2015-02-25 07:40:08 +03:00
|
|
|
bool write_protected;
|
2015-04-16 17:08:26 +03:00
|
|
|
bool lbpme;
|
|
|
|
bool lbprz;
|
2015-04-16 17:08:28 +03:00
|
|
|
bool dpofua;
|
2015-04-16 17:08:26 +03:00
|
|
|
bool has_write_same;
|
2015-06-16 14:45:07 +03:00
|
|
|
bool request_timed_out;
|
2011-10-25 12:24:24 +04:00
|
|
|
} IscsiLun;
|
|
|
|
|
2013-07-19 11:19:40 +04:00
|
|
|
typedef struct IscsiTask {
|
|
|
|
int status;
|
|
|
|
int complete;
|
|
|
|
int retries;
|
|
|
|
int do_retry;
|
|
|
|
struct scsi_task *task;
|
|
|
|
Coroutine *co;
|
2013-12-14 20:31:40 +04:00
|
|
|
QEMUBH *bh;
|
2014-05-08 18:34:42 +04:00
|
|
|
IscsiLun *iscsilun;
|
2014-05-31 01:36:47 +04:00
|
|
|
QEMUTimer retry_timer;
|
2015-11-05 08:00:09 +03:00
|
|
|
int err_code;
|
2013-07-19 11:19:40 +04:00
|
|
|
} IscsiTask;
|
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
typedef struct IscsiAIOCB {
|
2014-10-07 15:59:14 +04:00
|
|
|
BlockAIOCB common;
|
2011-10-25 12:24:24 +04:00
|
|
|
QEMUIOVector *qiov;
|
|
|
|
QEMUBH *bh;
|
|
|
|
IscsiLun *iscsilun;
|
|
|
|
struct scsi_task *task;
|
|
|
|
uint8_t *buf;
|
|
|
|
int status;
|
2013-02-21 19:15:54 +04:00
|
|
|
int64_t sector_num;
|
|
|
|
int nb_sectors;
|
2015-11-09 13:16:49 +03:00
|
|
|
int ret;
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
#ifdef __linux__
|
|
|
|
sg_io_hdr_t *ioh;
|
|
|
|
#endif
|
2011-10-25 12:24:24 +04:00
|
|
|
} IscsiAIOCB;
|
|
|
|
|
2015-06-16 14:45:07 +03:00
|
|
|
/* libiscsi uses time_t so its enough to process events every second */
|
|
|
|
#define EVENT_INTERVAL 1000
|
2012-12-06 13:46:47 +04:00
|
|
|
#define NOP_INTERVAL 5000
|
|
|
|
#define MAX_NOP_FAILURES 3
|
2014-05-31 01:36:47 +04:00
|
|
|
#define ISCSI_CMD_RETRIES ARRAY_SIZE(iscsi_retry_times)
|
2015-04-16 17:08:30 +03:00
|
|
|
static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048, 8192, 32768};
|
2012-12-06 13:46:47 +04:00
|
|
|
|
2014-06-13 22:42:57 +04:00
|
|
|
/* this threshold is a trade-off knob to choose between
|
2014-04-28 19:11:33 +04:00
|
|
|
* the potential additional overhead of an extra GET_LBA_STATUS request
|
|
|
|
* vs. unnecessarily reading a lot of zero sectors over the wire.
|
|
|
|
* If a read request is greater or equal than ISCSI_CHECKALLOC_THRES
|
|
|
|
* sectors we check the allocation status of the area covered by the
|
|
|
|
* request first if the allocationmap indicates that the area might be
|
|
|
|
* unallocated. */
|
|
|
|
#define ISCSI_CHECKALLOC_THRES 64
|
2012-12-06 13:46:47 +04:00
|
|
|
|
2012-08-19 01:37:31 +04:00
|
|
|
static void
|
2012-08-19 01:38:03 +04:00
|
|
|
iscsi_bh_cb(void *p)
|
2012-08-19 01:37:31 +04:00
|
|
|
{
|
|
|
|
IscsiAIOCB *acb = p;
|
|
|
|
|
|
|
|
qemu_bh_delete(acb->bh);
|
|
|
|
|
2013-01-22 20:34:29 +04:00
|
|
|
g_free(acb->buf);
|
|
|
|
acb->buf = NULL;
|
|
|
|
|
2014-09-11 09:41:15 +04:00
|
|
|
acb->common.cb(acb->common.opaque, acb->status);
|
2012-08-19 01:37:31 +04:00
|
|
|
|
2012-08-19 01:35:49 +04:00
|
|
|
if (acb->task != NULL) {
|
|
|
|
scsi_free_scsi_task(acb->task);
|
|
|
|
acb->task = NULL;
|
|
|
|
}
|
|
|
|
|
2014-09-11 09:41:28 +04:00
|
|
|
qemu_aio_unref(acb);
|
2012-08-19 01:37:31 +04:00
|
|
|
}
|
|
|
|
|
2012-08-19 01:38:03 +04:00
|
|
|
static void
|
|
|
|
iscsi_schedule_bh(IscsiAIOCB *acb)
|
2012-08-19 01:37:31 +04:00
|
|
|
{
|
2012-08-19 01:35:49 +04:00
|
|
|
if (acb->bh) {
|
|
|
|
return;
|
|
|
|
}
|
2014-05-08 18:34:42 +04:00
|
|
|
acb->bh = aio_bh_new(acb->iscsilun->aio_context, iscsi_bh_cb, acb);
|
2012-08-19 01:37:31 +04:00
|
|
|
qemu_bh_schedule(acb->bh);
|
|
|
|
}
|
|
|
|
|
2013-12-14 20:31:40 +04:00
|
|
|
static void iscsi_co_generic_bh_cb(void *opaque)
|
|
|
|
{
|
|
|
|
struct IscsiTask *iTask = opaque;
|
2014-06-10 11:52:16 +04:00
|
|
|
iTask->complete = 1;
|
2013-12-14 20:31:40 +04:00
|
|
|
qemu_bh_delete(iTask->bh);
|
coroutine: move entry argument to qemu_coroutine_create
In practice the entry argument is always known at creation time, and
it is confusing that sometimes qemu_coroutine_enter is used with a
non-NULL argument to re-enter a coroutine (this happens in
block/sheepdog.c and tests/test-coroutine.c). So pass the opaque value
at creation time, for consistency with e.g. aio_bh_new.
Mostly done with the following semantic patch:
@ entry1 @
expression entry, arg, co;
@@
- co = qemu_coroutine_create(entry);
+ co = qemu_coroutine_create(entry, arg);
...
- qemu_coroutine_enter(co, arg);
+ qemu_coroutine_enter(co);
@ entry2 @
expression entry, arg;
identifier co;
@@
- Coroutine *co = qemu_coroutine_create(entry);
+ Coroutine *co = qemu_coroutine_create(entry, arg);
...
- qemu_coroutine_enter(co, arg);
+ qemu_coroutine_enter(co);
@ entry3 @
expression entry, arg;
@@
- qemu_coroutine_enter(qemu_coroutine_create(entry), arg);
+ qemu_coroutine_enter(qemu_coroutine_create(entry, arg));
@ reentry @
expression co;
@@
- qemu_coroutine_enter(co, NULL);
+ qemu_coroutine_enter(co);
except for the aforementioned few places where the semantic patch
stumbled (as expected) and for test_co_queue, which would otherwise
produce an uninitialized variable warning.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2016-07-04 20:10:01 +03:00
|
|
|
qemu_coroutine_enter(iTask->co);
|
2013-12-14 20:31:40 +04:00
|
|
|
}
|
|
|
|
|
2014-05-31 01:36:47 +04:00
|
|
|
static void iscsi_retry_timer_expired(void *opaque)
|
|
|
|
{
|
|
|
|
struct IscsiTask *iTask = opaque;
|
2014-06-10 11:52:16 +04:00
|
|
|
iTask->complete = 1;
|
2014-05-31 01:36:47 +04:00
|
|
|
if (iTask->co) {
|
coroutine: move entry argument to qemu_coroutine_create
In practice the entry argument is always known at creation time, and
it is confusing that sometimes qemu_coroutine_enter is used with a
non-NULL argument to re-enter a coroutine (this happens in
block/sheepdog.c and tests/test-coroutine.c). So pass the opaque value
at creation time, for consistency with e.g. aio_bh_new.
Mostly done with the following semantic patch:
@ entry1 @
expression entry, arg, co;
@@
- co = qemu_coroutine_create(entry);
+ co = qemu_coroutine_create(entry, arg);
...
- qemu_coroutine_enter(co, arg);
+ qemu_coroutine_enter(co);
@ entry2 @
expression entry, arg;
identifier co;
@@
- Coroutine *co = qemu_coroutine_create(entry);
+ Coroutine *co = qemu_coroutine_create(entry, arg);
...
- qemu_coroutine_enter(co, arg);
+ qemu_coroutine_enter(co);
@ entry3 @
expression entry, arg;
@@
- qemu_coroutine_enter(qemu_coroutine_create(entry), arg);
+ qemu_coroutine_enter(qemu_coroutine_create(entry, arg));
@ reentry @
expression co;
@@
- qemu_coroutine_enter(co, NULL);
+ qemu_coroutine_enter(co);
except for the aforementioned few places where the semantic patch
stumbled (as expected) and for test_co_queue, which would otherwise
produce an uninitialized variable warning.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2016-07-04 20:10:01 +03:00
|
|
|
qemu_coroutine_enter(iTask->co);
|
2014-05-31 01:36:47 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned exp_random(double mean)
|
|
|
|
{
|
|
|
|
return -mean * log((double)rand() / RAND_MAX);
|
|
|
|
}
|
|
|
|
|
2015-11-05 08:00:09 +03:00
|
|
|
/* SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST was introduced in
|
|
|
|
* libiscsi 1.10.0, together with other constants we need. Use it as
|
|
|
|
* a hint that we have to define them ourselves if needed, to keep the
|
|
|
|
* minimum required libiscsi version at 1.9.0. We use an ASCQ macro for
|
|
|
|
* the test because SCSI_STATUS_* is an enum.
|
|
|
|
*
|
|
|
|
* To guard against future changes where SCSI_SENSE_ASCQ_* also becomes
|
|
|
|
* an enum, check against the LIBISCSI_API_VERSION macro, which was
|
|
|
|
* introduced in 1.11.0. If it is present, there is no need to define
|
|
|
|
* anything.
|
|
|
|
*/
|
|
|
|
#if !defined(SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST) && \
|
|
|
|
!defined(LIBISCSI_API_VERSION)
|
|
|
|
#define SCSI_STATUS_TASK_SET_FULL 0x28
|
|
|
|
#define SCSI_STATUS_TIMEOUT 0x0f000002
|
|
|
|
#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST 0x2600
|
|
|
|
#define SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR 0x1a00
|
2015-06-26 13:18:01 +03:00
|
|
|
#endif
|
|
|
|
|
2015-11-05 08:00:09 +03:00
|
|
|
static int iscsi_translate_sense(struct scsi_sense *sense)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
switch (sense->key) {
|
|
|
|
case SCSI_SENSE_NOT_READY:
|
|
|
|
return -EBUSY;
|
|
|
|
case SCSI_SENSE_DATA_PROTECTION:
|
|
|
|
return -EACCES;
|
|
|
|
case SCSI_SENSE_COMMAND_ABORTED:
|
|
|
|
return -ECANCELED;
|
|
|
|
case SCSI_SENSE_ILLEGAL_REQUEST:
|
|
|
|
/* Parse ASCQ */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
switch (sense->ascq) {
|
|
|
|
case SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR:
|
|
|
|
case SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE:
|
|
|
|
case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB:
|
|
|
|
case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST:
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE:
|
|
|
|
ret = -ENOSPC;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED:
|
|
|
|
ret = -ENOTSUP;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT:
|
|
|
|
case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED:
|
|
|
|
case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN:
|
|
|
|
ret = -ENOMEDIUM;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_ASCQ_WRITE_PROTECTED:
|
|
|
|
ret = -EACCES;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -EIO;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-07-19 11:19:40 +04:00
|
|
|
static void
|
|
|
|
iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
|
|
|
void *command_data, void *opaque)
|
|
|
|
{
|
|
|
|
struct IscsiTask *iTask = opaque;
|
|
|
|
struct scsi_task *task = command_data;
|
|
|
|
|
|
|
|
iTask->status = status;
|
|
|
|
iTask->do_retry = 0;
|
|
|
|
iTask->task = task;
|
|
|
|
|
|
|
|
if (status != SCSI_STATUS_GOOD) {
|
2014-05-31 01:36:47 +04:00
|
|
|
if (iTask->retries++ < ISCSI_CMD_RETRIES) {
|
|
|
|
if (status == SCSI_STATUS_CHECK_CONDITION
|
|
|
|
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
|
|
|
|
error_report("iSCSI CheckCondition: %s",
|
|
|
|
iscsi_get_error(iscsi));
|
|
|
|
iTask->do_retry = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-06-26 13:18:01 +03:00
|
|
|
if (status == SCSI_STATUS_BUSY ||
|
2015-11-05 08:00:09 +03:00
|
|
|
status == SCSI_STATUS_TIMEOUT ||
|
|
|
|
status == SCSI_STATUS_TASK_SET_FULL) {
|
2014-05-31 01:36:47 +04:00
|
|
|
unsigned retry_time =
|
|
|
|
exp_random(iscsi_retry_times[iTask->retries - 1]);
|
2015-11-05 08:00:09 +03:00
|
|
|
if (status == SCSI_STATUS_TIMEOUT) {
|
2015-06-16 14:45:07 +03:00
|
|
|
/* make sure the request is rescheduled AFTER the
|
|
|
|
* reconnect is initiated */
|
|
|
|
retry_time = EVENT_INTERVAL * 2;
|
|
|
|
iTask->iscsilun->request_timed_out = true;
|
|
|
|
}
|
|
|
|
error_report("iSCSI Busy/TaskSetFull/TimeOut"
|
|
|
|
" (retry #%u in %u ms): %s",
|
2014-05-31 01:36:47 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2015-11-05 08:00:09 +03:00
|
|
|
iTask->err_code = iscsi_translate_sense(&task->sense);
|
2014-02-18 16:08:39 +04:00
|
|
|
error_report("iSCSI Failure: %s", iscsi_get_error(iscsi));
|
2013-07-19 11:19:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (iTask->co) {
|
2014-05-08 18:34:42 +04:00
|
|
|
iTask->bh = aio_bh_new(iTask->iscsilun->aio_context,
|
|
|
|
iscsi_co_generic_bh_cb, iTask);
|
2013-12-14 20:31:40 +04:00
|
|
|
qemu_bh_schedule(iTask->bh);
|
2014-06-10 11:52:16 +04:00
|
|
|
} else {
|
|
|
|
iTask->complete = 1;
|
2013-07-19 11:19:40 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask)
|
|
|
|
{
|
|
|
|
*iTask = (struct IscsiTask) {
|
2014-05-31 01:36:47 +04:00
|
|
|
.co = qemu_coroutine_self(),
|
|
|
|
.iscsilun = iscsilun,
|
2013-07-19 11:19:40 +04:00
|
|
|
};
|
|
|
|
}
|
2012-08-19 01:37:31 +04:00
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
static void
|
|
|
|
iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
|
|
|
void *private_data)
|
|
|
|
{
|
2012-08-19 01:35:49 +04:00
|
|
|
IscsiAIOCB *acb = private_data;
|
|
|
|
|
|
|
|
acb->status = -ECANCELED;
|
|
|
|
iscsi_schedule_bh(acb);
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-07 15:59:14 +04:00
|
|
|
iscsi_aio_cancel(BlockAIOCB *blockacb)
|
2011-10-25 12:24:24 +04:00
|
|
|
{
|
|
|
|
IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
|
|
|
|
IscsiLun *iscsilun = acb->iscsilun;
|
|
|
|
|
2012-08-19 01:35:49 +04:00
|
|
|
if (acb->status != -EINPROGRESS) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-08-19 02:12:39 +04:00
|
|
|
/* send a task mgmt call to the target to cancel the task on the target */
|
2012-08-15 11:09:54 +04:00
|
|
|
iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
|
2012-08-19 01:35:49 +04:00
|
|
|
iscsi_abort_task_cb, acb);
|
2012-08-19 02:12:39 +04:00
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
2012-10-31 19:34:37 +04:00
|
|
|
static const AIOCBInfo iscsi_aiocb_info = {
|
2011-10-25 12:24:24 +04:00
|
|
|
.aiocb_size = sizeof(IscsiAIOCB),
|
2014-09-11 09:41:15 +04:00
|
|
|
.cancel_async = iscsi_aio_cancel,
|
2011-10-25 12:24:24 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void iscsi_process_read(void *arg);
|
|
|
|
static void iscsi_process_write(void *arg);
|
|
|
|
|
|
|
|
static void
|
|
|
|
iscsi_set_events(IscsiLun *iscsilun)
|
|
|
|
{
|
|
|
|
struct iscsi_context *iscsi = iscsilun->iscsi;
|
2015-04-07 23:08:15 +03:00
|
|
|
int ev = iscsi_which_events(iscsi);
|
2012-05-22 13:56:36 +04:00
|
|
|
|
|
|
|
if (ev != iscsilun->events) {
|
2015-10-23 06:08:05 +03:00
|
|
|
aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsi),
|
|
|
|
false,
|
2015-04-07 23:08:15 +03:00
|
|
|
(ev & POLLIN) ? iscsi_process_read : NULL,
|
2014-05-08 18:34:42 +04:00
|
|
|
(ev & POLLOUT) ? iscsi_process_write : NULL,
|
|
|
|
iscsilun);
|
2015-04-07 23:08:15 +03:00
|
|
|
iscsilun->events = ev;
|
|
|
|
}
|
|
|
|
}
|
2012-05-22 13:56:36 +04:00
|
|
|
|
2015-06-16 14:45:07 +03:00
|
|
|
static void iscsi_timed_check_events(void *opaque)
|
2015-04-07 23:08:15 +03:00
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = opaque;
|
2015-06-16 14:45:07 +03:00
|
|
|
|
|
|
|
/* check for timed out requests */
|
|
|
|
iscsi_service(iscsilun->iscsi, 0);
|
|
|
|
|
|
|
|
if (iscsilun->request_timed_out) {
|
|
|
|
iscsilun->request_timed_out = false;
|
|
|
|
iscsi_reconnect(iscsilun->iscsi);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* newer versions of libiscsi may return zero events. Ensure we are able
|
|
|
|
* to return to service once this situation changes. */
|
2015-04-07 23:08:15 +03:00
|
|
|
iscsi_set_events(iscsilun);
|
2015-06-16 14:45:07 +03:00
|
|
|
|
|
|
|
timer_mod(iscsilun->event_timer,
|
|
|
|
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL);
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iscsi_process_read(void *arg)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = arg;
|
|
|
|
struct iscsi_context *iscsi = iscsilun->iscsi;
|
|
|
|
|
|
|
|
iscsi_service(iscsi, POLLIN);
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iscsi_process_write(void *arg)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = arg;
|
|
|
|
struct iscsi_context *iscsi = iscsilun->iscsi;
|
|
|
|
|
|
|
|
iscsi_service(iscsi, POLLOUT);
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
}
|
|
|
|
|
2013-07-11 16:16:25 +04:00
|
|
|
static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun)
|
|
|
|
{
|
|
|
|
return sector * iscsilun->block_size / BDRV_SECTOR_SIZE;
|
|
|
|
}
|
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
|
|
|
|
{
|
|
|
|
return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
|
|
|
|
}
|
|
|
|
|
2016-06-02 00:10:05 +03:00
|
|
|
static bool is_byte_request_lun_aligned(int64_t offset, int count,
|
|
|
|
IscsiLun *iscsilun)
|
2013-07-11 16:16:27 +04:00
|
|
|
{
|
2016-06-02 00:10:05 +03:00
|
|
|
if (offset % iscsilun->block_size || count % iscsilun->block_size) {
|
|
|
|
error_report("iSCSI misaligned request: "
|
|
|
|
"iscsilun->block_size %u, offset %" PRIi64
|
|
|
|
", count %d",
|
|
|
|
iscsilun->block_size, offset, count);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_sector_request_lun_aligned(int64_t sector_num, int nb_sectors,
|
|
|
|
IscsiLun *iscsilun)
|
|
|
|
{
|
2016-06-20 12:24:40 +03:00
|
|
|
assert(nb_sectors <= BDRV_REQUEST_MAX_SECTORS);
|
2016-06-02 00:10:05 +03:00
|
|
|
return is_byte_request_lun_aligned(sector_num << BDRV_SECTOR_BITS,
|
|
|
|
nb_sectors << BDRV_SECTOR_BITS,
|
|
|
|
iscsilun);
|
2013-07-11 16:16:27 +04:00
|
|
|
}
|
|
|
|
|
2016-07-18 11:52:20 +03:00
|
|
|
static void iscsi_allocmap_free(IscsiLun *iscsilun)
|
2014-09-30 11:09:12 +04:00
|
|
|
{
|
2016-07-18 11:52:20 +03:00
|
|
|
g_free(iscsilun->allocmap);
|
|
|
|
g_free(iscsilun->allocmap_valid);
|
|
|
|
iscsilun->allocmap = NULL;
|
|
|
|
iscsilun->allocmap_valid = NULL;
|
2014-09-30 11:09:12 +04:00
|
|
|
}
|
|
|
|
|
2016-07-18 11:52:20 +03:00
|
|
|
|
|
|
|
static int iscsi_allocmap_init(IscsiLun *iscsilun, int open_flags)
|
2014-04-28 15:11:32 +04:00
|
|
|
{
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_free(iscsilun);
|
|
|
|
|
|
|
|
iscsilun->allocmap_size =
|
|
|
|
DIV_ROUND_UP(sector_lun2qemu(iscsilun->num_blocks, iscsilun),
|
|
|
|
iscsilun->cluster_sectors);
|
|
|
|
|
|
|
|
iscsilun->allocmap = bitmap_try_new(iscsilun->allocmap_size);
|
|
|
|
if (!iscsilun->allocmap) {
|
|
|
|
return -ENOMEM;
|
2014-04-28 15:11:32 +04:00
|
|
|
}
|
2016-07-18 11:52:20 +03:00
|
|
|
|
|
|
|
if (open_flags & BDRV_O_NOCACHE) {
|
|
|
|
/* in case that cache.direct = on all allocmap entries are
|
|
|
|
* treated as invalid to force a relookup of the block
|
|
|
|
* status on every read request */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
iscsilun->allocmap_valid = bitmap_try_new(iscsilun->allocmap_size);
|
|
|
|
if (!iscsilun->allocmap_valid) {
|
|
|
|
/* if we are under memory pressure free the allocmap as well */
|
|
|
|
iscsi_allocmap_free(iscsilun);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2014-04-28 15:11:32 +04:00
|
|
|
}
|
|
|
|
|
2016-07-18 11:52:20 +03:00
|
|
|
static void
|
|
|
|
iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num,
|
|
|
|
int nb_sectors, bool allocated, bool valid)
|
2014-04-28 15:11:32 +04:00
|
|
|
{
|
2016-07-18 11:52:20 +03:00
|
|
|
int64_t cl_num_expanded, nb_cls_expanded, cl_num_shrunk, nb_cls_shrunk;
|
|
|
|
|
|
|
|
if (iscsilun->allocmap == NULL) {
|
2014-04-28 15:11:32 +04:00
|
|
|
return;
|
|
|
|
}
|
2016-07-18 11:52:20 +03:00
|
|
|
/* expand to entirely contain all affected clusters */
|
|
|
|
cl_num_expanded = sector_num / iscsilun->cluster_sectors;
|
|
|
|
nb_cls_expanded = DIV_ROUND_UP(sector_num + nb_sectors,
|
|
|
|
iscsilun->cluster_sectors) - cl_num_expanded;
|
|
|
|
/* shrink to touch only completely contained clusters */
|
|
|
|
cl_num_shrunk = DIV_ROUND_UP(sector_num, iscsilun->cluster_sectors);
|
|
|
|
nb_cls_shrunk = (sector_num + nb_sectors) / iscsilun->cluster_sectors
|
|
|
|
- cl_num_shrunk;
|
|
|
|
if (allocated) {
|
|
|
|
bitmap_set(iscsilun->allocmap, cl_num_expanded, nb_cls_expanded);
|
|
|
|
} else {
|
|
|
|
bitmap_clear(iscsilun->allocmap, cl_num_shrunk, nb_cls_shrunk);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iscsilun->allocmap_valid == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (valid) {
|
|
|
|
bitmap_set(iscsilun->allocmap_valid, cl_num_shrunk, nb_cls_shrunk);
|
|
|
|
} else {
|
|
|
|
bitmap_clear(iscsilun->allocmap_valid, cl_num_expanded,
|
|
|
|
nb_cls_expanded);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iscsi_allocmap_set_allocated(IscsiLun *iscsilun, int64_t sector_num,
|
|
|
|
int nb_sectors)
|
|
|
|
{
|
|
|
|
iscsi_allocmap_update(iscsilun, sector_num, nb_sectors, true, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iscsi_allocmap_set_unallocated(IscsiLun *iscsilun, int64_t sector_num,
|
|
|
|
int nb_sectors)
|
|
|
|
{
|
|
|
|
/* Note: if cache.direct=on the fifth argument to iscsi_allocmap_update
|
|
|
|
* is ignored, so this will in effect be an iscsi_allocmap_set_invalid.
|
|
|
|
*/
|
|
|
|
iscsi_allocmap_update(iscsilun, sector_num, nb_sectors, false, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iscsi_allocmap_set_invalid(IscsiLun *iscsilun, int64_t sector_num,
|
|
|
|
int nb_sectors)
|
|
|
|
{
|
|
|
|
iscsi_allocmap_update(iscsilun, sector_num, nb_sectors, false, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iscsi_allocmap_invalidate(IscsiLun *iscsilun)
|
|
|
|
{
|
|
|
|
if (iscsilun->allocmap) {
|
|
|
|
bitmap_zero(iscsilun->allocmap, iscsilun->allocmap_size);
|
|
|
|
}
|
|
|
|
if (iscsilun->allocmap_valid) {
|
|
|
|
bitmap_zero(iscsilun->allocmap_valid, iscsilun->allocmap_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
iscsi_allocmap_is_allocated(IscsiLun *iscsilun, int64_t sector_num,
|
|
|
|
int nb_sectors)
|
|
|
|
{
|
|
|
|
unsigned long size;
|
|
|
|
if (iscsilun->allocmap == NULL) {
|
|
|
|
return true;
|
2014-04-28 15:11:32 +04:00
|
|
|
}
|
2016-07-18 11:52:20 +03:00
|
|
|
size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors);
|
|
|
|
return !(find_next_bit(iscsilun->allocmap, size,
|
|
|
|
sector_num / iscsilun->cluster_sectors) == size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun,
|
|
|
|
int64_t sector_num, int nb_sectors)
|
|
|
|
{
|
|
|
|
unsigned long size;
|
|
|
|
if (iscsilun->allocmap_valid == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors);
|
|
|
|
return (find_next_zero_bit(iscsilun->allocmap_valid, size,
|
|
|
|
sector_num / iscsilun->cluster_sectors) == size);
|
2014-04-28 15:11:32 +04:00
|
|
|
}
|
|
|
|
|
2016-03-10 15:55:50 +03:00
|
|
|
static int coroutine_fn
|
|
|
|
iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
|
|
|
QEMUIOVector *iov, int flags)
|
2011-10-25 12:24:24 +04:00
|
|
|
{
|
2013-12-05 19:47:17 +04:00
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
struct IscsiTask iTask;
|
2012-05-22 14:10:05 +04:00
|
|
|
uint64_t lba;
|
2013-12-05 19:47:17 +04:00
|
|
|
uint32_t num_sectors;
|
2016-05-04 01:39:06 +03:00
|
|
|
bool fua = flags & BDRV_REQ_FUA;
|
2011-10-25 12:24:24 +04:00
|
|
|
|
2016-05-04 01:39:06 +03:00
|
|
|
if (fua) {
|
|
|
|
assert(iscsilun->dpofua);
|
|
|
|
}
|
2016-06-02 00:10:05 +03:00
|
|
|
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
2013-12-05 19:47:17 +04:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2012-12-03 23:35:15 +04:00
|
|
|
|
2016-07-15 21:32:04 +03:00
|
|
|
if (bs->bl.max_transfer) {
|
|
|
|
assert(nb_sectors << BDRV_SECTOR_BITS <= bs->bl.max_transfer);
|
2014-10-27 12:18:48 +03:00
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
lba = sector_qemu2lun(sector_num, iscsilun);
|
|
|
|
num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
|
|
|
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
|
|
|
retry:
|
2014-06-04 17:47:39 +04:00
|
|
|
if (iscsilun->use_16_for_rw) {
|
|
|
|
iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba,
|
2014-06-18 20:40:22 +04:00
|
|
|
NULL, num_sectors * iscsilun->block_size,
|
2015-04-16 17:08:29 +03:00
|
|
|
iscsilun->block_size, 0, 0, fua, 0, 0,
|
2014-06-04 17:47:39 +04:00
|
|
|
iscsi_co_generic_cb, &iTask);
|
|
|
|
} else {
|
|
|
|
iTask.task = iscsi_write10_task(iscsilun->iscsi, iscsilun->lun, lba,
|
2014-06-18 20:40:22 +04:00
|
|
|
NULL, num_sectors * iscsilun->block_size,
|
2015-04-16 17:08:29 +03:00
|
|
|
iscsilun->block_size, 0, 0, fua, 0, 0,
|
2014-06-04 17:47:39 +04:00
|
|
|
iscsi_co_generic_cb, &iTask);
|
|
|
|
}
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.task == NULL) {
|
2013-12-20 13:02:47 +04:00
|
|
|
return -ENOMEM;
|
2012-05-22 14:10:05 +04:00
|
|
|
}
|
2013-12-05 19:47:17 +04:00
|
|
|
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
|
|
|
iov->niov);
|
|
|
|
while (!iTask.complete) {
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
qemu_coroutine_yield();
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.task != NULL) {
|
|
|
|
scsi_free_scsi_task(iTask.task);
|
|
|
|
iTask.task = NULL;
|
2013-07-11 16:16:27 +04:00
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.do_retry) {
|
2014-02-18 16:08:39 +04:00
|
|
|
iTask.complete = 0;
|
2013-12-05 19:47:17 +04:00
|
|
|
goto retry;
|
2013-02-21 19:15:54 +04:00
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.status != SCSI_STATUS_GOOD) {
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors);
|
2015-11-05 08:00:09 +03:00
|
|
|
return iTask.err_code;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_set_allocated(iscsilun, sector_num, nb_sectors);
|
2014-04-28 15:11:32 +04:00
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
return 0;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
2014-04-28 15:11:32 +04:00
|
|
|
|
|
|
|
|
|
|
|
static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
|
|
|
|
int64_t sector_num,
|
2016-01-26 06:58:48 +03:00
|
|
|
int nb_sectors, int *pnum,
|
|
|
|
BlockDriverState **file)
|
2014-04-28 15:11:32 +04:00
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
struct scsi_get_lba_status *lbas = NULL;
|
|
|
|
struct scsi_lba_status_descriptor *lbasd = NULL;
|
|
|
|
struct IscsiTask iTask;
|
|
|
|
int64_t ret;
|
|
|
|
|
|
|
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
|
|
|
|
2016-06-02 00:10:05 +03:00
|
|
|
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
2014-04-28 15:11:32 +04:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* default to all sectors allocated */
|
|
|
|
ret = BDRV_BLOCK_DATA;
|
|
|
|
ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID;
|
|
|
|
*pnum = nb_sectors;
|
|
|
|
|
|
|
|
/* LUN does not support logical block provisioning */
|
2015-04-16 17:08:26 +03:00
|
|
|
if (!iscsilun->lbpme) {
|
2014-04-28 15:11:32 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
retry:
|
|
|
|
if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
|
|
|
|
sector_qemu2lun(sector_num, iscsilun),
|
|
|
|
8 + 16, iscsi_co_generic_cb,
|
|
|
|
&iTask) == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!iTask.complete) {
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
qemu_coroutine_yield();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iTask.do_retry) {
|
|
|
|
if (iTask.task != NULL) {
|
|
|
|
scsi_free_scsi_task(iTask.task);
|
|
|
|
iTask.task = NULL;
|
|
|
|
}
|
|
|
|
iTask.complete = 0;
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iTask.status != SCSI_STATUS_GOOD) {
|
|
|
|
/* in case the get_lba_status_callout fails (i.e.
|
|
|
|
* because the device is busy or the cmd is not
|
|
|
|
* supported) we pretend all blocks are allocated
|
|
|
|
* for backwards compatibility */
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
lbas = scsi_datain_unmarshall(iTask.task);
|
|
|
|
if (lbas == NULL) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
lbasd = &lbas->descriptors[0];
|
|
|
|
|
|
|
|
if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun);
|
|
|
|
|
|
|
|
if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
|
|
|
|
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
|
|
|
|
ret &= ~BDRV_BLOCK_DATA;
|
|
|
|
if (iscsilun->lbprz) {
|
|
|
|
ret |= BDRV_BLOCK_ZERO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret & BDRV_BLOCK_ZERO) {
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_set_unallocated(iscsilun, sector_num, *pnum);
|
2014-04-28 15:11:32 +04:00
|
|
|
} else {
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_set_allocated(iscsilun, sector_num, *pnum);
|
2014-04-28 15:11:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (*pnum > nb_sectors) {
|
|
|
|
*pnum = nb_sectors;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (iTask.task != NULL) {
|
|
|
|
scsi_free_scsi_task(iTask.task);
|
|
|
|
}
|
2016-01-26 06:58:52 +03:00
|
|
|
if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) {
|
|
|
|
*file = bs;
|
|
|
|
}
|
2014-04-28 15:11:32 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
|
|
|
int64_t sector_num, int nb_sectors,
|
|
|
|
QEMUIOVector *iov)
|
2011-10-25 12:24:24 +04:00
|
|
|
{
|
2013-12-05 19:47:17 +04:00
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
struct IscsiTask iTask;
|
2013-02-21 19:15:54 +04:00
|
|
|
uint64_t lba;
|
|
|
|
uint32_t num_sectors;
|
2011-10-25 12:24:24 +04:00
|
|
|
|
2016-06-02 00:10:05 +03:00
|
|
|
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
2013-12-05 19:47:17 +04:00
|
|
|
return -EINVAL;
|
2012-05-22 14:10:05 +04:00
|
|
|
}
|
|
|
|
|
2016-07-15 21:32:04 +03:00
|
|
|
if (bs->bl.max_transfer) {
|
|
|
|
assert(nb_sectors << BDRV_SECTOR_BITS <= bs->bl.max_transfer);
|
2014-10-27 12:18:48 +03:00
|
|
|
}
|
|
|
|
|
2016-07-18 11:52:20 +03:00
|
|
|
/* if cache.direct is off and we have a valid entry in our allocation map
|
|
|
|
* we can skip checking the block status and directly return zeroes if
|
|
|
|
* the request falls within an unallocated area */
|
|
|
|
if (iscsi_allocmap_is_valid(iscsilun, sector_num, nb_sectors) &&
|
|
|
|
!iscsi_allocmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
|
|
|
|
qemu_iovec_memset(iov, 0, 0x00, iov->size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nb_sectors >= ISCSI_CHECKALLOC_THRES &&
|
|
|
|
!iscsi_allocmap_is_valid(iscsilun, sector_num, nb_sectors) &&
|
|
|
|
!iscsi_allocmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
|
2014-04-28 15:11:32 +04:00
|
|
|
int pnum;
|
2016-01-26 06:58:48 +03:00
|
|
|
BlockDriverState *file;
|
2016-07-18 11:52:20 +03:00
|
|
|
/* check the block status from the beginning of the cluster
|
|
|
|
* containing the start sector */
|
|
|
|
int64_t ret = iscsi_co_get_block_status(bs,
|
|
|
|
sector_num - sector_num % iscsilun->cluster_sectors,
|
|
|
|
BDRV_REQUEST_MAX_SECTORS, &pnum, &file);
|
2014-04-28 15:11:32 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2016-07-18 11:52:20 +03:00
|
|
|
/* if the whole request falls into an unallocated area we can avoid
|
|
|
|
* to read and directly return zeroes instead */
|
|
|
|
if (ret & BDRV_BLOCK_ZERO &&
|
|
|
|
pnum >= nb_sectors + sector_num % iscsilun->cluster_sectors) {
|
2014-04-28 15:11:32 +04:00
|
|
|
qemu_iovec_memset(iov, 0, 0x00, iov->size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
lba = sector_qemu2lun(sector_num, iscsilun);
|
|
|
|
num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
|
2012-05-22 14:10:05 +04:00
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
|
|
|
retry:
|
2014-06-04 17:47:39 +04:00
|
|
|
if (iscsilun->use_16_for_rw) {
|
2013-12-05 19:47:17 +04:00
|
|
|
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);
|
2014-06-04 17:47:39 +04:00
|
|
|
} else {
|
2013-12-05 19:47:17 +04:00
|
|
|
iTask.task = iscsi_read10_task(iscsilun->iscsi, iscsilun->lun, lba,
|
|
|
|
num_sectors * iscsilun->block_size,
|
2013-12-17 11:57:10 +04:00
|
|
|
iscsilun->block_size,
|
|
|
|
0, 0, 0, 0, 0,
|
2013-12-05 19:47:17 +04:00
|
|
|
iscsi_co_generic_cb, &iTask);
|
2012-05-22 14:10:05 +04:00
|
|
|
}
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.task == NULL) {
|
2013-12-20 13:02:47 +04:00
|
|
|
return -ENOMEM;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
2013-12-05 19:47:17 +04:00
|
|
|
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
2013-07-11 16:16:27 +04:00
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
while (!iTask.complete) {
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
qemu_coroutine_yield();
|
2013-02-21 19:15:54 +04:00
|
|
|
}
|
2011-10-25 12:24:24 +04:00
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.task != NULL) {
|
|
|
|
scsi_free_scsi_task(iTask.task);
|
|
|
|
iTask.task = NULL;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.do_retry) {
|
2014-02-18 16:08:39 +04:00
|
|
|
iTask.complete = 0;
|
2013-12-05 19:47:17 +04:00
|
|
|
goto retry;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.status != SCSI_STATUS_GOOD) {
|
2015-11-05 08:00:09 +03:00
|
|
|
return iTask.err_code;
|
2013-02-21 19:15:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
static int coroutine_fn iscsi_co_flush(BlockDriverState *bs)
|
2013-02-21 19:15:54 +04:00
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
2013-12-05 19:47:17 +04:00
|
|
|
struct IscsiTask iTask;
|
2013-02-21 19:15:54 +04:00
|
|
|
|
2015-04-16 17:08:29 +03:00
|
|
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
2013-12-05 19:47:17 +04:00
|
|
|
retry:
|
|
|
|
if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0,
|
|
|
|
0, iscsi_co_generic_cb, &iTask) == NULL) {
|
2013-12-20 13:02:47 +04:00
|
|
|
return -ENOMEM;
|
2013-12-05 19:47:17 +04:00
|
|
|
}
|
2013-02-21 19:15:54 +04:00
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
while (!iTask.complete) {
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
qemu_coroutine_yield();
|
|
|
|
}
|
2013-02-21 19:15:54 +04:00
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.task != NULL) {
|
|
|
|
scsi_free_scsi_task(iTask.task);
|
|
|
|
iTask.task = NULL;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.do_retry) {
|
2014-02-18 16:08:39 +04:00
|
|
|
iTask.complete = 0;
|
2013-12-05 19:47:17 +04:00
|
|
|
goto retry;
|
|
|
|
}
|
2011-10-25 12:24:24 +04:00
|
|
|
|
2013-12-05 19:47:17 +04:00
|
|
|
if (iTask.status != SCSI_STATUS_GOOD) {
|
2015-11-05 08:00:09 +03:00
|
|
|
return iTask.err_code;
|
2013-12-05 19:47:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
#ifdef __linux__
|
|
|
|
static void
|
|
|
|
iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
|
|
|
|
void *command_data, void *opaque)
|
|
|
|
{
|
|
|
|
IscsiAIOCB *acb = opaque;
|
|
|
|
|
2013-06-23 19:07:08 +04:00
|
|
|
g_free(acb->buf);
|
|
|
|
acb->buf = NULL;
|
|
|
|
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
acb->status = 0;
|
|
|
|
if (status < 0) {
|
|
|
|
error_report("Failed to ioctl(SG_IO) to iSCSI lun. %s",
|
|
|
|
iscsi_get_error(iscsi));
|
2015-11-05 08:00:09 +03:00
|
|
|
acb->status = iscsi_translate_sense(&acb->task->sense);
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
acb->ioh->driver_status = 0;
|
|
|
|
acb->ioh->host_status = 0;
|
|
|
|
acb->ioh->resid = 0;
|
2016-05-13 14:03:22 +03:00
|
|
|
acb->ioh->status = status;
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
|
|
|
|
#define SG_ERR_DRIVER_SENSE 0x08
|
|
|
|
|
|
|
|
if (status == SCSI_STATUS_CHECK_CONDITION && acb->task->datain.size >= 2) {
|
|
|
|
int ss;
|
|
|
|
|
|
|
|
acb->ioh->driver_status |= SG_ERR_DRIVER_SENSE;
|
|
|
|
|
|
|
|
acb->ioh->sb_len_wr = acb->task->datain.size - 2;
|
|
|
|
ss = (acb->ioh->mx_sb_len >= acb->ioh->sb_len_wr) ?
|
|
|
|
acb->ioh->mx_sb_len : acb->ioh->sb_len_wr;
|
|
|
|
memcpy(acb->ioh->sbp, &acb->task->datain.data[2], ss);
|
|
|
|
}
|
|
|
|
|
2012-08-19 01:38:03 +04:00
|
|
|
iscsi_schedule_bh(acb);
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:16:49 +03:00
|
|
|
static void iscsi_ioctl_bh_completion(void *opaque)
|
|
|
|
{
|
|
|
|
IscsiAIOCB *acb = opaque;
|
|
|
|
|
|
|
|
qemu_bh_delete(acb->bh);
|
|
|
|
acb->common.cb(acb->common.opaque, acb->ret);
|
|
|
|
qemu_aio_unref(acb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iscsi_ioctl_handle_emulated(IscsiAIOCB *acb, int req, void *buf)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs = acb->common.bs;
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
switch (req) {
|
|
|
|
case SG_GET_VERSION_NUM:
|
|
|
|
*(int *)buf = 30000;
|
|
|
|
break;
|
|
|
|
case SG_GET_SCSI_ID:
|
|
|
|
((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
assert(!acb->bh);
|
|
|
|
acb->bh = aio_bh_new(bdrv_get_aio_context(bs),
|
|
|
|
iscsi_ioctl_bh_completion, acb);
|
|
|
|
acb->ret = ret;
|
|
|
|
qemu_bh_schedule(acb->bh);
|
|
|
|
}
|
|
|
|
|
2014-10-07 15:59:14 +04:00
|
|
|
static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
unsigned long int req, void *buf,
|
2014-10-07 15:59:15 +04:00
|
|
|
BlockCompletionFunc *cb, void *opaque)
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
struct iscsi_context *iscsi = iscsilun->iscsi;
|
|
|
|
struct iscsi_data data;
|
|
|
|
IscsiAIOCB *acb;
|
|
|
|
|
2012-10-31 19:34:37 +04:00
|
|
|
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
|
|
|
|
acb->iscsilun = iscsilun;
|
2012-08-19 01:35:49 +04:00
|
|
|
acb->bh = NULL;
|
|
|
|
acb->status = -EINPROGRESS;
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
acb->buf = NULL;
|
|
|
|
acb->ioh = buf;
|
|
|
|
|
2015-11-09 13:16:49 +03:00
|
|
|
if (req != SG_IO) {
|
|
|
|
iscsi_ioctl_handle_emulated(acb, req, buf);
|
|
|
|
return &acb->common;
|
|
|
|
}
|
|
|
|
|
2016-05-24 11:59:28 +03:00
|
|
|
if (acb->ioh->cmd_len > SCSI_CDB_MAX_SIZE) {
|
|
|
|
error_report("iSCSI: ioctl error CDB exceeds max size (%d > %d)",
|
|
|
|
acb->ioh->cmd_len, SCSI_CDB_MAX_SIZE);
|
|
|
|
qemu_aio_unref(acb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
acb->task = malloc(sizeof(struct scsi_task));
|
|
|
|
if (acb->task == NULL) {
|
|
|
|
error_report("iSCSI: Failed to allocate task for scsi command. %s",
|
|
|
|
iscsi_get_error(iscsi));
|
2014-09-11 09:41:28 +04:00
|
|
|
qemu_aio_unref(acb);
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memset(acb->task, 0, sizeof(struct scsi_task));
|
|
|
|
|
|
|
|
switch (acb->ioh->dxfer_direction) {
|
|
|
|
case SG_DXFER_TO_DEV:
|
|
|
|
acb->task->xfer_dir = SCSI_XFER_WRITE;
|
|
|
|
break;
|
|
|
|
case SG_DXFER_FROM_DEV:
|
|
|
|
acb->task->xfer_dir = SCSI_XFER_READ;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
acb->task->xfer_dir = SCSI_XFER_NONE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
acb->task->cdb_size = acb->ioh->cmd_len;
|
|
|
|
memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len);
|
|
|
|
acb->task->expxferlen = acb->ioh->dxfer_len;
|
|
|
|
|
2013-06-23 19:07:08 +04:00
|
|
|
data.size = 0;
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
if (acb->task->xfer_dir == SCSI_XFER_WRITE) {
|
2013-06-23 19:07:08 +04:00
|
|
|
if (acb->ioh->iovec_count == 0) {
|
|
|
|
data.data = acb->ioh->dxferp;
|
|
|
|
data.size = acb->ioh->dxfer_len;
|
|
|
|
} else {
|
|
|
|
scsi_task_set_iov_out(acb->task,
|
|
|
|
(struct scsi_iovec *) acb->ioh->dxferp,
|
|
|
|
acb->ioh->iovec_count);
|
|
|
|
}
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
}
|
2013-06-23 19:07:08 +04:00
|
|
|
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
|
|
|
|
iscsi_aio_ioctl_cb,
|
2013-06-23 19:07:08 +04:00
|
|
|
(data.size > 0) ? &data : NULL,
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
acb) != 0) {
|
|
|
|
scsi_free_scsi_task(acb->task);
|
2014-09-11 09:41:28 +04:00
|
|
|
qemu_aio_unref(acb);
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* tell libiscsi to read straight into the buffer we got from ioctl */
|
|
|
|
if (acb->task->xfer_dir == SCSI_XFER_READ) {
|
2013-06-23 19:07:08 +04:00
|
|
|
if (acb->ioh->iovec_count == 0) {
|
|
|
|
scsi_task_add_data_in_buffer(acb->task,
|
|
|
|
acb->ioh->dxfer_len,
|
|
|
|
acb->ioh->dxferp);
|
|
|
|
} else {
|
|
|
|
scsi_task_set_iov_in(acb->task,
|
|
|
|
(struct scsi_iovec *) acb->ioh->dxferp,
|
|
|
|
acb->ioh->iovec_count);
|
|
|
|
}
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
|
|
|
|
return &acb->common;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
static int64_t
|
|
|
|
iscsi_getlength(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
int64_t len;
|
|
|
|
|
|
|
|
len = iscsilun->num_blocks;
|
|
|
|
len *= iscsilun->block_size;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2013-07-19 11:19:41 +04:00
|
|
|
static int
|
|
|
|
coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
|
|
|
|
int nb_sectors)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
struct IscsiTask iTask;
|
|
|
|
struct unmap_list list;
|
|
|
|
|
2016-06-02 00:10:05 +03:00
|
|
|
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
2013-07-19 11:19:41 +04:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!iscsilun->lbp.lbpu) {
|
|
|
|
/* UNMAP is not supported by the target */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
list.lba = sector_qemu2lun(sector_num, iscsilun);
|
2013-10-24 14:07:01 +04:00
|
|
|
list.num = sector_qemu2lun(nb_sectors, iscsilun);
|
2013-07-19 11:19:41 +04:00
|
|
|
|
2013-10-24 14:07:01 +04:00
|
|
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
2013-07-19 11:19:41 +04:00
|
|
|
retry:
|
2013-10-24 14:07:01 +04:00
|
|
|
if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1,
|
|
|
|
iscsi_co_generic_cb, &iTask) == NULL) {
|
2013-12-20 13:02:47 +04:00
|
|
|
return -ENOMEM;
|
2013-10-24 14:07:01 +04:00
|
|
|
}
|
2013-07-19 11:19:41 +04:00
|
|
|
|
2013-10-24 14:07:01 +04:00
|
|
|
while (!iTask.complete) {
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
qemu_coroutine_yield();
|
|
|
|
}
|
2013-07-19 11:19:41 +04:00
|
|
|
|
2013-10-24 14:07:01 +04:00
|
|
|
if (iTask.task != NULL) {
|
|
|
|
scsi_free_scsi_task(iTask.task);
|
|
|
|
iTask.task = NULL;
|
|
|
|
}
|
2013-07-19 11:19:41 +04:00
|
|
|
|
2013-10-24 14:07:01 +04:00
|
|
|
if (iTask.do_retry) {
|
2014-02-18 16:08:39 +04:00
|
|
|
iTask.complete = 0;
|
2013-10-24 14:07:01 +04:00
|
|
|
goto retry;
|
|
|
|
}
|
2013-07-19 11:19:41 +04:00
|
|
|
|
2013-10-24 14:07:01 +04:00
|
|
|
if (iTask.status == SCSI_STATUS_CHECK_CONDITION) {
|
|
|
|
/* the target might fail with a check condition if it
|
|
|
|
is not happy with the alignment of the UNMAP request
|
|
|
|
we silently fail in this case */
|
|
|
|
return 0;
|
|
|
|
}
|
2013-07-19 11:19:41 +04:00
|
|
|
|
2013-10-24 14:07:01 +04:00
|
|
|
if (iTask.status != SCSI_STATUS_GOOD) {
|
2015-11-05 08:00:09 +03:00
|
|
|
return iTask.err_code;
|
2013-07-19 11:19:41 +04:00
|
|
|
}
|
|
|
|
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors);
|
2014-04-28 15:11:32 +04:00
|
|
|
|
2013-07-19 11:19:41 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-24 14:07:02 +04:00
|
|
|
static int
|
2016-06-02 00:10:05 +03:00
|
|
|
coroutine_fn iscsi_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
|
|
|
|
int count, BdrvRequestFlags flags)
|
2013-10-24 14:07:02 +04:00
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
struct IscsiTask iTask;
|
|
|
|
uint64_t lba;
|
|
|
|
uint32_t nb_blocks;
|
2014-06-04 17:47:39 +04:00
|
|
|
bool use_16_for_ws = iscsilun->use_16_for_rw;
|
2013-10-24 14:07:02 +04:00
|
|
|
|
2016-06-02 00:10:05 +03:00
|
|
|
if (!is_byte_request_lun_aligned(offset, count, iscsilun)) {
|
|
|
|
return -ENOTSUP;
|
2013-10-24 14:07:02 +04:00
|
|
|
}
|
|
|
|
|
2014-06-04 17:47:39 +04:00
|
|
|
if (flags & BDRV_REQ_MAY_UNMAP) {
|
|
|
|
if (!use_16_for_ws && !iscsilun->lbp.lbpws10) {
|
|
|
|
/* 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;
|
|
|
|
use_16_for_ws = iscsilun->use_16_for_rw;
|
|
|
|
}
|
2013-11-22 16:39:54 +04:00
|
|
|
}
|
|
|
|
|
2014-04-28 15:23:25 +04:00
|
|
|
if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) {
|
2014-06-04 17:47:39 +04:00
|
|
|
/* WRITESAME without UNMAP is not supported by the target */
|
2013-10-24 14:07:02 +04:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2016-06-02 00:10:05 +03:00
|
|
|
lba = offset / iscsilun->block_size;
|
|
|
|
nb_blocks = count / iscsilun->block_size;
|
2013-10-24 14:07:02 +04:00
|
|
|
|
|
|
|
if (iscsilun->zeroblock == NULL) {
|
2014-05-20 15:30:49 +04:00
|
|
|
iscsilun->zeroblock = g_try_malloc0(iscsilun->block_size);
|
|
|
|
if (iscsilun->zeroblock == NULL) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2013-10-24 14:07:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
|
|
|
retry:
|
2014-06-04 17:47:39 +04:00
|
|
|
if (use_16_for_ws) {
|
|
|
|
iTask.task = iscsi_writesame16_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);
|
|
|
|
} 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) {
|
2013-12-20 13:02:47 +04:00
|
|
|
return -ENOMEM;
|
2013-10-24 14:07:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
while (!iTask.complete) {
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
qemu_coroutine_yield();
|
|
|
|
}
|
|
|
|
|
2014-02-22 16:17:24 +04:00
|
|
|
if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
|
|
|
|
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
2014-04-02 14:12:50 +04:00
|
|
|
(iTask.task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE ||
|
|
|
|
iTask.task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB)) {
|
2014-02-22 16:17:24 +04:00
|
|
|
/* WRITE SAME is not supported by the target */
|
|
|
|
iscsilun->has_write_same = false;
|
|
|
|
scsi_free_scsi_task(iTask.task);
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2013-10-24 14:07:02 +04:00
|
|
|
if (iTask.task != NULL) {
|
|
|
|
scsi_free_scsi_task(iTask.task);
|
|
|
|
iTask.task = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iTask.do_retry) {
|
2014-02-18 16:08:39 +04:00
|
|
|
iTask.complete = 0;
|
2013-10-24 14:07:02 +04:00
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iTask.status != SCSI_STATUS_GOOD) {
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
|
|
|
|
count >> BDRV_SECTOR_BITS);
|
2015-11-05 08:00:09 +03:00
|
|
|
return iTask.err_code;
|
2013-10-24 14:07:02 +04:00
|
|
|
}
|
|
|
|
|
2014-04-28 15:11:32 +04:00
|
|
|
if (flags & BDRV_REQ_MAY_UNMAP) {
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
|
|
|
|
count >> BDRV_SECTOR_BITS);
|
2014-04-28 15:11:32 +04:00
|
|
|
} else {
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_set_allocated(iscsilun, offset >> BDRV_SECTOR_BITS,
|
|
|
|
count >> BDRV_SECTOR_BITS);
|
2014-04-28 15:11:32 +04:00
|
|
|
}
|
|
|
|
|
2013-10-24 14:07:02 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-02-17 17:43:53 +04:00
|
|
|
static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
|
|
|
Error **errp)
|
2012-01-26 02:39:02 +04:00
|
|
|
{
|
|
|
|
QemuOptsList *list;
|
|
|
|
QemuOpts *opts;
|
|
|
|
const char *user = NULL;
|
|
|
|
const char *password = NULL;
|
2016-01-21 17:19:21 +03:00
|
|
|
const char *secretid;
|
|
|
|
char *secret = NULL;
|
2012-01-26 02:39:02 +04:00
|
|
|
|
|
|
|
list = qemu_find_opts("iscsi");
|
|
|
|
if (!list) {
|
2014-02-17 17:43:53 +04:00
|
|
|
return;
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
opts = qemu_opts_find(list, target);
|
|
|
|
if (opts == NULL) {
|
|
|
|
opts = QTAILQ_FIRST(&list->head);
|
|
|
|
if (!opts) {
|
2014-02-17 17:43:53 +04:00
|
|
|
return;
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
user = qemu_opt_get(opts, "user");
|
|
|
|
if (!user) {
|
2014-02-17 17:43:53 +04:00
|
|
|
return;
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
|
2016-01-21 17:19:21 +03:00
|
|
|
secretid = qemu_opt_get(opts, "password-secret");
|
2012-01-26 02:39:02 +04:00
|
|
|
password = qemu_opt_get(opts, "password");
|
2016-01-21 17:19:21 +03:00
|
|
|
if (secretid && password) {
|
|
|
|
error_setg(errp, "'password' and 'password-secret' properties are "
|
|
|
|
"mutually exclusive");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (secretid) {
|
|
|
|
secret = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
|
|
|
if (!secret) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
password = secret;
|
|
|
|
} else if (!password) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "CHAP username specified but no password was given");
|
|
|
|
return;
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "Failed to set initiator username and password");
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
2016-01-21 17:19:21 +03:00
|
|
|
|
|
|
|
g_free(secret);
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
|
2014-02-17 17:43:53 +04:00
|
|
|
static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
|
|
|
|
Error **errp)
|
2012-01-26 02:39:02 +04:00
|
|
|
{
|
|
|
|
QemuOptsList *list;
|
|
|
|
QemuOpts *opts;
|
|
|
|
const char *digest = NULL;
|
|
|
|
|
|
|
|
list = qemu_find_opts("iscsi");
|
|
|
|
if (!list) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
opts = qemu_opts_find(list, target);
|
|
|
|
if (opts == NULL) {
|
|
|
|
opts = QTAILQ_FIRST(&list->head);
|
|
|
|
if (!opts) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
digest = qemu_opt_get(opts, "header-digest");
|
|
|
|
if (!digest) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(digest, "CRC32C")) {
|
|
|
|
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C);
|
|
|
|
} else if (!strcmp(digest, "NONE")) {
|
|
|
|
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE);
|
|
|
|
} else if (!strcmp(digest, "CRC32C-NONE")) {
|
|
|
|
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE);
|
|
|
|
} else if (!strcmp(digest, "NONE-CRC32C")) {
|
|
|
|
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
|
|
|
} else {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "Invalid header-digest setting : %s", digest);
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *parse_initiator_name(const char *target)
|
|
|
|
{
|
|
|
|
QemuOptsList *list;
|
|
|
|
QemuOpts *opts;
|
2013-08-02 19:02:01 +04:00
|
|
|
const char *name;
|
|
|
|
char *iscsi_name;
|
|
|
|
UuidInfo *uuid_info;
|
2012-01-26 02:39:02 +04:00
|
|
|
|
|
|
|
list = qemu_find_opts("iscsi");
|
2012-08-06 12:54:41 +04:00
|
|
|
if (list) {
|
|
|
|
opts = qemu_opts_find(list, target);
|
2012-01-26 02:39:02 +04:00
|
|
|
if (!opts) {
|
2012-08-06 12:54:41 +04:00
|
|
|
opts = QTAILQ_FIRST(&list->head);
|
|
|
|
}
|
|
|
|
if (opts) {
|
|
|
|
name = qemu_opt_get(opts, "initiator-name");
|
2013-08-02 19:02:01 +04:00
|
|
|
if (name) {
|
|
|
|
return g_strdup(name);
|
|
|
|
}
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-02 19:02:01 +04:00
|
|
|
uuid_info = qmp_query_uuid(NULL);
|
|
|
|
if (strcmp(uuid_info->UUID, UUID_NONE) == 0) {
|
|
|
|
name = qemu_get_vm_name();
|
2012-08-06 12:54:41 +04:00
|
|
|
} else {
|
2013-08-02 19:02:01 +04:00
|
|
|
name = uuid_info->UUID;
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
2013-08-02 19:02:01 +04:00
|
|
|
iscsi_name = g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s",
|
|
|
|
name ? ":" : "", name ? name : "");
|
|
|
|
qapi_free_UuidInfo(uuid_info);
|
|
|
|
return iscsi_name;
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
|
2015-06-16 14:45:07 +03:00
|
|
|
static int parse_timeout(const char *target)
|
|
|
|
{
|
|
|
|
QemuOptsList *list;
|
|
|
|
QemuOpts *opts;
|
|
|
|
const char *timeout;
|
|
|
|
|
|
|
|
list = qemu_find_opts("iscsi");
|
|
|
|
if (list) {
|
|
|
|
opts = qemu_opts_find(list, target);
|
|
|
|
if (!opts) {
|
|
|
|
opts = QTAILQ_FIRST(&list->head);
|
|
|
|
}
|
|
|
|
if (opts) {
|
|
|
|
timeout = qemu_opt_get(opts, "timeout");
|
|
|
|
if (timeout) {
|
|
|
|
return atoi(timeout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-06 13:46:47 +04:00
|
|
|
static void iscsi_nop_timed_event(void *opaque)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = opaque;
|
|
|
|
|
2015-06-16 14:45:07 +03:00
|
|
|
if (iscsi_get_nops_in_flight(iscsilun->iscsi) >= MAX_NOP_FAILURES) {
|
2012-12-06 13:46:47 +04:00
|
|
|
error_report("iSCSI: NOP timeout. Reconnecting...");
|
2015-06-16 14:45:07 +03:00
|
|
|
iscsilun->request_timed_out = true;
|
|
|
|
} else if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) {
|
2012-12-06 13:46:47 +04:00
|
|
|
error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-21 19:03:08 +04:00
|
|
|
timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
|
2012-12-06 13:46:47 +04:00
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
}
|
|
|
|
|
2014-02-17 17:43:53 +04:00
|
|
|
static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
|
2013-02-18 17:50:46 +04:00
|
|
|
{
|
|
|
|
struct scsi_task *task = NULL;
|
|
|
|
struct scsi_readcapacity10 *rc10 = NULL;
|
|
|
|
struct scsi_readcapacity16 *rc16 = NULL;
|
|
|
|
int retries = ISCSI_CMD_RETRIES;
|
|
|
|
|
2013-05-31 15:56:24 +04:00
|
|
|
do {
|
|
|
|
if (task != NULL) {
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
task = NULL;
|
2013-02-18 17:50:46 +04:00
|
|
|
}
|
2013-05-31 15:56:24 +04:00
|
|
|
|
|
|
|
switch (iscsilun->type) {
|
|
|
|
case TYPE_DISK:
|
|
|
|
task = iscsi_readcapacity16_sync(iscsilun->iscsi, iscsilun->lun);
|
|
|
|
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
|
|
|
|
rc16 = scsi_datain_unmarshall(task);
|
|
|
|
if (rc16 == NULL) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "iSCSI: Failed to unmarshall readcapacity16 data.");
|
2013-05-31 15:56:24 +04:00
|
|
|
} else {
|
|
|
|
iscsilun->block_size = rc16->block_length;
|
|
|
|
iscsilun->num_blocks = rc16->returned_lba + 1;
|
2015-04-16 17:08:26 +03:00
|
|
|
iscsilun->lbpme = !!rc16->lbpme;
|
|
|
|
iscsilun->lbprz = !!rc16->lbprz;
|
2014-06-04 17:47:39 +04:00
|
|
|
iscsilun->use_16_for_rw = (rc16->returned_lba > 0xffffffff);
|
2013-05-31 15:56:24 +04:00
|
|
|
}
|
2015-12-29 06:32:14 +03:00
|
|
|
break;
|
2013-05-31 15:56:24 +04:00
|
|
|
}
|
2015-12-29 06:32:14 +03:00
|
|
|
if (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
|
|
|
|
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Fall through and try READ CAPACITY(10) instead. */
|
2013-05-31 15:56:24 +04:00
|
|
|
case TYPE_ROM:
|
|
|
|
task = iscsi_readcapacity10_sync(iscsilun->iscsi, iscsilun->lun, 0, 0);
|
|
|
|
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
|
|
|
|
rc10 = scsi_datain_unmarshall(task);
|
|
|
|
if (rc10 == NULL) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "iSCSI: Failed to unmarshall readcapacity10 data.");
|
2013-05-31 15:56:24 +04:00
|
|
|
} else {
|
|
|
|
iscsilun->block_size = rc10->block_size;
|
|
|
|
if (rc10->lba == 0) {
|
|
|
|
/* blank disk loaded */
|
|
|
|
iscsilun->num_blocks = 0;
|
|
|
|
} else {
|
|
|
|
iscsilun->num_blocks = rc10->lba + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2014-02-17 17:43:53 +04:00
|
|
|
return;
|
2013-02-18 17:50:46 +04:00
|
|
|
}
|
2013-05-31 15:56:24 +04:00
|
|
|
} while (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
|
|
|
|
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
|
|
|
&& retries-- > 0);
|
2013-02-18 17:50:46 +04:00
|
|
|
|
2013-05-31 15:56:24 +04:00
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
2015-12-16 07:40:40 +03:00
|
|
|
error_setg(errp, "iSCSI: failed to send readcapacity10/16 command");
|
2015-08-14 14:33:36 +03:00
|
|
|
} else if (!iscsilun->block_size ||
|
|
|
|
iscsilun->block_size % BDRV_SECTOR_SIZE) {
|
|
|
|
error_setg(errp, "iSCSI: the target returned an invalid "
|
|
|
|
"block size of %d.", iscsilun->block_size);
|
2013-05-31 15:56:24 +04:00
|
|
|
}
|
2013-02-18 17:50:46 +04:00
|
|
|
if (task) {
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-12 19:59:59 +04:00
|
|
|
/* TODO Convert to fine grained options */
|
|
|
|
static QemuOptsList runtime_opts = {
|
|
|
|
.name = "iscsi",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
|
|
|
.desc = {
|
|
|
|
{
|
|
|
|
.name = "filename",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "URL to the iscsi image",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2014-02-17 17:43:52 +04:00
|
|
|
static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun,
|
2014-02-17 21:34:08 +04:00
|
|
|
int evpd, int pc, void **inq, Error **errp)
|
2014-02-17 17:43:52 +04:00
|
|
|
{
|
|
|
|
int full_size;
|
|
|
|
struct scsi_task *task = NULL;
|
|
|
|
task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
|
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
full_size = scsi_datain_getfullsize(task);
|
|
|
|
if (full_size > task->datain.size) {
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
|
|
|
/* we need more data for the full list */
|
|
|
|
task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size);
|
2013-07-19 11:19:39 +04:00
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
goto fail;
|
|
|
|
}
|
2014-02-17 17:43:52 +04:00
|
|
|
}
|
2013-07-19 11:19:39 +04:00
|
|
|
|
2014-02-17 21:34:08 +04:00
|
|
|
*inq = scsi_datain_unmarshall(task);
|
|
|
|
if (*inq == NULL) {
|
|
|
|
error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob");
|
2014-04-25 18:50:35 +04:00
|
|
|
goto fail_with_err;
|
2014-02-17 21:34:08 +04:00
|
|
|
}
|
|
|
|
|
2014-02-17 17:43:52 +04:00
|
|
|
return task;
|
2013-07-19 11:19:39 +04:00
|
|
|
|
|
|
|
fail:
|
2014-04-25 18:50:35 +04:00
|
|
|
error_setg(errp, "iSCSI: Inquiry command failed : %s",
|
|
|
|
iscsi_get_error(iscsi));
|
|
|
|
fail_with_err:
|
2014-02-17 21:34:08 +04:00
|
|
|
if (task != NULL) {
|
2014-02-17 17:43:52 +04:00
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
}
|
|
|
|
return NULL;
|
2013-07-19 11:19:39 +04:00
|
|
|
}
|
|
|
|
|
2014-05-08 18:34:42 +04:00
|
|
|
static void iscsi_detach_aio_context(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
|
2015-10-23 06:08:05 +03:00
|
|
|
aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsilun->iscsi),
|
|
|
|
false, NULL, NULL, NULL);
|
2014-05-08 18:34:42 +04:00
|
|
|
iscsilun->events = 0;
|
|
|
|
|
|
|
|
if (iscsilun->nop_timer) {
|
|
|
|
timer_del(iscsilun->nop_timer);
|
|
|
|
timer_free(iscsilun->nop_timer);
|
|
|
|
iscsilun->nop_timer = NULL;
|
|
|
|
}
|
2015-04-07 23:08:15 +03:00
|
|
|
if (iscsilun->event_timer) {
|
|
|
|
timer_del(iscsilun->event_timer);
|
|
|
|
timer_free(iscsilun->event_timer);
|
|
|
|
iscsilun->event_timer = NULL;
|
|
|
|
}
|
2014-05-08 18:34:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void iscsi_attach_aio_context(BlockDriverState *bs,
|
|
|
|
AioContext *new_context)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
|
|
|
|
iscsilun->aio_context = new_context;
|
|
|
|
iscsi_set_events(iscsilun);
|
|
|
|
|
|
|
|
/* Set up a timer for sending out iSCSI NOPs */
|
|
|
|
iscsilun->nop_timer = aio_timer_new(iscsilun->aio_context,
|
|
|
|
QEMU_CLOCK_REALTIME, SCALE_MS,
|
|
|
|
iscsi_nop_timed_event, iscsilun);
|
|
|
|
timer_mod(iscsilun->nop_timer,
|
|
|
|
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
|
2015-04-07 23:08:15 +03:00
|
|
|
|
2015-06-16 14:45:07 +03:00
|
|
|
/* Set up a timer for periodic calls to iscsi_set_events and to
|
|
|
|
* scan for command timeout */
|
2015-04-07 23:08:15 +03:00
|
|
|
iscsilun->event_timer = aio_timer_new(iscsilun->aio_context,
|
|
|
|
QEMU_CLOCK_REALTIME, SCALE_MS,
|
2015-06-16 14:45:07 +03:00
|
|
|
iscsi_timed_check_events, iscsilun);
|
|
|
|
timer_mod(iscsilun->event_timer,
|
|
|
|
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL);
|
2014-05-08 18:34:42 +04:00
|
|
|
}
|
|
|
|
|
2015-04-16 17:08:27 +03:00
|
|
|
static void iscsi_modesense_sync(IscsiLun *iscsilun)
|
2014-10-30 14:23:46 +03:00
|
|
|
{
|
|
|
|
struct scsi_task *task;
|
|
|
|
struct scsi_mode_sense *ms = NULL;
|
2015-04-16 17:08:27 +03:00
|
|
|
iscsilun->write_protected = false;
|
2015-04-16 17:08:28 +03:00
|
|
|
iscsilun->dpofua = false;
|
2014-10-30 14:23:46 +03:00
|
|
|
|
|
|
|
task = iscsi_modesense6_sync(iscsilun->iscsi, iscsilun->lun,
|
|
|
|
1, SCSI_MODESENSE_PC_CURRENT,
|
|
|
|
0x3F, 0, 255);
|
|
|
|
if (task == NULL) {
|
|
|
|
error_report("iSCSI: Failed to send MODE_SENSE(6) command: %s",
|
|
|
|
iscsi_get_error(iscsilun->iscsi));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (task->status != SCSI_STATUS_GOOD) {
|
|
|
|
error_report("iSCSI: Failed MODE_SENSE(6), LUN assumed writable");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ms = scsi_datain_unmarshall(task);
|
|
|
|
if (!ms) {
|
|
|
|
error_report("iSCSI: Failed to unmarshall MODE_SENSE(6) data: %s",
|
|
|
|
iscsi_get_error(iscsilun->iscsi));
|
|
|
|
goto out;
|
|
|
|
}
|
2015-04-16 17:08:27 +03:00
|
|
|
iscsilun->write_protected = ms->device_specific_parameter & 0x80;
|
2015-04-16 17:08:28 +03:00
|
|
|
iscsilun->dpofua = ms->device_specific_parameter & 0x10;
|
2014-10-30 14:23:46 +03:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (task) {
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
/*
|
|
|
|
* We support iscsi url's on the form
|
|
|
|
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
|
|
|
|
*/
|
2013-09-05 16:22:29 +04:00
|
|
|
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|
|
|
Error **errp)
|
2011-10-25 12:24:24 +04:00
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
struct iscsi_context *iscsi = NULL;
|
|
|
|
struct iscsi_url *iscsi_url = NULL;
|
2012-11-17 17:37:39 +04:00
|
|
|
struct scsi_task *task = NULL;
|
|
|
|
struct scsi_inquiry_standard *inq = NULL;
|
2014-02-17 21:34:08 +04:00
|
|
|
struct scsi_inquiry_supported_pages *inq_vpd;
|
2012-01-26 02:39:02 +04:00
|
|
|
char *initiator_name = NULL;
|
2013-04-12 19:59:59 +04:00
|
|
|
QemuOpts *opts;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
const char *filename;
|
2015-06-26 13:18:01 +03:00
|
|
|
int i, ret = 0, timeout = 0;
|
2011-10-25 12:24:24 +04:00
|
|
|
|
2014-01-02 06:49:17 +04:00
|
|
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
2013-04-12 19:59:59 +04:00
|
|
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_propagate(errp, local_err);
|
2013-04-12 19:59:59 +04:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
filename = qemu_opt_get(opts, "filename");
|
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
iscsi_url = iscsi_parse_full_url(iscsi, filename);
|
|
|
|
if (iscsi_url == NULL) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "Failed to parse URL : %s", filename);
|
2011-10-25 12:24:24 +04:00
|
|
|
ret = -EINVAL;
|
2012-08-06 12:52:22 +04:00
|
|
|
goto out;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
2012-01-26 02:39:02 +04:00
|
|
|
memset(iscsilun, 0, sizeof(IscsiLun));
|
|
|
|
|
|
|
|
initiator_name = parse_initiator_name(iscsi_url->target);
|
|
|
|
|
|
|
|
iscsi = iscsi_create_context(initiator_name);
|
|
|
|
if (iscsi == NULL) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "iSCSI: Failed to create iSCSI context.");
|
2012-01-26 02:39:02 +04:00
|
|
|
ret = -ENOMEM;
|
2012-08-06 12:52:22 +04:00
|
|
|
goto out;
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "iSCSI: Failed to set target name.");
|
2011-10-25 12:24:24 +04:00
|
|
|
ret = -EINVAL;
|
2012-08-06 12:52:22 +04:00
|
|
|
goto out;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
2015-03-07 12:14:30 +03:00
|
|
|
if (iscsi_url->user[0] != '\0') {
|
2011-10-25 12:24:24 +04:00
|
|
|
ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
|
|
|
|
iscsi_url->passwd);
|
|
|
|
if (ret != 0) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "Failed to set initiator username and password");
|
2011-10-25 12:24:24 +04:00
|
|
|
ret = -EINVAL;
|
2012-08-06 12:52:22 +04:00
|
|
|
goto out;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
}
|
2012-01-26 02:39:02 +04:00
|
|
|
|
|
|
|
/* check if we got CHAP username/password via the options */
|
2014-02-17 17:43:53 +04:00
|
|
|
parse_chap(iscsi, iscsi_url->target, &local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
2012-01-26 02:39:02 +04:00
|
|
|
ret = -EINVAL;
|
2012-08-06 12:52:22 +04:00
|
|
|
goto out;
|
2012-01-26 02:39:02 +04:00
|
|
|
}
|
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "iSCSI: Failed to set session type to normal.");
|
2011-10-25 12:24:24 +04:00
|
|
|
ret = -EINVAL;
|
2012-08-06 12:52:22 +04:00
|
|
|
goto out;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
|
|
|
|
2012-01-26 02:39:02 +04:00
|
|
|
/* check if we got HEADER_DIGEST via the options */
|
2014-02-17 17:43:53 +04:00
|
|
|
parse_header_digest(iscsi, iscsi_url->target, &local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-01-26 02:39:02 +04:00
|
|
|
|
2015-06-26 13:18:01 +03:00
|
|
|
/* timeout handling is broken in libiscsi before 1.15.0 */
|
|
|
|
timeout = parse_timeout(iscsi_url->target);
|
|
|
|
#if defined(LIBISCSI_API_VERSION) && LIBISCSI_API_VERSION >= 20150621
|
|
|
|
iscsi_set_timeout(iscsi, timeout);
|
|
|
|
#else
|
|
|
|
if (timeout) {
|
|
|
|
error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
|
|
|
|
}
|
|
|
|
#endif
|
2015-06-16 14:45:07 +03:00
|
|
|
|
2012-11-17 17:37:39 +04:00
|
|
|
if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
|
2014-02-17 17:43:53 +04:00
|
|
|
error_setg(errp, "iSCSI: Failed to connect to LUN : %s",
|
2012-11-17 17:37:39 +04:00
|
|
|
iscsi_get_error(iscsi));
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-10-25 12:24:24 +04:00
|
|
|
|
|
|
|
iscsilun->iscsi = iscsi;
|
2014-05-08 18:34:42 +04:00
|
|
|
iscsilun->aio_context = bdrv_get_aio_context(bs);
|
2011-10-25 12:24:24 +04:00
|
|
|
iscsilun->lun = iscsi_url->lun;
|
2014-02-17 21:34:08 +04:00
|
|
|
iscsilun->has_write_same = true;
|
2011-10-25 12:24:24 +04:00
|
|
|
|
2014-02-17 21:34:08 +04:00
|
|
|
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 0, 0,
|
|
|
|
(void **) &inq, errp);
|
|
|
|
if (task == NULL) {
|
2011-10-25 12:24:24 +04:00
|
|
|
ret = -EINVAL;
|
2012-08-06 12:52:22 +04:00
|
|
|
goto out;
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
2012-11-17 17:37:39 +04:00
|
|
|
iscsilun->type = inq->periperal_device_type;
|
2014-02-17 21:34:08 +04:00
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
task = NULL;
|
2012-11-17 17:37:39 +04:00
|
|
|
|
2015-04-16 17:08:27 +03:00
|
|
|
iscsi_modesense_sync(iscsilun);
|
2016-05-04 01:39:06 +03:00
|
|
|
if (iscsilun->dpofua) {
|
|
|
|
bs->supported_write_flags = BDRV_REQ_FUA;
|
|
|
|
}
|
block: Honor BDRV_REQ_FUA during write_zeroes
The block layer has a couple of cases where it can lose
Force Unit Access semantics when writing a large block of
zeroes, such that the request returns before the zeroes
have been guaranteed to land on underlying media.
SCSI does not support FUA during WRITESAME(10/16); FUA is only
supported if it falls back to WRITE(10/16). But where the
underlying device is new enough to not need a fallback, it
means that any upper layer request with FUA semantics was
silently ignoring BDRV_REQ_FUA.
Conversely, NBD has situations where it can support FUA but not
ZERO_WRITE; when that happens, the generic block layer fallback
to bdrv_driver_pwritev() (or the older bdrv_co_writev() in qemu
2.6) was losing the FUA flag.
The problem of losing flags unrelated to ZERO_WRITE has been
latent in bdrv_co_do_write_zeroes() since commit aa7bfbff, but
back then, it did not matter because there was no FUA flag. It
became observable when commit 93f5e6d8 paved the way for flags
that can impact correctness, when we should have been using
bdrv_co_writev_flags() with modified flags. Compare to commit
9eeb6dd, which got flag manipulation right in
bdrv_co_do_zero_pwritev().
Symptoms: I tested with qemu-io with default writethrough cache
(which is supposed to use FUA semantics on every write), and
targetted an NBD client connected to a server that intentionally
did not advertise NBD_FLAG_SEND_FUA. When doing 'write 0 512',
the NBD client sent two operations (NBD_CMD_WRITE then
NBD_CMD_FLUSH) to get the fallback FUA semantics; but when doing
'write -z 0 512', the NBD client sent only NBD_CMD_WRITE.
The fix is do to a cleanup bdrv_co_flush() at the end of the
operation if any step in the middle relied on a BDS that does
not natively support FUA for that step (note that we don't
need to flush after every operation, if the operation is broken
into chunks based on bounce-buffer sizing). Each BDS gains a
new flag .supported_zero_flags, which parallels the use of
.supported_write_flags but only when accessing a zero write
operation (the flags MUST be different, because of SCSI having
different semantics based on WRITE vs. WRITESAME; and also
because BDRV_REQ_MAY_UNMAP only makes sense on zero writes).
Also fix some documentation to describe -ENOTSUP semantics,
particularly since iscsi depends on those semantics.
Down the road, we may want to add a driver where its
.bdrv_co_pwritev() honors all three of BDRV_REQ_FUA,
BDRV_REQ_ZERO_WRITE, and BDRV_REQ_MAY_UNMAP, and advertise
this via bs->supported_write_flags for blocks opened by that
driver; such a driver should NOT supply .bdrv_co_write_zeroes
nor .supported_zero_flags. But none of the drivers touched
in this patch want to do that (the act of writing zeroes is
different enough from normal writes to deserve a second
callback).
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2016-05-04 01:39:07 +03:00
|
|
|
bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP;
|
2015-04-16 17:08:27 +03:00
|
|
|
|
2014-10-30 14:23:46 +03:00
|
|
|
/* Check the write protect flag of the LUN if we want to write */
|
|
|
|
if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) &&
|
2015-02-25 07:40:08 +03:00
|
|
|
iscsilun->write_protected) {
|
2014-10-30 14:23:46 +03:00
|
|
|
error_setg(errp, "Cannot open a write protected LUN as read-write");
|
|
|
|
ret = -EACCES;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-02-17 17:43:53 +04:00
|
|
|
iscsi_readcapacity_sync(iscsilun, &local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
2014-04-10 05:33:55 +04:00
|
|
|
ret = -EINVAL;
|
2013-02-18 17:50:46 +04:00
|
|
|
goto out;
|
2012-11-17 17:37:39 +04:00
|
|
|
}
|
2013-07-11 16:16:25 +04:00
|
|
|
bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
|
2012-11-17 17:37:39 +04:00
|
|
|
|
2014-03-05 18:45:00 +04:00
|
|
|
/* We don't have any emulation for devices other than disks and CD-ROMs, so
|
|
|
|
* this must be sg ioctl compatible. We force it to be sg, otherwise qemu
|
|
|
|
* will try to read from the device to guess the image format.
|
2012-05-26 08:56:41 +04:00
|
|
|
*/
|
2014-03-05 18:45:00 +04:00
|
|
|
if (iscsilun->type != TYPE_DISK && iscsilun->type != TYPE_ROM) {
|
2016-06-24 01:37:26 +03:00
|
|
|
bs->sg = true;
|
2012-05-26 08:56:41 +04:00
|
|
|
}
|
|
|
|
|
2014-02-17 21:34:08 +04:00
|
|
|
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
|
|
|
SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES,
|
|
|
|
(void **) &inq_vpd, errp);
|
|
|
|
if (task == NULL) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
2013-07-19 11:19:39 +04:00
|
|
|
}
|
2014-02-17 21:34:08 +04:00
|
|
|
for (i = 0; i < inq_vpd->num_pages; i++) {
|
|
|
|
struct scsi_task *inq_task;
|
|
|
|
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
|
2013-07-19 11:19:39 +04:00
|
|
|
struct scsi_inquiry_block_limits *inq_bl;
|
2014-02-17 21:34:08 +04:00
|
|
|
switch (inq_vpd->pages[i]) {
|
|
|
|
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
|
|
|
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
|
|
|
SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING,
|
|
|
|
(void **) &inq_lbp, errp);
|
|
|
|
if (inq_task == NULL) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
memcpy(&iscsilun->lbp, inq_lbp,
|
|
|
|
sizeof(struct scsi_inquiry_logical_block_provisioning));
|
|
|
|
scsi_free_scsi_task(inq_task);
|
|
|
|
break;
|
|
|
|
case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS:
|
|
|
|
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
|
|
|
SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS,
|
|
|
|
(void **) &inq_bl, errp);
|
|
|
|
if (inq_task == NULL) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
memcpy(&iscsilun->bl, inq_bl,
|
|
|
|
sizeof(struct scsi_inquiry_block_limits));
|
|
|
|
scsi_free_scsi_task(inq_task);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2013-07-19 11:19:39 +04:00
|
|
|
}
|
|
|
|
}
|
2014-02-17 21:34:08 +04:00
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
task = NULL;
|
2013-07-19 11:19:39 +04:00
|
|
|
|
2014-05-08 18:34:42 +04:00
|
|
|
iscsi_attach_aio_context(bs, iscsilun->aio_context);
|
2012-12-06 13:46:47 +04:00
|
|
|
|
2014-04-28 15:11:32 +04:00
|
|
|
/* Guess the internal cluster (page) size of the iscsi target by the means
|
|
|
|
* of opt_unmap_gran. Transfer the unmap granularity only if it has a
|
|
|
|
* reasonable size */
|
2014-04-28 19:18:32 +04:00
|
|
|
if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 4 * 1024 &&
|
2014-04-28 15:11:32 +04:00
|
|
|
iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) {
|
|
|
|
iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran *
|
|
|
|
iscsilun->block_size) >> BDRV_SECTOR_BITS;
|
2015-04-16 17:08:33 +03:00
|
|
|
if (iscsilun->lbprz) {
|
2016-07-18 11:52:20 +03:00
|
|
|
ret = iscsi_allocmap_init(iscsilun, bs->open_flags);
|
2014-04-28 15:11:32 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-06 12:52:22 +04:00
|
|
|
out:
|
2013-04-12 19:59:59 +04:00
|
|
|
qemu_opts_del(opts);
|
2014-06-06 20:25:12 +04:00
|
|
|
g_free(initiator_name);
|
2011-10-25 12:24:24 +04:00
|
|
|
if (iscsi_url != NULL) {
|
|
|
|
iscsi_destroy_url(iscsi_url);
|
|
|
|
}
|
2012-11-17 17:37:39 +04:00
|
|
|
if (task != NULL) {
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
}
|
2012-08-06 12:52:22 +04:00
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
if (iscsi != NULL) {
|
2015-04-16 17:08:25 +03:00
|
|
|
if (iscsi_is_logged_in(iscsi)) {
|
|
|
|
iscsi_logout_sync(iscsi);
|
|
|
|
}
|
2012-08-06 12:52:22 +04:00
|
|
|
iscsi_destroy_context(iscsi);
|
|
|
|
}
|
|
|
|
memset(iscsilun, 0, sizeof(IscsiLun));
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iscsi_close(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
struct iscsi_context *iscsi = iscsilun->iscsi;
|
|
|
|
|
2014-05-08 18:34:42 +04:00
|
|
|
iscsi_detach_aio_context(bs);
|
2015-04-16 17:08:25 +03:00
|
|
|
if (iscsi_is_logged_in(iscsi)) {
|
|
|
|
iscsi_logout_sync(iscsi);
|
|
|
|
}
|
2011-10-25 12:24:24 +04:00
|
|
|
iscsi_destroy_context(iscsi);
|
2013-10-24 14:07:02 +04:00
|
|
|
g_free(iscsilun->zeroblock);
|
2016-07-18 11:52:20 +03:00
|
|
|
iscsi_allocmap_free(iscsilun);
|
2011-10-25 12:24:24 +04:00
|
|
|
memset(iscsilun, 0, sizeof(IscsiLun));
|
|
|
|
}
|
|
|
|
|
2014-10-27 12:18:45 +03:00
|
|
|
static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp)
|
|
|
|
{
|
2013-12-11 22:26:16 +04:00
|
|
|
/* We don't actually refresh here, but just return data queried in
|
|
|
|
* iscsi_open(): iscsi targets don't change their limits. */
|
2014-10-27 12:18:45 +03:00
|
|
|
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
2016-06-24 01:37:19 +03:00
|
|
|
uint64_t max_xfer_len = iscsilun->use_16_for_rw ? 0xffffffff : 0xffff;
|
2014-10-27 12:18:45 +03:00
|
|
|
|
2016-06-24 01:37:24 +03:00
|
|
|
bs->bl.request_alignment = iscsilun->block_size;
|
2016-06-24 01:37:14 +03:00
|
|
|
|
2014-10-27 12:18:45 +03:00
|
|
|
if (iscsilun->bl.max_xfer_len) {
|
|
|
|
max_xfer_len = MIN(max_xfer_len, iscsilun->bl.max_xfer_len);
|
|
|
|
}
|
|
|
|
|
2016-06-24 01:37:19 +03:00
|
|
|
if (max_xfer_len * iscsilun->block_size < INT_MAX) {
|
|
|
|
bs->bl.max_transfer = max_xfer_len * iscsilun->block_size;
|
|
|
|
}
|
2014-10-27 12:18:45 +03:00
|
|
|
|
2014-04-02 17:30:29 +04:00
|
|
|
if (iscsilun->lbp.lbpu) {
|
2016-06-24 01:37:21 +03:00
|
|
|
if (iscsilun->bl.max_unmap < 0xffffffff / iscsilun->block_size) {
|
|
|
|
bs->bl.max_pdiscard =
|
|
|
|
iscsilun->bl.max_unmap * iscsilun->block_size;
|
2013-12-11 22:26:16 +04:00
|
|
|
}
|
2016-06-24 01:37:21 +03:00
|
|
|
bs->bl.pdiscard_alignment =
|
|
|
|
iscsilun->bl.opt_unmap_gran * iscsilun->block_size;
|
2016-06-02 00:10:01 +03:00
|
|
|
} else {
|
2016-06-24 01:37:21 +03:00
|
|
|
bs->bl.pdiscard_alignment = iscsilun->block_size;
|
2014-04-02 17:30:29 +04:00
|
|
|
}
|
2013-12-11 22:26:16 +04:00
|
|
|
|
2016-06-02 00:10:02 +03:00
|
|
|
if (iscsilun->bl.max_ws_len < 0xffffffff / iscsilun->block_size) {
|
|
|
|
bs->bl.max_pwrite_zeroes =
|
|
|
|
iscsilun->bl.max_ws_len * iscsilun->block_size;
|
2014-04-02 17:30:29 +04:00
|
|
|
}
|
|
|
|
if (iscsilun->lbp.lbpws) {
|
2016-06-02 00:10:02 +03:00
|
|
|
bs->bl.pwrite_zeroes_alignment =
|
|
|
|
iscsilun->bl.opt_unmap_gran * iscsilun->block_size;
|
2016-06-02 00:10:01 +03:00
|
|
|
} else {
|
2016-06-02 00:10:02 +03:00
|
|
|
bs->bl.pwrite_zeroes_alignment = iscsilun->block_size;
|
2013-12-11 22:26:16 +04:00
|
|
|
}
|
2016-06-24 01:37:19 +03:00
|
|
|
if (iscsilun->bl.opt_xfer_len &&
|
|
|
|
iscsilun->bl.opt_xfer_len < INT_MAX / iscsilun->block_size) {
|
|
|
|
bs->bl.opt_transfer = pow2floor(iscsilun->bl.opt_xfer_len *
|
|
|
|
iscsilun->block_size);
|
|
|
|
}
|
2014-01-25 03:50:14 +04:00
|
|
|
}
|
2013-12-11 22:26:16 +04:00
|
|
|
|
2015-02-25 07:40:08 +03:00
|
|
|
/* Note that this will not re-establish a connection with an iSCSI target - it
|
|
|
|
* is effectively a NOP. */
|
2014-01-14 22:10:24 +04:00
|
|
|
static int iscsi_reopen_prepare(BDRVReopenState *state,
|
|
|
|
BlockReopenQueue *queue, Error **errp)
|
|
|
|
{
|
2015-02-25 07:40:08 +03:00
|
|
|
IscsiLun *iscsilun = state->bs->opaque;
|
|
|
|
|
|
|
|
if (state->flags & BDRV_O_RDWR && iscsilun->write_protected) {
|
|
|
|
error_setg(errp, "Cannot open a write protected LUN as read-write");
|
|
|
|
return -EACCES;
|
|
|
|
}
|
2013-12-11 22:26:16 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-18 11:52:20 +03:00
|
|
|
static void iscsi_reopen_commit(BDRVReopenState *reopen_state)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = reopen_state->bs->opaque;
|
|
|
|
|
|
|
|
/* the cache.direct status might have changed */
|
|
|
|
if (iscsilun->allocmap != NULL) {
|
|
|
|
iscsi_allocmap_init(iscsilun, reopen_state->flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-18 17:50:46 +04:00
|
|
|
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
2014-02-17 17:43:53 +04:00
|
|
|
Error *local_err = NULL;
|
2013-02-18 17:50:46 +04:00
|
|
|
|
|
|
|
if (iscsilun->type != TYPE_DISK) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2014-02-17 17:43:53 +04:00
|
|
|
iscsi_readcapacity_sync(iscsilun, &local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
error_free(local_err);
|
|
|
|
return -EIO;
|
2013-02-18 17:50:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (offset > iscsi_getlength(bs)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-07-18 11:52:20 +03:00
|
|
|
if (iscsilun->allocmap != NULL) {
|
|
|
|
iscsi_allocmap_init(iscsilun, bs->open_flags);
|
2014-04-28 15:11:32 +04:00
|
|
|
}
|
|
|
|
|
2013-02-18 17:50:46 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-05 13:20:55 +04:00
|
|
|
static int iscsi_create(const char *filename, QemuOpts *opts, Error **errp)
|
2012-11-17 19:13:24 +04:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int64_t total_size = 0;
|
2013-08-23 05:14:45 +04:00
|
|
|
BlockDriverState *bs;
|
2012-11-17 19:13:24 +04:00
|
|
|
IscsiLun *iscsilun = NULL;
|
2013-04-12 19:59:59 +04:00
|
|
|
QDict *bs_options;
|
2012-11-17 19:13:24 +04:00
|
|
|
|
2014-10-07 15:59:03 +04:00
|
|
|
bs = bdrv_new();
|
2012-11-17 19:13:24 +04:00
|
|
|
|
|
|
|
/* Read out options */
|
2014-09-10 13:05:45 +04:00
|
|
|
total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
|
|
|
BDRV_SECTOR_SIZE);
|
block: Use g_new() & friends where that makes obvious sense
g_new(T, n) is neater than g_malloc(sizeof(T) * n). It's also safer,
for two reasons. One, it catches multiplication overflowing size_t.
Two, it returns T * rather than void *, which lets the compiler catch
more type errors.
Patch created with Coccinelle, with two manual changes on top:
* Add const to bdrv_iterate_format() to keep the types straight
* Convert the allocation in bdrv_drop_intermediate(), which Coccinelle
inexplicably misses
Coccinelle semantic patch:
@@
type T;
@@
-g_malloc(sizeof(T))
+g_new(T, 1)
@@
type T;
@@
-g_try_malloc(sizeof(T))
+g_try_new(T, 1)
@@
type T;
@@
-g_malloc0(sizeof(T))
+g_new0(T, 1)
@@
type T;
@@
-g_try_malloc0(sizeof(T))
+g_try_new0(T, 1)
@@
type T;
expression n;
@@
-g_malloc(sizeof(T) * (n))
+g_new(T, n)
@@
type T;
expression n;
@@
-g_try_malloc(sizeof(T) * (n))
+g_try_new(T, n)
@@
type T;
expression n;
@@
-g_malloc0(sizeof(T) * (n))
+g_new0(T, n)
@@
type T;
expression n;
@@
-g_try_malloc0(sizeof(T) * (n))
+g_try_new0(T, n)
@@
type T;
expression p, n;
@@
-g_realloc(p, sizeof(T) * (n))
+g_renew(T, p, n)
@@
type T;
expression p, n;
@@
-g_try_realloc(p, sizeof(T) * (n))
+g_try_renew(T, p, n)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2014-08-19 12:31:08 +04:00
|
|
|
bs->opaque = g_new0(struct IscsiLun, 1);
|
2013-08-23 05:14:45 +04:00
|
|
|
iscsilun = bs->opaque;
|
2012-11-17 19:13:24 +04:00
|
|
|
|
2013-04-12 19:59:59 +04:00
|
|
|
bs_options = qdict_new();
|
|
|
|
qdict_put(bs_options, "filename", qstring_from_str(filename));
|
2013-09-05 16:22:29 +04:00
|
|
|
ret = iscsi_open(bs, bs_options, 0, NULL);
|
2013-04-12 19:59:59 +04:00
|
|
|
QDECREF(bs_options);
|
|
|
|
|
2012-11-17 19:13:24 +04:00
|
|
|
if (ret != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2014-05-08 18:34:42 +04:00
|
|
|
iscsi_detach_aio_context(bs);
|
2012-11-17 19:13:24 +04:00
|
|
|
if (iscsilun->type != TYPE_DISK) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
2013-08-23 05:14:45 +04:00
|
|
|
if (bs->total_sectors < total_size) {
|
2012-11-17 19:13:24 +04:00
|
|
|
ret = -ENOSPC;
|
2013-07-11 16:16:24 +04:00
|
|
|
goto out;
|
2012-11-17 19:13:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
if (iscsilun->iscsi != NULL) {
|
|
|
|
iscsi_destroy_context(iscsilun->iscsi);
|
|
|
|
}
|
2013-08-23 05:14:45 +04:00
|
|
|
g_free(bs->opaque);
|
|
|
|
bs->opaque = NULL;
|
2013-08-23 05:14:47 +04:00
|
|
|
bdrv_unref(bs);
|
2012-11-17 19:13:24 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-10-24 14:06:55 +04:00
|
|
|
static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
2015-04-16 17:08:26 +03:00
|
|
|
bdi->unallocated_blocks_are_zero = iscsilun->lbprz;
|
2013-10-24 14:06:55 +04:00
|
|
|
bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws;
|
2014-04-28 15:11:32 +04:00
|
|
|
bdi->cluster_size = iscsilun->cluster_sectors * BDRV_SECTOR_SIZE;
|
2013-10-24 14:06:55 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-18 11:52:20 +03:00
|
|
|
static void iscsi_invalidate_cache(BlockDriverState *bs,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
IscsiLun *iscsilun = bs->opaque;
|
|
|
|
iscsi_allocmap_invalidate(iscsilun);
|
|
|
|
}
|
|
|
|
|
2014-06-05 13:20:55 +04:00
|
|
|
static QemuOptsList iscsi_create_opts = {
|
|
|
|
.name = "iscsi-create-opts",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
|
|
|
|
.desc = {
|
|
|
|
{
|
|
|
|
.name = BLOCK_OPT_SIZE,
|
|
|
|
.type = QEMU_OPT_SIZE,
|
|
|
|
.help = "Virtual disk size"
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
}
|
2012-11-17 19:13:24 +04:00
|
|
|
};
|
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
static BlockDriver bdrv_iscsi = {
|
|
|
|
.format_name = "iscsi",
|
|
|
|
.protocol_name = "iscsi",
|
|
|
|
|
|
|
|
.instance_size = sizeof(IscsiLun),
|
2013-09-24 19:07:04 +04:00
|
|
|
.bdrv_needs_filename = true,
|
2011-10-25 12:24:24 +04:00
|
|
|
.bdrv_file_open = iscsi_open,
|
|
|
|
.bdrv_close = iscsi_close,
|
2014-06-05 13:21:11 +04:00
|
|
|
.bdrv_create = iscsi_create,
|
2014-06-05 13:20:55 +04:00
|
|
|
.create_opts = &iscsi_create_opts,
|
2016-07-18 11:52:20 +03:00
|
|
|
.bdrv_reopen_prepare = iscsi_reopen_prepare,
|
|
|
|
.bdrv_reopen_commit = iscsi_reopen_commit,
|
|
|
|
.bdrv_invalidate_cache = iscsi_invalidate_cache,
|
2011-10-25 12:24:24 +04:00
|
|
|
|
|
|
|
.bdrv_getlength = iscsi_getlength,
|
2013-10-24 14:06:55 +04:00
|
|
|
.bdrv_get_info = iscsi_get_info,
|
2013-02-18 17:50:46 +04:00
|
|
|
.bdrv_truncate = iscsi_truncate,
|
2013-12-11 22:26:16 +04:00
|
|
|
.bdrv_refresh_limits = iscsi_refresh_limits,
|
2011-10-25 12:24:24 +04:00
|
|
|
|
2013-07-19 11:19:40 +04:00
|
|
|
.bdrv_co_get_block_status = iscsi_co_get_block_status,
|
2013-07-19 11:19:41 +04:00
|
|
|
.bdrv_co_discard = iscsi_co_discard,
|
2016-06-02 00:10:05 +03:00
|
|
|
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
|
2013-12-05 19:47:17 +04:00
|
|
|
.bdrv_co_readv = iscsi_co_readv,
|
2016-03-10 15:55:50 +03:00
|
|
|
.bdrv_co_writev_flags = iscsi_co_writev_flags,
|
2013-12-05 19:47:17 +04:00
|
|
|
.bdrv_co_flush_to_disk = iscsi_co_flush,
|
2012-04-24 10:29:04 +04:00
|
|
|
|
ISCSI: Add SCSI passthrough via scsi-generic to libiscsi
Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi
device is forced to be scsi-generic.
Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend,
emulate the SG_IO ioctl and pass the SCSI commands across to the
iscsi target.
This allows end-to-end passthrough of SCSI all the way from the guest,
to qemu, via scsi-generic, then libiscsi all the way to the iscsi target.
To activate this you need to specify that the iscsi lun should be treated
as a scsi-generic device.
Example:
-device lsi -device scsi-generic,drive=MyISCSI \
-drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI
Note, you can currently not boot a qemu guest from a scsi device.
Note,
This only works when the host is linux, since the emulation relies on
definitions of SG_IO from the scsi-generic implementation in the
linux kernel.
It should be fairly easy to re-implement some structures similar enough
for non-linux hosts to do the same style of passthrough via a fake
scsi generic layer and libiscsi if need be.
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2012-05-25 15:59:01 +04:00
|
|
|
#ifdef __linux__
|
|
|
|
.bdrv_aio_ioctl = iscsi_aio_ioctl,
|
|
|
|
#endif
|
2014-05-08 18:34:42 +04:00
|
|
|
|
|
|
|
.bdrv_detach_aio_context = iscsi_detach_aio_context,
|
|
|
|
.bdrv_attach_aio_context = iscsi_attach_aio_context,
|
2011-10-25 12:24:24 +04:00
|
|
|
};
|
|
|
|
|
2012-11-26 19:03:42 +04:00
|
|
|
static QemuOptsList qemu_iscsi_opts = {
|
|
|
|
.name = "iscsi",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
|
|
|
|
.desc = {
|
|
|
|
{
|
|
|
|
.name = "user",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "username for CHAP authentication to target",
|
|
|
|
},{
|
|
|
|
.name = "password",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "password for CHAP authentication to target",
|
2016-01-21 17:19:21 +03:00
|
|
|
},{
|
|
|
|
.name = "password-secret",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "ID of the secret providing password for CHAP "
|
|
|
|
"authentication to target",
|
2012-11-26 19:03:42 +04:00
|
|
|
},{
|
|
|
|
.name = "header-digest",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "HeaderDigest setting. "
|
|
|
|
"{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
|
|
|
|
},{
|
|
|
|
.name = "initiator-name",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "Initiator iqn name to use when connecting",
|
2015-06-16 14:45:07 +03:00
|
|
|
},{
|
|
|
|
.name = "timeout",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "Request timeout in seconds (default 0 = no timeout)",
|
2012-11-26 19:03:42 +04:00
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-10-25 12:24:24 +04:00
|
|
|
static void iscsi_block_init(void)
|
|
|
|
{
|
|
|
|
bdrv_register(&bdrv_iscsi);
|
2012-11-26 19:03:42 +04:00
|
|
|
qemu_add_opts(&qemu_iscsi_opts);
|
2011-10-25 12:24:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
block_init(iscsi_block_init);
|