Re: [Qemu-block] [Qemu-stable] [PATCH v7 0/8] block: Mirror discarded sectors

2015-06-24 Thread Fam Zheng
On Thu, 06/11 16:29, Fam Zheng wrote:
 On Mon, 06/08 14:02, Stefan Hajnoczi wrote:
  On Mon, Jun 08, 2015 at 01:56:06PM +0800, Fam Zheng wrote:
   v7: Fix the lost assignment of s-unmap.
   
   v6: Fix pnum in bdrv_get_block_status_above. [Paolo]
   
   v5: Rewrite patch 1.
   Address Eric's comments on patch 3.
   Add Eric's rev-by to patches 2  4.
   Check BDRV_BLOCK_DATA in patch 3. [Paolo]
   
   This fixes the mirror assert failure reported by wangxiaolong:
   
   https://lists.gnu.org/archive/html/qemu-devel/2015-04/msg04458.html
   
   The direct cause is that hbitmap code couldn't handle unset of bits 
   *after*
   iterator's current position. We could fix that, but the 
   bdrv_reset_dirty() call
   is more questionable:
   
   Before, if guest discarded some sectors during migration, it could see
   different data after moving to dest side, depending on block backends of 
   the
   src and the dest. This is IMO worse than mirroring the actual reading as 
   done
   in this series, because we don't know what the guest is doing.
   
   For example if a guest first issues WRITE SAME to wipe out the area then 
   issues
   UNMAP to discard it, just to get rid of some sensitive data completely, 
   we may
   miss both operations and leave stale data on dest image.
   
   
   Fam Zheng (8):
 block: Add bdrv_get_block_status_above
 qmp: Add optional bool unmap to drive-mirror
 mirror: Do zero write on target if sectors not allocated
 block: Fix dirty bitmap in bdrv_co_discard
 block: Remove bdrv_reset_dirty
 qemu-iotests: Make block job methods common
 qemu-iotests: Add test case for mirror with unmap
 iotests: Use event_wait in wait_ready
   
block.c   | 12 
block/io.c| 60 
   ++-
block/mirror.c| 28 +++---
blockdev.c|  5 
hmp.c |  2 +-
include/block/block.h |  4 +++
include/block/block_int.h |  4 +--
qapi/block-core.json  |  8 +-
qmp-commands.hx   |  3 ++
tests/qemu-iotests/041| 66 
   ++-
tests/qemu-iotests/132| 59 ++
tests/qemu-iotests/132.out|  5 
tests/qemu-iotests/group  |  1 +
tests/qemu-iotests/iotests.py | 23 +++
14 files changed, 196 insertions(+), 84 deletions(-)
create mode 100644 tests/qemu-iotests/132
create mode 100644 tests/qemu-iotests/132.out
   
   -- 
   2.4.2
   
  
  Thanks, applied to my block tree:
  https://github.com/stefanha/qemu/commits/block
 
 Stefan,
 
 The only controversial patches are the qmp/drive-mirror ones (1-3), while
 patches 4-8 are still useful on their own: they fix the mentioned crash and
 improve iotests.
 
 Shall we merge the second half (of course none of them depend on 1-3) now that
 softfreeze is approaching?

Stefan, would you consider applying patches 4-8?

Fam




[Qemu-block] [PATCH] virito-blk: drop duplicate check

2015-06-24 Thread arei.gonglei
From: Gonglei arei.gong...@huawei.com

in_num = req-elem.in_num, and req-elem.in_num is
checked in line 489, so the check about in_num variable
is superflous, let's drop it.

Signed-off-by: Gonglei arei.gong...@huawei.com
---
 hw/block/virtio-blk.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index cd539aa..cce552a 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -499,8 +499,7 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, 
MultiReqBuffer *mrb)
 
 iov_discard_front(iov, out_num, sizeof(req-out));
 
