28 Mar, 2020

2 commits

  • dm_clone_nr_of_hydrated_regions() returns the number of regions that
    have been hydrated so far. In order to do so it employs bitmap_weight().

    Until now, the return type of dm_clone_nr_of_hydrated_regions() was
    unsigned long.

    Because bitmap_weight() returns an int, in case BITS_PER_LONG == 64 and
    the return value of bitmap_weight() is 2^31 (the maximum allowed number
    of regions for a device), the result is sign extended from 32 bits to 64
    bits and an incorrect value is displayed, in the status output of
    dm-clone, as the number of hydrated regions.

    Fix this by having dm_clone_nr_of_hydrated_regions() return an unsigned
    int.

    Fixes: 7431b7835f55 ("dm: add clone target")
    Cc: stable@vger.kernel.org # v5.4+
    Signed-off-by: Nikos Tsironis
    Signed-off-by: Mike Snitzer

    Nikos Tsironis
     
  • There is a bug in the way dm-clone handles discards, which can lead to
    discarding the wrong blocks or trying to discard blocks beyond the end
    of the device.

    This could lead to data corruption, if the destination device indeed
    discards the underlying blocks, i.e., if the discard operation results
    in the original contents of a block to be lost.

    The root of the problem is the code that calculates the range of regions
    covered by a discard request and decides which regions to discard.

    Since dm-clone handles the device in units of regions, we don't discard
    parts of a region, only whole regions.

    The range is calculated as:

    rs = dm_sector_div_up(bio->bi_iter.bi_sector, clone->region_size);
    re = bio_end_sector(bio) >> clone->region_shift;

    , where 'rs' is the first region to discard and (re - rs) is the number
    of regions to discard.

    The bug manifests when we try to discard part of a single region, i.e.,
    when we try to discard a block with size < region_size, and the discard
    request both starts at an offset with respect to the beginning of that
    region and ends before the end of the region.

    The root cause is the following comparison:

    if (rs == re)
    // skip discard and complete original bio immediately

    , which doesn't take into account that 'rs' might be greater than 're'.

    Thus, we then issue a discard request for the wrong blocks, instead of
    skipping the discard all together.

    Fix the check to also take into account the above case, so we don't end
    up discarding the wrong blocks.

    Also, add some range checks to dm_clone_set_region_hydrated() and
    dm_clone_cond_set_range(), which update dm-clone's region bitmap.

    Note that the aforementioned bug doesn't cause invalid memory accesses,
    because dm_clone_is_range_hydrated() returns True for this case, so the
    checks are just precautionary.

    Fixes: 7431b7835f55 ("dm: add clone target")
    Cc: stable@vger.kernel.org # v5.4+
    Signed-off-by: Nikos Tsironis
    Signed-off-by: Mike Snitzer

    Nikos Tsironis
     

06 Dec, 2019

2 commits

  • Split the metadata commit in two parts:

    1. dm_clone_metadata_pre_commit(): Prepare the current transaction for
    committing. After this is called, all subsequent metadata updates,
    done through either dm_clone_set_region_hydrated() or
    dm_clone_cond_set_range(), will be part of the next transaction.

    2. dm_clone_metadata_commit(): Actually commit the current transaction
    to disk and start a new transaction.

    This is required by the following commit. It allows dm-clone to flush
    the destination device after step (1) to ensure that all freshly
    hydrated regions, for which we are updating the metadata, are properly
    written to non-volatile storage and won't be lost in case of a crash.

    Fixes: 7431b7835f55 ("dm: add clone target")
    Cc: stable@vger.kernel.org # v5.4+
    Signed-off-by: Nikos Tsironis
    Signed-off-by: Mike Snitzer

    Nikos Tsironis
     
  • Extend struct dirty_map with a second bitmap which tracks the exact
    regions that were hydrated during the current metadata transaction.

    Moreover, fix __flush_dmap() to only commit the metadata of the regions
    that were hydrated during the current transaction.

    This is required by the following commits to fix a data corruption bug.

    Fixes: 7431b7835f55 ("dm: add clone target")
    Cc: stable@vger.kernel.org # v5.4+
    Signed-off-by: Nikos Tsironis
    Signed-off-by: Mike Snitzer

    Nikos Tsironis
     

06 Nov, 2019

1 commit

  • If we are in a place where it is known that interrupts are enabled,
    functions spin_lock_irq/spin_unlock_irq should be used instead of
    spin_lock_irqsave/spin_unlock_irqrestore.

    spin_lock_irq and spin_unlock_irq are faster because they don't need to
    push and pop the flags register.

    Signed-off-by: Mikulas Patocka
    Signed-off-by: Nikos Tsironis
    Signed-off-by: Mike Snitzer

    Mikulas Patocka
     

12 Sep, 2019

1 commit

  • Add the dm-clone target, which allows cloning of arbitrary block
    devices.

    dm-clone produces a one-to-one copy of an existing, read-only source
    device into a writable destination device: It presents a virtual block
    device which makes all data appear immediately, and redirects reads and
    writes accordingly.

    The main use case of dm-clone is to clone a potentially remote,
    high-latency, read-only, archival-type block device into a writable,
    fast, primary-type device for fast, low-latency I/O. The cloned device
    is visible/mountable immediately and the copy of the source device to
    the destination device happens in the background, in parallel with user
    I/O.

    When the cloning completes, the dm-clone table can be removed altogether
    and be replaced, e.g., by a linear table, mapping directly to the
    destination device.

    For further information and examples of how to use dm-clone, please read
    Documentation/admin-guide/device-mapper/dm-clone.rst

    Suggested-by: Vangelis Koukis
    Co-developed-by: Ilias Tsitsimpis
    Signed-off-by: Ilias Tsitsimpis
    Signed-off-by: Nikos Tsironis
    Signed-off-by: Mike Snitzer

    Nikos Tsironis