Skip to content

Commit

Permalink
btrfs: zoned: support dev-replace in zoned filesystems
Browse files Browse the repository at this point in the history
This is 4/4 patch to implement device-replace on zoned filesystems.

Even after the copying is done, the write pointers of the source device
and the destination device may not be synchronized. For example, when
the last allocated extent is freed before device-replace process, the
extent is not copied, leaving a hole there.

Synchronize the write pointers by writing zeroes to the destination
device.

Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
Naohiro Aota authored and David Sterba committed Feb 9, 2021
1 parent de17add commit 7db1c5d
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 0 deletions.
40 changes: 40 additions & 0 deletions fs/btrfs/scrub.c
Original file line number Diff line number Diff line change
Expand Up @@ -1628,6 +1628,9 @@ static int fill_writer_pointer_gap(struct scrub_ctx *sctx, u64 physical)
if (!btrfs_is_zoned(sctx->fs_info))
return 0;

if (!btrfs_dev_is_sequential(sctx->wr_tgtdev, physical))
return 0;

if (sctx->write_pointer < physical) {
length = physical - sctx->write_pointer;

Expand Down Expand Up @@ -3069,6 +3072,32 @@ static void sync_replace_for_zoned(struct scrub_ctx *sctx)
wait_event(sctx->list_wait, atomic_read(&sctx->bios_in_flight) == 0);
}

static int sync_write_pointer_for_zoned(struct scrub_ctx *sctx, u64 logical,
u64 physical, u64 physical_end)
{
struct btrfs_fs_info *fs_info = sctx->fs_info;
int ret = 0;

if (!btrfs_is_zoned(fs_info))
return 0;

wait_event(sctx->list_wait, atomic_read(&sctx->bios_in_flight) == 0);

mutex_lock(&sctx->wr_lock);
if (sctx->write_pointer < physical_end) {
ret = btrfs_sync_zone_write_pointer(sctx->wr_tgtdev, logical,
physical,
sctx->write_pointer);
if (ret)
btrfs_err(fs_info,
"zoned: failed to recover write pointer");
}
mutex_unlock(&sctx->wr_lock);
btrfs_dev_clear_zone_empty(sctx->wr_tgtdev, physical);

return ret;
}

static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
struct map_lookup *map,
struct btrfs_device *scrub_dev,
Expand Down Expand Up @@ -3475,6 +3504,17 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
blk_finish_plug(&plug);
btrfs_free_path(path);
btrfs_free_path(ppath);

if (sctx->is_dev_replace && ret >= 0) {
int ret2;

ret2 = sync_write_pointer_for_zoned(sctx, base + offset,
map->stripes[num].physical,
physical_end);
if (ret2)
ret = ret2;
}

return ret < 0 ? ret : 0;
}

Expand Down
74 changes: 74 additions & 0 deletions fs/btrfs/zoned.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "block-group.h"
#include "transaction.h"
#include "dev-replace.h"
#include "space-info.h"

/* Maximum number of zones to report per blkdev_report_zones() call */
#define BTRFS_REPORT_NR_ZONES 4096
Expand Down Expand Up @@ -1385,3 +1386,76 @@ int btrfs_zoned_issue_zeroout(struct btrfs_device *device, u64 physical, u64 len
return blkdev_issue_zeroout(device->bdev, physical >> SECTOR_SHIFT,
length >> SECTOR_SHIFT, GFP_NOFS, 0);
}

static int read_zone_info(struct btrfs_fs_info *fs_info, u64 logical,
struct blk_zone *zone)
{
struct btrfs_bio *bbio = NULL;
u64 mapped_length = PAGE_SIZE;
unsigned int nofs_flag;
int nmirrors;
int i, ret;

ret = btrfs_map_sblock(fs_info, BTRFS_MAP_GET_READ_MIRRORS, logical,
&mapped_length, &bbio);
if (ret || !bbio || mapped_length < PAGE_SIZE) {
btrfs_put_bbio(bbio);
return -EIO;
}

if (bbio->map_type & BTRFS_BLOCK_GROUP_RAID56_MASK)
return -EINVAL;

nofs_flag = memalloc_nofs_save();
nmirrors = (int)bbio->num_stripes;
for (i = 0; i < nmirrors; i++) {
u64 physical = bbio->stripes[i].physical;
struct btrfs_device *dev = bbio->stripes[i].dev;

/* Missing device */
if (!dev->bdev)
continue;

ret = btrfs_get_dev_zone(dev, physical, zone);
/* Failing device */
if (ret == -EIO || ret == -EOPNOTSUPP)
continue;
break;
}
memalloc_nofs_restore(nofs_flag);

return ret;
}

/*
* Synchronize write pointer in a zone at @physical_start on @tgt_dev, by
* filling zeros between @physical_pos to a write pointer of dev-replace
* source device.
*/
int btrfs_sync_zone_write_pointer(struct btrfs_device *tgt_dev, u64 logical,
u64 physical_start, u64 physical_pos)
{
struct btrfs_fs_info *fs_info = tgt_dev->fs_info;
struct blk_zone zone;
u64 length;
u64 wp;
int ret;

if (!btrfs_dev_is_sequential(tgt_dev, physical_pos))
return 0;

ret = read_zone_info(fs_info, logical, &zone);
if (ret)
return ret;

wp = physical_start + ((zone.wp - zone.start) << SECTOR_SHIFT);

if (physical_pos == wp)
return 0;

if (physical_pos > wp)
return -EUCLEAN;

length = wp - physical_pos;
return btrfs_zoned_issue_zeroout(tgt_dev, physical_pos, length);
}
9 changes: 9 additions & 0 deletions fs/btrfs/zoned.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ bool btrfs_check_meta_write_pointer(struct btrfs_fs_info *fs_info,
void btrfs_revert_meta_write_pointer(struct btrfs_block_group *cache,
struct extent_buffer *eb);
int btrfs_zoned_issue_zeroout(struct btrfs_device *device, u64 physical, u64 length);
int btrfs_sync_zone_write_pointer(struct btrfs_device *tgt_dev, u64 logical,
u64 physical_start, u64 physical_pos);
#else /* CONFIG_BLK_DEV_ZONED */
static inline int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
struct blk_zone *zone)
Expand Down Expand Up @@ -176,6 +178,13 @@ static inline int btrfs_zoned_issue_zeroout(struct btrfs_device *device,
return -EOPNOTSUPP;
}

static inline int btrfs_sync_zone_write_pointer(struct btrfs_device *tgt_dev,
u64 logical, u64 physical_start,
u64 physical_pos)
{
return -EOPNOTSUPP;
}

#endif

static inline bool btrfs_dev_is_sequential(struct btrfs_device *device, u64 pos)
Expand Down

0 comments on commit 7db1c5d

Please sign in to comment.