-if (in_num  1 ||
-in_iov[in_num - 1].iov_len  sizeof(struct virtio_blk_inhdr)) {
+if (in_iov[in_num - 1].iov_len  sizeof(struct virtio_blk_inhdr)) {
 error_report(virtio-blk request inhdr too short);
 exit(1);
 }
-- 
1.7.12.4





Re: [Qemu-block] [PATCH v2 02/13] block: Introduce bdrv_lock and bdrv_unlock API

2015-06-24 Thread Paolo Bonzini


On 24/06/2015 04:47, Fam Zheng wrote:
  2. Is this about thread safety?  (No, it's about exclusive access to a
 BDS *within* the AioContext.)
 As it has to quiesce iothreads as well (for now it's even more urgent than
 exclusive access within the same AioContext), I'd rather take it as yes.

For now it's a no, because there are no races between threads: the
main thread has acquired the AioContext and cut away the iothread.

However, as we move towards fine-grained AioContext critical sections,
it will become a yes.

Paolo



Re: [Qemu-block] [PATCH] migration: flush the bdrv before stopping VM

2015-06-24 Thread Li, Liang Z
 Right now, we don't have an interface to detect that cases and
 got back to the iterative stage.
   
How about go back to the iterative stage when detect that the
pending_size is larger Than max_size, like this:
   
+/* do flush here is aimed to shorten the VM downtime,
+ * bdrv_flush_all is a time consuming operation
+ * when the guest has done some file writing */
+bdrv_flush_all();
+pending_size = qemu_savevm_state_pending(s-file,
 max_size);
+if (pending_size  pending_size = max_size) {
+qemu_mutex_unlock_iothread();
+continue;
+}
  ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
  if (ret = 0) {
  qemu_file_set_rate_limit(s-file,
INT64_MAX);
   
and this is quite simple.
   
Yes, but it is too simple. If you hold all the locks during
bdrv_flush_all(), your VM will effectively stop as soon as it
performs the next I/O access, so you don't win much. And you
still don't have a timeout for cases where the flush takes really long.
  
   This is probably better than what we had now (basically we are
 meassuring
   after bdrv_flush_all how much the amount of dirty memory has
   changed, and return to iterative stage if it took too much.  A
   timeout would be better anyways.  And an interface te start the
   synchronization sooner asynchronously would be also good.
  
   Notice that my understanding is that any proper fix for this is 2.4
 material.
  
   Then, how to deal with this issue in 2.3, leave it here? or make an
   incomplete fix like I do above?
 
  I think it is better to leave it here for 2.3. With a patch like this
  one, we improve in one load and we got worse in a different load
  (depens a lot in the ratio of dirtying memory vs disk).  I have no
  data which load is more common, so I prefer to be conservative so late
  in the cycle.  What do you think?
 
 I agree, it's too late in the release cycle for such a change.
 
 Kevin

Hi Juan  Kevin,

I have not found the related patches to fix the issue which lead to long VM 
downtime,  how is it going?

Liang



Re: [Qemu-block] [Qemu-devel] [PATCH] virito-blk: drop duplicate check

2015-06-24 Thread Fam Zheng
On Wed, 06/24 17:29, arei.gong...@huawei.com wrote:
 From: Gonglei arei.gong...@huawei.com
 
 in_num = req-elem.in_num, and req-elem.in_num is
 checked in line 489, so the check about in_num variable
 is superflous, let's drop it.
 
 Signed-off-by: Gonglei arei.gong...@huawei.com

Reviewed-by: Fam Zheng f...@redhat.com

 ---
  hw/block/virtio-blk.c | 3 +--
  1 file changed, 1 insertion(+), 2 deletions(-)
 
 diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
 index cd539aa..cce552a 100644
 --- a/hw/block/virtio-blk.c
 +++ b/hw/block/virtio-blk.c
 @@ -499,8 +499,7 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, 
 MultiReqBuffer *mrb)
  
  iov_discard_front(iov, out_num, sizeof(req-out));
  
 -if (in_num  1 ||
 -in_iov[in_num - 1].iov_len  sizeof(struct virtio_blk_inhdr)) {
 +if (in_iov[in_num - 1].iov_len  sizeof(struct virtio_blk_inhdr)) {
  error_report(virtio-blk request inhdr too short);
  exit(1);
  }
 -- 
 1.7.12.4
 
 
 



Re: [Qemu-block] [Qemu-devel] [PATCH COLO-Block v6 07/16] Add new block driver interface to connect/disconnect the remote target

2015-06-24 Thread Dr. David Alan Gilbert
* Wen Congyang (ghost...@gmail.com) wrote:
 At 2015/6/19 18:49, Stefan Hajnoczi Wrote:
 On Fri, Jun 19, 2015 at 08:54:56AM +0800, Wen Congyang wrote:
 On 06/19/2015 12:06 AM, Stefan Hajnoczi wrote:
 On Thu, Jun 18, 2015 at 10:36:39PM +0800, Wen Congyang wrote:
 At 2015/6/18 20:55, Stefan Hajnoczi Wrote:
 On Thu, Jun 18, 2015 at 04:49:12PM +0800, Wen Congyang wrote:
 +void bdrv_connect(BlockDriverState *bs, Error **errp)
 +{
 +BlockDriver *drv = bs-drv;
 +
 +if (drv  drv-bdrv_connect) {
 +drv-bdrv_connect(bs, errp);
 +} else if (bs-file) {
 +bdrv_connect(bs-file, errp);
 +} else {
 +error_setg(errp, this feature or command is not currently 
 supported);
 +}
 +}
 +
 +void bdrv_disconnect(BlockDriverState *bs)
 +{
 +BlockDriver *drv = bs-drv;
 +
 +if (drv  drv-bdrv_disconnect) {
 +drv-bdrv_disconnect(bs);
 +} else if (bs-file) {
 +bdrv_disconnect(bs-file);
 +}
 +}
 
 Please add doc comments describing the semantics of these commands.
 
 Where should it be documented? In the header file?
 
 block.h doesn't document prototypes in the header file, please document
 the function definition in block.c.  (QEMU is not consistent here, some
 places do it the other way around.)
 
 Why are these operations needed when there is already a bs-drv == NULL
 case which means the BDS is not ready for read/write?
 
 
 The purpos is that: don't connect to nbd server when opening a nbd client.
 connect/disconnect
 to nbd server when we need to do it.
 
 IIUC, if bs-drv is NULL, it means that the driver is ejected? Here,
 connect/disconnect
 means that connect/disconnect to remote target(The target may be in 
 another
 host).
 
 Connect/disconnect puts something on the QEMU command-line that isn't
 ready at startup time.
 
 How about using monitor commands to add objects when needed instead?
 
 That is cleaner because it doesn't introduce a new state (which is only
 implemented for nbd).
 
 
 The problem is that, nbd client is one child of quorum, and quorum must 
 have more
 than one child. The nbd server is not ready until colo is running.
 
 A monitor command to hot add/remove quorum children solves this problem
 and could also be used in other scenarios (e.g. user decides to take a
 quorum child offline).
 
 
 For replication case, we always do checkpoint again and again after
 migration. If the disk is not synced before migration, we will use disk 
 mirgation or mirror
 job to sync it.

Can you document the way that you use disk migration or mirror, together
with your COLO setup?   I think it would make it easier to understand this
restriction.
At the moment I don't understand how you can switch from doing a disk migration
into COLO mode - there seems to be a gap between the end of disk migration and 
the start
of COLO.

 So we cannot start block replication when migration is running. We need
 that nbd
 client is not ready when migration is running, and it is ready between
 migration ends
 and checkpoint begins. Using a monotir command add the nbd client will cause
 larger
 downtime. So if the nbd client has been added(only not connect to the nbd
 server),
 we can connect to nbd server automatically.

Without the disk migration or mirror, I can't see the need for the delayed 
bdrv_connect.
I can see that you want to do a disconnect at failover, however you need
to take care because if the network is broken at the point of failover
you need to make sure nothing blocks.

Dave

 
 Thanks
 Wen Congyang
--
Dr. David Alan Gilbert / dgilb...@redhat.com / Manchester, UK



Re: [Qemu-block] [Qemu-devel] [PATCH 0/9] block: add differential backup support

2015-06-24 Thread Stefan Hajnoczi
On Tue, Jun 23, 2015 at 01:00:44PM -0400, John Snow wrote:
 Stefan: I know you said this wasn't expressly requisite, but I was
 encouraged by Kevin to send it out as a usability patch for non-libvirt
 users, so here it is.
 
 Final Yeah sure or No, definitely not as 2.4 approaches?
 
 Also note that if the answer is Ehhh, maybe not now, nobody's REALLY
 asked for it we should still probably merge the first patch alone,
 before our API solidifies -- this will give us the breathing room to add
 these features in later if we want.

I'll take a look at the first patch.


pgpM1je6r0lNc.pgp
Description: PGP signature


Re: [Qemu-block] [PATCH 3/6] block: Extract blockdev part of qmp_drive_mirror

2015-06-24 Thread Fam Zheng
On Wed, 06/24 18:34, Max Reitz wrote:
 On 09.06.2015 04:13, Fam Zheng wrote:
 This is the part that will be reused by blockdev-mirror.
 
 Signed-off-by: Fam Zheng f...@redhat.com
 ---
   blockdev.c | 158 
  +++--
   1 file changed, 92 insertions(+), 66 deletions(-)
 
 diff --git a/blockdev.c b/blockdev.c
 index b573e56..c32a9a9 100644
 --- a/blockdev.c
 +++ b/blockdev.c
 @@ -2883,29 +2883,22 @@ out:
   #define DEFAULT_MIRROR_BUF_SIZE   (10  20)
 -void qmp_drive_mirror(const char *device, const char *target,
 -  bool has_format, const char *format,
 -  bool has_node_name, const char *node_name,
 -  bool has_replaces, const char *replaces,
 -  enum MirrorSyncMode sync,
 -  bool has_mode, enum NewImageMode mode,
 -  bool has_speed, int64_t speed,
 -  bool has_granularity, uint32_t granularity,
 -  bool has_buf_size, int64_t buf_size,
 -  bool has_on_source_error, BlockdevOnError 
 on_source_error,
 -  bool has_on_target_error, BlockdevOnError 
 on_target_error,
 -  Error **errp)
 +/* Parameter check and block job starting for mirroring.
 + * Caller should hold @device and @target's aio context (They must to be on 
 the
 + * same aio context). */
 +static void blockdev_mirror_common(BlockDriverState *bs,
 +   BlockDriverState *target,
 +   bool has_replaces, const char *replaces,
 +   enum MirrorSyncMode sync,
 +   bool has_speed, int64_t speed,
 +   bool has_granularity, uint32_t 
 granularity,
 +   bool has_buf_size, int64_t buf_size,
 +   bool has_on_source_error,
 +   BlockdevOnError on_source_error,
 +   bool has_on_target_error,
 +   BlockdevOnError on_target_error,
 +   Error **errp)
   {
 -BlockBackend *blk;
 -BlockDriverState *bs;
 -BlockDriverState *source, *target_bs;
 -AioContext *aio_context;
 -BlockDriver *drv = NULL;
 -Error *local_err = NULL;
 -QDict *options = NULL;
 -int flags;
 -int64_t size;
 -int ret;
   if (!has_speed) {
   speed = 0;
 @@ -2916,9 +2909,6 @@ void qmp_drive_mirror(const char *device, const char 
 *target,
   if (!has_on_target_error) {
   on_target_error = BLOCKDEV_ON_ERROR_REPORT;
   }
 -if (!has_mode) {
 -mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
 -}
   if (!has_granularity) {
   granularity = 0;
   }
 @@ -2936,35 +2926,73 @@ void qmp_drive_mirror(const char *device, const char 
 *target,
   return;
   }
 -blk = blk_by_name(device);
 -if (!blk) {
 -error_set(errp, QERR_DEVICE_NOT_FOUND, device);
 -return;
 -}
 -
 -aio_context = blk_get_aio_context(blk);
 -aio_context_acquire(aio_context);
 -
 -if (!blk_is_available(blk)) {
 -error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
 -goto out;
 -}
 -bs = blk_bs(blk);
 -
 -if (!has_format) {
 -format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : 
 bs-drv-format_name;
 -}
 -if (format) {
 -drv = bdrv_find_format(format);
 -if (!drv) {
 -error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
 -goto out;
 -}
 -}
 -
   if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
 +return;
 +}
 +
 +if (!bs-backing_hd  sync == MIRROR_SYNC_MODE_TOP) {
 +sync = MIRROR_SYNC_MODE_FULL;
 +}
 +
 +/* pass the node name to replace to mirror start since it's loose 
 coupling
 + * and will allow to check whether the node still exist at mirror 
 completion
 + */
 +mirror_start(bs, target,
 + has_replaces ? replaces : NULL,
 + speed, granularity, buf_size, sync,
 + on_source_error, on_target_error,
 + block_job_cb, bs, errp);
 +}
 +
 +void qmp_drive_mirror(const char *device, const char *target,
 +  bool has_format, const char *format,
 +  bool has_node_name, const char *node_name,
 +  bool has_replaces, const char *replaces,
 +  enum MirrorSyncMode sync,
 +  bool has_mode, enum NewImageMode mode,
 +  bool has_speed, int64_t speed,
 +  bool has_granularity, uint32_t granularity,
 +  bool has_buf_size, int64_t buf_size,
 +  bool has_on_source_error, BlockdevOnError 
 on_source_error,
 +  

Re: [Qemu-block] [PATCH] virito-blk: drop duplicate check

2015-06-24 Thread Stefan Hajnoczi
On Wed, Jun 24, 2015 at 05:29:24PM +0800, arei.gong...@huawei.com wrote:
 From: Gonglei arei.gong...@huawei.com
 
 in_num = req-elem.in_num, and req-elem.in_num is
 checked in line 489, so the check about in_num variable
 is superflous, let's drop it.
 
 Signed-off-by: Gonglei arei.gong...@huawei.com
 ---
  hw/block/virtio-blk.c | 3 +--
  1 file changed, 1 insertion(+), 2 deletions(-)

Thanks, applied to my block tree:
https://github.com/stefanha/qemu/commits/block

Stefan


pgpkE8d0idVhW.pgp
Description: PGP signature


[Qemu-block] [PATCH v2 2/6] block: Rename BLOCK_OP_TYPE_MIRROR to BLOCK_OP_TYPE_MIRROR_SOURCE

2015-06-24 Thread Fam Zheng
It's necessary to distinguish source and target before we can add
blockdev-mirror, because we would want a concrete type of operation to
check on target bs before starting.

Signed-off-by: Fam Zheng f...@redhat.com
Reviewed-by: Max Reitz mre...@redhat.com
---
 blockdev.c  | 2 +-
 hw/block/dataplane/virtio-blk.c | 2 +-
 include/block/block.h   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index 90f6e77..b573e56 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2962,7 +2962,7 @@ void qmp_drive_mirror(const char *device, const char 
*target,
 }
 }
 
-if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
+if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
 goto out;
 }
 
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 3db139b..dac37de 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -206,7 +206,7 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, 
VirtIOBlkConf *conf,
 blk_op_unblock(conf-conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, 
s-blocker);
 blk_op_unblock(conf-conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE,
s-blocker);
-blk_op_unblock(conf-conf.blk, BLOCK_OP_TYPE_MIRROR, s-blocker);
+blk_op_unblock(conf-conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s-blocker);
 blk_op_unblock(conf-conf.blk, BLOCK_OP_TYPE_STREAM, s-blocker);
 blk_op_unblock(conf-conf.blk, BLOCK_OP_TYPE_REPLACE, s-blocker);
 
diff --git a/include/block/block.h b/include/block/block.h
index f5c0a90..69cb676 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -155,7 +155,7 @@ typedef enum BlockOpType {
 BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
 BLOCK_OP_TYPE_INTERNAL_SNAPSHOT,
 BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE,
-BLOCK_OP_TYPE_MIRROR,
+BLOCK_OP_TYPE_MIRROR_SOURCE,
 BLOCK_OP_TYPE_RESIZE,
 BLOCK_OP_TYPE_STREAM,
 BLOCK_OP_TYPE_REPLACE,
-- 
2.4.3




[Qemu-block] [PATCH v2 5/6] qmp: Add blockdev-mirror command

2015-06-24 Thread Fam Zheng
This will start a mirror job from a named device to another named
device, its relation with drive-mirror is similar with blockdev-backup
to drive-backup.

In blockdev-mirror, the target node should be prepared by blockdev-add,
which will be responsible for assigning a name to the new node, so
'node-name' in drive-mirror is dropped.

Signed-off-by: Fam Zheng f...@redhat.com
---
 blockdev.c   | 61 
 qapi/block-core.json | 47 
 qmp-commands.hx  | 48 +
 3 files changed, 156 insertions(+)

diff --git a/blockdev.c b/blockdev.c
index de6383f..e0dba07 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2932,6 +2932,10 @@ static void blockdev_mirror_common(BlockDriverState *bs,
 if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) {
 return;
 }
+if (target-blk) {
+error_setg(errp, Cannot mirror to an attached block device);
+return;
+}
 
 if (!bs-backing_hd  sync == MIRROR_SYNC_MODE_TOP) {
 sync = MIRROR_SYNC_MODE_FULL;
@@ -3107,6 +3111,63 @@ out:
 aio_context_release(aio_context);
 }
 
+void qmp_blockdev_mirror(const char *device, const char *target,
+ bool has_replaces, const char *replaces,
+ MirrorSyncMode sync,
+ bool has_speed, int64_t speed,
+ bool has_granularity, uint32_t granularity,
+ bool has_buf_size, int64_t buf_size,
+ bool has_on_source_error,
+ BlockdevOnError on_source_error,
+ bool has_on_target_error,
+ BlockdevOnError on_target_error,
+ Error **errp)
+{
+BlockDriverState *bs;
+BlockBackend *blk;
+BlockDriverState *target_bs;
+AioContext *aio_context;
+Error *local_err = NULL;
+
+blk = blk_by_name(device);
+if (!blk) {
+error_setg(errp, Device '%s' not found, device);
+return;
+}
+bs = blk_bs(blk);
+
+if (!bs) {
+error_setg(errp, Device '%s' has no media, device);
+return;
+}
+
+target_bs = bdrv_lookup_bs(target, target, errp);
+if (!target_bs) {
+return;
+}
+
+aio_context = bdrv_get_aio_context(bs);
+aio_context_acquire(aio_context);
+
+bdrv_ref(target_bs);
+bdrv_set_aio_context(target_bs, aio_context);
+
+blockdev_mirror_common(bs, target_bs,
+   has_replaces, replaces, sync,
+   has_speed, speed,
+   has_granularity, granularity,
+   has_buf_size, buf_size,
+   has_on_source_error, on_source_error,
+   has_on_target_error, on_target_error,
+   local_err);
+if (local_err) {
+error_propagate(errp, local_err);
+bdrv_unref(target_bs);
+}
+
+aio_context_release(aio_context);
+}
+
 /* Get the block job for a given device name and acquire its AioContext */
 static BlockJob *find_block_job(const char *device, AioContext **aio_context,
 Error **errp)
diff --git a/qapi/block-core.json b/qapi/block-core.json
index b5c4559..fe440e1 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1059,6 +1059,53 @@
   'data': 'BlockDirtyBitmap' }
 
 ##
+# @blockdev-mirror
+#
+# Start mirroring a block device's writes to a new destination.
+#
+# @device: the name of the device whose writes should be mirrored.
+#
+# @target: the name of the device to mirror to.
+#
+# @replaces: #optional with sync=full graph node name to be replaced by the new
+#image when a whole image copy is done. This can be used to repair
+#broken Quorum files.
+#
+# @speed:  #optional the maximum speed, in bytes per second
+#
+# @sync: what parts of the disk image should be copied to the destination
+#(all the disk, only the sectors allocated in the topmost image, or
+#only new I/O).
+#
+# @granularity: #optional granularity of the dirty bitmap, default is 64K
+#   if the image format doesn't have clusters, 4K if the clusters
+#   are smaller than that, else the cluster size.  Must be a
+#   power of 2 between 512 and 64M
+#
+# @buf-size: #optional maximum amount of data in flight from source to
+#target
+#
+# @on-source-error: #optional the action to take on an error on the source,
+#   default 'report'.  'stop' and 'enospc' can only be used
+#   if the block device supports io-status (see BlockInfo).
+#
+# @on-target-error: #optional the action to take on an error on the target,
+#   default 'report' (no limitations, since this applies to
+#   a different block device than @device).
+#
+# Returns: nothing 

Re: [Qemu-block] [PATCH 3/6] block: Extract blockdev part of qmp_drive_mirror

2015-06-24 Thread Max Reitz

On 09.06.2015 04:13, Fam Zheng wrote:

This is the part that will be reused by blockdev-mirror.

Signed-off-by: Fam Zheng f...@redhat.com
---
  blockdev.c | 158 +++--
  1 file changed, 92 insertions(+), 66 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index b573e56..c32a9a9 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2883,29 +2883,22 @@ out:
  
  #define DEFAULT_MIRROR_BUF_SIZE   (10  20)
  
-void qmp_drive_mirror(const char *device, const char *target,

-  bool has_format, const char *format,
-  bool has_node_name, const char *node_name,
-  bool has_replaces, const char *replaces,
-  enum MirrorSyncMode sync,
-  bool has_mode, enum NewImageMode mode,
-  bool has_speed, int64_t speed,
-  bool has_granularity, uint32_t granularity,
-  bool has_buf_size, int64_t buf_size,
-  bool has_on_source_error, BlockdevOnError 
on_source_error,
-  bool has_on_target_error, BlockdevOnError 
on_target_error,
-  Error **errp)
+/* Parameter check and block job starting for mirroring.
+ * Caller should hold @device and @target's aio context (They must to be on the
+ * same aio context). */
+static void blockdev_mirror_common(BlockDriverState *bs,
+   BlockDriverState *target,
+   bool has_replaces, const char *replaces,
+   enum MirrorSyncMode sync,
+   bool has_speed, int64_t speed,
+   bool has_granularity, uint32_t granularity,
+   bool has_buf_size, int64_t buf_size,
+   bool has_on_source_error,
+   BlockdevOnError on_source_error,
+   bool has_on_target_error,
+   BlockdevOnError on_target_error,
+   Error **errp)
  {
-BlockBackend *blk;
-BlockDriverState *bs;
-BlockDriverState *source, *target_bs;
-AioContext *aio_context;
-BlockDriver *drv = NULL;
-Error *local_err = NULL;
-QDict *options = NULL;
-int flags;
-int64_t size;
-int ret;
  
  if (!has_speed) {

  speed = 0;
@@ -2916,9 +2909,6 @@ void qmp_drive_mirror(const char *device, const char 
*target,
  if (!has_on_target_error) {
  on_target_error = BLOCKDEV_ON_ERROR_REPORT;
  }
-if (!has_mode) {
-mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
-}
  if (!has_granularity) {
  granularity = 0;
  }
@@ -2936,35 +2926,73 @@ void qmp_drive_mirror(const char *device, const char 
*target,
  return;
  }
  
-blk = blk_by_name(device);

-if (!blk) {
-error_set(errp, QERR_DEVICE_NOT_FOUND, device);
-return;
-}
-
-aio_context = blk_get_aio_context(blk);
-aio_context_acquire(aio_context);
-
-if (!blk_is_available(blk)) {
-error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
-goto out;
-}
-bs = blk_bs(blk);
-
-if (!has_format) {
-format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs-drv-format_name;
-}
-if (format) {
-drv = bdrv_find_format(format);
-if (!drv) {
-error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
-goto out;
-}
-}
-
  if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
+return;
+}
+
+if (!bs-backing_hd  sync == MIRROR_SYNC_MODE_TOP) {
+sync = MIRROR_SYNC_MODE_FULL;
+}
+
+/* pass the node name to replace to mirror start since it's loose coupling
+ * and will allow to check whether the node still exist at mirror 
completion
+ */
+mirror_start(bs, target,
+ has_replaces ? replaces : NULL,
+ speed, granularity, buf_size, sync,
+ on_source_error, on_target_error,
+ block_job_cb, bs, errp);
+}
+
+void qmp_drive_mirror(const char *device, const char *target,
+  bool has_format, const char *format,
+  bool has_node_name, const char *node_name,
+  bool has_replaces, const char *replaces,
+  enum MirrorSyncMode sync,
+  bool has_mode, enum NewImageMode mode,
+  bool has_speed, int64_t speed,
+  bool has_granularity, uint32_t granularity,
+  bool has_buf_size, int64_t buf_size,
+  bool has_on_source_error, BlockdevOnError 
on_source_error,
+  bool has_on_target_error, BlockdevOnError 
on_target_error,
+  Error **errp)
+{
+BlockDriverState *bs;
+BlockBackend *blk;
+

Re: [Qemu-block] [PATCH 2/6] block: Rename BLOCK_OP_TYPE_MIRROR to BLOCK_OP_TYPE_MIRROR_SOURCE

2015-06-24 Thread Max Reitz

On 09.06.2015 04:13, Fam Zheng wrote:

It's necessary to distinguish source and target before we can add
blockdev-mirror, because we would want a concrete type of operation to
check on target bs before starting.

Signed-off-by: Fam Zheng f...@redhat.com
---
  blockdev.c  | 2 +-
  hw/block/dataplane/virtio-blk.c | 2 +-
  include/block/block.h   | 2 +-
  3 files changed, 3 insertions(+), 3 deletions(-)


Reviewed-by: Max Reitz mre...@redhat.com



Re: [Qemu-block] [PATCH 4/6] block: Add check on mirror target

2015-06-24 Thread Max Reitz

On 09.06.2015 04:13, Fam Zheng wrote:

Signed-off-by: Fam Zheng f...@redhat.com
---
  blockdev.c| 3 +++
  include/block/block.h | 1 +
  2 files changed, 4 insertions(+)


Reviewed-by: Max Reitz mre...@redhat.com



Re: [Qemu-block] [PATCH 1/6] block: Add blocker on mirror target

2015-06-24 Thread Max Reitz

On 09.06.2015 04:13, Fam Zheng wrote:

In block/backup.c, we already check and add blocker on the target bs,
which is necessary so that it won't be intervened with other operations.

In block/mirror.c we should also protect the mirror target bs, because it
could have a node-name (drive-mirror ... node-name=XXX), and on top of
that it's safe to add blockdev-mirror.

Signed-off-by: Fam Zheng f...@redhat.com
---
  block/mirror.c | 2 ++
  1 file changed, 2 insertions(+)

diff --git a/block/mirror.c b/block/mirror.c
index 33c640f..8b23ddd 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -360,6 +360,7 @@ static void mirror_exit(BlockJob *job, void *opaque)
  aio_context_release(replace_aio_context);
  }
  g_free(s-replaces);
+bdrv_op_unblock_all(s-target, s-common.blocker);
  bdrv_unref(s-target);
  block_job_completed(s-common, data-ret);
  g_free(data);
@@ -682,6 +683,7 @@ static void mirror_start_job(BlockDriverState *bs, 
BlockDriverState *target,
  return;
  }
  
+bdrv_op_block_all(target, s-common.blocker);

  s-replaces = g_strdup(replaces);
  s-on_source_error = on_source_error;
  s-on_target_error = on_target_error;


Do we need a bdrv_op_unblock_all() if bdrv_create_dirty_bitmap() fails?

Max



Re: [Qemu-block] [Qemu-devel] [PATCH] qcow2: Handle EAGAIN returned from update_refcount

2015-06-24 Thread Max Reitz

On 24.06.2015 07:05, Jindřich Makovička wrote:

Fixes a crash during image compression

Signed-off-by: Jindřich Makovička makov...@gmail.com
---
  block/qcow2-refcount.c | 22 --
  1 file changed, 12 insertions(+), 10 deletions(-)


Reviewed-by: Max Reitz mre...@redhat.com



Re: [Qemu-block] [PATCH 6/6] iotests: Add test cases for blockdev-mirror

2015-06-24 Thread Max Reitz

On 09.06.2015 04:13, Fam Zheng wrote:

Signed-off-by: Fam Zheng f...@redhat.com
---
  tests/qemu-iotests/041 | 100 +++--
  tests/qemu-iotests/041.out |   4 +-
  2 files changed, 80 insertions(+), 24 deletions(-)

diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index 59a8f73..922f53c 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -65,8 +65,23 @@ class ImageMirroringTestCase(iotests.QMPTestCase):
  event = self.wait_until_completed(drive=drive)
  self.assert_qmp(event, 'data/type', 'mirror')
  
+def blockdev_add(self, filename, drive_id=None, node_name=None):

+options = {'driver': iotests.imgfmt,
+   'file': {
+   'filename': filename,
+   'driver': 'file'}}
+if drive_id:
+options['id'] = drive_id
+if node_name:
+options['node-name'] = node_name
+result = self.vm.qmp('blockdev-add', options=options)
+self.assert_qmp(result, 'return', {})
+
  class TestSingleDrive(ImageMirroringTestCase):
  image_len = 1 * 1024 * 1024 # MB
+qmp_cmd = 'drive-mirror'
+qmp_target = target_img
+not_found_error = 'DeviceNotFound'
  
  def setUp(self):

  iotests.create_image(backing_img, self.image_len)
@@ -86,8 +101,8 @@ class TestSingleDrive(ImageMirroringTestCase):
  def test_complete(self):
  self.assert_no_active_block_jobs()
  
-result = self.vm.qmp('drive-mirror', device='drive0', sync='full',

- target=target_img)
+result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
+ target=self.qmp_target)
  self.assert_qmp(result, 'return', {})
  
  self.complete_and_wait()

@@ -100,8 +115,8 @@ class TestSingleDrive(ImageMirroringTestCase):
  def test_cancel(self):
  self.assert_no_active_block_jobs()
  
-result = self.vm.qmp('drive-mirror', device='drive0', sync='full',

- target=target_img)
+result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
+ target=self.qmp_target)
  self.assert_qmp(result, 'return', {})
  
  self.cancel_and_wait(force=True)

@@ -112,8 +127,8 @@ class TestSingleDrive(ImageMirroringTestCase):
  def test_cancel_after_ready(self):
  self.assert_no_active_block_jobs()
  
-result = self.vm.qmp('drive-mirror', device='drive0', sync='full',

- target=target_img)
+result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
+ target=self.qmp_target)
  self.assert_qmp(result, 'return', {})
  
  self.wait_ready_and_cancel()

