iscsi/qemu-img/block-migration enhancements
v4 cover iscsi/qemu-img/block-migration enhancements
this series adds logical block provisioning functions to the iscsi layer. it also is the first step to the change of migration to coroutines in block/iscsi.
v3->v4:
- this series collapsed into 2 patches not yet merged. as per discussion write_zero optimization will be a different approach handled at the block layer.
- patch 1 will now copy the complete vpd pages as almost all fields will be needed later.
Peter Lieven (2):
iscsi: add logical block provisioning information to iscsilun
iscsi: add .bdrv_co_is_allocated
block/iscsi.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+)
Signature · 1 line
1.7.9.5
PATCH v4 1/2 iscsi: add logical block provisioning information to iscsilun
--- block/iscsi.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+)
block/iscsi.c +77 -0
@@ -49,6 +49,10 @@ typedef struct IscsiLun {uint64_t num_blocks;int events;QEMUTimer *nop_timer; +uint8_t lbpme; +uint8_t lbprz; +struct scsi_inquiry_logical_block_provisioning lbp; +struct scsi_inquiry_block_limits bl; } IscsiLun; typedef struct IscsiAIOCB {
@@ -948,6 +952,8 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun) }else { iscsilun->block_size = rc16->block_length; iscsilun->num_blocks = rc16->returned_lba +1 ; + iscsilun->lbpme = rc16->lbpme; + iscsilun->lbprz = rc16->lbprz; } }break ;
@@ -1000,6 +1006,37 @@ static QemuOptsList runtime_opts = { }, }; +static struct scsi_task *iscsi_do_inquiry (struct iscsi_context *iscsi, +int lun,int evpd,int pc) { +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); +if (task ==NULL || task->status !=SCSI_STATUS_GOOD ) { + goto fail; + } + } + +return task; + +fail: +error_report ("iSCSI: Inquiry command failed : %s" , +iscsi_get_error (iscsi)); +if (task) { +scsi_free_scsi_task (task); +return NULL ; + } +return NULL ; +} +/* * We support iscsi url's on the form * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
@@ -1130,6 +1167,46 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags) bs->sg =1 ; } +if (iscsilun->lbpme) { +struct scsi_inquiry_logical_block_provisioning *inq_lbp; + task =iscsi_do_inquiry (iscsilun->iscsi, iscsilun->lun,1 , +SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING ); +if (task ==NULL ) { + ret = -EINVAL ; + goto out; + } + inq_lbp =scsi_datain_unmarshall (task); +if (inq_lbp ==NULL ) { +error_report ("iSCSI: failed to unmarshall inquiry datain blob" ); + ret = -EINVAL ; + goto out; + } +memcpy (&iscsilun->lbp, inq_lbp, +sizeof (struct scsi_inquiry_logical_block_provisioning )); +scsi_free_scsi_task (task); + task =NULL ; + } + +if (iscsilun->lbp.lbpu) { +struct scsi_inquiry_block_limits *inq_bl; + task =iscsi_do_inquiry (iscsilun->iscsi, iscsilun->lun,1 , +SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS ); +if (task ==NULL ) { + ret = -EINVAL ; + goto out; + } + inq_bl =scsi_datain_unmarshall (task); +if (inq_bl ==NULL ) { +error_report ("iSCSI: failed to unmarshall inquiry datain blob" ); + ret = -EINVAL ; + goto out; + } +memcpy (&iscsilun->bl, inq_bl, +sizeof (struct scsi_inquiry_block_limits )); +scsi_free_scsi_task (task); + task =NULL ; + } +#if defined(LIBISCSI_FEATURE_NOP_COUNTER )/* Set up a timer for sending out iSCSI NOPs */ iscsilun->nop_timer =qemu_new_timer_ms (rt_clock, iscsi_nop_timed_event, iscsilun);
Signature · 1 line
1.7.9.5
Re: [Qemu-devel] [PATCHv4 1/2] iscsi: add logical block provisioning information to iscsilun
Il 18/07/2013 10:19, Peter Lieven ha scritto:
Quoted 10 lines
Signed-off-by Peter Lieven <[email protected]>--- block/iscsi.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+)block/iscsi.c +0
-0
block/iscsi.c typedef struct IscsiLun { · line 55
Quoted 7 lines
block/iscsi.c +4
-0@@ -49,6 +49,10 @@ typedef struct IscsiLun {uint64_t num_blocks;int events;QEMUTimer *nop_timer; +uint8_t lbpme; +uint8_t lbprz; +struct scsi_inquiry_logical_block_provisioning lbp; +struct scsi_inquiry_block_limits bl;
Available since 1.3.0, looks good.
Quoted 98 lines
block/iscsi.c +73
-0} IscsiLun; typedef struct IscsiAIOCB {@@ -948,6 +952,8 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun) }else { iscsilun->block_size = rc16->block_length; iscsilun->num_blocks = rc16->returned_lba +1 ; + iscsilun->lbpme = rc16->lbpme; + iscsilun->lbprz = rc16->lbprz; } }break ;@@ -1000,6 +1006,37 @@ static QemuOptsList runtime_opts = { }, }; +static struct scsi_task *iscsi_do_inquiry (struct iscsi_context *iscsi, +int lun,int evpd,int pc) { +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); +if (task ==NULL || task->status !=SCSI_STATUS_GOOD ) { + goto fail; + } + } + +return task; + +fail: +error_report ("iSCSI: Inquiry command failed : %s" , +iscsi_get_error (iscsi)); +if (task) { +scsi_free_scsi_task (task); +return NULL ; + } +return NULL ; +} +/* * We support iscsi url's on the form * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun> @@ -1130,6 +1167,46 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags) bs->sg =1 ; } +if (iscsilun->lbpme) { +struct scsi_inquiry_logical_block_provisioning *inq_lbp; + task =iscsi_do_inquiry (iscsilun->iscsi, iscsilun->lun,1 , +SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING ); +if (task ==NULL ) { + ret = -EINVAL ; + goto out; + } + inq_lbp =scsi_datain_unmarshall (task); +if (inq_lbp ==NULL ) { +error_report ("iSCSI: failed to unmarshall inquiry datain blob" ); + ret = -EINVAL ; + goto out; + } +memcpy (&iscsilun->lbp, inq_lbp, +sizeof (struct scsi_inquiry_logical_block_provisioning )); +scsi_free_scsi_task (task); + task =NULL ; + } + +if (iscsilun->lbp.lbpu) { +struct scsi_inquiry_block_limits *inq_bl; + task =iscsi_do_inquiry (iscsilun->iscsi, iscsilun->lun,1 , +SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS ); +if (task ==NULL ) { + ret = -EINVAL ; + goto out; + } + inq_bl =scsi_datain_unmarshall (task); +if (inq_bl ==NULL ) { +error_report ("iSCSI: failed to unmarshall inquiry datain blob" ); + ret = -EINVAL ; + goto out; + } +memcpy (&iscsilun->bl, inq_bl, +sizeof (struct scsi_inquiry_block_limits )); +scsi_free_scsi_task (task); + task =NULL ; + } +#if defined(LIBISCSI_FEATURE_NOP_COUNTER )/* Set up a timer for sending out iSCSI NOPs */ iscsilun->nop_timer =qemu_new_timer_ms (rt_clock, iscsi_nop_timed_event, iscsilun);
PATCH v4 2/2 iscsi: add .bdrv_co_is_allocated
In reply to Peter Lieventhis patch adds a coroutine for .bdrv_co_is_allocated as well as a generic framework that can be used to build coroutines in block/iscsi.
--- block/iscsi.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+)
block/iscsi.c +130 -0
@@ -55,6 +55,15 @@ typedef struct IscsiLun {struct scsi_inquiry_block_limits bl; } IscsiLun; +typedef struct IscsiTask { +int status; +int complete; +int retries; +int do_retry; +struct scsi_task *task; +Coroutine *co; +}IscsiTask ; + typedef struct IscsiAIOCB { BlockDriverAIOCB common; QEMUIOVector *qiov;
@@ -110,6 +119,41 @@ iscsi_schedule_bh(IscsiAIOCB *acb)qemu_bh_schedule (acb->bh); } +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->complete =1 ; + iTask->status = status; + iTask->do_retry =0 ; + iTask->task = task; + +if (iTask->retries-- >0 && status ==SCSI_STATUS_CHECK_CONDITION + && task->sense.key ==SCSI_SENSE_UNIT_ATTENTION ) { + iTask->do_retry =1 ; + goto out; + } + +if (status !=SCSI_STATUS_GOOD ) { +error_report ("iSCSI: Failure. %s" ,iscsi_get_error (iscsi)); + } + +out: +if (iTask->co) { +qemu_coroutine_enter (iTask->co,NULL ); + } +} + +static void iscsi_co_init_iscsitask (IscsiLun *iscsilun,struct IscsiTask *iTask) +{ + *iTask = (struct IscsiTask ) { + .co =qemu_coroutine_self (), + .retries =ISCSI_CMD_RETRIES , + }; +} static void iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
@@ -804,6 +848,90 @@ iscsi_getlength(BlockDriverState *bs)return len; } +static int coroutine_fn iscsi_co_is_allocated(BlockDriverState *bs, +int64_t sector_num, +int nb_sectors,int *pnum) +{ +IscsiLun *iscsilun = bs->opaque; +struct scsi_get_lba_status *lbas =NULL ; +struct scsi_lba_status_descriptor *lbasd =NULL ; +struct IscsiTask iTask; +int ret; + +/* default to all sectors allocated */ + ret =1 ; + *pnum = nb_sectors; + +/* LUN does not support logical block provisioning */ +if (iscsilun->lbpme ==0 ) { + goto out; + } + +iscsi_co_init_iscsitask (iscsilun, &iTask); + +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 =0 ; + *pnum =0 ; + 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 ; + } + 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 compatiblity */ + goto out; + } + + lbas =scsi_datain_unmarshall (iTask.task); +if (lbas ==NULL ) { + ret =0 ; + *pnum =0 ; + goto out; + } + + lbasd = &lbas->descriptors[0 ]; + +if (sector_qemu2lun (sector_num, iscsilun) != lbasd->lba) { + ret =0 ; + *pnum =0 ; + goto out; + } + + *pnum = lbasd->num_blocks * (iscsilun->block_size /BDRV_SECTOR_SIZE ); +if (*pnum > nb_sectors) { + *pnum = nb_sectors; + } + +if (lbasd->provisioning ==SCSI_PROVISIONING_TYPE_DEALLOCATED || + lbasd->provisioning ==SCSI_PROVISIONING_TYPE_ANCHORED ) { + ret =0 ; + } + +out: +if (iTask.task !=NULL ) { +scsi_free_scsi_task (iTask.task); + } +return ret; +} +static int parse_chap (struct iscsi_context *iscsi,const char *target) {QemuOptsList *list;
@@ -1345,6 +1473,8 @@ static BlockDriver bdrv_iscsi = { .bdrv_getlength = iscsi_getlength, .bdrv_truncate = iscsi_truncate, + .bdrv_co_is_allocated = iscsi_co_is_allocated, + .bdrv_aio_readv = iscsi_aio_readv, .bdrv_aio_writev = iscsi_aio_writev, .bdrv_aio_flush = iscsi_aio_flush,
Signature · 1 line
1.7.9.5
Re: [Qemu-devel] [PATCHv4 2/2] iscsi: add .bdrv_co_is_allocated
Il 18/07/2013 10:19, Peter Lieven ha scritto:
Quoted 2 lines
this patch adds a coroutine for .bdrv_co_is_allocated as well as a generic framework that can be used to build coroutines in block/iscsi.
This conflicts with my bdrv_get_block_status patches.
Paolo
Quoted 168 lines
Signed-off-by Peter Lieven <[email protected]>--- block/iscsi.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+)block/iscsi.c +130
-0@@ -55,6 +55,15 @@ typedef struct IscsiLun {struct scsi_inquiry_block_limits bl; } IscsiLun; +typedef struct IscsiTask { +int status; +int complete; +int retries; +int do_retry; +struct scsi_task *task; +Coroutine *co; +}IscsiTask ; + typedef struct IscsiAIOCB { BlockDriverAIOCB common; QEMUIOVector *qiov;@@ -110,6 +119,41 @@ iscsi_schedule_bh(IscsiAIOCB *acb)qemu_bh_schedule (acb->bh); } +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->complete =1 ; + iTask->status = status; + iTask->do_retry =0 ; + iTask->task = task; + +if (iTask->retries-- >0 && status ==SCSI_STATUS_CHECK_CONDITION + && task->sense.key ==SCSI_SENSE_UNIT_ATTENTION ) { + iTask->do_retry =1 ; + goto out; + } + +if (status !=SCSI_STATUS_GOOD ) { +error_report ("iSCSI: Failure. %s" ,iscsi_get_error (iscsi)); + } + +out: +if (iTask->co) { +qemu_coroutine_enter (iTask->co,NULL ); + } +} + +static void iscsi_co_init_iscsitask (IscsiLun *iscsilun,struct IscsiTask *iTask) +{ + *iTask = (struct IscsiTask ) { + .co =qemu_coroutine_self (), + .retries =ISCSI_CMD_RETRIES , + }; +} static void iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,@@ -804,6 +848,90 @@ iscsi_getlength(BlockDriverState *bs)return len; } +static int coroutine_fn iscsi_co_is_allocated(BlockDriverState *bs, +int64_t sector_num, +int nb_sectors,int *pnum) +{ +IscsiLun *iscsilun = bs->opaque; +struct scsi_get_lba_status *lbas =NULL ; +struct scsi_lba_status_descriptor *lbasd =NULL ; +struct IscsiTask iTask; +int ret; + +/* default to all sectors allocated */ + ret =1 ; + *pnum = nb_sectors; + +/* LUN does not support logical block provisioning */ +if (iscsilun->lbpme ==0 ) { + goto out; + } + +iscsi_co_init_iscsitask (iscsilun, &iTask); + +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 =0 ; + *pnum =0 ; + 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 ; + } + 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 compatiblity */ + goto out; + } + + lbas =scsi_datain_unmarshall (iTask.task); +if (lbas ==NULL ) { + ret =0 ; + *pnum =0 ; + goto out; + } + + lbasd = &lbas->descriptors[0 ]; + +if (sector_qemu2lun (sector_num, iscsilun) != lbasd->lba) { + ret =0 ; + *pnum =0 ; + goto out; + } + + *pnum = lbasd->num_blocks * (iscsilun->block_size /BDRV_SECTOR_SIZE ); +if (*pnum > nb_sectors) { + *pnum = nb_sectors; + } + +if (lbasd->provisioning ==SCSI_PROVISIONING_TYPE_DEALLOCATED || + lbasd->provisioning ==SCSI_PROVISIONING_TYPE_ANCHORED ) { + ret =0 ; + } + +out: +if (iTask.task !=NULL ) { +scsi_free_scsi_task (iTask.task); + } +return ret; +} +static int parse_chap (struct iscsi_context *iscsi,const char *target) {QemuOptsList *list;@@ -1345,6 +1473,8 @@ static BlockDriver bdrv_iscsi = { .bdrv_getlength = iscsi_getlength, .bdrv_truncate = iscsi_truncate, + .bdrv_co_is_allocated = iscsi_co_is_allocated, + .bdrv_aio_readv = iscsi_aio_readv, .bdrv_aio_writev = iscsi_aio_writev, .bdrv_aio_flush = iscsi_aio_flush,
Re: [Qemu-devel] [PATCHv4 2/2] iscsi: add .bdrv_co_is_allocated
Quoted 4 lines · Paolo Bonzini
Il 18/07/2013 10:19, Peter Lieven ha scritto:
Quoted 2 lines
this patch adds a coroutine for .bdrv_co_is_allocated as well as a generic framework that can be used to build coroutines in block/iscsi.
This conflicts with my
bdrv_get_block_statuspatches.
I know, but this one is ready. changing this to get_block_status is trivial.
My idea was to merge this now and you can then easily add a small patch to rename the function to iscsi_co_get_block_status even adding the BDRV_BLOCK_ZERO.
Peter
Am 18.07.2013 um 10:19 hat Peter Lieven geschrieben:
Quoted 11 lines
this series adds logical block provisioning functions to the iscsi layer. it also is the first step to the change of migration to coroutines in block/iscsi.
v3->v4:
- this series collapsed into 2 patches not yet merged. as per discussionwrite_zerooptimization will be a different approach handled at the block layer.
- patch 1 will now copy the complete vpd pages as almost all fields will be needed later.
Quoted 6 lines
Peter Lieven (2):
iscsi: add logical block provisioning information to iscsilun
iscsi: add .bdrv_co_is_allocatedblock/iscsi.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+)