diff mbox

[v4,2/2] bitmaps.md: Convert to rST; move it into 'interop' dir

Message ID 1498661881-9605-3-git-send-email-kchamart@redhat.com
State New
Headers show

Commit Message

Kashyap Chamarthy June 28, 2017, 2:58 p.m. UTC
This is part of the on-going effort to convert QEMU upstream
documentation syntax to reStructuredText (rST).

The conversion to rST was done using:

    $ pandoc -f markdown -t rst bitmaps.md -o bitmaps.rst

Then, make a couple of small syntactical adjustments.  While at it,
reword a statement to avoid ambiguity.  Addressing the feedback from
this thread:

    https://lists.nongnu.org/archive/html/qemu-devel/2017-06/msg05428.html

Signed-off-by: Kashyap Chamarthy <kchamart@redhat.com>
---
* A Sphinx-rendered HTML version is here:
  https://kashyapc.fedorapeople.org/v4-QEMU-Docs/_build/html/docs/bitmaps.html

* The patch has "v4" subject prefix because I rolled this in along with
  the other document (live-block-operations.rst) that is in review,
  which is actually at v4.
---
 docs/devel/bitmaps.md    | 505 ------------------------------------------
 docs/interop/bitmaps.rst | 555 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 555 insertions(+), 505 deletions(-)
 delete mode 100644 docs/devel/bitmaps.md
 create mode 100644 docs/interop/bitmaps.rst

Comments

John Snow June 29, 2017, 9:20 p.m. UTC | #1
On 06/28/2017 10:58 AM, Kashyap Chamarthy wrote:
> This is part of the on-going effort to convert QEMU upstream
> documentation syntax to reStructuredText (rST).
> 
> The conversion to rST was done using:
> 
>     $ pandoc -f markdown -t rst bitmaps.md -o bitmaps.rst
> 
> Then, make a couple of small syntactical adjustments.  While at it,
> reword a statement to avoid ambiguity.  Addressing the feedback from
> this thread:
> 
>     https://lists.nongnu.org/archive/html/qemu-devel/2017-06/msg05428.html
> 
> Signed-off-by: Kashyap Chamarthy <kchamart@redhat.com>

Reviewed-by: John Snow <jsnow@redhat.com>