@@ -126,8 +141,8 @@ class TestSingleDrive(ImageMirroringTestCase):
  def test_pause(self):
  self.assert_no_active_block_jobs()
  
-result = self.vm.qmp('drive-mirror', device='drive0', sync='full',

- target=target_img)
+result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
+ target=self.qmp_target)
  self.assert_qmp(result, 'return', {})
  
  result = self.vm.qmp('block-job-pause', device='drive0')

@@ -153,8 +168,8 @@ class TestSingleDrive(ImageMirroringTestCase):
  self.assert_no_active_block_jobs()
  
  # A small buffer is rounded up automatically

-result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
- buf_size=4096, target=target_img)
+result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
+ buf_size=4096, target=self.qmp_target)
  self.assert_qmp(result, 'return', {})
  
  self.complete_and_wait()

@@ -169,8 +184,8 @@ class TestSingleDrive(ImageMirroringTestCase):
  
  qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'

  % (self.image_len, self.image_len), target_img)
-result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
- buf_size=65536, mode='existing', 
target=target_img)
+result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
+ buf_size=65536, mode='existing', 
target=self.qmp_target)
  self.assert_qmp(result, 'return', {})
  
  self.complete_and_wait()

@@ -185,8 +200,8 @@ class TestSingleDrive(ImageMirroringTestCase):
  
  qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'

  % (self.image_len, backing_img), target_img)
