diff mbox

[PATCHv4] iscsi: add iscsi_truncate support

Message ID 512231B6.4000300@dlhnet.de
State New
Headers show

Commit Message

Peter Lieven Feb. 18, 2013, 1:50 p.m. UTC
this patch adds iscsi_truncate which effectively allows for
online resizing of iscsi volumes. for this to work you have
to resize the volume on your storage and then call
block_resize command in qemu which will issue a
readcapacity16 to update the capacity.

v4:
  - factor out complete readcapacity logic into a separate function
  - handle capacity change check condition in readcapacity function
    (this happens if the block_resize cmd is the first iscsi task
    executed after a resize on the storage)

v3:
  - remove switch statement in iscsi_open
  - create separate patch for brdv_drain_all() in bdrv_truncate()

v2:
  - add a general bdrv_drain_all() before bdrv_truncate() to avoid
    in-flight AIOs while the device is truncated
  - since no AIOs are in flight we can use a sync libiscsi call
    to re-read the capacity
  - factor out the readcapacity16 logic as it is redundant
    to iscsi_open() and iscsi_truncate().

Signed-off-by: Peter Lieven <pl@kamp.de>
---
  block/iscsi.c |  133 +++++++++++++++++++++++++++++++++++++--------------------
  1 file changed, 87 insertions(+), 46 deletions(-)

Comments

Paolo Bonzini Feb. 25, 2013, 11:11 a.m. UTC | #1
Il 18/02/2013 14:50, Peter Lieven ha scritto:
> this patch adds iscsi_truncate which effectively allows for
> online resizing of iscsi volumes. for this to work you have
> to resize the volume on your storage and then call
> block_resize command in qemu which will issue a
> readcapacity16 to update the capacity.
> 
> v4:
>  - factor out complete readcapacity logic into a separate function
>  - handle capacity change check condition in readcapacity function
>    (this happens if the block_resize cmd is the first iscsi task
>    executed after a resize on the storage)
> 
> v3:
>  - remove switch statement in iscsi_open
>  - create separate patch for brdv_drain_all() in bdrv_truncate()
> 
> v2:
>  - add a general bdrv_drain_all() before bdrv_truncate() to avoid
>    in-flight AIOs while the device is truncated
>  - since no AIOs are in flight we can use a sync libiscsi call
>    to re-read the capacity
>  - factor out the readcapacity16 logic as it is redundant
>    to iscsi_open() and iscsi_truncate().
> 
> Signed-off-by: Peter Lieven <pl@kamp.de>

Applied to scsi-next branch, thanks.

Paolo

