if the device supports unmapping and unmapped blocks read as zero ensure that the whole device is unmapped and report .has_zero_init = 1 in this case to speed up qemu-img convert.
Signed-off-by: Peter Lieven <p...@kamp.de> --- block/iscsi.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/block/iscsi.c b/block/iscsi.c index 92e66a6..621ca40 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1436,7 +1436,8 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset) static int iscsi_has_zero_init(BlockDriverState *bs) { - return 0; + IscsiLun *iscsilun = bs->opaque; + return (iscsilun->lbpu && iscsilun->lbprz) ? 1 : 0; } static int iscsi_create(const char *filename, QEMUOptionParameter *options) @@ -1446,6 +1447,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options) BlockDriverState bs; IscsiLun *iscsilun = NULL; QDict *bs_options; + struct scsi_task *task = NULL; memset(&bs, 0, sizeof(BlockDriverState)); @@ -1481,7 +1483,75 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options) } ret = 0; + + if (iscsilun->lbpu && iscsilun->lbprz) { + uint64_t lba = 0; + while (lba < iscsilun->num_blocks) { + struct scsi_get_lba_status *lbas = NULL; + struct scsi_lba_status_descriptor *lbasd = NULL; + enum scsi_provisioning_type provisioning; + uint32_t nb_sectors; + + task = iscsi_get_lba_status_sync(iscsilun->iscsi, iscsilun->lun, + lba, 8 + 16); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + error_report("iSCSI: Failed to get_lba_status on iSCSI lun. %s", + iscsi_get_error(iscsilun->iscsi)); + ret = -EINVAL; + goto out; + } + + lbas = scsi_datain_unmarshall(task); + if (lbas == NULL) { + error_report("iSCSI: failed to unmarshall inquiry datain blob"); + ret = -EINVAL; + goto out; + } + lbasd = &lbas->descriptors[0]; + if (lbasd->lba != lba) { + ret = -EINVAL; + goto out; + } + nb_sectors = lbasd->num_blocks; + provisioning = lbasd->provisioning; + scsi_free_scsi_task(task); + task = NULL; + + /* blocks from lba to lba + nb_sectors - 1 are not mapped + * and read as zero (lbprz==1) so we can skip them */ + if (provisioning != SCSI_PROVISIONING_TYPE_MAPPED) { + lba += nb_sectors; + continue; + } + + uint64_t lba2 = lba + nb_sectors; + while (lba < lba2) { + struct unmap_list list[1]; + list[0].lba = lba; + list[0].num = iscsilun->max_unmap; + if (lba + list[0].num > iscsilun->num_blocks) { + list[0].num = iscsilun->num_blocks - lba; + } + task = iscsi_unmap_sync(iscsilun->iscsi, + iscsilun->lun, + 0, 0, &list[0], 1); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + error_report("iSCSI: Failed to unmap data on iSCSI lun. %s", + iscsi_get_error(iscsilun->iscsi)); + ret = -EINVAL; + goto out; + } + scsi_free_scsi_task(task); + task = NULL; + lba += list[0].num; + } + } + } + out: + if (task) { + scsi_free_scsi_task(task); + } if (iscsilun->iscsi != NULL) { iscsi_destroy_context(iscsilun->iscsi); } -- 1.7.9.5