-result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
- mode='existing', target=target_img)
+result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
+ 

Re: [Qemu-block] [PATCH v6 0/4] Clean unused entries in the qcow2 L2/refcount cache

2015-06-24 Thread Alberto Garcia
Ping...

https://lists.gnu.org/archive/html/qemu-devel/2015-06/msg01929.html

On Fri 05 Jun 2015 06:27:20 PM CEST, Alberto Garcia wrote:
 v6:
 - Update documentation to clarify what unused entries mean.

 v5: https://lists.gnu.org/archive/html/qemu-devel/2015-06/msg00573.html
 - Fix build in mingw.
 - Use getpagesize() instead of sysconf(_SC_PAGESIZE).
 - Clarify that 0 is the default value for 'cache-clean-interval', and that
   it disables the feature.
 - Add the patch that documents how to configure the cache to this
   series, expanded with the explanation of 'cache-clean-interval'.
   (previous version: 
 https://lists.gnu.org/archive/html/qemu-devel/2015-05/msg02253.html)

 v4: https://lists.gnu.org/archive/html/qemu-devel/2015-05/msg06120.html
 - Revert the 'cache-clean-interval' change. This should probably go
   into a new BlockDeviceInfoSpecific struct (along with other
   settings), but is out of the scope for this series.

 v3: https://lists.gnu.org/archive/html/qemu-devel/2015-05/msg05473.html
 - Add 'cache-clean-interval' field to ImageInfoSpecificQCow2.

 v2: https://lists.gnu.org/archive/html/qemu-devel/2015-05/msg05316.html
 - Clarify that the block-commit mentioned in the first patch refers to
   the HMP commit command.
 - Check the value of cache_clean_interval and cast it accordingly to
   prevent it from overflowing.

 v1: https://lists.gnu.org/archive/html/qemu-devel/2015-05/msg03510.html

 Regards,

 Berto

 Alberto Garcia (4):
   qcow2: mark the memory as no longer needed after qcow2_cache_empty()
   qcow2: add option to clean unused cache entries after some time
   docs: document how to configure the qcow2 L2/refcount caches
   qcow2: reorder fields in Qcow2CachedTable to reduce padding

  block/qcow2-cache.c  |  63 +++-
  block/qcow2.c|  64 
  block/qcow2.h|   4 ++
  docs/qcow2-cache.txt | 164 
 +++
  qapi/block-core.json |   7 ++-
  5 files changed, 300 insertions(+), 2 deletions(-)
  create mode 100644 docs/qcow2-cache.txt

 -- 
 2.1.4



[Qemu-block] [PATCH 0/2] block: vpc - prevent overflow

2015-06-24 Thread Jeff Cody
This series fixes a bug found by Richard Jones.

When we allocate the pagetable based on max_table_entries, we multiply
the max table entry value by 4 to accomodate a table of 32-bit integers.
However, max_table_entries is a uint32_t, and the VPC driver accepts
ranges for that entry over 0x4000.  So during this allocation:

s-pagetable = qemu_try_blockalign(bs-file, s-max_table_entries * 4);

The size arg overflows, allocating significantly less memory than
expected.

Since qemu_try_blockalign() size argument is size_t, cast the
multiplication correctly to prevent overflow.

The value of max_table_entries * 4 is used elsewhere in the code as
well, so store the correct value for use in all those cases.

Jeff Cody (2):
  block: vpc - prevent overflow if max_table_entries = 0x4000
  block: qemu-iotests - add check for multiplication overflow in vpc

 block/vpc.c   |  10 +++--
 tests/qemu-iotests/135|  54 ++
 tests/qemu-iotests/135.out|   5 +++
 tests/qemu-iotests/group  |   1 +
 tests/qemu-iotests/sample_images/afl5.img.bz2 | Bin 0 - 175 bytes
 5 files changed, 66 insertions(+), 4 deletions(-)
 create mode 100755 tests/qemu-iotests/135
 create mode 100644 tests/qemu-iotests/135.out
 create mode 100644 tests/qemu-iotests/sample_images/afl5.img.bz2

-- 
1.9.3




[Qemu-block] [PATCH 1/2] block: vpc - prevent overflow if max_table_entries = 0x40000000

2015-06-24 Thread Jeff Cody
When we allocate the pagetable based on max_table_entries, we multiply
the max table entry value by 4 to accomodate a table of 32-bit integers.
However, max_table_entries is a uint32_t, and the VPC driver accepts
ranges for that entry over 0x4000.  So during this allocation:

s-pagetable = qemu_try_blockalign(bs-file, s-max_table_entries * 4);

The size arg overflows, allocating significantly less memory than
expected.

Since qemu_try_blockalign() size argument is size_t, cast the
multiplication correctly to prevent overflow.

The value of max_table_entries * 4 is used elsewhere in the code as
well, so store the correct value for use in all those cases.

Reported-by: Richard W.M. Jones rjo...@redhat.com
Signed-off-by: Jeff Cody jc...@redhat.com
---
 block/vpc.c | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/block/vpc.c b/block/vpc.c
index 37572ba..4108b5e 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -168,6 +168,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, 
int flags,
 uint8_t buf[HEADER_SIZE];
 uint32_t checksum;
 uint64_t computed_size;
+uint64_t pagetable_size;
 int disk_type = VHD_DYNAMIC;
 int ret;
 
@@ -269,7 +270,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, 
int flags,
 goto fail;
 }
 
