get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/813567/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 813567,
    "url": "http://patchwork.ozlabs.org/api/patches/813567/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20170913181910.29688-16-mreitz@redhat.com/",
    "project": {
        "id": 14,
        "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api",
        "name": "QEMU Development",
        "link_name": "qemu-devel",
        "list_id": "qemu-devel.nongnu.org",
        "list_email": "qemu-devel@nongnu.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20170913181910.29688-16-mreitz@redhat.com>",
    "list_archive_url": null,
    "date": "2017-09-13T18:19:07",
    "name": "[15/18] block/mirror: Add active mirroring",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "7c9757864da9ab4efd9eb17543a09148e345e314",
    "submitter": {
        "id": 36836,
        "url": "http://patchwork.ozlabs.org/api/people/36836/?format=api",
        "name": "Max Reitz",
        "email": "mreitz@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20170913181910.29688-16-mreitz@redhat.com/mbox/",
    "series": [
        {
            "id": 2960,
            "url": "http://patchwork.ozlabs.org/api/series/2960/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=2960",
            "date": "2017-09-13T18:18:52",
            "name": "block/mirror: Add active-sync mirroring",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/2960/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/813567/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/813567/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org",
        "Authentication-Results": [
            "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=nongnu.org\n\t(client-ip=2001:4830:134:3::11; helo=lists.gnu.org;\n\tenvelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n\treceiver=<UNKNOWN>)",
            "ext-mx06.extmail.prod.ext.phx2.redhat.com;\n\tdmarc=none (p=none dis=none) header.from=redhat.com",
            "ext-mx06.extmail.prod.ext.phx2.redhat.com;\n\tspf=fail smtp.mailfrom=mreitz@redhat.com"
        ],
        "Received": [
            "from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11])\n\t(using TLSv1 with cipher AES256-SHA (256/256 bits))\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xsqvZ3FpVz9s3T\n\tfor <incoming@patchwork.ozlabs.org>;\n\tThu, 14 Sep 2017 04:29:46 +1000 (AEST)",
            "from localhost ([::1]:44038 helo=lists.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.71) (envelope-from\n\t<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>)\n\tid 1dsCPw-0007ql-Ci\n\tfor incoming@patchwork.ozlabs.org; Wed, 13 Sep 2017 14:29:44 -0400",
            "from eggs.gnu.org ([2001:4830:134:3::10]:37787)\n\tby lists.gnu.org with esmtp (Exim 4.71)\n\t(envelope-from <mreitz@redhat.com>) id 1dsCIT-00019T-L2\n\tfor qemu-devel@nongnu.org; Wed, 13 Sep 2017 14:22:03 -0400",
            "from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71)\n\t(envelope-from <mreitz@redhat.com>) id 1dsCIR-0006aX-HP\n\tfor qemu-devel@nongnu.org; Wed, 13 Sep 2017 14:22:01 -0400",
            "from mx1.redhat.com ([209.132.183.28]:47016)\n\tby eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32)\n\t(Exim 4.71) (envelope-from <mreitz@redhat.com>)\n\tid 1dsCIK-0006W6-DT; Wed, 13 Sep 2017 14:21:52 -0400",
            "from smtp.corp.redhat.com\n\t(int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11])\n\t(using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))\n\t(No client certificate requested)\n\tby mx1.redhat.com (Postfix) with ESMTPS id 8764F356F4;\n\tWed, 13 Sep 2017 18:21:51 +0000 (UTC)",
            "from localhost (ovpn-204-23.brq.redhat.com [10.40.204.23])\n\tby smtp.corp.redhat.com (Postfix) with ESMTPS id A817769FBC;\n\tWed, 13 Sep 2017 18:21:41 +0000 (UTC)"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.3.2 mx1.redhat.com 8764F356F4",
        "From": "Max Reitz <mreitz@redhat.com>",
        "To": "qemu-block@nongnu.org",
        "Date": "Wed, 13 Sep 2017 20:19:07 +0200",
        "Message-Id": "<20170913181910.29688-16-mreitz@redhat.com>",
        "In-Reply-To": "<20170913181910.29688-1-mreitz@redhat.com>",
        "References": "<20170913181910.29688-1-mreitz@redhat.com>",
        "X-Scanned-By": "MIMEDefang 2.79 on 10.5.11.11",
        "X-Greylist": "Sender IP whitelisted, not delayed by milter-greylist-4.5.16\n\t(mx1.redhat.com [10.5.110.30]);\n\tWed, 13 Sep 2017 18:21:51 +0000 (UTC)",
        "X-detected-operating-system": "by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic]\n\t[fuzzy]",
        "X-Received-From": "209.132.183.28",
        "Subject": "[Qemu-devel] [PATCH 15/18] block/mirror: Add active mirroring",
        "X-BeenThere": "qemu-devel@nongnu.org",
        "X-Mailman-Version": "2.1.21",
        "Precedence": "list",
        "List-Id": "<qemu-devel.nongnu.org>",
        "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n\t<mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>",
        "List-Archive": "<http://lists.nongnu.org/archive/html/qemu-devel/>",
        "List-Post": "<mailto:qemu-devel@nongnu.org>",
        "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>",
        "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n\t<mailto:qemu-devel-request@nongnu.org?subject=subscribe>",
        "Cc": "Kevin Wolf <kwolf@redhat.com>, Fam Zheng <famz@redhat.com>,\n\tqemu-devel@nongnu.org, Max Reitz <mreitz@redhat.com>,\n\tStefan Hajnoczi <stefanha@redhat.com>, John Snow <jsnow@redhat.com>",
        "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org",
        "Sender": "\"Qemu-devel\"\n\t<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>"
    },
    "content": "This patch implements active synchronous mirroring.  In active mode, the\npassive mechanism will still be in place and is used to copy all\ninitially dirty clusters off the source disk; but every write request\nwill write data both to the source and the target disk, so the source\ncannot be dirtied faster than data is mirrored to the target.  Also,\nonce the block job has converged (BLOCK_JOB_READY sent), source and\ntarget are guaranteed to stay in sync (unless an error occurs).\n\nOptionally, dirty data can be copied to the target disk on read\noperations, too.\n\nActive mode is completely optional and currently disabled at runtime.  A\nlater patch will add a way for users to enable it.\n\nSigned-off-by: Max Reitz <mreitz@redhat.com>\n---\n qapi/block-core.json |  23 +++++++\n block/mirror.c       | 187 +++++++++++++++++++++++++++++++++++++++++++++++++--\n 2 files changed, 205 insertions(+), 5 deletions(-)",
    "diff": "diff --git a/qapi/block-core.json b/qapi/block-core.json\nindex bb11815608..e072cfa67c 100644\n--- a/qapi/block-core.json\n+++ b/qapi/block-core.json\n@@ -938,6 +938,29 @@\n   'data': ['top', 'full', 'none', 'incremental'] }\n \n ##\n+# @MirrorCopyMode:\n+#\n+# An enumeration whose values tell the mirror block job when to\n+# trigger writes to the target.\n+#\n+# @passive: copy data in background only.\n+#\n+# @active-write: when data is written to the source, write it\n+#                (synchronously) to the target as well.  In addition,\n+#                data is copied in background just like in @passive\n+#                mode.\n+#\n+# @active-read-write: write data to the target (synchronously) both\n+#                     when it is read from and written to the source.\n+#                     In addition, data is copied in background just\n+#                     like in @passive mode.\n+#\n+# Since: 2.11\n+##\n+{ 'enum': 'MirrorCopyMode',\n+  'data': ['passive', 'active-write', 'active-read-write'] }\n+\n+##\n # @BlockJobType:\n #\n # Type of a block job.\ndiff --git a/block/mirror.c b/block/mirror.c\nindex 8fea619a68..c429aa77bb 100644\n--- a/block/mirror.c\n+++ b/block/mirror.c\n@@ -54,8 +54,12 @@ typedef struct MirrorBlockJob {\n     Error *replace_blocker;\n     bool is_none_mode;\n     BlockMirrorBackingMode backing_mode;\n+    MirrorCopyMode copy_mode;\n     BlockdevOnError on_source_error, on_target_error;\n     bool synced;\n+    /* Set when the target is synced (dirty bitmap is clean, nothing\n+     * in flight) and the job is running in active mode */\n+    bool actively_synced;\n     bool should_complete;\n     int64_t granularity;\n     size_t buf_size;\n@@ -77,6 +81,7 @@ typedef struct MirrorBlockJob {\n     int target_cluster_size;\n     int max_iov;\n     bool initial_zeroing_ongoing;\n+    int in_active_write_counter;\n \n     /* Signals that we are no longer accessing source and target and the mirror\n      * BDS should thus relinquish all permissions */\n@@ -112,6 +117,7 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,\n                                             int error)\n {\n     s->synced = false;\n+    s->actively_synced = false;\n     if (read) {\n         return block_job_error_action(&s->common, s->on_source_error,\n                                       true, error);\n@@ -283,13 +289,12 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset,\n     return ret;\n }\n \n-static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)\n+static inline void mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)\n {\n     MirrorOp *op;\n \n     QTAILQ_FOREACH(op, &s->ops_in_flight, next) {\n-        if (!op->is_active_write) {\n-            /* Only non-active operations use up in-flight slots */\n+        if (op->is_active_write == active) {\n             qemu_co_queue_wait(&op->waiting_requests, NULL);\n             return;\n         }\n@@ -297,6 +302,12 @@ static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)\n     abort();\n }\n \n+static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)\n+{\n+    /* Only non-active operations use up in-flight slots */\n+    mirror_wait_for_any_operation(s, false);\n+}\n+\n /* Submit async read while handling COW.\n  * Returns: The number of bytes copied after and including offset,\n  *          excluding any bytes copied prior to offset due to alignment.\n@@ -861,6 +872,7 @@ static void coroutine_fn mirror_run(void *opaque)\n         /* Report BLOCK_JOB_READY and wait for complete. */\n         block_job_event_ready(&s->common);\n         s->synced = true;\n+        s->actively_synced = true;\n         while (!block_job_is_cancelled(&s->common) && !s->should_complete) {\n             block_job_yield(&s->common);\n         }\n@@ -912,6 +924,12 @@ static void coroutine_fn mirror_run(void *opaque)\n         int64_t cnt, delta;\n         bool should_complete;\n \n+        /* Do not start passive operations while there are active\n+         * writes in progress */\n+        while (s->in_active_write_counter) {\n+            mirror_wait_for_any_operation(s, true);\n+        }\n+\n         if (s->ret < 0) {\n             ret = s->ret;\n             goto immediate_exit;\n@@ -961,6 +979,9 @@ static void coroutine_fn mirror_run(void *opaque)\n                  */\n                 block_job_event_ready(&s->common);\n                 s->synced = true;\n+                if (s->copy_mode != MIRROR_COPY_MODE_PASSIVE) {\n+                    s->actively_synced = true;\n+                }\n             }\n \n             should_complete = s->should_complete ||\n@@ -1195,16 +1216,171 @@ static BdrvChildRole source_child_role = {\n     .drained_end        = source_child_cb_drained_end,\n };\n \n+static void do_sync_target_write(MirrorBlockJob *job, uint64_t offset,\n+                                 uint64_t bytes, QEMUIOVector *qiov, int flags)\n+{\n+    BdrvDirtyBitmapIter *iter;\n+    QEMUIOVector target_qiov;\n+    uint64_t dirty_offset;\n+    int dirty_bytes;\n+\n+    qemu_iovec_init(&target_qiov, qiov->niov);\n+\n+    iter = bdrv_dirty_iter_new(job->dirty_bitmap, offset >> BDRV_SECTOR_BITS);\n+\n+    while (true) {\n+        bool valid_area;\n+        int ret;\n+\n+        bdrv_dirty_bitmap_lock(job->dirty_bitmap);\n+        valid_area = bdrv_dirty_iter_next_area(iter, offset + bytes,\n+                                               &dirty_offset, &dirty_bytes);\n+        bdrv_dirty_bitmap_unlock(job->dirty_bitmap);\n+        if (!valid_area) {\n+            break;\n+        }\n+\n+        job->common.len += dirty_bytes;\n+\n+        assert(dirty_offset - offset <= SIZE_MAX);\n+        if (qiov) {\n+            qemu_iovec_reset(&target_qiov);\n+            qemu_iovec_concat(&target_qiov, qiov,\n+                              dirty_offset - offset, dirty_bytes);\n+        }\n+\n+        ret = blk_co_pwritev(job->target, dirty_offset, dirty_bytes,\n+                             qiov ? &target_qiov : NULL, flags);\n+        if (ret >= 0) {\n+            assert(dirty_offset % BDRV_SECTOR_SIZE == 0);\n+            assert(dirty_bytes % BDRV_SECTOR_SIZE == 0);\n+            bdrv_reset_dirty_bitmap(job->dirty_bitmap,\n+                                    dirty_offset >> BDRV_SECTOR_BITS,\n+                                    dirty_bytes >> BDRV_SECTOR_BITS);\n+\n+            job->common.offset += dirty_bytes;\n+        } else {\n+            BlockErrorAction action;\n+\n+            action = mirror_error_action(job, false, -ret);\n+            if (action == BLOCK_ERROR_ACTION_REPORT) {\n+                if (!job->ret) {\n+                    job->ret = ret;\n+                }\n+                break;\n+            }\n+        }\n+    }\n+\n+    bdrv_dirty_iter_free(iter);\n+    qemu_iovec_destroy(&target_qiov);\n+}\n+\n+static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s,\n+                                                   uint64_t offset,\n+                                                   uint64_t bytes)\n+{\n+    MirrorOp *op;\n+    uint64_t start_chunk = offset / s->granularity;\n+    uint64_t end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity);\n+\n+    op = g_new(MirrorOp, 1);\n+    *op = (MirrorOp){\n+        .s                  = s,\n+        .offset             = offset,\n+        .bytes              = bytes,\n+        .is_active_write    = true,\n+    };\n+    qemu_co_queue_init(&op->waiting_requests);\n+    QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next);\n+\n+    s->in_active_write_counter++;\n+\n+    mirror_wait_on_conflicts(op, s, offset, bytes);\n+\n+    bitmap_set(s->in_flight_bitmap, start_chunk, end_chunk - start_chunk);\n+\n+    return op;\n+}\n+\n+static void coroutine_fn active_write_settle(MirrorOp *op)\n+{\n+    uint64_t start_chunk = op->offset / op->s->granularity;\n+    uint64_t end_chunk = DIV_ROUND_UP(op->offset + op->bytes,\n+                                      op->s->granularity);\n+\n+    if (!--op->s->in_active_write_counter && op->s->actively_synced) {\n+        /* Assert that we are back in sync once all active write\n+         * operations are settled */\n+        assert(!bdrv_get_dirty_count(op->s->dirty_bitmap));\n+    }\n+    bitmap_clear(op->s->in_flight_bitmap, start_chunk, end_chunk - start_chunk);\n+    QTAILQ_REMOVE(&op->s->ops_in_flight, op, next);\n+    qemu_co_queue_restart_all(&op->waiting_requests);\n+    g_free(op);\n+}\n+\n static int coroutine_fn bdrv_mirror_top_preadv(BlockDriverState *bs,\n     uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)\n {\n-    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);\n+    MirrorOp *op = NULL;\n+    MirrorBDSOpaque *s = bs->opaque;\n+    int ret = 0;\n+    bool copy_to_target;\n+\n+    copy_to_target = s->job->ret >= 0 &&\n+                     s->job->copy_mode == MIRROR_COPY_MODE_ACTIVE_READ_WRITE;\n+\n+    if (copy_to_target) {\n+        op = active_write_prepare(s->job, offset, bytes);\n+    }\n+\n+    ret = bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);\n+    if (ret < 0) {\n+        goto out;\n+    }\n+\n+    if (copy_to_target) {\n+        do_sync_target_write(s->job, offset, bytes, qiov, 0);\n+    }\n+\n+out:\n+    if (copy_to_target) {\n+        active_write_settle(op);\n+    }\n+    return ret;\n }\n \n static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs,\n     uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)\n {\n-    return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);\n+    MirrorOp *op = NULL;\n+    MirrorBDSOpaque *s = bs->opaque;\n+    int ret = 0;\n+    bool copy_to_target;\n+\n+    copy_to_target = s->job->ret >= 0 &&\n+                     (s->job->copy_mode == MIRROR_COPY_MODE_ACTIVE_WRITE ||\n+                      s->job->copy_mode == MIRROR_COPY_MODE_ACTIVE_READ_WRITE);\n+\n+    if (copy_to_target) {\n+        op = active_write_prepare(s->job, offset, bytes);\n+    }\n+\n+    ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);\n+    if (ret < 0) {\n+        goto out;\n+    }\n+\n+    if (copy_to_target) {\n+        do_sync_target_write(s->job, offset, bytes, qiov, flags);\n+    }\n+\n+out:\n+    if (copy_to_target) {\n+        active_write_settle(op);\n+    }\n+    return ret;\n }\n \n static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs)\n@@ -1398,6 +1574,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,\n     s->on_target_error = on_target_error;\n     s->is_none_mode = is_none_mode;\n     s->backing_mode = backing_mode;\n+    s->copy_mode = MIRROR_COPY_MODE_PASSIVE;\n     s->base = base;\n     s->granularity = granularity;\n     s->buf_size = ROUND_UP(buf_size, granularity);\n",
    "prefixes": [
        "15/18"
    ]
}