diff mbox

iscsi: add iscsi_truncate support

Message ID 511E14D3.1080001@dlhnet.de
State New
Headers show

Commit Message

Peter Lieven Feb. 15, 2013, 10:58 a.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.

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

Comments

Paolo Bonzini Feb. 15, 2013, 11:09 a.m. UTC | #1
Il 15/02/2013 11:58, 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.
> 
> Signed-off-by: Peter Lieven <pl@kamp.de>
> ---
>  block/iscsi.c |   74
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 74 insertions(+)
> 
> diff --git a/block/iscsi.c b/block/iscsi.c
> index deb3b68..37f12b3 100644
> --- a/block/iscsi.c
> +++ b/block/iscsi.c
> @@ -67,6 +67,12 @@ typedef struct IscsiAIOCB {
>  #endif
>  } IscsiAIOCB;
> 
> +struct IscsiTask {
> +    IscsiLun *iscsilun;
> +    int status;
> +    int complete;
> +};
> +
>  #define NOP_INTERVAL 5000
>  #define MAX_NOP_FAILURES 3
> 
> @@ -700,6 +706,73 @@ iscsi_getlength(BlockDriverState *bs)
>      return len;
>  }
> 
> +static void
> +iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
> +                        void *command_data, void *opaque)
> +{
> +    struct IscsiTask *itask = opaque;
> +    struct scsi_readcapacity16 *rc16;
> +    struct scsi_task *task = command_data;
> +
> +    if (status != 0) {
> +        error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
> +                     iscsi_get_error(iscsi));
> +        itask->status   = 1;
> +        itask->complete = 1;
> +        scsi_free_scsi_task(task);
> +        return;
> +    }
> +
> +    rc16 = scsi_datain_unmarshall(task);
> +    if (rc16 == NULL) {
> +        error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
> +        itask->status   = 1;
> +        itask->complete = 1;
> +        scsi_free_scsi_task(task);
> +        return;
> +    }
> +
> +    itask->iscsilun->block_size = rc16->block_length;
> +    itask->iscsilun->num_blocks = rc16->returned_lba + 1;
> +
> +    itask->status   = 0;
> +    itask->complete = 1;
> +    scsi_free_scsi_task(task);
> +}
> +
> +static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
> +{
> +    IscsiLun *iscsilun = bs->opaque;
> +    struct IscsiTask itask;
> +    struct scsi_task *task = NULL;
> +
> +    if (iscsilun->type != TYPE_DISK) {
> +        return -ENOTSUP;
> +    }
> +
> +    itask.iscsilun = iscsilun;
> +    itask.status = 0;
> +    itask.complete = 0;
> +
> +    task = iscsi_readcapacity16_task(iscsilun->iscsi, iscsilun->lun,
> +                                 iscsi_readcapacity16_cb, &itask);

You can use iscsi_readcapacity16_sync.  In fact, you probably should
extract code from iscsi_open and reuse it here.

Paolo

> +    if (task == NULL) {
> +         error_report("iSCSI: failed to send readcapacity16 command.");
> +         return -EINVAL;
> +    }
> +
> +    while (!itask.complete) {
> +        iscsi_set_events(iscsilun);
> +        qemu_aio_wait();
> +    }
> +
> +    if (offset > iscsi_getlength(bs)) {
> +        return -EINVAL;
> +    }
> +
> +    return 0;
> +}
> +
>  static int parse_chap(struct iscsi_context *iscsi, const char *target)
>  {
>      QemuOptsList *list;
> @@ -1093,6 +1166,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,
Peter Lieven Feb. 15, 2013, 11:18 a.m. UTC | #2
On 15.02.2013 12:09, Paolo Bonzini wrote:
> Il 15/02/2013 11:58, 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.
>>
>> Signed-off-by: Peter Lieven <pl@kamp.de>
>> ---
>>   block/iscsi.c |   74
>> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 74 insertions(+)
>>
>> diff --git a/block/iscsi.c b/block/iscsi.c
>> index deb3b68..37f12b3 100644
>> --- a/block/iscsi.c
>> +++ b/block/iscsi.c
>> @@ -67,6 +67,12 @@ typedef struct IscsiAIOCB {
>>   #endif
>>   } IscsiAIOCB;
>>
>> +struct IscsiTask {
>> +    IscsiLun *iscsilun;
>> +    int status;
>> +    int complete;
>> +};
>> +
>>   #define NOP_INTERVAL 5000
>>   #define MAX_NOP_FAILURES 3
>>
>> @@ -700,6 +706,73 @@ iscsi_getlength(BlockDriverState *bs)
>>       return len;
>>   }
>>
>> +static void
>> +iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
>> +                        void *command_data, void *opaque)
>> +{
>> +    struct IscsiTask *itask = opaque;
>> +    struct scsi_readcapacity16 *rc16;
>> +    struct scsi_task *task = command_data;
>> +
>> +    if (status != 0) {
>> +        error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
>> +                     iscsi_get_error(iscsi));
>> +        itask->status   = 1;
>> +        itask->complete = 1;
>> +        scsi_free_scsi_task(task);
>> +        return;
>> +    }
>> +
>> +    rc16 = scsi_datain_unmarshall(task);
>> +    if (rc16 == NULL) {
>> +        error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
>> +        itask->status   = 1;
>> +        itask->complete = 1;
>> +        scsi_free_scsi_task(task);
>> +        return;
>> +    }
>> +
>> +    itask->iscsilun->block_size = rc16->block_length;
>> +    itask->iscsilun->num_blocks = rc16->returned_lba + 1;
>> +
>> +    itask->status   = 0;
>> +    itask->complete = 1;
>> +    scsi_free_scsi_task(task);
>> +}
>> +
>> +static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
>> +{
>> +    IscsiLun *iscsilun = bs->opaque;
>> +    struct IscsiTask itask;
>> +    struct scsi_task *task = NULL;
>> +
>> +    if (iscsilun->type != TYPE_DISK) {
>> +        return -ENOTSUP;
>> +    }
>> +
>> +    itask.iscsilun = iscsilun;
>> +    itask.status = 0;
>> +    itask.complete = 0;
>> +
>> +    task = iscsi_readcapacity16_task(iscsilun->iscsi, iscsilun->lun,
>> +                                 iscsi_readcapacity16_cb, &itask);
>
> You can use iscsi_readcapacity16_sync.  In fact, you probably should
> extract code from iscsi_open and reuse it here.

Thats not possible afaik. Mixing sync and async commands in libiscsi is
a very bad thing. It will leed to nested event loops.

Peter


>
> Paolo
>
>> +    if (task == NULL) {
>> +         error_report("iSCSI: failed to send readcapacity16 command.");
>> +         return -EINVAL;
>> +    }
>> +
>> +    while (!itask.complete) {
>> +        iscsi_set_events(iscsilun);
>> +        qemu_aio_wait();
>> +    }
>> +
>> +    if (offset > iscsi_getlength(bs)) {
>> +        return -EINVAL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>   static int parse_chap(struct iscsi_context *iscsi, const char *target)
>>   {
>>       QemuOptsList *list;
>> @@ -1093,6 +1166,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,
>
Paolo Bonzini Feb. 15, 2013, 11:54 a.m. UTC | #3
Il 15/02/2013 12:18, Peter Lieven ha scritto:
>>>
>>> +    task = iscsi_readcapacity16_task(iscsilun->iscsi, iscsilun->lun,
>>> +                                 iscsi_readcapacity16_cb, &itask);
>>
>> You can use iscsi_readcapacity16_sync.  In fact, you probably should
>> extract code from iscsi_open and reuse it here.
> 
> Thats not possible afaik. Mixing sync and async commands in libiscsi is
> a very bad thing. It will leed to nested event loops.

Ah, I thought qmp_block_resize did a bdrv_drain_all before calling
bdrv_truncate.

Maybe it should.  Kevin, what do you think?

Paolo
Peter Lieven Feb. 15, 2013, 11:57 a.m. UTC | #4
Am 15.02.2013 um 12:54 schrieb Paolo Bonzini <pbonzini@redhat.com>:

> Il 15/02/2013 12:18, Peter Lieven ha scritto:
>>>> 
>>>> +    task = iscsi_readcapacity16_task(iscsilun->iscsi, iscsilun->lun,
>>>> +                                 iscsi_readcapacity16_cb, &itask);
>>> 
>>> You can use iscsi_readcapacity16_sync.  In fact, you probably should
>>> extract code from iscsi_open and reuse it here.
>> 
>> Thats not possible afaik. Mixing sync and async commands in libiscsi is
>> a very bad thing. It will leed to nested event loops.
> 
> Ah, I thought qmp_block_resize did a bdrv_drain_all before calling
> bdrv_truncate.

Ah ok. In this special case it might be ok. We should ask Ronnie.
What must not happen is that the sync task loop calls the async callbacks.

Peter

> 
> Maybe it should.  Kevin, what do you think?
> 
> Paolo
Kevin Wolf Feb. 15, 2013, 12:18 p.m. UTC | #5
On Fri, Feb 15, 2013 at 12:54:51PM +0100, Paolo Bonzini wrote:
> Il 15/02/2013 12:18, Peter Lieven ha scritto:
> >>>
> >>> +    task = iscsi_readcapacity16_task(iscsilun->iscsi, iscsilun->lun,
> >>> +                                 iscsi_readcapacity16_cb, &itask);
> >>
> >> You can use iscsi_readcapacity16_sync.  In fact, you probably should
> >> extract code from iscsi_open and reuse it here.
> > 
> > Thats not possible afaik. Mixing sync and async commands in libiscsi is
> > a very bad thing. It will leed to nested event loops.
> 
> Ah, I thought qmp_block_resize did a bdrv_drain_all before calling
> bdrv_truncate.
> 
> Maybe it should.  Kevin, what do you think?

Probably. bdrv_truncate() invalidates the bdrv_check_request() result for
in-flight requests, so there better be none.

Kevin
Peter Lieven Feb. 15, 2013, 12:36 p.m. UTC | #6
Am 15.02.2013 um 13:18 schrieb Kevin Wolf <kwolf@redhat.com>:

> On Fri, Feb 15, 2013 at 12:54:51PM +0100, Paolo Bonzini wrote:
>> Il 15/02/2013 12:18, Peter Lieven ha scritto:
>>>>> 
>>>>> +    task = iscsi_readcapacity16_task(iscsilun->iscsi, iscsilun->lun,
>>>>> +                                 iscsi_readcapacity16_cb, &itask);
>>>> 
>>>> You can use iscsi_readcapacity16_sync.  In fact, you probably should
>>>> extract code from iscsi_open and reuse it here.
>>> 
>>> Thats not possible afaik. Mixing sync and async commands in libiscsi is
>>> a very bad thing. It will leed to nested event loops.
>> 
>> Ah, I thought qmp_block_resize did a bdrv_drain_all before calling
>> bdrv_truncate.
>> 
>> Maybe it should.  Kevin, what do you think?
> 
> Probably. bdrv_truncate() invalidates the bdrv_check_request() result for
> in-flight requests, so there better be none.

So you would call brdv_drain_all() before calling brdv_truncate and then
use a sync iscsi call?

Peter


> 
> Kevin
Paolo Bonzini Feb. 15, 2013, 12:56 p.m. UTC | #7
Il 15/02/2013 13:36, Peter Lieven ha scritto:
>>> >> Ah, I thought qmp_block_resize did a bdrv_drain_all before calling
>>> >> bdrv_truncate.
>>> >> 
>>> >> Maybe it should.  Kevin, what do you think?
>> > 
>> > Probably. bdrv_truncate() invalidates the bdrv_check_request() result for
>> > in-flight requests, so there better be none.
> So you would call brdv_drain_all() before calling brdv_truncate and then
> use a sync iscsi call?

Yes, please.

Paolo
diff mbox

Patch

diff --git a/block/iscsi.c b/block/iscsi.c
index deb3b68..37f12b3 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -67,6 +67,12 @@  typedef struct IscsiAIOCB {
  #endif
  } IscsiAIOCB;

+struct IscsiTask {
+    IscsiLun *iscsilun;
+    int status;
+    int complete;
+};
+
  #define NOP_INTERVAL 5000
  #define MAX_NOP_FAILURES 3

@@ -700,6 +706,73 @@  iscsi_getlength(BlockDriverState *bs)
      return len;
  }

+static void
+iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
+                        void *command_data, void *opaque)
+{
+    struct IscsiTask *itask = opaque;
+    struct scsi_readcapacity16 *rc16;
+    struct scsi_task *task = command_data;
+
+    if (status != 0) {
+        error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
+                     iscsi_get_error(iscsi));
+        itask->status   = 1;
+        itask->complete = 1;
+        scsi_free_scsi_task(task);
+        return;
+    }
+
+    rc16 = scsi_datain_unmarshall(task);
+    if (rc16 == NULL) {
+        error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
+        itask->status   = 1;
+        itask->complete = 1;
+        scsi_free_scsi_task(task);
+        return;
+    }
+
+    itask->iscsilun->block_size = rc16->block_length;
+    itask->iscsilun->num_blocks = rc16->returned_lba + 1;
+
+    itask->status   = 0;
+    itask->complete = 1;
+    scsi_free_scsi_task(task);
+}
+
+static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
+{
+    IscsiLun *iscsilun = bs->opaque;
+    struct IscsiTask itask;
+    struct scsi_task *task = NULL;
+
+    if (iscsilun->type != TYPE_DISK) {
+        return -ENOTSUP;
+    }
+
+    itask.iscsilun = iscsilun;
+    itask.status = 0;
+    itask.complete = 0;
+
+    task = iscsi_readcapacity16_task(iscsilun->iscsi, iscsilun->lun,
+                                 iscsi_readcapacity16_cb, &itask);
+    if (task == NULL) {
+         error_report("iSCSI: failed to send readcapacity16 command.");
+         return -EINVAL;
+    }
+
+    while (!itask.complete) {
+        iscsi_set_events(iscsilun);
+        qemu_aio_wait();
+    }
+
+    if (offset > iscsi_getlength(bs)) {
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
  static int parse_chap(struct iscsi_context *iscsi, const char *target)
  {
      QemuOptsList *list;
@@ -1093,6 +1166,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,