-s-pagetable = qemu_try_blockalign(bs-file, s-max_table_entries * 4);
+pagetable_size = (size_t) s-max_table_entries * 4;
+
+s-pagetable = qemu_try_blockalign(bs-file, pagetable_size);
 if (s-pagetable == NULL) {
 ret = -ENOMEM;
 goto fail;
@@ -277,14 +280,13 @@ static int vpc_open(BlockDriverState *bs, QDict *options, 
int flags,
 
 s-bat_offset = be64_to_cpu(dyndisk_header-table_offset);
 
-ret = bdrv_pread(bs-file, s-bat_offset, s-pagetable,
- s-max_table_entries * 4);
+ret = bdrv_pread(bs-file, s-bat_offset, s-pagetable, 
pagetable_size);
 if (ret  0) {
 goto fail;
 }
 
 s-free_data_block_offset =
-(s-bat_offset + (s-max_table_entries * 4) + 511)  ~511;
+(s-bat_offset + pagetable_size + 511)  ~511;
 
 for (i = 0; i  s-max_table_entries; i++) {
 be32_to_cpus(s-pagetable[i]);
-- 
1.9.3




[Qemu-block] [PATCH 2/2] block: qemu-iotests - add check for multiplication overflow in vpc

2015-06-24 Thread Jeff Cody
This checks that VPC is able to successfully fail (without segfault)
on an image file with a max_table_entries that exceeds 0x4000.

This table entry is within the valid range for VPC (although too large
for this sample image).

Signed-off-by: Jeff Cody jc...@redhat.com
---
 tests/qemu-iotests/135|  54 ++
 tests/qemu-iotests/135.out|   5 +++
 tests/qemu-iotests/group  |   1 +
 tests/qemu-iotests/sample_images/afl5.img.bz2 | Bin 0 - 175 bytes
 4 files changed, 60 insertions(+)
 create mode 100755 tests/qemu-iotests/135
 create mode 100644 tests/qemu-iotests/135.out
 create mode 100644 tests/qemu-iotests/sample_images/afl5.img.bz2

diff --git a/tests/qemu-iotests/135 b/tests/qemu-iotests/135
new file mode 100755
index 000..16bf736
--- /dev/null
+++ b/tests/qemu-iotests/135
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# Test VPC open of image with large Max Table Entries value.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+#
+
+# creator
+owner=jc...@redhat.com
+
+seq=`basename $0`
+echo QA output created by $seq
+
+here=`pwd`
+tmp=/tmp/$$
+status=1   # failure is the default!
+
+_cleanup()
+{
+_cleanup_test_img
+}
+trap _cleanup; exit \$status 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt vpc
+_supported_proto generic
+_supported_os Linux
+
+_use_sample_img afl5.img.bz2
+
+echo
+echo === Verify image open and failure 
+$QEMU_IMG info $TEST_IMG 21| _filter_testdir
+
+# success, all done
+echo *** done
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/135.out b/tests/qemu-iotests/135.out
new file mode 100644
index 000..70b305e
--- /dev/null
+++ b/tests/qemu-iotests/135.out
@@ -0,0 +1,5 @@
+QA output created by 135
+
+=== Verify image open and failure 
+qemu-img: Could not open 'TEST_DIR/afl5.img': block-vpc: 
free_data_block_offset points after the end of file. The image has been 
truncated.
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 4597fc1..557e2a2 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -132,3 +132,4 @@
 130 rw auto quick
 131 rw auto quick
 134 rw auto quick
+135 rw auto
diff --git a/tests/qemu-iotests/sample_images/afl5.img.bz2 
b/tests/qemu-iotests/sample_images/afl5.img.bz2
new file mode 100644
index 
..1614348865e5b2cfcb0340eab9474841717be2c5
GIT binary patch
literal 175
zcmV;g08sxzT4*^jL0KkKSqT!KVgLXwfB*jgAVdfNFaTf(B!Frw|3pDR00;sy03ZSY
z3IG5B1Sp^YbSh$=r=c!nr#TgFeYj0XnKQ9RLxB?V^M@KMwpn4hJ!zOVwhn^RnWP
zlo2h)1BC$~JU|O6a~hBm5oG|a2~t!d6NaCTwor7gVwVQS9W$Kd2?dJH~Ej+J=Q^
dtom#MI_=bg;S5HeF^MqnF64@Ep$|^KE!naKsf*a

literal 0
HcmV?d1

-- 
1.9.3