> ---
> * A Sphinx-rendered HTML version is here:
>   https://kashyapc.fedorapeople.org/v4-QEMU-Docs/_build/html/docs/bitmaps.html
> 
> * The patch has "v4" subject prefix because I rolled this in along with
>   the other document (live-block-operations.rst) that is in review,
>   which is actually at v4.
> ---
>  docs/devel/bitmaps.md    | 505 ------------------------------------------
>  docs/interop/bitmaps.rst | 555 +++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 555 insertions(+), 505 deletions(-)
>  delete mode 100644 docs/devel/bitmaps.md
>  create mode 100644 docs/interop/bitmaps.rst
> 
> diff --git a/docs/devel/bitmaps.md b/docs/devel/bitmaps.md
> deleted file mode 100644
> index a2e8d51..0000000
> --- a/docs/devel/bitmaps.md
> +++ /dev/null
> @@ -1,505 +0,0 @@
> -<!--
> -Copyright 2015 John Snow <jsnow@redhat.com> and Red Hat, Inc.
> -All rights reserved.
> -
> -This file is licensed via The FreeBSD Documentation License, the full text of
> -which is included at the end of this document.
> --->
> -
> -# Dirty Bitmaps and Incremental Backup
> -
> -* Dirty Bitmaps are objects that track which data needs to be backed up for the
> -  next incremental backup.
> -
> -* Dirty bitmaps can be created at any time and attached to any node
> -  (not just complete drives.)
> -
> -## Dirty Bitmap Names
> -
> -* A dirty bitmap's name is unique to the node, but bitmaps attached to different
> -  nodes can share the same name.
> -
> -* Dirty bitmaps created for internal use by QEMU may be anonymous and have no
> -  name, but any user-created bitmaps may not be. There can be any number of
> -  anonymous bitmaps per node.
> -
> -* The name of a user-created bitmap must not be empty ("").
> -
> -## Bitmap Modes
> -
> -* A Bitmap can be "frozen," which means that it is currently in-use by a backup
> -  operation and cannot be deleted, renamed, written to, reset,
> -  etc.
> -
> -* The normal operating mode for a bitmap is "active."
> -
> -## Basic QMP Usage
> -
> -### Supported Commands ###
> -
> -* block-dirty-bitmap-add
> -* block-dirty-bitmap-remove
> -* block-dirty-bitmap-clear
> -
> -### Creation
> -
> -* To create a new bitmap, enabled, on the drive with id=drive0:
> -
> -```json
> -{ "execute": "block-dirty-bitmap-add",
> -  "arguments": {
> -    "node": "drive0",
> -    "name": "bitmap0"
> -  }
> -}
> -```
> -
> -* This bitmap will have a default granularity that matches the cluster size of
> -  its associated drive, if available, clamped to between [4KiB, 64KiB].
> -  The current default for qcow2 is 64KiB.
> -
> -* To create a new bitmap that tracks changes in 32KiB segments:
> -
> -```json
> -{ "execute": "block-dirty-bitmap-add",
> -  "arguments": {
> -    "node": "drive0",
> -    "name": "bitmap0",
> -    "granularity": 32768
> -  }
> -}
> -```
> -
> -### Deletion
> -
> -* Bitmaps that are frozen cannot be deleted.
> -
> -* Deleting the bitmap does not impact any other bitmaps attached to the same
> -  node, nor does it affect any backups already created from this node.
> -
> -* Because bitmaps are only unique to the node to which they are attached,
> -  you must specify the node/drive name here, too.
> -
> -```json
> -{ "execute": "block-dirty-bitmap-remove",
> -  "arguments": {
> -    "node": "drive0",
> -    "name": "bitmap0"
> -  }
> -}
> -```
> -
> -### Resetting
> -
> -* Resetting a bitmap will clear all information it holds.
> -
> -* An incremental backup created from an empty bitmap will copy no data,
> -  as if nothing has changed.
> -
> -```json
> -{ "execute": "block-dirty-bitmap-clear",
> -  "arguments": {
> -    "node": "drive0",
> -    "name": "bitmap0"
> -  }
> -}
> -```
> -
> -## Transactions
> -
> -### Justification
> -
> -Bitmaps can be safely modified when the VM is paused or halted by using
> -the basic QMP commands. For instance, you might perform the following actions:
> -
> -1. Boot the VM in a paused state.
> -2. Create a full drive backup of drive0.
> -3. Create a new bitmap attached to drive0.
> -4. Resume execution of the VM.
> -5. Incremental backups are ready to be created.
> -
> -At this point, the bitmap and drive backup would be correctly in sync,
> -and incremental backups made from this point forward would be correctly aligned
> -to the full drive backup.
> -
> -This is not particularly useful if we decide we want to start incremental
> -backups after the VM has been running for a while, for which we will need to
> -perform actions such as the following:
> -
> -1. Boot the VM and begin execution.
> -2. Using a single transaction, perform the following operations:
> -    * Create bitmap0.
> -    * Create a full drive backup of drive0.
> -3. Incremental backups are now ready to be created.
> -
> -### Supported Bitmap Transactions
> -
> -* block-dirty-bitmap-add
> -* block-dirty-bitmap-clear
> -
> -The usages are identical to their respective QMP commands, but see below
> -for examples.
> -
> -### Example: New Incremental Backup
> -
> -As outlined in the justification, perhaps we want to create a new incremental
> -backup chain attached to a drive.
> -
> -```json
> -{ "execute": "transaction",
> -  "arguments": {
> -    "actions": [
> -      {"type": "block-dirty-bitmap-add",
> -       "data": {"node": "drive0", "name": "bitmap0"} },
> -      {"type": "drive-backup",
> -       "data": {"device": "drive0", "target": "/path/to/full_backup.img",
> -                "sync": "full", "format": "qcow2"} }
> -    ]
> -  }
> -}
> -```
> -
> -### Example: New Incremental Backup Anchor Point
> -
> -Maybe we just want to create a new full backup with an existing bitmap and
> -want to reset the bitmap to track the new chain.
> -
> -```json
> -{ "execute": "transaction",
> -  "arguments": {
> -    "actions": [
> -      {"type": "block-dirty-bitmap-clear",
> -       "data": {"node": "drive0", "name": "bitmap0"} },
> -      {"type": "drive-backup",
> -       "data": {"device": "drive0", "target": "/path/to/new_full_backup.img",
> -                "sync": "full", "format": "qcow2"} }
> -    ]
> -  }
> -}
> -```
> -
> -## Incremental Backups
> -
> -The star of the show.
> -
> -**Nota Bene!** Only incremental backups of entire drives are supported for now.
> -So despite the fact that you can attach a bitmap to any arbitrary node, they are
> -only currently useful when attached to the root node. This is because
> -drive-backup only supports drives/devices instead of arbitrary nodes.
> -
> -### Example: First Incremental Backup
> -
> -1. Create a full backup and sync it to the dirty bitmap, as in the transactional
> -examples above; or with the VM offline, manually create a full copy and then
> -create a new bitmap before the VM begins execution.
> -
> -    * Let's assume the full backup is named 'full_backup.img'.
> -    * Let's assume the bitmap you created is 'bitmap0' attached to 'drive0'.
> -
> -2. Create a destination image for the incremental backup that utilizes the
> -full backup as a backing image.
> -
> -    * Let's assume it is named 'incremental.0.img'.
> -
> -    ```sh
> -    # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
> -    ```
> -
> -3. Issue the incremental backup command:
> -
> -    ```json
> -    { "execute": "drive-backup",
> -      "arguments": {
> -        "device": "drive0",
> -        "bitmap": "bitmap0",
> -        "target": "incremental.0.img",
> -        "format": "qcow2",
> -        "sync": "incremental",
> -        "mode": "existing"
> -      }
> -    }
> -    ```
> -
> -### Example: Second Incremental Backup
> -
> -1. Create a new destination image for the incremental backup that points to the
> -   previous one, e.g.: 'incremental.1.img'
> -
> -    ```sh
> -    # qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2
> -    ```
> -
> -2. Issue a new incremental backup command. The only difference here is that we
> -   have changed the target image below.
> -
> -    ```json
> -    { "execute": "drive-backup",
> -      "arguments": {
> -        "device": "drive0",
> -        "bitmap": "bitmap0",
> -        "target": "incremental.1.img",
> -        "format": "qcow2",
> -        "sync": "incremental",
> -        "mode": "existing"
> -      }
> -    }
> -    ```
> -
> -## Errors
> -
> -* In the event of an error that occurs after a backup job is successfully
> -  launched, either by a direct QMP command or a QMP transaction, the user
> -  will receive a BLOCK_JOB_COMPLETE event with a failure message, accompanied
> -  by a BLOCK_JOB_ERROR event.
> -
> -* In the case of an event being cancelled, the user will receive a
> -  BLOCK_JOB_CANCELLED event instead of a pair of COMPLETE and ERROR events.
> -
> -* In either case, the incremental backup data contained within the bitmap is
> -  safely rolled back, and the data within the bitmap is not lost. The image
> -  file created for the failed attempt can be safely deleted.
> -
> -* Once the underlying problem is fixed (e.g. more storage space is freed up),
> -  you can simply retry the incremental backup command with the same bitmap.
> -
> -### Example
> -
> -1. Create a target image:
> -
> -    ```sh
> -    # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
> -    ```
> -
> -2. Attempt to create an incremental backup via QMP:
> -
> -    ```json
> -    { "execute": "drive-backup",
> -      "arguments": {
> -        "device": "drive0",
> -        "bitmap": "bitmap0",
> -        "target": "incremental.0.img",
> -        "format": "qcow2",
> -        "sync": "incremental",
> -        "mode": "existing"
> -      }
> -    }
> -    ```
> -
> -3. Receive an event notifying us of failure:
> -
> -    ```json
> -    { "timestamp": { "seconds": 1424709442, "microseconds": 844524 },
> -      "data": { "speed": 0, "offset": 0, "len": 67108864,
> -                "error": "No space left on device",
> -                "device": "drive1", "type": "backup" },
> -      "event": "BLOCK_JOB_COMPLETED" }
> -    ```
> -
> -4. Delete the failed incremental, and re-create the image.
> -
> -    ```sh
> -    # rm incremental.0.img
> -    # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
> -    ```
> -
> -5. Retry the command after fixing the underlying problem,
> -   such as freeing up space on the backup volume:
> -
> -    ```json
> -    { "execute": "drive-backup",
> -      "arguments": {
> -        "device": "drive0",
> -        "bitmap": "bitmap0",
> -        "target": "incremental.0.img",
> -        "format": "qcow2",
> -        "sync": "incremental",
> -        "mode": "existing"
> -      }
> -    }
> -    ```
> -
> -6. Receive confirmation that the job completed successfully:
> -
> -    ```json
> -    { "timestamp": { "seconds": 1424709668, "microseconds": 526525 },
> -      "data": { "device": "drive1", "type": "backup",
> -                "speed": 0, "len": 67108864, "offset": 67108864},
> -      "event": "BLOCK_JOB_COMPLETED" }
> -    ```
> -
> -### Partial Transactional Failures
> -
> -* Sometimes, a transaction will succeed in launching and return success,
> -  but then later the backup jobs themselves may fail. It is possible that
> -  a management application may have to deal with a partial backup failure
> -  after a successful transaction.
> -
> -* If multiple backup jobs are specified in a single transaction, when one of
> -  them fails, it will not interact with the other backup jobs in any way.
> -
> -* The job(s) that succeeded will clear the dirty bitmap associated with the
> -  operation, but the job(s) that failed will not. It is not "safe" to delete
> -  any incremental backups that were created successfully in this scenario,
> -  even though others failed.
> -
> -#### Example
> -
> -* QMP example highlighting two backup jobs:
> -
> -    ```json
> -    { "execute": "transaction",
> -      "arguments": {
> -        "actions": [
> -          { "type": "drive-backup",
> -            "data": { "device": "drive0", "bitmap": "bitmap0",
> -                      "format": "qcow2", "mode": "existing",
> -                      "sync": "incremental", "target": "d0-incr-1.qcow2" } },
> -          { "type": "drive-backup",
> -            "data": { "device": "drive1", "bitmap": "bitmap1",
> -                      "format": "qcow2", "mode": "existing",
> -                      "sync": "incremental", "target": "d1-incr-1.qcow2" } },
> -        ]
> -      }
> -    }
> -    ```
> -
> -* QMP example response, highlighting one success and one failure:
> -    * Acknowledgement that the Transaction was accepted and jobs were launched:
> -        ```json
> -        { "return": {} }
> -        ```
> -
> -    * Later, QEMU sends notice that the first job was completed:
> -        ```json
> -        { "timestamp": { "seconds": 1447192343, "microseconds": 615698 },
> -          "data": { "device": "drive0", "type": "backup",
> -                     "speed": 0, "len": 67108864, "offset": 67108864 },
> -          "event": "BLOCK_JOB_COMPLETED"
> -        }
> -        ```
> -
> -    * Later yet, QEMU sends notice that the second job has failed:
> -        ```json
> -        { "timestamp": { "seconds": 1447192399, "microseconds": 683015 },
> -          "data": { "device": "drive1", "action": "report",
> -                    "operation": "read" },
> -          "event": "BLOCK_JOB_ERROR" }
> -        ```
> -
> -        ```json
> -        { "timestamp": { "seconds": 1447192399, "microseconds": 685853 },
> -          "data": { "speed": 0, "offset": 0, "len": 67108864,
> -                    "error": "Input/output error",
> -                    "device": "drive1", "type": "backup" },
> -          "event": "BLOCK_JOB_COMPLETED" }
> -
> -* In the above example, "d0-incr-1.qcow2" is valid and must be kept,
> -  but "d1-incr-1.qcow2" is invalid and should be deleted. If a VM-wide
> -  incremental backup of all drives at a point-in-time is to be made,
> -  new backups for both drives will need to be made, taking into account
> -  that a new incremental backup for drive0 needs to be based on top of
> -  "d0-incr-1.qcow2."
> -
> -### Grouped Completion Mode
> -
> -* While jobs launched by transactions normally complete or fail on their own,
> -  it is possible to instruct them to complete or fail together as a group.
> -
> -* QMP transactions take an optional properties structure that can affect
> -  the semantics of the transaction.
> -
> -* The "completion-mode" transaction property can be either "individual"
> -  which is the default, legacy behavior described above, or "grouped,"
> -  a new behavior detailed below.
> -
> -* Delayed Completion: In grouped completion mode, no jobs will report
> -  success until all jobs are ready to report success.
> -
> -* Grouped failure: If any job fails in grouped completion mode, all remaining
> -  jobs will be cancelled. Any incremental backups will restore their dirty
> -  bitmap objects as if no backup command was ever issued.
> -
> -    * Regardless of if QEMU reports a particular incremental backup job as
> -      CANCELLED or as an ERROR, the in-memory bitmap will be restored.
> -
> -#### Example
> -
> -* Here's the same example scenario from above with the new property:
> -
> -    ```json
> -    { "execute": "transaction",
> -      "arguments": {
> -        "actions": [
> -          { "type": "drive-backup",
> -            "data": { "device": "drive0", "bitmap": "bitmap0",
> -                      "format": "qcow2", "mode": "existing",
> -                      "sync": "incremental", "target": "d0-incr-1.qcow2" } },
> -          { "type": "drive-backup",
> -            "data": { "device": "drive1", "bitmap": "bitmap1",
> -                      "format": "qcow2", "mode": "existing",
> -                      "sync": "incremental", "target": "d1-incr-1.qcow2" } },
> -        ],
> -        "properties": {
> -          "completion-mode": "grouped"
> -        }
> -      }
> -    }
> -    ```
> -
> -* QMP example response, highlighting a failure for drive2:
> -    * Acknowledgement that the Transaction was accepted and jobs were launched:
> -        ```json
> -        { "return": {} }
> -        ```
> -
> -    * Later, QEMU sends notice that the second job has errored out,
> -      but that the first job was also cancelled:
> -        ```json
> -        { "timestamp": { "seconds": 1447193702, "microseconds": 632377 },
> -          "data": { "device": "drive1", "action": "report",
> -                    "operation": "read" },
> -          "event": "BLOCK_JOB_ERROR" }
> -        ```
> -
> -        ```json
> -        { "timestamp": { "seconds": 1447193702, "microseconds": 640074 },
> -          "data": { "speed": 0, "offset": 0, "len": 67108864,
> -                    "error": "Input/output error",
> -                    "device": "drive1", "type": "backup" },
> -          "event": "BLOCK_JOB_COMPLETED" }
> -        ```
> -
> -        ```json
> -        { "timestamp": { "seconds": 1447193702, "microseconds": 640163 },
> -          "data": { "device": "drive0", "type": "backup", "speed": 0,
> -                    "len": 67108864, "offset": 16777216 },
> -          "event": "BLOCK_JOB_CANCELLED" }
> -        ```
> -
> -<!--
> -The FreeBSD Documentation License
> -
> -Redistribution and use in source (Markdown) and 'compiled' forms (SGML, HTML,
> -PDF, PostScript, RTF and so forth) with or without modification, are permitted
> -provided that the following conditions are met:
> -
> -Redistributions of source code (Markdown) must retain the above copyright
> -notice, this list of conditions and the following disclaimer of this file
> -unmodified.
> -
> -Redistributions in compiled form (transformed to other DTDs, converted to PDF,
> -PostScript, RTF and other formats) must reproduce the above copyright notice,
> -this list of conditions and the following disclaimer in the documentation and/or
> -other materials provided with the distribution.
> -
> -THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
> -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  PURPOSE ARE
> -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
> -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> -THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> --->
> diff --git a/docs/interop/bitmaps.rst b/docs/interop/bitmaps.rst
> new file mode 100644
> index 0000000..b154d54
> --- /dev/null
> +++ b/docs/interop/bitmaps.rst
> @@ -0,0 +1,555 @@
> +..
> +   Copyright 2015 John Snow <jsnow@redhat.com> and Red Hat, Inc.
> +   All rights reserved.
> +
> +   This file is licensed via The FreeBSD Documentation License, the full
> +   text of which is included at the end of this document.
> +
> +====================================
> +Dirty Bitmaps and Incremental Backup
> +====================================
> +
> +-  Dirty Bitmaps are objects that track which data needs to be backed up
> +   for the next incremental backup.
> +
> +-  Dirty bitmaps can be created at any time and attached to any node
> +   (not just complete drives.)
> +
> +.. contents::
> +
> +Dirty Bitmap Names
> +------------------
> +
> +-  A dirty bitmap's name is unique to the node, but bitmaps attached to
> +   different nodes can share the same name.
> +
> +-  Dirty bitmaps created for internal use by QEMU may be anonymous and
> +   have no name, but any user-created bitmaps may not be. There can be
> +   any number of anonymous bitmaps per node.
> +
> +-  The name of a user-created bitmap must not be empty ("").
> +
> +Bitmap Modes
> +------------
> +
> +-  A bitmap can be "frozen," which means that it is currently in-use by
> +   a backup operation and cannot be deleted, renamed, written to, reset,
> +   etc.
> +
> +-  The normal operating mode for a bitmap is "active."
> +
> +Basic QMP Usage
> +---------------
> +
> +Supported Commands
> +~~~~~~~~~~~~~~~~~~
> +
> +- ``block-dirty-bitmap-add``
> +- ``block-dirty-bitmap-remove``
> +- ``block-dirty-bitmap-clear``
> +
> +Creation
> +~~~~~~~~
> +
> +-  To create a new bitmap, enabled, on the drive with id=drive0:
> +
> +.. code:: json
> +
> +    { "execute": "block-dirty-bitmap-add",
> +      "arguments": {
> +        "node": "drive0",
> +        "name": "bitmap0"
> +      }
> +    }
> +
> +-  This bitmap will have a default granularity that matches the cluster
> +   size of its associated drive, if available, clamped to between [4KiB,
> +   64KiB]. The current default for qcow2 is 64KiB.
> +
> +-  To create a new bitmap that tracks changes in 32KiB segments:
> +
> +.. code:: json
> +
> +    { "execute": "block-dirty-bitmap-add",
> +      "arguments": {
> +        "node": "drive0",
> +        "name": "bitmap0",
> +        "granularity": 32768
> +      }
> +    }
> +
> +Deletion
> +~~~~~~~~
> +
> +-  Bitmaps that are frozen cannot be deleted.
> +
> +-  Deleting the bitmap does not impact any other bitmaps attached to the
> +   same node, nor does it affect any backups already created from this
> +   node.
> +
> +-  Because bitmaps are only unique to the node to which they are
> +   attached, you must specify the node/drive name here, too.
> +
> +.. code:: json
> +
> +    { "execute": "block-dirty-bitmap-remove",
> +      "arguments": {
> +        "node": "drive0",
> +        "name": "bitmap0"
> +      }
> +    }
> +
> +Resetting
> +~~~~~~~~~
> +
> +-  Resetting a bitmap will clear all information it holds.
> +
> +-  An incremental backup created from an empty bitmap will copy no data,
> +   as if nothing has changed.
> +
> +.. code:: json
> +
> +    { "execute": "block-dirty-bitmap-clear",
> +      "arguments": {
> +        "node": "drive0",
> +        "name": "bitmap0"
> +      }
> +    }
> +
> +Transactions
> +------------
> +
> +Justification
> +~~~~~~~~~~~~~
> +
> +Bitmaps can be safely modified when the VM is paused or halted by using
> +the basic QMP commands. For instance, you might perform the following
> +actions:
> +
> +1. Boot the VM in a paused state.
> +2. Create a full drive backup of drive0.
> +3. Create a new bitmap attached to drive0.
> +4. Resume execution of the VM.
> +5. Incremental backups are ready to be created.
> +
> +At this point, the bitmap and drive backup would be correctly in sync,
> +and incremental backups made from this point forward would be correctly
> +aligned to the full drive backup.
> +
> +This is not particularly useful if we decide we want to start
> +incremental backups after the VM has been running for a while, for which
> +we will need to perform actions such as the following:
> +
> +1. Boot the VM and begin execution.
> +2. Using a single transaction, perform the following operations:
> +
> +   -  Create ``bitmap0``.
> +   -  Create a full drive backup of ``drive0``.
> +
> +3. Incremental backups are now ready to be created.
> +
> +Supported Bitmap Transactions
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +-  ``block-dirty-bitmap-add``
> +-  ``block-dirty-bitmap-clear``
> +
> +The usages are identical to their respective QMP commands, but see below
> +for examples.
> +
> +Example: New Incremental Backup
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +As outlined in the justification, perhaps we want to create a new
> +incremental backup chain attached to a drive.
> +
> +.. code:: json
> +
> +    { "execute": "transaction",
> +      "arguments": {
> +        "actions": [
> +          {"type": "block-dirty-bitmap-add",
> +           "data": {"node": "drive0", "name": "bitmap0"} },
> +          {"type": "drive-backup",
> +           "data": {"device": "drive0", "target": "/path/to/full_backup.img",
> +                    "sync": "full", "format": "qcow2"} }
> +        ]
> +      }
> +    }
> +
> +Example: New Incremental Backup Anchor Point
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Maybe we just want to create a new full backup with an existing bitmap
> +and want to reset the bitmap to track the new chain.
> +
> +.. code:: json
> +
> +    { "execute": "transaction",
> +      "arguments": {
> +        "actions": [
> +          {"type": "block-dirty-bitmap-clear",
> +           "data": {"node": "drive0", "name": "bitmap0"} },
> +          {"type": "drive-backup",
> +           "data": {"device": "drive0", "target": "/path/to/new_full_backup.img",
> +                    "sync": "full", "format": "qcow2"} }
> +        ]
> +      }
> +    }
> +
> +Incremental Backups
> +-------------------
> +
> +The star of the show.
> +
> +**Nota Bene!** Only incremental backups of entire drives are supported
> +for now. So despite the fact that you can attach a bitmap to any
> +arbitrary node, they are only currently useful when attached to the root
> +node. This is because drive-backup only supports drives/devices instead
> +of arbitrary nodes.
> +
> +Example: First Incremental Backup
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +1. Create a full backup and sync it to the dirty bitmap, as in the
> +   transactional examples above; or with the VM offline, manually create
> +   a full copy and then create a new bitmap before the VM begins
> +   execution.
> +
> +   -  Let's assume the full backup is named ``full_backup.img``.
> +   -  Let's assume the bitmap you created is ``bitmap0`` attached to
> +      ``drive0``.
> +
> +2. Create a destination image for the incremental backup that utilizes
> +   the full backup as a backing image.
> +
> +   -  Let's assume the new incremental image is named
> +      ``incremental.0.img``.
> +
> +   .. code:: bash
> +
> +       $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
> +
> +3. Issue the incremental backup command:
> +
> +   .. code:: json
> +
> +       { "execute": "drive-backup",
> +         "arguments": {
> +           "device": "drive0",
> +           "bitmap": "bitmap0",
> +           "target": "incremental.0.img",
> +           "format": "qcow2",
> +           "sync": "incremental",
> +           "mode": "existing"
> +         }
> +       }
> +
> +Example: Second Incremental Backup
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +1. Create a new destination image for the incremental backup that points
> +   to the previous one, e.g.: ``incremental.1.img``
> +
> +   .. code:: bash
> +
> +       $ qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2
> +
> +2. Issue a new incremental backup command. The only difference here is
> +   that we have changed the target image below.
> +
> +   .. code:: json
> +
> +       { "execute": "drive-backup",
> +         "arguments": {
> +           "device": "drive0",
> +           "bitmap": "bitmap0",
> +           "target": "incremental.1.img",
> +           "format": "qcow2",
> +           "sync": "incremental",
> +           "mode": "existing"
> +         }
> +       }
> +
> +Errors
> +------
> +
> +-  In the event of an error that occurs after a backup job is
> +   successfully launched, either by a direct QMP command or a QMP
> +   transaction, the user will receive a ``BLOCK_JOB_COMPLETE`` event with
> +   a failure message, accompanied by a ``BLOCK_JOB_ERROR`` event.
> +
> +-  In the case of an event being cancelled, the user will receive a
> +   ``BLOCK_JOB_CANCELLED`` event instead of a pair of COMPLETE and ERROR
> +   events.
> +
> +-  In either case, the incremental backup data contained within the
> +   bitmap is safely rolled back, and the data within the bitmap is not
> +   lost. The image file created for the failed attempt can be safely
> +   deleted.
> +
> +-  Once the underlying problem is fixed (e.g. more storage space is
> +   freed up), you can simply retry the incremental backup command with
> +   the same bitmap.
> +
> +Example
> +~~~~~~~
> +
> +1. Create a target image:
> +
> +   .. code:: bash
> +
> +       $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
> +
> +2. Attempt to create an incremental backup via QMP:
> +
> +   .. code:: json
> +
> +       { "execute": "drive-backup",
> +         "arguments": {
> +           "device": "drive0",
> +           "bitmap": "bitmap0",
> +           "target": "incremental.0.img",
> +           "format": "qcow2",
> +           "sync": "incremental",
> +           "mode": "existing"
> +         }
> +       }
> +
> +3. Receive an event notifying us of failure:
> +
> +   .. code:: json
> +
> +       { "timestamp": { "seconds": 1424709442, "microseconds": 844524 },
> +         "data": { "speed": 0, "offset": 0, "len": 67108864,
> +                   "error": "No space left on device",
> +                   "device": "drive1", "type": "backup" },
> +         "event": "BLOCK_JOB_COMPLETED" }
> +
> +4. Delete the failed incremental, and re-create the image.
> +
> +   .. code:: bash
> +
> +       $ rm incremental.0.img
> +       $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
> +
> +5. Retry the command after fixing the underlying problem, such as
> +   freeing up space on the backup volume:
> +
> +   .. code:: json
> +
> +       { "execute": "drive-backup",
> +         "arguments": {
> +           "device": "drive0",
> +           "bitmap": "bitmap0",
> +           "target": "incremental.0.img",
> +           "format": "qcow2",
> +           "sync": "incremental",
> +           "mode": "existing"
> +         }
> +       }
> +
> +6. Receive confirmation that the job completed successfully:
> +
> +   .. code:: json
> +
> +       { "timestamp": { "seconds": 1424709668, "microseconds": 526525 },
> +         "data": { "device": "drive1", "type": "backup",
> +                   "speed": 0, "len": 67108864, "offset": 67108864},
> +         "event": "BLOCK_JOB_COMPLETED" }
> +
> +Partial Transactional Failures
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +-  Sometimes, a transaction will succeed in launching and return
> +   success, but then later the backup jobs themselves may fail. It is
> +   possible that a management application may have to deal with a
> +   partial backup failure after a successful transaction.
> +
> +-  If multiple backup jobs are specified in a single transaction, when
> +   one of them fails, it will not interact with the other backup jobs in
> +   any way.
> +
> +-  The job(s) that succeeded will clear the dirty bitmap associated with
> +   the operation, but the job(s) that failed will not. It is not "safe"
> +   to delete any incremental backups that were created successfully in
> +   this scenario, even though others failed.
> +
> +Example
> +^^^^^^^
> +
> +-  QMP example highlighting two backup jobs:
> +
> +   .. code:: json
> +
> +       { "execute": "transaction",
> +         "arguments": {
> +           "actions": [
> +             { "type": "drive-backup",
> +               "data": { "device": "drive0", "bitmap": "bitmap0",
> +                         "format": "qcow2", "mode": "existing",
> +                         "sync": "incremental", "target": "d0-incr-1.qcow2" } },
> +             { "type": "drive-backup",
> +               "data": { "device": "drive1", "bitmap": "bitmap1",
> +                         "format": "qcow2", "mode": "existing",
> +                         "sync": "incremental", "target": "d1-incr-1.qcow2" } },
> +           ]
> +         }
> +       }
> +
> +-  QMP example response, highlighting one success and one failure:
> +
> +   -  Acknowledgement that the Transaction was accepted and jobs were
> +      launched:
> +
> +      .. code:: json
> +
> +          { "return": {} }
> +
> +   -  Later, QEMU sends notice that the first job was completed:
> +
> +      .. code:: json
> +
> +          { "timestamp": { "seconds": 1447192343, "microseconds": 615698 },
> +            "data": { "device": "drive0", "type": "backup",
> +                       "speed": 0, "len": 67108864, "offset": 67108864 },
> +            "event": "BLOCK_JOB_COMPLETED"
> +          }
> +
> +   -  Later yet, QEMU sends notice that the second job has failed:
> +
> +      .. code:: json
> +
> +          { "timestamp": { "seconds": 1447192399, "microseconds": 683015 },
> +            "data": { "device": "drive1", "action": "report",
> +                      "operation": "read" },
> +            "event": "BLOCK_JOB_ERROR" }
> +
> +      .. code:: json
> +
> +          { "timestamp": { "seconds": 1447192399, "microseconds":
> +          685853 }, "data": { "speed": 0, "offset": 0, "len": 67108864,
> +          "error": "Input/output error", "device": "drive1", "type":
> +          "backup" }, "event": "BLOCK_JOB_COMPLETED" }
> +
> +-  In the above example, ``d0-incr-1.qcow2`` is valid and must be kept,
> +   but ``d1-incr-1.qcow2`` is invalid and should be deleted. If a VM-wide
> +   incremental backup of all drives at a point-in-time is to be made,
> +   new backups for both drives will need to be made, taking into account
> +   that a new incremental backup for drive0 needs to be based on top of
> +   ``d0-incr-1.qcow2``.
> +
> +Grouped Completion Mode
> +~~~~~~~~~~~~~~~~~~~~~~~
> +
> +-  While jobs launched by transactions normally complete or fail on
> +   their own, it is possible to instruct them to complete or fail
> +   together as a group.
> +
> +-  QMP transactions take an optional properties structure that can
> +   affect the semantics of the transaction.
> +
> +-  The "completion-mode" transaction property can be either "individual"
> +   which is the default, legacy behavior described above, or "grouped,"
> +   a new behavior detailed below.
> +
> +-  Delayed Completion: In grouped completion mode, no jobs will report
> +   success until all jobs are ready to report success.
> +
> +-  Grouped failure: If any job fails in grouped completion mode, all
> +   remaining jobs will be cancelled. Any incremental backups will
> +   restore their dirty bitmap objects as if no backup command was ever
> +   issued.
> +
> +   -  Regardless of if QEMU reports a particular incremental backup job
> +      as CANCELLED or as an ERROR, the in-memory bitmap will be
> +      restored.
> +
> +Example
> +^^^^^^^
> +
> +-  Here's the same example scenario from above with the new property:
> +
> +   .. code:: json
> +
> +       { "execute": "transaction",
> +         "arguments": {
> +           "actions": [
> +             { "type": "drive-backup",
> +               "data": { "device": "drive0", "bitmap": "bitmap0",
> +                         "format": "qcow2", "mode": "existing",
> +                         "sync": "incremental", "target": "d0-incr-1.qcow2" } },
> +             { "type": "drive-backup",
> +               "data": { "device": "drive1", "bitmap": "bitmap1",
> +                         "format": "qcow2", "mode": "existing",
> +                         "sync": "incremental", "target": "d1-incr-1.qcow2" } },
> +           ],
> +           "properties": {
> +             "completion-mode": "grouped"
> +           }
> +         }
> +       }
> +
> +-  QMP example response, highlighting a failure for ``drive2``:
> +
> +   -  Acknowledgement that the Transaction was accepted and jobs were
> +      launched:
> +
> +      .. code:: json
> +
> +          { "return": {} }
> +
> +   -  Later, QEMU sends notice that the second job has errored out, but
> +      that the first job was also cancelled:
> +
> +      .. code:: json
> +
> +          { "timestamp": { "seconds": 1447193702, "microseconds": 632377 },
> +            "data": { "device": "drive1", "action": "report",
> +                      "operation": "read" },
> +            "event": "BLOCK_JOB_ERROR" }
> +
> +      .. code:: json
> +
> +          { "timestamp": { "seconds": 1447193702, "microseconds": 640074 },
> +            "data": { "speed": 0, "offset": 0, "len": 67108864,
> +                      "error": "Input/output error",
> +                      "device": "drive1", "type": "backup" },
> +            "event": "BLOCK_JOB_COMPLETED" }
> +
> +      .. code:: json
> +
> +          { "timestamp": { "seconds": 1447193702, "microseconds": 640163 },
> +            "data": { "device": "drive0", "type": "backup", "speed": 0,
> +                      "len": 67108864, "offset": 16777216 },
> +            "event": "BLOCK_JOB_CANCELLED" }
> +
> +.. raw:: html
> +
> +   <!--
> +   The FreeBSD Documentation License
> +
> +   Redistribution and use in source (Markdown) and 'compiled' forms (SGML, HTML,
> +   PDF, PostScript, RTF and so forth) with or without modification, are permitted
> +   provided that the following conditions are met:
> +
> +   Redistributions of source code (Markdown) must retain the above copyright
> +   notice, this list of conditions and the following disclaimer of this file
> +   unmodified.
> +
> +   Redistributions in compiled form (transformed to other DTDs, converted to PDF,
> +   PostScript, RTF and other formats) must reproduce the above copyright notice,
> +   this list of conditions and the following disclaimer in the documentation and/or
> +   other materials provided with the distribution.
> +
> +   THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
> +   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> +   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  PURPOSE ARE
> +   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
> +   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> +   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> +   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> +   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> +   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> +   THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> +   -->
>
diff mbox

Patch

diff --git a/docs/devel/bitmaps.md b/docs/devel/bitmaps.md
deleted file mode 100644
index a2e8d51..0000000
--- a/docs/devel/bitmaps.md
+++ /dev/null
@@ -1,505 +0,0 @@ 
-<!--
-Copyright 2015 John Snow <jsnow@redhat.com> and Red Hat, Inc.
-All rights reserved.
-
-This file is licensed via The FreeBSD Documentation License, the full text of
-which is included at the end of this document.
--->
-
-# Dirty Bitmaps and Incremental Backup
-
-* Dirty Bitmaps are objects that track which data needs to be backed up for the
-  next incremental backup.
-
-* Dirty bitmaps can be created at any time and attached to any node
-  (not just complete drives.)
-
-## Dirty Bitmap Names
-
-* A dirty bitmap's name is unique to the node, but bitmaps attached to different
-  nodes can share the same name.
-
-* Dirty bitmaps created for internal use by QEMU may be anonymous and have no
-  name, but any user-created bitmaps may not be. There can be any number of
-  anonymous bitmaps per node.
-
-* The name of a user-created bitmap must not be empty ("").
-
-## Bitmap Modes
-
-* A Bitmap can be "frozen," which means that it is currently in-use by a backup
-  operation and cannot be deleted, renamed, written to, reset,
-  etc.
-
-* The normal operating mode for a bitmap is "active."
-
-## Basic QMP Usage
-
-### Supported Commands ###
-
-* block-dirty-bitmap-add
-* block-dirty-bitmap-remove
-* block-dirty-bitmap-clear
-
-### Creation
-
-* To create a new bitmap, enabled, on the drive with id=drive0:
-
-```json
-{ "execute": "block-dirty-bitmap-add",
-  "arguments": {
-    "node": "drive0",
-    "name": "bitmap0"
-  }
-}
-```
-
-* This bitmap will have a default granularity that matches the cluster size of
-  its associated drive, if available, clamped to between [4KiB, 64KiB].
-  The current default for qcow2 is 64KiB.
-
-* To create a new bitmap that tracks changes in 32KiB segments:
-
-```json
-{ "execute": "block-dirty-bitmap-add",
-  "arguments": {
-    "node": "drive0",
-    "name": "bitmap0",
-    "granularity": 32768
-  }
-}
-```
-
-### Deletion
-
-* Bitmaps that are frozen cannot be deleted.
-
-* Deleting the bitmap does not impact any other bitmaps attached to the same
-  node, nor does it affect any backups already created from this node.
-
-* Because bitmaps are only unique to the node to which they are attached,
-  you must specify the node/drive name here, too.
-
-```json
-{ "execute": "block-dirty-bitmap-remove",
-  "arguments": {
-    "node": "drive0",
-    "name": "bitmap0"
-  }
-}
-```
-
-### Resetting
-
-* Resetting a bitmap will clear all information it holds.
-
-* An incremental backup created from an empty bitmap will copy no data,
-  as if nothing has changed.
-
-```json
-{ "execute": "block-dirty-bitmap-clear",
-  "arguments": {
-    "node": "drive0",
-    "name": "bitmap0"
-  }
-}
-```
-
-## Transactions
-
-### Justification
-
-Bitmaps can be safely modified when the VM is paused or halted by using
-the basic QMP commands. For instance, you might perform the following actions:
-
-1. Boot the VM in a paused state.
-2. Create a full drive backup of drive0.
-3. Create a new bitmap attached to drive0.
-4. Resume execution of the VM.
-5. Incremental backups are ready to be created.
-
-At this point, the bitmap and drive backup would be correctly in sync,
-and incremental backups made from this point forward would be correctly aligned
-to the full drive backup.
-
-This is not particularly useful if we decide we want to start incremental
-backups after the VM has been running for a while, for which we will need to
-perform actions such as the following:
-
-1. Boot the VM and begin execution.
-2. Using a single transaction, perform the following operations:
-    * Create bitmap0.
-    * Create a full drive backup of drive0.
-3. Incremental backups are now ready to be created.
-
-### Supported Bitmap Transactions
-
-* block-dirty-bitmap-add
-* block-dirty-bitmap-clear
-
-The usages are identical to their respective QMP commands, but see below
-for examples.
-
-### Example: New Incremental Backup
-
-As outlined in the justification, perhaps we want to create a new incremental
-backup chain attached to a drive.
-
-```json
-{ "execute": "transaction",
-  "arguments": {
-    "actions": [
-      {"type": "block-dirty-bitmap-add",
-       "data": {"node": "drive0", "name": "bitmap0"} },
-      {"type": "drive-backup",
-       "data": {"device": "drive0", "target": "/path/to/full_backup.img",
-                "sync": "full", "format": "qcow2"} }
-    ]
-  }
-}
-```
-
-### Example: New Incremental Backup Anchor Point
-
-Maybe we just want to create a new full backup with an existing bitmap and
-want to reset the bitmap to track the new chain.
-
-```json
-{ "execute": "transaction",
-  "arguments": {
-    "actions": [
-      {"type": "block-dirty-bitmap-clear",
-       "data": {"node": "drive0", "name": "bitmap0"} },
-      {"type": "drive-backup",
-       "data": {"device": "drive0", "target": "/path/to/new_full_backup.img",
-                "sync": "full", "format": "qcow2"} }
-    ]
-  }
-}
-```
-
-## Incremental Backups
-
-The star of the show.
-
-**Nota Bene!** Only incremental backups of entire drives are supported for now.
-So despite the fact that you can attach a bitmap to any arbitrary node, they are
-only currently useful when attached to the root node. This is because
-drive-backup only supports drives/devices instead of arbitrary nodes.
-
-### Example: First Incremental Backup
-
-1. Create a full backup and sync it to the dirty bitmap, as in the transactional
-examples above; or with the VM offline, manually create a full copy and then
-create a new bitmap before the VM begins execution.
-
-    * Let's assume the full backup is named 'full_backup.img'.
-    * Let's assume the bitmap you created is 'bitmap0' attached to 'drive0'.
-
-2. Create a destination image for the incremental backup that utilizes the
-full backup as a backing image.
-
-    * Let's assume it is named 'incremental.0.img'.
-
-    ```sh
-    # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
-    ```
-
-3. Issue the incremental backup command:
-
-    ```json
-    { "execute": "drive-backup",
-      "arguments": {
-        "device": "drive0",
-        "bitmap": "bitmap0",
-        "target": "incremental.0.img",
-        "format": "qcow2",
-        "sync": "incremental",
-        "mode": "existing"
-      }
-    }
-    ```
-
-### Example: Second Incremental Backup
-
-1. Create a new destination image for the incremental backup that points to the
-   previous one, e.g.: 'incremental.1.img'
-
-    ```sh
-    # qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2
-    ```
-
-2. Issue a new incremental backup command. The only difference here is that we
-   have changed the target image below.
-
-    ```json
-    { "execute": "drive-backup",
-      "arguments": {
-        "device": "drive0",
-        "bitmap": "bitmap0",
-        "target": "incremental.1.img",
-        "format": "qcow2",
-        "sync": "incremental",
-        "mode": "existing"
-      }
-    }
-    ```
-
-## Errors
-
-* In the event of an error that occurs after a backup job is successfully
-  launched, either by a direct QMP command or a QMP transaction, the user
-  will receive a BLOCK_JOB_COMPLETE event with a failure message, accompanied
-  by a BLOCK_JOB_ERROR event.
-
-* In the case of an event being cancelled, the user will receive a
-  BLOCK_JOB_CANCELLED event instead of a pair of COMPLETE and ERROR events.
-
-* In either case, the incremental backup data contained within the bitmap is
-  safely rolled back, and the data within the bitmap is not lost. The image
-  file created for the failed attempt can be safely deleted.
-
-* Once the underlying problem is fixed (e.g. more storage space is freed up),
-  you can simply retry the incremental backup command with the same bitmap.
-
-### Example
-
-1. Create a target image:
-
-    ```sh
-    # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
-    ```
-
-2. Attempt to create an incremental backup via QMP:
-
-    ```json
-    { "execute": "drive-backup",
-      "arguments": {
-        "device": "drive0",
-        "bitmap": "bitmap0",
-        "target": "incremental.0.img",
-        "format": "qcow2",
-        "sync": "incremental",
-        "mode": "existing"
-      }
-    }
-    ```
-
-3. Receive an event notifying us of failure:
-
-    ```json
-    { "timestamp": { "seconds": 1424709442, "microseconds": 844524 },
-      "data": { "speed": 0, "offset": 0, "len": 67108864,
-                "error": "No space left on device",
-                "device": "drive1", "type": "backup" },
-      "event": "BLOCK_JOB_COMPLETED" }
-    ```
-
-4. Delete the failed incremental, and re-create the image.
-
-    ```sh
-    # rm incremental.0.img
-    # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
-    ```
-
-5. Retry the command after fixing the underlying problem,
-   such as freeing up space on the backup volume:
-
-    ```json
-    { "execute": "drive-backup",
-      "arguments": {
-        "device": "drive0",
-        "bitmap": "bitmap0",
-        "target": "incremental.0.img",
-        "format": "qcow2",
-        "sync": "incremental",
-        "mode": "existing"
-      }
-    }
-    ```
-
-6. Receive confirmation that the job completed successfully:
-
-    ```json
-    { "timestamp": { "seconds": 1424709668, "microseconds": 526525 },
-      "data": { "device": "drive1", "type": "backup",
-                "speed": 0, "len": 67108864, "offset": 67108864},
-      "event": "BLOCK_JOB_COMPLETED" }
-    ```
-
-### Partial Transactional Failures
-
-* Sometimes, a transaction will succeed in launching and return success,
-  but then later the backup jobs themselves may fail. It is possible that
-  a management application may have to deal with a partial backup failure
-  after a successful transaction.
-
-* If multiple backup jobs are specified in a single transaction, when one of
-  them fails, it will not interact with the other backup jobs in any way.
-
-* The job(s) that succeeded will clear the dirty bitmap associated with the
-  operation, but the job(s) that failed will not. It is not "safe" to delete
-  any incremental backups that were created successfully in this scenario,
-  even though others failed.
-
-#### Example
-
-* QMP example highlighting two backup jobs:
-
-    ```json
-    { "execute": "transaction",
-      "arguments": {
-        "actions": [
-          { "type": "drive-backup",
-            "data": { "device": "drive0", "bitmap": "bitmap0",
-                      "format": "qcow2", "mode": "existing",
-                      "sync": "incremental", "target": "d0-incr-1.qcow2" } },
-          { "type": "drive-backup",
-            "data": { "device": "drive1", "bitmap": "bitmap1",
-                      "format": "qcow2", "mode": "existing",
-                      "sync": "incremental", "target": "d1-incr-1.qcow2" } },
-        ]
-      }
-    }
-    ```
-
-* QMP example response, highlighting one success and one failure:
-    * Acknowledgement that the Transaction was accepted and jobs were launched:
-        ```json
-        { "return": {} }
-        ```
-
-    * Later, QEMU sends notice that the first job was completed:
-        ```json
-        { "timestamp": { "seconds": 1447192343, "microseconds": 615698 },
-          "data": { "device": "drive0", "type": "backup",
-                     "speed": 0, "len": 67108864, "offset": 67108864 },
-          "event": "BLOCK_JOB_COMPLETED"
-        }
-        ```
-
-    * Later yet, QEMU sends notice that the second job has failed:
-        ```json
-        { "timestamp": { "seconds": 1447192399, "microseconds": 683015 },
-          "data": { "device": "drive1", "action": "report",
-                    "operation": "read" },
-          "event": "BLOCK_JOB_ERROR" }
-        ```
-
-        ```json
-        { "timestamp": { "seconds": 1447192399, "microseconds": 685853 },
-          "data": { "speed": 0, "offset": 0, "len": 67108864,
-                    "error": "Input/output error",
-                    "device": "drive1", "type": "backup" },
-          "event": "BLOCK_JOB_COMPLETED" }
-
-* In the above example, "d0-incr-1.qcow2" is valid and must be kept,
-  but "d1-incr-1.qcow2" is invalid and should be deleted. If a VM-wide
-  incremental backup of all drives at a point-in-time is to be made,
-  new backups for both drives will need to be made, taking into account
-  that a new incremental backup for drive0 needs to be based on top of
-  "d0-incr-1.qcow2."
-
-### Grouped Completion Mode
-
-* While jobs launched by transactions normally complete or fail on their own,
-  it is possible to instruct them to complete or fail together as a group.
-
-* QMP transactions take an optional properties structure that can affect
-  the semantics of the transaction.
-
-* The "completion-mode" transaction property can be either "individual"
-  which is the default, legacy behavior described above, or "grouped,"
-  a new behavior detailed below.
-
-* Delayed Completion: In grouped completion mode, no jobs will report
-  success until all jobs are ready to report success.
-
-* Grouped failure: If any job fails in grouped completion mode, all remaining
-  jobs will be cancelled. Any incremental backups will restore their dirty
-  bitmap objects as if no backup command was ever issued.
-
-    * Regardless of if QEMU reports a particular incremental backup job as
-      CANCELLED or as an ERROR, the in-memory bitmap will be restored.
-
-#### Example
-
-* Here's the same example scenario from above with the new property:
-
-    ```json
-    { "execute": "transaction",
-      "arguments": {
-        "actions": [
-          { "type": "drive-backup",
-            "data": { "device": "drive0", "bitmap": "bitmap0",
-                      "format": "qcow2", "mode": "existing",
-                      "sync": "incremental", "target": "d0-incr-1.qcow2" } },
-          { "type": "drive-backup",
-            "data": { "device": "drive1", "bitmap": "bitmap1",
-                      "format": "qcow2", "mode": "existing",
-                      "sync": "incremental", "target": "d1-incr-1.qcow2" } },
-        ],
-        "properties": {
-          "completion-mode": "grouped"
-        }
-      }
-    }
-    ```
-
-* QMP example response, highlighting a failure for drive2:
-    * Acknowledgement that the Transaction was accepted and jobs were launched:
-        ```json
-        { "return": {} }
-        ```
-
-    * Later, QEMU sends notice that the second job has errored out,
-      but that the first job was also cancelled:
-        ```json
-        { "timestamp": { "seconds": 1447193702, "microseconds": 632377 },
-          "data": { "device": "drive1", "action": "report",
-                    "operation": "read" },
-          "event": "BLOCK_JOB_ERROR" }
-        ```
-
-        ```json
-        { "timestamp": { "seconds": 1447193702, "microseconds": 640074 },
-          "data": { "speed": 0, "offset": 0, "len": 67108864,
-                    "error": "Input/output error",
-                    "device": "drive1", "type": "backup" },
-          "event": "BLOCK_JOB_COMPLETED" }
-        ```
-
-        ```json
-        { "timestamp": { "seconds": 1447193702, "microseconds": 640163 },
-          "data": { "device": "drive0", "type": "backup", "speed": 0,
-                    "len": 67108864, "offset": 16777216 },
-          "event": "BLOCK_JOB_CANCELLED" }
-        ```
-
-<!--
-The FreeBSD Documentation License
-
-Redistribution and use in source (Markdown) and 'compiled' forms (SGML, HTML,
-PDF, PostScript, RTF and so forth) with or without modification, are permitted
-provided that the following conditions are met:
-
-Redistributions of source code (Markdown) must retain the above copyright
-notice, this list of conditions and the following disclaimer of this file
-unmodified.
-
-Redistributions in compiled form (transformed to other DTDs, converted to PDF,
-PostScript, RTF and other formats) must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
-
-THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
diff --git a/docs/interop/bitmaps.rst b/docs/interop/bitmaps.rst
new file mode 100644
index 0000000..b154d54
--- /dev/null
+++ b/docs/interop/bitmaps.rst
@@ -0,0 +1,555 @@ 
+..
+   Copyright 2015 John Snow <jsnow@redhat.com> and Red Hat, Inc.
+   All rights reserved.
+
+   This file is licensed via The FreeBSD Documentation License, the full
+   text of which is included at the end of this document.
+
+====================================
+Dirty Bitmaps and Incremental Backup
+====================================
+
+-  Dirty Bitmaps are objects that track which data needs to be backed up
+   for the next incremental backup.
+
+-  Dirty bitmaps can be created at any time and attached to any node
+   (not just complete drives.)
+
+.. contents::
+
+Dirty Bitmap Names
+------------------
+
+-  A dirty bitmap's name is unique to the node, but bitmaps attached to
+   different nodes can share the same name.
+
+-  Dirty bitmaps created for internal use by QEMU may be anonymous and
+   have no name, but any user-created bitmaps may not be. There can be
+   any number of anonymous bitmaps per node.
+
+-  The name of a user-created bitmap must not be empty ("").
+
+Bitmap Modes
+------------
+
+-  A bitmap can be "frozen," which means that it is currently in-use by
+   a backup operation and cannot be deleted, renamed, written to, reset,
+   etc.
+
+-  The normal operating mode for a bitmap is "active."
+
+Basic QMP Usage
+---------------
+
+Supported Commands
+~~~~~~~~~~~~~~~~~~
+
+- ``block-dirty-bitmap-add``
+- ``block-dirty-bitmap-remove``
+- ``block-dirty-bitmap-clear``
+
+Creation
+~~~~~~~~
+
+-  To create a new bitmap, enabled, on the drive with id=drive0:
+
+.. code:: json
+
+    { "execute": "block-dirty-bitmap-add",
+      "arguments": {
+        "node": "drive0",
+        "name": "bitmap0"
+      }
+    }
+
+-  This bitmap will have a default granularity that matches the cluster
+   size of its associated drive, if available, clamped to between [4KiB,
+   64KiB]. The current default for qcow2 is 64KiB.
+
+-  To create a new bitmap that tracks changes in 32KiB segments:
+
+.. code:: json
+
+    { "execute": "block-dirty-bitmap-add",
+      "arguments": {
+        "node": "drive0",
+        "name": "bitmap0",
+        "granularity": 32768
+      }
+    }
+
+Deletion
+~~~~~~~~
+
+-  Bitmaps that are frozen cannot be deleted.
+
+-  Deleting the bitmap does not impact any other bitmaps attached to the
+   same node, nor does it affect any backups already created from this
+   node.
+
+-  Because bitmaps are only unique to the node to which they are
+   attached, you must specify the node/drive name here, too.
+
+.. code:: json
+
+    { "execute": "block-dirty-bitmap-remove",
+      "arguments": {
+        "node": "drive0",
+        "name": "bitmap0"
+      }
+    }
+
+Resetting
+~~~~~~~~~
+
+-  Resetting a bitmap will clear all information it holds.
+
+-  An incremental backup created from an empty bitmap will copy no data,
+   as if nothing has changed.
+
+.. code:: json
+
+    { "execute": "block-dirty-bitmap-clear",
+      "arguments": {
+        "node": "drive0",
+        "name": "bitmap0"
+      }
+    }
+
+Transactions
+------------
+
+Justification
+~~~~~~~~~~~~~
+
+Bitmaps can be safely modified when the VM is paused or halted by using
+the basic QMP commands. For instance, you might perform the following
+actions:
+
+1. Boot the VM in a paused state.
+2. Create a full drive backup of drive0.
+3. Create a new bitmap attached to drive0.
+4. Resume execution of the VM.
+5. Incremental backups are ready to be created.
+
+At this point, the bitmap and drive backup would be correctly in sync,
+and incremental backups made from this point forward would be correctly
+aligned to the full drive backup.
+
+This is not particularly useful if we decide we want to start
+incremental backups after the VM has been running for a while, for which
+we will need to perform actions such as the following:
+
+1. Boot the VM and begin execution.
+2. Using a single transaction, perform the following operations:
+
+   -  Create ``bitmap0``.
+   -  Create a full drive backup of ``drive0``.
+
+3. Incremental backups are now ready to be created.
+
+Supported Bitmap Transactions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+-  ``block-dirty-bitmap-add``
+-  ``block-dirty-bitmap-clear``
+
+The usages are identical to their respective QMP commands, but see below
+for examples.
+
+Example: New Incremental Backup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As outlined in the justification, perhaps we want to create a new
+incremental backup chain attached to a drive.
+
+.. code:: json
+
+    { "execute": "transaction",
+      "arguments": {
+        "actions": [
+          {"type": "block-dirty-bitmap-add",
+           "data": {"node": "drive0", "name": "bitmap0"} },
+          {"type": "drive-backup",
+           "data": {"device": "drive0", "target": "/path/to/full_backup.img",
+                    "sync": "full", "format": "qcow2"} }
+        ]
+      }
+    }
+
+Example: New Incremental Backup Anchor Point
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Maybe we just want to create a new full backup with an existing bitmap
+and want to reset the bitmap to track the new chain.
+
+.. code:: json
+
+    { "execute": "transaction",
+      "arguments": {
+        "actions": [
+          {"type": "block-dirty-bitmap-clear",
+           "data": {"node": "drive0", "name": "bitmap0"} },
+          {"type": "drive-backup",
+           "data": {"device": "drive0", "target": "/path/to/new_full_backup.img",
+                    "sync": "full", "format": "qcow2"} }
+        ]
+      }
+    }
+
+Incremental Backups
+-------------------
+
+The star of the show.
+
+**Nota Bene!** Only incremental backups of entire drives are supported
+for now. So despite the fact that you can attach a bitmap to any
+arbitrary node, they are only currently useful when attached to the root
+node. This is because drive-backup only supports drives/devices instead
+of arbitrary nodes.
+
+Example: First Incremental Backup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Create a full backup and sync it to the dirty bitmap, as in the
+   transactional examples above; or with the VM offline, manually create
+   a full copy and then create a new bitmap before the VM begins
+   execution.
+
+   -  Let's assume the full backup is named ``full_backup.img``.
+   -  Let's assume the bitmap you created is ``bitmap0`` attached to
+      ``drive0``.
+
+2. Create a destination image for the incremental backup that utilizes
+   the full backup as a backing image.
+
+   -  Let's assume the new incremental image is named
+      ``incremental.0.img``.
+
+   .. code:: bash
+
+       $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
+
+3. Issue the incremental backup command:
+
+   .. code:: json
+
+       { "execute": "drive-backup",
+         "arguments": {
+           "device": "drive0",
+           "bitmap": "bitmap0",
+           "target": "incremental.0.img",
+           "format": "qcow2",
+           "sync": "incremental",
+           "mode": "existing"
+         }
+       }
+
+Example: Second Incremental Backup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Create a new destination image for the incremental backup that points
+   to the previous one, e.g.: ``incremental.1.img``
+
+   .. code:: bash
+
+       $ qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2
+
+2. Issue a new incremental backup command. The only difference here is
+   that we have changed the target image below.
+
+   .. code:: json
+
+       { "execute": "drive-backup",
+         "arguments": {
+           "device": "drive0",
+           "bitmap": "bitmap0",
+           "target": "incremental.1.img",
+           "format": "qcow2",
+           "sync": "incremental",
+           "mode": "existing"
+         }
+       }
+
+Errors
+------
+
+-  In the event of an error that occurs after a backup job is
+   successfully launched, either by a direct QMP command or a QMP
+   transaction, the user will receive a ``BLOCK_JOB_COMPLETE`` event with
+   a failure message, accompanied by a ``BLOCK_JOB_ERROR`` event.
+
+-  In the case of an event being cancelled, the user will receive a
+   ``BLOCK_JOB_CANCELLED`` event instead of a pair of COMPLETE and ERROR
+   events.
+
+-  In either case, the incremental backup data contained within the
+   bitmap is safely rolled back, and the data within the bitmap is not
+   lost. The image file created for the failed attempt can be safely
+   deleted.
+
+-  Once the underlying problem is fixed (e.g. more storage space is
+   freed up), you can simply retry the incremental backup command with
+   the same bitmap.
+
+Example
+~~~~~~~
+
+1. Create a target image:
+
+   .. code:: bash
+
+       $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
+
+2. Attempt to create an incremental backup via QMP:
+
+   .. code:: json
+
+       { "execute": "drive-backup",
+         "arguments": {
+           "device": "drive0",
+           "bitmap": "bitmap0",
+           "target": "incremental.0.img",
+           "format": "qcow2",
+           "sync": "incremental",
+           "mode": "existing"
+         }
+       }
+
+3. Receive an event notifying us of failure:
+
+   .. code:: json
+
+       { "timestamp": { "seconds": 1424709442, "microseconds": 844524 },
+         "data": { "speed": 0, "offset": 0, "len": 67108864,
+                   "error": "No space left on device",
+                   "device": "drive1", "type": "backup" },
+         "event": "BLOCK_JOB_COMPLETED" }
+
+4. Delete the failed incremental, and re-create the image.
+
+   .. code:: bash
+
+       $ rm incremental.0.img
+       $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
+
+5. Retry the command after fixing the underlying problem, such as
+   freeing up space on the backup volume:
+
+   .. code:: json
+
+       { "execute": "drive-backup",
+         "arguments": {
+           "device": "drive0",
+           "bitmap": "bitmap0",
+           "target": "incremental.0.img",
+           "format": "qcow2",
+           "sync": "incremental",
+           "mode": "existing"
+         }
+       }
+
+6. Receive confirmation that the job completed successfully:
+
+   .. code:: json
+
+       { "timestamp": { "seconds": 1424709668, "microseconds": 526525 },
+         "data": { "device": "drive1", "type": "backup",
+                   "speed": 0, "len": 67108864, "offset": 67108864},
+         "event": "BLOCK_JOB_COMPLETED" }
+
+Partial Transactional Failures
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+-  Sometimes, a transaction will succeed in launching and return
+   success, but then later the backup jobs themselves may fail. It is
+   possible that a management application may have to deal with a
+   partial backup failure after a successful transaction.
+
+-  If multiple backup jobs are specified in a single transaction, when
+   one of them fails, it will not interact with the other backup jobs in
+   any way.
+
+-  The job(s) that succeeded will clear the dirty bitmap associated with
+   the operation, but the job(s) that failed will not. It is not "safe"
+   to delete any incremental backups that were created successfully in
+   this scenario, even though others failed.
+
+Example
+^^^^^^^
+
+-  QMP example highlighting two backup jobs:
+
+   .. code:: json
+
+       { "execute": "transaction",
+         "arguments": {
+           "actions": [
+             { "type": "drive-backup",
+               "data": { "device": "drive0", "bitmap": "bitmap0",
+                         "format": "qcow2", "mode": "existing",
+                         "sync": "incremental", "target": "d0-incr-1.qcow2" } },
+             { "type": "drive-backup",
+               "data": { "device": "drive1", "bitmap": "bitmap1",
+                         "format": "qcow2", "mode": "existing",
+                         "sync": "incremental", "target": "d1-incr-1.qcow2" } },
+           ]
+         }
+       }
+
+-  QMP example response, highlighting one success and one failure:
+
+   -  Acknowledgement that the Transaction was accepted and jobs were
+      launched:
+
+      .. code:: json
+
+          { "return": {} }
+
+   -  Later, QEMU sends notice that the first job was completed:
+
+      .. code:: json
+
+          { "timestamp": { "seconds": 1447192343, "microseconds": 615698 },
+            "data": { "device": "drive0", "type": "backup",
+                       "speed": 0, "len": 67108864, "offset": 67108864 },
+            "event": "BLOCK_JOB_COMPLETED"
+          }
+
+   -  Later yet, QEMU sends notice that the second job has failed:
+
+      .. code:: json
+
+          { "timestamp": { "seconds": 1447192399, "microseconds": 683015 },
+            "data": { "device": "drive1", "action": "report",
+                      "operation": "read" },
+            "event": "BLOCK_JOB_ERROR" }
+
+      .. code:: json
+
+          { "timestamp": { "seconds": 1447192399, "microseconds":
+          685853 }, "data": { "speed": 0, "offset": 0, "len": 67108864,
+          "error": "Input/output error", "device": "drive1", "type":
+          "backup" }, "event": "BLOCK_JOB_COMPLETED" }
+
+-  In the above example, ``d0-incr-1.qcow2`` is valid and must be kept,
+   but ``d1-incr-1.qcow2`` is invalid and should be deleted. If a VM-wide
+   incremental backup of all drives at a point-in-time is to be made,
+   new backups for both drives will need to be made, taking into account
+   that a new incremental backup for drive0 needs to be based on top of
+   ``d0-incr-1.qcow2``.
+
+Grouped Completion Mode
+~~~~~~~~~~~~~~~~~~~~~~~
+
+-  While jobs launched by transactions normally complete or fail on
+   their own, it is possible to instruct them to complete or fail
+   together as a group.
+
+-  QMP transactions take an optional properties structure that can
+   affect the semantics of the transaction.
+
+-  The "completion-mode" transaction property can be either "individual"
+   which is the default, legacy behavior described above, or "grouped,"
+   a new behavior detailed below.
+
+-  Delayed Completion: In grouped completion mode, no jobs will report
+   success until all jobs are ready to report success.
+
+-  Grouped failure: If any job fails in grouped completion mode, all
+   remaining jobs will be cancelled. Any incremental backups will
+   restore their dirty bitmap objects as if no backup command was ever
+   issued.
+
+   -  Regardless of if QEMU reports a particular incremental backup job
+      as CANCELLED or as an ERROR, the in-memory bitmap will be
+      restored.
+
+Example
+^^^^^^^
+
+-  Here's the same example scenario from above with the new property:
+
+   .. code:: json
+
+       { "execute": "transaction",
+         "arguments": {
+           "actions": [
+             { "type": "drive-backup",
+               "data": { "device": "drive0", "bitmap": "bitmap0",
+                         "format": "qcow2", "mode": "existing",
+                         "sync": "incremental", "target": "d0-incr-1.qcow2" } },
+             { "type": "drive-backup",
+               "data": { "device": "drive1", "bitmap": "bitmap1",
+                         "format": "qcow2", "mode": "existing",
+                         "sync": "incremental", "target": "d1-incr-1.qcow2" } },
+           ],
+           "properties": {
+             "completion-mode": "grouped"
+           }
+         }
+       }
+
+-  QMP example response, highlighting a failure for ``drive2``:
+
+   -  Acknowledgement that the Transaction was accepted and jobs were
+      launched:
+
+      .. code:: json
+
+          { "return": {} }
+
+   -  Later, QEMU sends notice that the second job has errored out, but
+      that the first job was also cancelled:
+
+      .. code:: json
+
+          { "timestamp": { "seconds": 1447193702, "microseconds": 632377 },
+            "data": { "device": "drive1", "action": "report",
+                      "operation": "read" },
+            "event": "BLOCK_JOB_ERROR" }
+
+      .. code:: json
+
+          { "timestamp": { "seconds": 1447193702, "microseconds": 640074 },
+            "data": { "speed": 0, "offset": 0, "len": 67108864,
+                      "error": "Input/output error",
+                      "device": "drive1", "type": "backup" },
+            "event": "BLOCK_JOB_COMPLETED" }
+
+      .. code:: json
+
+          { "timestamp": { "seconds": 1447193702, "microseconds": 640163 },
+            "data": { "device": "drive0", "type": "backup", "speed": 0,
+                      "len": 67108864, "offset": 16777216 },
+            "event": "BLOCK_JOB_CANCELLED" }
+
+.. raw:: html
+
+   <!--
+   The FreeBSD Documentation License
+
+   Redistribution and use in source (Markdown) and 'compiled' forms (SGML, HTML,
+   PDF, PostScript, RTF and so forth) with or without modification, are permitted
+   provided that the following conditions are met:
+
+   Redistributions of source code (Markdown) must retain the above copyright
+   notice, this list of conditions and the following disclaimer of this file
+   unmodified.
+
+   Redistributions in compiled form (transformed to other DTDs, converted to PDF,
+   PostScript, RTF and other formats) must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation and/or
+   other materials provided with the distribution.
+
+   THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
+   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+   THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+   -->