123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629 |
- ..
- Copyright 2019 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 in-memory objects that track writes to block devices. They
- can be used in conjunction with various block job operations to perform
- incremental or differential backup regimens.
- This document explains the conceptual mechanisms, as well as up-to-date,
- complete and comprehensive documentation on the API to manipulate them.
- (Hopefully, the "why", "what", and "how".)
- The intended audience for this document is developers who are adding QEMU
- backup features to management applications, or power users who run and
- administer QEMU directly via QMP.
- .. contents::
- Overview
- --------
- Bitmaps are bit vectors where each '1' bit in the vector indicates a modified
- ("dirty") segment of the corresponding block device. The size of the segment
- that is tracked is the granularity of the bitmap. If the granularity of a
- bitmap is 64K, each '1' bit means that a 64K region as a whole may have
- changed in some way, possibly by as little as one byte.
- Smaller granularities mean more accurate tracking of modified disk data, but
- requires more computational overhead and larger bitmap sizes. Larger
- granularities mean smaller bitmap sizes, but less targeted backups.
- The size of a bitmap (in bytes) can be computed as such:
- ``size`` = ceil(ceil(``image_size`` / ``granularity``) / 8)
- e.g. the size of a 64KiB granularity bitmap on a 2TiB image is:
- ``size`` = ((2147483648K / 64K) / 8)
- = 4194304B = 4MiB.
- QEMU uses these bitmaps when making incremental backups to know which sections
- of the file to copy out. They are not enabled by default and must be
- explicitly added in order to begin tracking writes.
- Bitmaps can be created at any time and can be attached to any arbitrary block
- node in the storage graph, but are most useful conceptually when attached to
- the root node attached to the guest's storage device model.
- That is to say: It's likely most useful to track the guest's writes to disk,
- but you could theoretically track things like qcow2 metadata changes by
- attaching the bitmap elsewhere in the storage graph. This is beyond the scope
- of this document.
- QEMU supports persisting these bitmaps to disk via the qcow2 image format.
- Bitmaps which are stored or loaded in this way are called "persistent",
- whereas bitmaps that are not are called "transient".
- QEMU also supports the migration of both transient bitmaps (tracking any
- arbitrary image format) or persistent bitmaps (qcow2) via live migration.
- Supported Image Formats
- -----------------------
- QEMU supports all documented features below on the qcow2 image format.
- However, qcow2 is only strictly necessary for the persistence feature, which
- writes bitmap data to disk upon close. If persistence is not required for a
- specific use case, all bitmap features excepting persistence are available for
- any arbitrary image format.
- For example, Dirty Bitmaps can be combined with the 'raw' image format, but
- any changes to the bitmap will be discarded upon exit.
- .. warning:: Transient bitmaps will not be saved on QEMU exit! Persistent
- bitmaps are available only on qcow2 images.
- Dirty Bitmap Names
- ------------------
- Bitmap objects need a method to reference them in the API. All API-created and
- managed bitmaps have a human-readable name chosen by the user at creation
- time.
- - A bitmap's name is unique to the node, but bitmaps attached to different
- nodes can share the same name. Therefore, all bitmaps are addressed via
- their (node, name) pair.
- - The name of a user-created bitmap cannot be empty ("").
- - Transient bitmaps can have JSON unicode names that are effectively not
- length limited. (QMP protocol may restrict messages to less than 64MiB.)
- - Persistent storage formats may impose their own requirements on bitmap names
- and namespaces. Presently, only qcow2 supports persistent bitmaps. See
- docs/interop/qcow2.txt for more details on restrictions. Notably:
- - qcow2 bitmap names are limited to between 1 and 1023 bytes long.
- - No two bitmaps saved to the same qcow2 file may share the same name.
- - QEMU occasionally uses bitmaps for internal use which have no name. They are
- hidden from API query calls, cannot be manipulated by the external API, are
- never persistent, nor ever migrated.
- Bitmap Status
- -------------
- Dirty Bitmap objects can be queried with the QMP command `query-block
- <qemu-qmp-ref.html#index-query_002dblock>`_, and are visible via the
- `BlockDirtyInfo <qemu-qmp-ref.html#index-BlockDirtyInfo>`_ QAPI structure.
- This struct shows the name, granularity, and dirty byte count for each bitmap.
- Additionally, it shows several boolean status indicators:
- - ``recording``: This bitmap is recording writes.
- - ``busy``: This bitmap is in-use by an operation.
- - ``persistent``: This bitmap is a persistent type.
- - ``inconsistent``: This bitmap is corrupted and cannot be used.
- The ``+busy`` status prohibits you from deleting, clearing, or otherwise
- modifying a bitmap, and happens when the bitmap is being used for a backup
- operation or is in the process of being loaded from a migration. Many of the
- commands documented below will refuse to work on such bitmaps.
- The ``+inconsistent`` status similarly prohibits almost all operations,
- notably allowing only the ``block-dirty-bitmap-remove`` operation.
- There is also a deprecated ``status`` field of type `DirtyBitmapStatus
- <qemu-qmp-ref.html#index-DirtyBitmapStatus>`_. A bitmap historically had
- five visible states:
- #. ``Frozen``: This bitmap is currently in-use by an operation and is
- immutable. It can't be deleted, renamed, reset, etc.
- (This is now ``+busy``.)
- #. ``Disabled``: This bitmap is not recording new writes.
- (This is now ``-recording -busy``.)
- #. ``Active``: This bitmap is recording new writes.
- (This is now ``+recording -busy``.)
- #. ``Locked``: This bitmap is in-use by an operation, and is immutable.
- The difference from "Frozen" was primarily implementation details.
- (This is now ``+busy``.)
- #. ``Inconsistent``: This persistent bitmap was not saved to disk
- correctly, and can no longer be used. It remains in memory to serve as
- an indicator of failure.
- (This is now ``+inconsistent``.)
- These states are directly replaced by the status indicators and should not be
- used. The difference between ``Frozen`` and ``Locked`` is an implementation
- detail and should not be relevant to external users.
- Basic QMP Usage
- ---------------
- The primary interface to manipulating bitmap objects is via the QMP
- interface. If you are not familiar, see the :doc:`qmp-spec` for the
- protocol, and :doc:`qemu-qmp-ref` for a full reference of all QMP
- commands.
- Supported Commands
- ~~~~~~~~~~~~~~~~~~
- There are six primary bitmap-management API commands:
- - ``block-dirty-bitmap-add``
- - ``block-dirty-bitmap-remove``
- - ``block-dirty-bitmap-clear``
- - ``block-dirty-bitmap-disable``
- - ``block-dirty-bitmap-enable``
- - ``block-dirty-bitmap-merge``
- And one related query command:
- - ``query-block``
- Creation: block-dirty-bitmap-add
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- `block-dirty-bitmap-add
- <qemu-qmp-ref.html#index-block_002ddirty_002dbitmap_002dadd>`_:
- Creates a new bitmap that tracks writes to the specified node. granularity,
- persistence, and recording state can be adjusted at creation time.
- .. admonition:: Example
- to create a new, actively recording persistent bitmap:
- .. code-block:: QMP
- -> { "execute": "block-dirty-bitmap-add",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0",
- "persistent": true,
- }
- }
- <- { "return": {} }
- - 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.
- .. admonition:: Example
- To create a new, disabled (``-recording``), transient bitmap that tracks
- changes in 32KiB segments:
- .. code-block:: QMP
- -> { "execute": "block-dirty-bitmap-add",
- "arguments": {
- "node": "drive0",
- "name": "bitmap1",
- "granularity": 32768,
- "disabled": true
- }
- }
- <- { "return": {} }
- Deletion: block-dirty-bitmap-remove
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- `block-dirty-bitmap-remove
- <qemu-qmp-ref.html#index-block_002ddirty_002dbitmap_002dremove>`_:
- Deletes a bitmap. Bitmaps that are ``+busy`` cannot be removed.
- - Deleting a bitmap does not impact any other bitmaps attached to the same
- node, nor does it affect any backups already created from this bitmap or
- node.
- - Because bitmaps are only unique to the node to which they are attached, you
- must specify the node/drive name here, too.
- - Deleting a persistent bitmap will remove it from the qcow2 file.
- .. admonition:: Example
- Remove a bitmap named ``bitmap0`` from node ``drive0``:
- .. code-block:: QMP
- -> { "execute": "block-dirty-bitmap-remove",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
- }
- <- { "return": {} }
- Resetting: block-dirty-bitmap-clear
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- `block-dirty-bitmap-clear
- <qemu-qmp-ref.html#index-block_002ddirty_002dbitmap_002dclear>`_:
- Clears all dirty bits from a bitmap. ``+busy`` bitmaps cannot be cleared.
- - An incremental backup created from an empty bitmap will copy no data, as if
- nothing has changed.
- .. admonition:: Example
- Clear all dirty bits from bitmap ``bitmap0`` on node ``drive0``:
- .. code-block:: QMP
- -> { "execute": "block-dirty-bitmap-clear",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
- }
- <- { "return": {} }
- Enabling: block-dirty-bitmap-enable
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- `block-dirty-bitmap-enable
- <qemu-qmp-ref.html#index-block_002ddirty_002dbitmap_002denable>`_:
- "Enables" a bitmap, setting the ``recording`` bit to true, causing writes to
- begin being recorded. ``+busy`` bitmaps cannot be enabled.
- - Bitmaps default to being enabled when created, unless configured otherwise.
- - Persistent enabled bitmaps will remember their ``+recording`` status on
- load.
- .. admonition:: Example
- To set ``+recording`` on bitmap ``bitmap0`` on node ``drive0``:
- .. code-block:: QMP
- -> { "execute": "block-dirty-bitmap-enable",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
- }
- <- { "return": {} }
- Enabling: block-dirty-bitmap-disable
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- `block-dirty-bitmap-disable
- <qemu-qmp-ref.html#index-block_002ddirty_002dbitmap_002ddisable>`_:
- "Disables" a bitmap, setting the ``recording`` bit to false, causing further
- writes to begin being ignored. ``+busy`` bitmaps cannot be disabled.
- .. warning::
- This is potentially dangerous: QEMU makes no effort to stop any writes if
- there are disabled bitmaps on a node, and will not mark any disabled bitmaps
- as ``+inconsistent`` if any such writes do happen. Backups made from such
- bitmaps will not be able to be used to reconstruct a coherent image.
- - Disabling a bitmap may be useful for examining which sectors of a disk
- changed during a specific time period, or for explicit management of
- differential backup windows.
- - Persistent disabled bitmaps will remember their ``-recording`` status on
- load.
- .. admonition:: Example
- To set ``-recording`` on bitmap ``bitmap0`` on node ``drive0``:
- .. code-block:: QMP
- -> { "execute": "block-dirty-bitmap-disable",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
- }
- <- { "return": {} }
- Merging, Copying: block-dirty-bitmap-merge
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- `block-dirty-bitmap-merge
- <qemu-qmp-ref.html#index-block_002ddirty_002dbitmap_002dmerge>`_:
- Merges one or more bitmaps into a target bitmap. For any segment that is dirty
- in any one source bitmap, the target bitmap will mark that segment dirty.
- - Merge takes one or more bitmaps as a source and merges them together into a
- single destination, such that any segment marked as dirty in any source
- bitmap(s) will be marked dirty in the destination bitmap.
- - Merge does not create the destination bitmap if it does not exist. A blank
- bitmap can be created beforehand to achieve the same effect.
- - The destination is not cleared prior to merge, so subsequent merge
- operations will continue to cumulatively mark more segments as dirty.
- - If the merge operation should fail, the destination bitmap is guaranteed to
- be unmodified. The operation may fail if the source or destination bitmaps
- are busy, or have different granularities.
- - Bitmaps can only be merged on the same node. There is only one "node"
- argument, so all bitmaps must be attached to that same node.
- - Copy can be achieved by merging from a single source to an empty
- destination.
- .. admonition:: Example
- Merge the data from ``bitmap0`` into the bitmap ``new_bitmap`` on node
- ``drive0``. If ``new_bitmap`` was empty prior to this command, this achieves
- a copy.
- .. code-block:: QMP
- -> { "execute": "block-dirty-bitmap-merge",
- "arguments": {
- "node": "drive0",
- "target": "new_bitmap",
- "bitmaps": [ "bitmap0" ]
- }
- }
- <- { "return": {} }
- Querying: query-block
- ~~~~~~~~~~~~~~~~~~~~~
- `query-block
- <qemu-qmp-ref.html#index-query_002dblock>`_:
- Not strictly a bitmaps command, but will return information about any bitmaps
- attached to nodes serving as the root for guest devices.
- - The "inconsistent" bit will not appear when it is false, appearing only when
- the value is true to indicate there is a problem.
- .. admonition:: Example
- Query the block sub-system of QEMU. The following json has trimmed irrelevant
- keys from the response to highlight only the bitmap-relevant portions of the
- API. This result highlights a bitmap ``bitmap0`` attached to the root node of
- device ``drive0``.
- .. code-block:: QMP
- -> {
- "execute": "query-block",
- "arguments": {}
- }
- <- {
- "return": [ {
- "dirty-bitmaps": [ {
- "status": "active",
- "count": 0,
- "busy": false,
- "name": "bitmap0",
- "persistent": false,
- "recording": true,
- "granularity": 65536
- } ],
- "device": "drive0",
- } ]
- }
- Bitmap Persistence
- ------------------
- As outlined in `Supported Image Formats`_, QEMU can persist bitmaps to qcow2
- files. Demonstrated in `Creation: block-dirty-bitmap-add`_, passing
- ``persistent: true`` to ``block-dirty-bitmap-add`` will persist that bitmap to
- disk.
- Persistent bitmaps will be automatically loaded into memory upon load, and
- will be written back to disk upon close. Their usage should be mostly
- transparent.
- However, if QEMU does not get a chance to close the file cleanly, the bitmap
- will be marked as ``+inconsistent`` at next load and considered unsafe to use
- for any operation. At this point, the only valid operation on such bitmaps is
- ``block-dirty-bitmap-remove``.
- Losing a bitmap in this way does not invalidate any existing backups that have
- been made from this bitmap, but no further backups will be able to be issued
- for this chain.
- Transactions
- ------------
- Transactions are a QMP feature that allows you to submit multiple QMP commands
- at once, being guaranteed that they will all succeed or fail atomically,
- together. The interaction of bitmaps and transactions are demonstrated below.
- See `transaction <qemu-qmp.ref.html#index-transaction>`_ in the QMP reference
- for more details.
- Justification
- ~~~~~~~~~~~~~
- Bitmaps can generally be modified at any time, but certain operations often
- only make sense when paired directly with other commands. When a VM is paused,
- it's easy to ensure that no guest writes occur between individual QMP
- commands. When a VM is running, this is difficult to accomplish with
- individual QMP commands that may allow guest writes to occur between each
- command.
- For example, using only individual QMP commands, we could:
- #. Boot the VM in a paused state.
- #. Create a full drive backup of drive0.
- #. Create a new bitmap attached to drive0, confident that nothing has been
- written to drive0 in the meantime.
- #. Resume execution of the VM.
- #. At a later point, issue incremental backups from ``bitmap0``.
- 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 would want to
- perform actions such as the following:
- #. Boot the VM and begin execution.
- #. Using a single transaction, perform the following operations:
- - Create ``bitmap0``.
- - Create a full drive backup of ``drive0``.
- #. At a later point, issue incremental backups from ``bitmap0``.
- .. note:: As a consideration, if ``bitmap0`` is created prior to the full
- drive backup, incremental backups can still be authored from this
- bitmap, but they will copy extra segments reflecting writes that
- occurred prior to the backup operation. Transactions allow us to
- narrow critical points in time to reduce waste, or, in the other
- direction, to ensure that no segments are omitted.
- Supported Bitmap Transactions
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- - ``block-dirty-bitmap-add``
- - ``block-dirty-bitmap-clear``
- - ``block-dirty-bitmap-enable``
- - ``block-dirty-bitmap-disable``
- - ``block-dirty-bitmap-merge``
- The usages for these commands are identical to their respective QMP commands,
- but see the sections below for concrete examples.
- Incremental Backups - Push Model
- --------------------------------
- Incremental backups are simply partial disk images that can be combined with
- other partial disk images on top of a base image to reconstruct a full backup
- from the point in time at which the incremental backup was issued.
- The "Push Model" here references the fact that QEMU is "pushing" the modified
- blocks out to a destination. We will be using the `blockdev-backup
- <qemu-qmp-ref.html#index-blockdev_002dbackup>`_ QMP command to create both
- full and incremental backups.
- The command is a background job, which has its own QMP API for querying and
- management documented in `Background jobs
- <qemu-qmp-ref.html#Background-jobs>`_.
- Example: New Incremental Backup Anchor Point
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- As outlined in the Transactions - `Justification`_ section, perhaps we want to
- create a new incremental backup chain attached to a drive.
- This example creates a new, full backup of "drive0" and accompanies it with a
- new, empty bitmap that records writes from this point in time forward.
- The target can be created with the help of `blockdev-add
- <qemu-qmp-ref.html#index-blockdev_002dadd>`_ or `blockdev-create
- <qemu-qmp-ref.html#index-blockdev_002dcreate>`_ command.
- .. note:: Any new writes that happen after this command is issued, even while
- the backup job runs, will be written locally and not to the backup
- destination. These writes will be recorded in the bitmap
- accordingly.
- .. code-block:: QMP
- -> {
- "execute": "transaction",
- "arguments": {
- "actions": [
- {
- "type": "block-dirty-bitmap-add",
- "data": {
- "node": "drive0",
- "name": "bitmap0"
- }
- },
- {
- "type": "blockdev-backup",
- "data": {
- "device": "drive0",
- "target": "target0",
- "sync": "full"
- }
- }
- ]
- }
- }
- <- { "return": {} }
- <- {
- "timestamp": {
- "seconds": 1555436945,
- "microseconds": 179620
- },
- "data": {
- "status": "created",
- "id": "drive0"
- },
- "event": "JOB_STATUS_CHANGE"
- }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 68719476736,
- "offset": 68719476736
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- <- {
- "timestamp": {...},
- "data": {
- "status": "concluded",
- "id": "drive0"
- },
- "event": "JOB_STATUS_CHANGE"
- }
- <- {
- "timestamp": {...},
- "data": {
- "status": "null",
- "id": "drive0"
- },
- "event": "JOB_STATUS_CHANGE"
- }
- A full explanation of the job transition semantics and the JOB_STATUS_CHANGE
- event are beyond the scope of this document and will be omitted in all
- subsequent examples; above, several more events have been omitted for brevity.
- .. note:: Subsequent examples will omit all events except BLOCK_JOB_COMPLETED
- except where necessary to illustrate workflow differences.
- Omitted events and json objects will be represented by ellipses:
- ``...``
- Example: Resetting an Incremental Backup Anchor Point
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- If we want to start a new backup chain with an existing bitmap, we can also
- use a transaction to reset the bitmap while making a new full backup:
- .. code-block:: QMP
- -> {
- "execute": "transaction",
- "arguments": {
- "actions": [
- {
- "type": "block-dirty-bitmap-clear",
- "data": {
- "node": "drive0",
- "name": "bitmap0"
- }
- },
- {
- "type": "blockdev-backup",
- "data": {
- "device": "drive0",
- "target": "target0",
- "sync": "full"
- }
- }
- ]
- }
- }
- <- { "return": {} }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 68719476736,
- "offset": 68719476736
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ...
- The result of this example is identical to the first, but we clear an existing
- bitmap instead of adding a new one.
- .. tip:: In both of these examples, "bitmap0" is tied conceptually to the
- creation of new, full backups. This relationship is not saved or
- remembered by QEMU; it is up to the operator or management layer to
- remember which bitmaps are associated with which backups.
- Example: First Incremental Backup
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- #. Create a full backup and sync it to a dirty bitmap using any method:
- - Either of the two live backup method demonstrated above,
- - Using QMP commands with the VM paused as in the `Justification`_ section,
- or
- - With the VM offline, manually copy the image and start the VM in a paused
- state, careful to add a new bitmap before the VM begins execution.
- Whichever method is chosen, let's assume that at the end of this step:
- - The full backup is named ``drive0.full.qcow2``.
- - The bitmap we created is named ``bitmap0``, attached to ``drive0``.
- #. 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 ``drive0.inc0.qcow2``:
- .. code:: bash
- $ qemu-img create -f qcow2 drive0.inc0.qcow2 \
- -b drive0.full.qcow2 -F qcow2
- #. Add target block node:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-add",
- "arguments": {
- "node-name": "target0",
- "driver": "qcow2",
- "file": {
- "driver": "file",
- "filename": "drive0.inc0.qcow2"
- }
- }
- }
- <- { "return": {} }
- #. Issue an incremental backup command:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "target0",
- "sync": "incremental"
- }
- }
- <- { "return": {} }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 68719476736,
- "offset": 68719476736
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ...
- This copies any blocks modified since the full backup was created into the
- ``drive0.inc0.qcow2`` file. During the operation, ``bitmap0`` is marked
- ``+busy``. If the operation is successful, ``bitmap0`` will be cleared to
- reflect the "incremental" backup regimen, which only copies out new changes
- from each incremental backup.
- .. note:: Any new writes that occur after the backup operation starts do not
- get copied to the destination. The backup's "point in time" is when
- the backup starts, not when it ends. These writes are recorded in a
- special bitmap that gets re-added to bitmap0 when the backup ends so
- that the next incremental backup can copy them out.
- Example: Second Incremental Backup
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- #. Create a new destination image for the incremental backup that points to
- the previous one, e.g.: ``drive0.inc1.qcow2``
- .. code:: bash
- $ qemu-img create -f qcow2 drive0.inc1.qcow2 \
- -b drive0.inc0.qcow2 -F qcow2
- #. Add target block node:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-add",
- "arguments": {
- "node-name": "target0",
- "driver": "qcow2",
- "file": {
- "driver": "file",
- "filename": "drive0.inc1.qcow2"
- }
- }
- }
- <- { "return": {} }
- #. Issue a new incremental backup command. The only difference here is that we
- have changed the target image below.
- .. code-block:: QMP
- -> {
- "execute": "blockdev-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "target0",
- "sync": "incremental"
- }
- }
- <- { "return": {} }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 68719476736,
- "offset": 68719476736
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ...
- Because the first incremental backup from the previous example completed
- successfully, ``bitmap0`` was synchronized with ``drive0.inc0.qcow2``. Here,
- we use ``bitmap0`` again to create a new incremental backup that targets the
- previous one, creating a chain of three images:
- .. admonition:: Diagram
- .. code:: text
- +-------------------+ +-------------------+ +-------------------+
- | drive0.full.qcow2 |<--| drive0.inc0.qcow2 |<--| drive0.inc1.qcow2 |
- +-------------------+ +-------------------+ +-------------------+
- Each new incremental backup re-synchronizes the bitmap to the latest backup
- authored, allowing a user to continue to "consume" it to create new backups on
- top of an existing chain.
- In the above diagram, neither drive0.inc1.qcow2 nor drive0.inc0.qcow2 are
- complete images by themselves, but rely on their backing chain to reconstruct
- a full image. The dependency terminates with each full backup.
- Each backup in this chain remains independent, and is unchanged by new entries
- made later in the chain. For instance, drive0.inc0.qcow2 remains a perfectly
- valid backup of the disk as it was when that backup was issued.
- Example: Incremental Push Backups without Backing Files
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Backup images are best kept off-site, so we often will not have the preceding
- backups in a chain available to link against. This is not a problem at backup
- time; we simply do not set the backing image when creating the destination
- image:
- #. Create a new destination image with no backing file set. We will need to
- specify the size of the base image, because the backing file isn't
- available for QEMU to use to determine it.
- .. code:: bash
- $ qemu-img create -f qcow2 drive0.inc2.qcow2 64G
- .. note:: Alternatively, you can omit ``mode: "existing"`` from the push
- backup commands to have QEMU create an image without a backing
- file for you, but you lose control over format options like
- compatibility and preallocation presets.
- #. Add target block node:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-add",
- "arguments": {
- "node-name": "target0",
- "driver": "qcow2",
- "file": {
- "driver": "file",
- "filename": "drive0.inc2.qcow2"
- }
- }
- }
- <- { "return": {} }
- #. Issue a new incremental backup command. Apart from the new destination
- image, there is no difference from the last two examples.
- .. code-block:: QMP
- -> {
- "execute": "blockdev-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "target0",
- "sync": "incremental"
- }
- }
- <- { "return": {} }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 68719476736,
- "offset": 68719476736
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ...
- The only difference from the perspective of the user is that you will need to
- set the backing image when attempting to restore the backup:
- .. code:: bash
- $ qemu-img rebase drive0.inc2.qcow2 \
- -u -b drive0.inc1.qcow2
- This uses the "unsafe" rebase mode to simply set the backing file to a file
- that isn't present.
- It is also possible to use ``--image-opts`` to specify the entire backing
- chain by hand as an ephemeral property at runtime, but that is beyond the
- scope of this document.
- Example: Multi-drive Incremental Backup
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Assume we have a VM with two drives, "drive0" and "drive1" and we wish to back
- both of them up such that the two backups represent the same crash-consistent
- point in time.
- #. For each drive, create an empty image:
- .. code:: bash
- $ qemu-img create -f qcow2 drive0.full.qcow2 64G
- $ qemu-img create -f qcow2 drive1.full.qcow2 64G
- #. Add target block nodes:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-add",
- "arguments": {
- "node-name": "target0",
- "driver": "qcow2",
- "file": {
- "driver": "file",
- "filename": "drive0.full.qcow2"
- }
- }
- }
- <- { "return": {} }
- -> {
- "execute": "blockdev-add",
- "arguments": {
- "node-name": "target1",
- "driver": "qcow2",
- "file": {
- "driver": "file",
- "filename": "drive1.full.qcow2"
- }
- }
- }
- <- { "return": {} }
- #. Create a full (anchor) backup for each drive, with accompanying bitmaps:
- .. code-block:: QMP
- -> {
- "execute": "transaction",
- "arguments": {
- "actions": [
- {
- "type": "block-dirty-bitmap-add",
- "data": {
- "node": "drive0",
- "name": "bitmap0"
- }
- },
- {
- "type": "block-dirty-bitmap-add",
- "data": {
- "node": "drive1",
- "name": "bitmap0"
- }
- },
- {
- "type": "blockdev-backup",
- "data": {
- "device": "drive0",
- "target": "target0",
- "sync": "full"
- }
- },
- {
- "type": "blockdev-backup",
- "data": {
- "device": "drive1",
- "target": "target1",
- "sync": "full"
- }
- }
- ]
- }
- }
- <- { "return": {} }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 68719476736,
- "offset": 68719476736
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive1",
- "type": "backup",
- "speed": 0,
- "len": 68719476736,
- "offset": 68719476736
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ...
- #. Later, create new destination images for each of the incremental backups
- that point to their respective full backups:
- .. code:: bash
- $ qemu-img create -f qcow2 drive0.inc0.qcow2 \
- -b drive0.full.qcow2 -F qcow2
- $ qemu-img create -f qcow2 drive1.inc0.qcow2 \
- -b drive1.full.qcow2 -F qcow2
- #. Add target block nodes:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-add",
- "arguments": {
- "node-name": "target0",
- "driver": "qcow2",
- "file": {
- "driver": "file",
- "filename": "drive0.inc0.qcow2"
- }
- }
- }
- <- { "return": {} }
- -> {
- "execute": "blockdev-add",
- "arguments": {
- "node-name": "target1",
- "driver": "qcow2",
- "file": {
- "driver": "file",
- "filename": "drive1.inc0.qcow2"
- }
- }
- }
- <- { "return": {} }
- #. Issue a multi-drive incremental push backup transaction:
- .. code-block:: QMP
- -> {
- "execute": "transaction",
- "arguments": {
- "actions": [
- {
- "type": "blockev-backup",
- "data": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "sync": "incremental",
- "target": "target0"
- }
- },
- {
- "type": "blockdev-backup",
- "data": {
- "device": "drive1",
- "bitmap": "bitmap0",
- "sync": "incremental",
- "target": "target1"
- }
- },
- ]
- }
- }
- <- { "return": {} }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 68719476736,
- "offset": 68719476736
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive1",
- "type": "backup",
- "speed": 0,
- "len": 68719476736,
- "offset": 68719476736
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ...
- Push Backup Errors & Recovery
- -----------------------------
- In the event of an error that occurs after a push backup job is successfully
- launched, either by an individual 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 a job being cancelled, the user will receive a
- ``BLOCK_JOB_CANCELLED`` event instead of a pair of COMPLETE and ERROR
- events.
- In either failure case, the bitmap used for the failed operation is not
- cleared. It will contain all of the dirty bits it did at the start of the
- operation, plus any new bits that got marked during the operation.
- Effectively, the "point in time" that a bitmap is recording differences
- against is kept at the issuance of the last successful incremental backup,
- instead of being moved forward to the start of this now-failed backup.
- Once the underlying problem is addressed (e.g. more storage space is allocated
- on the destination), the incremental backup command can be retried with the
- same bitmap.
- Example: Individual Failures
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Incremental Push Backup jobs that fail individually behave simply as
- described above. This example demonstrates the single-job failure case:
- #. Create a target image:
- .. code:: bash
- $ qemu-img create -f qcow2 drive0.inc0.qcow2 \
- -b drive0.full.qcow2 -F qcow2
- #. Add target block node:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-add",
- "arguments": {
- "node-name": "target0",
- "driver": "qcow2",
- "file": {
- "driver": "file",
- "filename": "drive0.inc0.qcow2"
- }
- }
- }
- <- { "return": {} }
- #. Attempt to create an incremental backup via QMP:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "target0",
- "sync": "incremental"
- }
- }
- <- { "return": {} }
- #. Receive a pair of events indicating failure:
- .. code-block:: QMP
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "action": "report",
- "operation": "write"
- },
- "event": "BLOCK_JOB_ERROR"
- }
- <- {
- "timestamp": {...},
- "data": {
- "speed": 0,
- "offset": 0,
- "len": 67108864,
- "error": "No space left on device",
- "device": "drive0",
- "type": "backup"
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- #. Remove target node:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-del",
- "arguments": {
- "node-name": "target0",
- }
- }
- <- { "return": {} }
- #. Delete the failed image, and re-create it.
- .. code:: bash
- $ rm drive0.inc0.qcow2
- $ qemu-img create -f qcow2 drive0.inc0.qcow2 \
- -b drive0.full.qcow2 -F qcow2
- #. Add target block node:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-add",
- "arguments": {
- "node-name": "target0",
- "driver": "qcow2",
- "file": {
- "driver": "file",
- "filename": "drive0.inc0.qcow2"
- }
- }
- }
- <- { "return": {} }
- #. Retry the command after fixing the underlying problem, such as
- freeing up space on the backup volume:
- .. code-block:: QMP
- -> {
- "execute": "blockdev-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "target0",
- "sync": "incremental"
- }
- }
- <- { "return": {} }
- #. Receive confirmation that the job completed successfully:
- .. code-block:: QMP
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 67108864,
- "offset": 67108864
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- Example: Partial Transactional Failures
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- QMP commands like `blockdev-backup
- <qemu-qmp-ref.html#index-blockdev_002dbackup>`_
- conceptually only start a job, and so transactions containing these commands
- may succeed even if the job it created later fails. This might have surprising
- interactions with notions of how a "transaction" ought to behave.
- This distinction means that on occasion, a transaction containing such job
- launching commands may appear to succeed and return success, but later
- individual jobs associated with the transaction 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, if one of those
- jobs fails, it will not interact with the other backup jobs in any way by
- default. The job(s) that succeeded will clear the dirty bitmap associated with
- the operation, but the job(s) that failed will not. It is therefore not safe
- to delete any incremental backups that were created successfully in this
- scenario, even though others failed.
- This example illustrates a transaction with two backup jobs, where one fails
- and one succeeds:
- #. Issue the transaction to start a backup of both drives.
- .. code-block:: QMP
- -> {
- "execute": "transaction",
- "arguments": {
- "actions": [
- {
- "type": "blockdev-backup",
- "data": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "sync": "incremental",
- "target": "target0"
- }
- },
- {
- "type": "blockdev-backup",
- "data": {
- "device": "drive1",
- "bitmap": "bitmap0",
- "sync": "incremental",
- "target": "target1"
- }
- }]
- }
- }
- #. Receive notice that the Transaction was accepted, and jobs were
- launched:
- .. code-block:: QMP
- <- { "return": {} }
- #. Receive notice that the first job has completed:
- .. code-block:: QMP
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 67108864,
- "offset": 67108864
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- #. Receive notice that the second job has failed:
- .. code-block:: QMP
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive1",
- "action": "report",
- "operation": "read"
- },
- "event": "BLOCK_JOB_ERROR"
- }
- ...
- <- {
- "timestamp": {...},
- "data": {
- "speed": 0,
- "offset": 0,
- "len": 67108864,
- "error": "Input/output error",
- "device": "drive1",
- "type": "backup"
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- At the conclusion of the above example, ``drive0.inc0.qcow2`` is valid and
- must be kept, but ``drive1.inc0.qcow2`` is incomplete 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
- ``drive0.inc0.qcow2``.
- For this example, an incremental backup for ``drive0`` was created, but not
- for ``drive1``. The last VM-wide crash-consistent backup that is available in
- this case is the full backup:
- .. code:: text
- [drive0.full.qcow2] <-- [drive0.inc0.qcow2]
- [drive1.full.qcow2]
- To repair this, issue a new incremental backup across both drives. The result
- will be backup chains that resemble the following:
- .. code:: text
- [drive0.full.qcow2] <-- [drive0.inc0.qcow2] <-- [drive0.inc1.qcow2]
- [drive1.full.qcow2] <-------------------------- [drive1.inc1.qcow2]
- Example: Grouped Completion Mode
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- While jobs launched by transactions normally complete or fail individually,
- it's possible to instruct them to complete or fail together as a group. QMP
- transactions take an optional properties structure that can affect the
- behavior of the transaction.
- The ``completion-mode`` transaction property can be either ``individual``
- which is the default legacy behavior described above, or ``grouped``, detailed
- below.
- In ``grouped`` completion mode, no jobs will report success until all jobs are
- ready to report success. If any job fails, all other jobs will be cancelled.
- Regardless of if a participating incremental backup job failed or was
- cancelled, their associated bitmaps will all be held at their existing
- points-in-time, as in individual failure cases.
- Here's the same multi-drive backup scenario from `Example: Partial
- Transactional Failures`_, but with the ``grouped`` completion-mode property
- applied:
- #. Issue the multi-drive incremental backup transaction:
- .. code-block:: QMP
- -> {
- "execute": "transaction",
- "arguments": {
- "properties": {
- "completion-mode": "grouped"
- },
- "actions": [
- {
- "type": "blockdev-backup",
- "data": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "sync": "incremental",
- "target": "target0"
- }
- },
- {
- "type": "blockdev-backup",
- "data": {
- "device": "drive1",
- "bitmap": "bitmap0",
- "sync": "incremental",
- "target": "target1"
- }
- }]
- }
- }
- #. Receive notice that the Transaction was accepted, and jobs were launched:
- .. code-block:: QMP
- <- { "return": {} }
- #. Receive notification that the backup job for ``drive1`` has failed:
- .. code-block:: QMP
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive1",
- "action": "report",
- "operation": "read"
- },
- "event": "BLOCK_JOB_ERROR"
- }
- <- {
- "timestamp": {...},
- "data": {
- "speed": 0,
- "offset": 0,
- "len": 67108864,
- "error": "Input/output error",
- "device": "drive1",
- "type": "backup"
- },
- "event": "BLOCK_JOB_COMPLETED"
- }
- #. Receive notification that the job for ``drive0`` has been cancelled:
- .. code-block:: QMP
- <- {
- "timestamp": {...},
- "data": {
- "device": "drive0",
- "type": "backup",
- "speed": 0,
- "len": 67108864,
- "offset": 16777216
- },
- "event": "BLOCK_JOB_CANCELLED"
- }
- At the conclusion of *this* example, both jobs have been aborted due to a
- failure. Both destination images should be deleted and are no longer of use.
- The transaction as a whole can simply be re-issued at a later time.
- .. raw:: html
- <!--
- The FreeBSD Documentation License
- Redistribution and use in source (ReST) 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 (ReST) 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.
- -->
|