* QEMU Block driver for iSCSI images
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
#include "block/block_int.h"
#include "block/scsi.h"
#include "qemu/iov.h"
-#include "sysemu/sysemu.h"
+#include "qemu/uuid.h"
#include "qmp-commands.h"
#include "qapi/qmp/qstring.h"
#include "crypto/secret.h"
#ifdef __linux__
#include <scsi/sg.h>
-#include <block/scsi.h>
#endif
typedef struct IscsiLun {
struct scsi_inquiry_logical_block_provisioning lbp;
struct scsi_inquiry_block_limits bl;
unsigned char *zeroblock;
- unsigned long *allocationmap;
+ /* 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;
int cluster_sectors;
bool use_16_for_rw;
bool write_protected;
int do_retry;
struct scsi_task *task;
Coroutine *co;
- QEMUBH *bh;
IscsiLun *iscsilun;
QEMUTimer retry_timer;
int err_code;
{
struct IscsiTask *iTask = opaque;
iTask->complete = 1;
- qemu_bh_delete(iTask->bh);
- qemu_coroutine_enter(iTask->co, NULL);
+ qemu_coroutine_enter(iTask->co);
}
static void iscsi_retry_timer_expired(void *opaque)
struct IscsiTask *iTask = opaque;
iTask->complete = 1;
if (iTask->co) {
- qemu_coroutine_enter(iTask->co, NULL);
+ qemu_coroutine_enter(iTask->co);
}
}
#define SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR 0x1a00
#endif
+#ifndef LIBISCSI_API_VERSION
+#define LIBISCSI_API_VERSION 20130701
+#endif
+
static int iscsi_translate_sense(struct scsi_sense *sense)
{
int ret;
out:
if (iTask->co) {
- iTask->bh = aio_bh_new(iTask->iscsilun->aio_context,
- iscsi_co_generic_bh_cb, iTask);
- qemu_bh_schedule(iTask->bh);
+ aio_bh_schedule_oneshot(iTask->iscsilun->aio_context,
+ iscsi_co_generic_bh_cb, iTask);
} else {
iTask->complete = 1;
}
return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
}
-static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors,
- IscsiLun *iscsilun)
+static bool is_byte_request_lun_aligned(int64_t offset, int count,
+ IscsiLun *iscsilun)
{
- if ((sector_num * BDRV_SECTOR_SIZE) % iscsilun->block_size ||
- (nb_sectors * BDRV_SECTOR_SIZE) % iscsilun->block_size) {
- error_report("iSCSI misaligned request: "
- "iscsilun->block_size %u, sector_num %" PRIi64
- ", nb_sectors %d",
- iscsilun->block_size, sector_num, nb_sectors);
- return 0;
- }
- return 1;
+ 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 unsigned long *iscsi_allocationmap_init(IscsiLun *iscsilun)
+static bool is_sector_request_lun_aligned(int64_t sector_num, int nb_sectors,
+ IscsiLun *iscsilun)
{
- return bitmap_try_new(DIV_ROUND_UP(sector_lun2qemu(iscsilun->num_blocks,
- iscsilun),
- iscsilun->cluster_sectors));
+ assert(nb_sectors <= BDRV_REQUEST_MAX_SECTORS);
+ return is_byte_request_lun_aligned(sector_num << BDRV_SECTOR_BITS,
+ nb_sectors << BDRV_SECTOR_BITS,
+ iscsilun);
}
-static void iscsi_allocationmap_set(IscsiLun *iscsilun, int64_t sector_num,
- int nb_sectors)
+static void iscsi_allocmap_free(IscsiLun *iscsilun)
{
- if (iscsilun->allocationmap == NULL) {
- return;
+ g_free(iscsilun->allocmap);
+ g_free(iscsilun->allocmap_valid);
+ iscsilun->allocmap = NULL;
+ iscsilun->allocmap_valid = NULL;
+}
+
+
+static int iscsi_allocmap_init(IscsiLun *iscsilun, int open_flags)
+{
+ 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;
+ }
+
+ 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;
}
- bitmap_set(iscsilun->allocationmap,
- sector_num / iscsilun->cluster_sectors,
- DIV_ROUND_UP(nb_sectors, iscsilun->cluster_sectors));
+
+ return 0;
}
-static void iscsi_allocationmap_clear(IscsiLun *iscsilun, int64_t sector_num,
- int nb_sectors)
+static void
+iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num,
+ int nb_sectors, bool allocated, bool valid)
{
- int64_t cluster_num, nb_clusters;
- if (iscsilun->allocationmap == NULL) {
+ int64_t cl_num_expanded, nb_cls_expanded, cl_num_shrunk, nb_cls_shrunk;
+
+ if (iscsilun->allocmap == NULL) {
+ return;
+ }
+ /* 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;
}
- cluster_num = DIV_ROUND_UP(sector_num, iscsilun->cluster_sectors);
- nb_clusters = (sector_num + nb_sectors) / iscsilun->cluster_sectors
- - cluster_num;
- if (nb_clusters > 0) {
- bitmap_clear(iscsilun->allocationmap, cluster_num, nb_clusters);
+ 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;
+ }
+ 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);
+}
+
static int coroutine_fn
iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
QEMUIOVector *iov, int flags)
if (fua) {
assert(iscsilun->dpofua);
}
- if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+ if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
return -EINVAL;
}
- if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) {
- error_report("iSCSI Error: Write of %d sectors exceeds max_xfer_len "
- "of %d sectors", nb_sectors, bs->bl.max_transfer_length);
- return -EINVAL;
+ if (bs->bl.max_transfer) {
+ assert(nb_sectors << BDRV_SECTOR_BITS <= bs->bl.max_transfer);
}
lba = sector_qemu2lun(sector_num, iscsilun);
iscsi_co_init_iscsitask(iscsilun, &iTask);
retry:
if (iscsilun->use_16_for_rw) {
+#if LIBISCSI_API_VERSION >= (20160603)
+ iTask.task = iscsi_write16_iov_task(iscsilun->iscsi, iscsilun->lun, lba,
+ NULL, num_sectors * iscsilun->block_size,
+ iscsilun->block_size, 0, 0, fua, 0, 0,
+ iscsi_co_generic_cb, &iTask,
+ (struct scsi_iovec *)iov->iov, iov->niov);
+ } else {
+ iTask.task = iscsi_write10_iov_task(iscsilun->iscsi, iscsilun->lun, lba,
+ NULL, num_sectors * iscsilun->block_size,
+ iscsilun->block_size, 0, 0, fua, 0, 0,
+ iscsi_co_generic_cb, &iTask,
+ (struct scsi_iovec *)iov->iov, iov->niov);
+ }
+#else
iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba,
NULL, num_sectors * iscsilun->block_size,
iscsilun->block_size, 0, 0, fua, 0, 0,
iscsilun->block_size, 0, 0, fua, 0, 0,
iscsi_co_generic_cb, &iTask);
}
+#endif
if (iTask.task == NULL) {
return -ENOMEM;
}
+#if LIBISCSI_API_VERSION < (20160603)
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
iov->niov);
+#endif
while (!iTask.complete) {
iscsi_set_events(iscsilun);
qemu_coroutine_yield();
}
if (iTask.status != SCSI_STATUS_GOOD) {
+ iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors);
return iTask.err_code;
}
- iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors);
+ iscsi_allocmap_set_allocated(iscsilun, sector_num, nb_sectors);
return 0;
}
-static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun,
- int64_t sector_num, int nb_sectors)
-{
- unsigned long size;
- if (iscsilun->allocationmap == NULL) {
- return true;
- }
- size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors);
- return !(find_next_bit(iscsilun->allocationmap, size,
- sector_num / iscsilun->cluster_sectors) == size);
-}
static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
int64_t sector_num,
iscsi_co_init_iscsitask(iscsilun, &iTask);
- if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+ if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
ret = -EINVAL;
goto out;
}
}
if (ret & BDRV_BLOCK_ZERO) {
- iscsi_allocationmap_clear(iscsilun, sector_num, *pnum);
+ iscsi_allocmap_set_unallocated(iscsilun, sector_num, *pnum);
} else {
- iscsi_allocationmap_set(iscsilun, sector_num, *pnum);
+ iscsi_allocmap_set_allocated(iscsilun, sector_num, *pnum);
}
if (*pnum > nb_sectors) {
uint64_t lba;
uint32_t num_sectors;
- if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+ if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
return -EINVAL;
}
- if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) {
- error_report("iSCSI Error: Read of %d sectors exceeds max_xfer_len "
- "of %d sectors", nb_sectors, bs->bl.max_transfer_length);
- return -EINVAL;
+ if (bs->bl.max_transfer) {
+ assert(nb_sectors << BDRV_SECTOR_BITS <= bs->bl.max_transfer);
+ }
+
+ /* 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 (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES &&
- !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
- int64_t ret;
+ if (nb_sectors >= ISCSI_CHECKALLOC_THRES &&
+ !iscsi_allocmap_is_valid(iscsilun, sector_num, nb_sectors) &&
+ !iscsi_allocmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
int pnum;
BlockDriverState *file;
- ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum, &file);
+ /* 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);
if (ret < 0) {
return ret;
}
- if (ret & BDRV_BLOCK_ZERO && pnum >= nb_sectors) {
+ /* 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) {
qemu_iovec_memset(iov, 0, 0x00, iov->size);
return 0;
}
iscsi_co_init_iscsitask(iscsilun, &iTask);
retry:
if (iscsilun->use_16_for_rw) {
+#if LIBISCSI_API_VERSION >= (20160603)
+ iTask.task = iscsi_read16_iov_task(iscsilun->iscsi, iscsilun->lun, lba,
+ num_sectors * iscsilun->block_size,
+ iscsilun->block_size, 0, 0, 0, 0, 0,
+ iscsi_co_generic_cb, &iTask,
+ (struct scsi_iovec *)iov->iov, iov->niov);
+ } else {
+ iTask.task = iscsi_read10_iov_task(iscsilun->iscsi, iscsilun->lun, lba,
+ num_sectors * iscsilun->block_size,
+ iscsilun->block_size,
+ 0, 0, 0, 0, 0,
+ iscsi_co_generic_cb, &iTask,
+ (struct scsi_iovec *)iov->iov, iov->niov);
+ }
+#else
iTask.task = iscsi_read16_task(iscsilun->iscsi, iscsilun->lun, lba,
num_sectors * iscsilun->block_size,
iscsilun->block_size, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
iscsi_co_generic_cb, &iTask);
}
+#endif
if (iTask.task == NULL) {
return -ENOMEM;
}
+#if LIBISCSI_API_VERSION < (20160603)
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
-
+#endif
while (!iTask.complete) {
iscsi_set_events(iscsilun);
qemu_coroutine_yield();
}
static int
-coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors)
+coroutine_fn iscsi_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
{
IscsiLun *iscsilun = bs->opaque;
struct IscsiTask iTask;
struct unmap_list list;
- if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
- return -EINVAL;
- }
+ assert(is_byte_request_lun_aligned(offset, count, iscsilun));
if (!iscsilun->lbp.lbpu) {
/* UNMAP is not supported by the target */
return 0;
}
- list.lba = sector_qemu2lun(sector_num, iscsilun);
- list.num = sector_qemu2lun(nb_sectors, iscsilun);
+ list.lba = offset / iscsilun->block_size;
+ list.num = count / iscsilun->block_size;
iscsi_co_init_iscsitask(iscsilun, &iTask);
retry:
if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1,
- iscsi_co_generic_cb, &iTask) == NULL) {
+ iscsi_co_generic_cb, &iTask) == NULL) {
return -ENOMEM;
}
return iTask.err_code;
}
- iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors);
+ iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
+ count >> BDRV_SECTOR_BITS);
return 0;
}
static int
-coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, BdrvRequestFlags flags)
+coroutine_fn iscsi_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
+ int count, BdrvRequestFlags flags)
{
IscsiLun *iscsilun = bs->opaque;
struct IscsiTask iTask;
uint32_t nb_blocks;
bool use_16_for_ws = iscsilun->use_16_for_rw;
- if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
- return -EINVAL;
+ if (!is_byte_request_lun_aligned(offset, count, iscsilun)) {
+ return -ENOTSUP;
}
if (flags & BDRV_REQ_MAY_UNMAP) {
return -ENOTSUP;
}
- lba = sector_qemu2lun(sector_num, iscsilun);
- nb_blocks = sector_qemu2lun(nb_sectors, iscsilun);
+ lba = offset / iscsilun->block_size;
+ nb_blocks = count / iscsilun->block_size;
if (iscsilun->zeroblock == NULL) {
iscsilun->zeroblock = g_try_malloc0(iscsilun->block_size);
}
if (iTask.status != SCSI_STATUS_GOOD) {
+ iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
+ count >> BDRV_SECTOR_BITS);
return iTask.err_code;
}
if (flags & BDRV_REQ_MAY_UNMAP) {
- iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors);
+ iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
+ count >> BDRV_SECTOR_BITS);
} else {
- iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors);
+ iscsi_allocmap_set_allocated(iscsilun, offset >> BDRV_SECTOR_BITS,
+ count >> BDRV_SECTOR_BITS);
}
return 0;
ret = -ENOMEM;
goto out;
}
-
+#if LIBISCSI_API_VERSION >= (20160603)
+ if (iscsi_init_transport(iscsi, iscsi_url->transport)) {
+ error_setg(errp, ("Error initializing transport."));
+ ret = -EINVAL;
+ goto out;
+ }
+#endif
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
error_setg(errp, "iSCSI: Failed to set target name.");
ret = -EINVAL;
/* 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
+#if LIBISCSI_API_VERSION >= 20150621
iscsi_set_timeout(iscsi, timeout);
#else
if (timeout) {
goto out;
}
bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
- bs->request_alignment = iscsilun->block_size;
/* 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.
*/
if (iscsilun->type != TYPE_DISK && iscsilun->type != TYPE_ROM) {
- bs->sg = 1;
+ bs->sg = true;
}
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran *
iscsilun->block_size) >> BDRV_SECTOR_BITS;
if (iscsilun->lbprz) {
- iscsilun->allocationmap = iscsi_allocationmap_init(iscsilun);
- if (iscsilun->allocationmap == NULL) {
- ret = -ENOMEM;
- }
+ ret = iscsi_allocmap_init(iscsilun, bs->open_flags);
}
}
}
iscsi_destroy_context(iscsi);
g_free(iscsilun->zeroblock);
- g_free(iscsilun->allocationmap);
+ iscsi_allocmap_free(iscsilun);
memset(iscsilun, 0, sizeof(IscsiLun));
}
-static int sector_limits_lun2qemu(int64_t sector, IscsiLun *iscsilun)
-{
- return MIN(sector_lun2qemu(sector, iscsilun), INT_MAX / 2 + 1);
-}
-
static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp)
{
/* We don't actually refresh here, but just return data queried in
* iscsi_open(): iscsi targets don't change their limits. */
IscsiLun *iscsilun = bs->opaque;
- uint32_t max_xfer_len = iscsilun->use_16_for_rw ? 0xffffffff : 0xffff;
+ uint64_t max_xfer_len = iscsilun->use_16_for_rw ? 0xffffffff : 0xffff;
+ unsigned int block_size = MAX(BDRV_SECTOR_SIZE, iscsilun->block_size);
+
+ assert(iscsilun->block_size >= BDRV_SECTOR_SIZE || bs->sg);
+
+ bs->bl.request_alignment = block_size;
if (iscsilun->bl.max_xfer_len) {
max_xfer_len = MIN(max_xfer_len, iscsilun->bl.max_xfer_len);
}
- bs->bl.max_transfer_length = sector_limits_lun2qemu(max_xfer_len, iscsilun);
+ if (max_xfer_len * block_size < INT_MAX) {
+ bs->bl.max_transfer = max_xfer_len * iscsilun->block_size;
+ }
if (iscsilun->lbp.lbpu) {
- if (iscsilun->bl.max_unmap < 0xffffffff) {
- bs->bl.max_discard =
- sector_limits_lun2qemu(iscsilun->bl.max_unmap, iscsilun);
+ if (iscsilun->bl.max_unmap < 0xffffffff / block_size) {
+ bs->bl.max_pdiscard =
+ iscsilun->bl.max_unmap * iscsilun->block_size;
}
- bs->bl.discard_alignment =
- sector_limits_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun);
+ bs->bl.pdiscard_alignment =
+ iscsilun->bl.opt_unmap_gran * iscsilun->block_size;
+ } else {
+ bs->bl.pdiscard_alignment = iscsilun->block_size;
}
- if (iscsilun->bl.max_ws_len < 0xffffffff) {
- bs->bl.max_write_zeroes =
- sector_limits_lun2qemu(iscsilun->bl.max_ws_len, iscsilun);
+ if (iscsilun->bl.max_ws_len < 0xffffffff / block_size) {
+ bs->bl.max_pwrite_zeroes =
+ iscsilun->bl.max_ws_len * iscsilun->block_size;
}
if (iscsilun->lbp.lbpws) {
- bs->bl.write_zeroes_alignment =
- sector_limits_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun);
+ bs->bl.pwrite_zeroes_alignment =
+ iscsilun->bl.opt_unmap_gran * iscsilun->block_size;
+ } else {
+ bs->bl.pwrite_zeroes_alignment = iscsilun->block_size;
+ }
+ if (iscsilun->bl.opt_xfer_len &&
+ iscsilun->bl.opt_xfer_len < INT_MAX / block_size) {
+ bs->bl.opt_transfer = pow2floor(iscsilun->bl.opt_xfer_len *
+ iscsilun->block_size);
}
- bs->bl.opt_transfer_length =
- sector_limits_lun2qemu(iscsilun->bl.opt_xfer_len, iscsilun);
}
/* Note that this will not re-establish a connection with an iSCSI target - it
return 0;
}
+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);
+ }
+}
+
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
{
IscsiLun *iscsilun = bs->opaque;
return -EINVAL;
}
- if (iscsilun->allocationmap != NULL) {
- g_free(iscsilun->allocationmap);
- iscsilun->allocationmap = iscsi_allocationmap_init(iscsilun);
+ if (iscsilun->allocmap != NULL) {
+ iscsi_allocmap_init(iscsilun, bs->open_flags);
}
return 0;
return 0;
}
+static void iscsi_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ iscsi_allocmap_invalidate(iscsilun);
+}
+
static QemuOptsList iscsi_create_opts = {
.name = "iscsi-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
.bdrv_close = iscsi_close,
.bdrv_create = iscsi_create,
.create_opts = &iscsi_create_opts,
- .bdrv_reopen_prepare = iscsi_reopen_prepare,
+ .bdrv_reopen_prepare = iscsi_reopen_prepare,
+ .bdrv_reopen_commit = iscsi_reopen_commit,
+ .bdrv_invalidate_cache = iscsi_invalidate_cache,
.bdrv_getlength = iscsi_getlength,
.bdrv_get_info = iscsi_get_info,
.bdrv_refresh_limits = iscsi_refresh_limits,
.bdrv_co_get_block_status = iscsi_co_get_block_status,
- .bdrv_co_discard = iscsi_co_discard,
- .bdrv_co_write_zeroes = iscsi_co_write_zeroes,
+ .bdrv_co_pdiscard = iscsi_co_pdiscard,
+ .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
.bdrv_co_readv = iscsi_co_readv,
.bdrv_co_writev_flags = iscsi_co_writev_flags,
.bdrv_co_flush_to_disk = iscsi_co_flush,
.bdrv_attach_aio_context = iscsi_attach_aio_context,
};
-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",
- },{
- .name = "password-secret",
- .type = QEMU_OPT_STRING,
- .help = "ID of the secret providing password for CHAP "
- "authentication to target",
- },{
- .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",
- },{
- .name = "timeout",
- .type = QEMU_OPT_NUMBER,
- .help = "Request timeout in seconds (default 0 = no timeout)",
- },
- { /* end of list */ }
- },
+#if LIBISCSI_API_VERSION >= (20160603)
+static BlockDriver bdrv_iser = {
+ .format_name = "iser",
+ .protocol_name = "iser",
+
+ .instance_size = sizeof(IscsiLun),
+ .bdrv_needs_filename = true,
+ .bdrv_file_open = iscsi_open,
+ .bdrv_close = iscsi_close,
+ .bdrv_create = iscsi_create,
+ .create_opts = &iscsi_create_opts,
+ .bdrv_reopen_prepare = iscsi_reopen_prepare,
+ .bdrv_reopen_commit = iscsi_reopen_commit,
+ .bdrv_invalidate_cache = iscsi_invalidate_cache,
+
+ .bdrv_getlength = iscsi_getlength,
+ .bdrv_get_info = iscsi_get_info,
+ .bdrv_truncate = iscsi_truncate,
+ .bdrv_refresh_limits = iscsi_refresh_limits,
+
+ .bdrv_co_get_block_status = iscsi_co_get_block_status,
+ .bdrv_co_pdiscard = iscsi_co_pdiscard,
+ .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
+ .bdrv_co_readv = iscsi_co_readv,
+ .bdrv_co_writev_flags = iscsi_co_writev_flags,
+ .bdrv_co_flush_to_disk = iscsi_co_flush,
+
+#ifdef __linux__
+ .bdrv_aio_ioctl = iscsi_aio_ioctl,
+#endif
+
+ .bdrv_detach_aio_context = iscsi_detach_aio_context,
+ .bdrv_attach_aio_context = iscsi_attach_aio_context,
};
+#endif
static void iscsi_block_init(void)
{
bdrv_register(&bdrv_iscsi);
- qemu_add_opts(&qemu_iscsi_opts);
+#if LIBISCSI_API_VERSION >= (20160603)
+ bdrv_register(&bdrv_iser);
+#endif
}
block_init(iscsi_block_init);