> ---
>  block/iscsi.c |  133
> +++++++++++++++++++++++++++++++++++++--------------------
>  1 file changed, 87 insertions(+), 46 deletions(-)
> 
> diff --git a/block/iscsi.c b/block/iscsi.c
> index deb3b68..1ea290e 100644
> --- a/block/iscsi.c
> +++ b/block/iscsi.c
> @@ -823,6 +823,70 @@ static void iscsi_nop_timed_event(void *opaque)
>  }
>  #endif
> 
> +static int iscsi_readcapacity_sync(IscsiLun *iscsilun) {
> +    struct scsi_task *task = NULL;
> +    struct scsi_readcapacity10 *rc10 = NULL;
> +    struct scsi_readcapacity16 *rc16 = NULL;
> +    int ret = 0;
> +
> +try_again:
> +    switch (iscsilun->type) {
> +    case TYPE_DISK:
> +        task = iscsi_readcapacity16_sync(iscsilun->iscsi, iscsilun->lun);
> +        if (task == NULL || task->status != SCSI_STATUS_GOOD) {
> +            /* Capacity data has changed */
> +            if (task != NULL && task->status ==
> SCSI_STATUS_CHECK_CONDITION
> +                    && task->sense.key == SCSI_SENSE_UNIT_ATTENTION
> +                    && task->sense.ascq == 0x2a09) {
> +                scsi_free_scsi_task(task);
> +                goto try_again;
> +            }
> +            error_report("iSCSI: failed to send readcapacity16 command.");
> +            ret = -EINVAL;
> +            goto out;
> +        }
> +        rc16 = scsi_datain_unmarshall(task);
> +        if (rc16 == NULL) {
> +            error_report("iSCSI: Failed to unmarshall readcapacity16
> data.");
> +            ret = -EINVAL;
> +            goto out;
> +        }
> +        iscsilun->block_size = rc16->block_length;
> +        iscsilun->num_blocks = rc16->returned_lba + 1;
> +        break;
> +    case TYPE_ROM:
> +        task = iscsi_readcapacity10_sync(iscsilun->iscsi,
> iscsilun->lun, 0, 0);
> +        if (task == NULL || task->status != SCSI_STATUS_GOOD) {
> +            error_report("iSCSI: failed to send readcapacity10 command.");
> +            ret = -EINVAL;
> +            goto out;
> +        }
> +        rc10 = scsi_datain_unmarshall(task);
> +        if (rc10 == NULL) {
> +            error_report("iSCSI: Failed to unmarshall readcapacity10
> data.");
> +            ret = -EINVAL;
> +            goto out;
> +        }
> +        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:
> +        break;
> +    }
> +
> +out:
> +    if (task) {
> +        scsi_free_scsi_task(task);
> +    }
> +
> +    return ret;
> +}
> +
>  /*
>   * We support iscsi url's on the form
>   * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
> @@ -834,8 +898,6 @@ static int iscsi_open(BlockDriverState *bs, const
> char *filename, int flags)
>      struct iscsi_url *iscsi_url = NULL;
>      struct scsi_task *task = NULL;
>      struct scsi_inquiry_standard *inq = NULL;
> -    struct scsi_readcapacity10 *rc10 = NULL;
> -    struct scsi_readcapacity16 *rc16 = NULL;
>      char *initiator_name = NULL;
>      int ret;
> 
> @@ -925,48 +987,8 @@ static int iscsi_open(BlockDriverState *bs, const
> char *filename, int flags)
> 
>      iscsilun->type = inq->periperal_device_type;
> 
> -    scsi_free_scsi_task(task);
> -
> -    switch (iscsilun->type) {
> -    case TYPE_DISK:
> -        task = iscsi_readcapacity16_sync(iscsi, iscsilun->lun);
> -        if (task == NULL || task->status != SCSI_STATUS_GOOD) {
> -            error_report("iSCSI: failed to send readcapacity16 command.");
> -            ret = -EINVAL;
> -            goto out;
> -        }
> -        rc16 = scsi_datain_unmarshall(task);
> -        if (rc16 == NULL) {
> -            error_report("iSCSI: Failed to unmarshall readcapacity16
> data.");
> -            ret = -EINVAL;
> -            goto out;
> -        }
> -        iscsilun->block_size = rc16->block_length;
> -        iscsilun->num_blocks = rc16->returned_lba + 1;
> -        break;
> -    case TYPE_ROM:
> -        task = iscsi_readcapacity10_sync(iscsi, iscsilun->lun, 0, 0);
> -        if (task == NULL || task->status != SCSI_STATUS_GOOD) {
> -            error_report("iSCSI: failed to send readcapacity10 command.");
> -            ret = -EINVAL;
> -            goto out;
> -        }
> -        rc10 = scsi_datain_unmarshall(task);
> -        if (rc10 == NULL) {
> -            error_report("iSCSI: Failed to unmarshall readcapacity10
> data.");
> -            ret = -EINVAL;
> -            goto out;
> -        }
> -        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:
> -        break;
> +    if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
> +        goto out;
>      }
> 
>      bs->total_sectors    = iscsilun->num_blocks *
> @@ -981,8 +1003,6 @@ static int iscsi_open(BlockDriverState *bs, const
> char *filename, int flags)
>          bs->sg = 1;
>      }
> 
> -    ret = 0;
> -
>  #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);
> @@ -1023,6 +1043,26 @@ static void iscsi_close(BlockDriverState *bs)
>      memset(iscsilun, 0, sizeof(IscsiLun));
>  }
> 
> +static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
> +{
> +    IscsiLun *iscsilun = bs->opaque;
> +    int ret = 0;
> +
> +    if (iscsilun->type != TYPE_DISK) {
> +        return -ENOTSUP;
> +    }
> +
> +    if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
> +        return ret;
> +    }
> +
> +    if (offset > iscsi_getlength(bs)) {
> +        return -EINVAL;
> +    }
> +
> +    return 0;
> +}
> +
>  static int iscsi_has_zero_init(BlockDriverState *bs)
>  {
>      return 0;
> @@ -1093,6 +1133,7 @@ static BlockDriver bdrv_iscsi = {
>      .create_options  = iscsi_create_options,
> 
>      .bdrv_getlength  = iscsi_getlength,
> +    .bdrv_truncate   = iscsi_truncate,
> 
>      .bdrv_aio_readv  = iscsi_aio_readv,
>      .bdrv_aio_writev = iscsi_aio_writev,
diff mbox

Patch

diff --git a/block/iscsi.c b/block/iscsi.c
index deb3b68..1ea290e 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -823,6 +823,70 @@  static void iscsi_nop_timed_event(void *opaque)
  }
  #endif

+static int iscsi_readcapacity_sync(IscsiLun *iscsilun) {
+    struct scsi_task *task = NULL;
+    struct scsi_readcapacity10 *rc10 = NULL;
+    struct scsi_readcapacity16 *rc16 = NULL;
+    int ret = 0;
+
+try_again:
+    switch (iscsilun->type) {
+    case TYPE_DISK:
+        task = iscsi_readcapacity16_sync(iscsilun->iscsi, iscsilun->lun);
+        if (task == NULL || task->status != SCSI_STATUS_GOOD) {
+            /* Capacity data has changed */
+            if (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
+                    && task->sense.key == SCSI_SENSE_UNIT_ATTENTION
+                    && task->sense.ascq == 0x2a09) {
+                scsi_free_scsi_task(task);
+                goto try_again;
+            }
+            error_report("iSCSI: failed to send readcapacity16 command.");
+            ret = -EINVAL;
+            goto out;
+        }
+        rc16 = scsi_datain_unmarshall(task);
+        if (rc16 == NULL) {
+            error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
+            ret = -EINVAL;
+            goto out;
+        }
+        iscsilun->block_size = rc16->block_length;
+        iscsilun->num_blocks = rc16->returned_lba + 1;
+        break;
+    case TYPE_ROM:
+        task = iscsi_readcapacity10_sync(iscsilun->iscsi, iscsilun->lun, 0, 0);
+        if (task == NULL || task->status != SCSI_STATUS_GOOD) {
+            error_report("iSCSI: failed to send readcapacity10 command.");
+            ret = -EINVAL;
+            goto out;
+        }
+        rc10 = scsi_datain_unmarshall(task);
+        if (rc10 == NULL) {
+            error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
+            ret = -EINVAL;
+            goto out;
+        }
+        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:
+        break;
+    }
+
+out:
+    if (task) {
+        scsi_free_scsi_task(task);
+    }
+
+    return ret;
+}
+
  /*
   * We support iscsi url's on the form
   * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
@@ -834,8 +898,6 @@  static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
      struct iscsi_url *iscsi_url = NULL;
      struct scsi_task *task = NULL;
      struct scsi_inquiry_standard *inq = NULL;
-    struct scsi_readcapacity10 *rc10 = NULL;
-    struct scsi_readcapacity16 *rc16 = NULL;
      char *initiator_name = NULL;
      int ret;

@@ -925,48 +987,8 @@  static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)

      iscsilun->type = inq->periperal_device_type;

-    scsi_free_scsi_task(task);
-
-    switch (iscsilun->type) {
-    case TYPE_DISK:
-        task = iscsi_readcapacity16_sync(iscsi, iscsilun->lun);
-        if (task == NULL || task->status != SCSI_STATUS_GOOD) {
-            error_report("iSCSI: failed to send readcapacity16 command.");
-            ret = -EINVAL;
-            goto out;
-        }
-        rc16 = scsi_datain_unmarshall(task);
-        if (rc16 == NULL) {
-            error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
-            ret = -EINVAL;
-            goto out;
-        }
-        iscsilun->block_size = rc16->block_length;
-        iscsilun->num_blocks = rc16->returned_lba + 1;
-        break;
-    case TYPE_ROM:
-        task = iscsi_readcapacity10_sync(iscsi, iscsilun->lun, 0, 0);
-        if (task == NULL || task->status != SCSI_STATUS_GOOD) {
-            error_report("iSCSI: failed to send readcapacity10 command.");
-            ret = -EINVAL;
-            goto out;
-        }
-        rc10 = scsi_datain_unmarshall(task);
-        if (rc10 == NULL) {
-            error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
-            ret = -EINVAL;
-            goto out;
-        }
-        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:
-        break;
+    if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
+        goto out;
      }

      bs->total_sectors    = iscsilun->num_blocks *
@@ -981,8 +1003,6 @@  static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
          bs->sg = 1;
      }

-    ret = 0;
-
  #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);
@@ -1023,6 +1043,26 @@  static void iscsi_close(BlockDriverState *bs)
      memset(iscsilun, 0, sizeof(IscsiLun));
  }

+static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
+{
+    IscsiLun *iscsilun = bs->opaque;
+    int ret = 0;
+
+    if (iscsilun->type != TYPE_DISK) {
+        return -ENOTSUP;
+    }
+
+    if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
+        return ret;
+    }
+
+    if (offset > iscsi_getlength(bs)) {
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
  static int iscsi_has_zero_init(BlockDriverState *bs)
  {
      return 0;
@@ -1093,6 +1133,7 @@  static BlockDriver bdrv_iscsi = {
      .create_options  = iscsi_create_options,

      .bdrv_getlength  = iscsi_getlength,
+    .bdrv_truncate   = iscsi_truncate,

      .bdrv_aio_readv  = iscsi_aio_readv,
      .bdrv_aio_writev = iscsi_aio_writev,