linux/fs/btrfs/dev-replace.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) STRATO AG 2012.  All rights reserved.
 */

#include <linux/sched.h>
#include <linux/bio.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/math64.h>
#include "misc.h"
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
#include "volumes.h"
#include "async-thread.h"
#include "dev-replace.h"
#include "sysfs.h"
#include "zoned.h"
#include "block-group.h"
#include "fs.h"
#include "accessors.h"
#include "scrub.h"

/*
 * Device replace overview
 *
 * [Objective]
 * To copy all extents (both new and on-disk) from source device to target
 * device, while still keeping the filesystem read-write.
 *
 * [Method]
 * There are two main methods involved:
 *
 * - Write duplication
 *
 *   All new writes will be written to both target and source devices, so even
 *   if replace gets canceled, sources device still contains up-to-date data.
 *
 *   Location:		handle_ops_on_dev_replace() from btrfs_map_block()
 *   Start:		btrfs_dev_replace_start()
 *   End:		btrfs_dev_replace_finishing()
 *   Content:		Latest data/metadata
 *
 * - Copy existing extents
 *
 *   This happens by re-using scrub facility, as scrub also iterates through
 *   existing extents from commit root.
 *
 *   Location:		scrub_write_block_to_dev_replace() from
 *   			scrub_block_complete()
 *   Content:		Data/meta from commit root.
 *
 * Due to the content difference, we need to avoid nocow write when dev-replace
 * is happening.  This is done by marking the block group read-only and waiting
 * for NOCOW writes.
 *
 * After replace is done, the finishing part is done by swapping the target and
 * source devices.
 *
 *   Location:		btrfs_dev_replace_update_device_in_mapping_tree() from
 *   			btrfs_dev_replace_finishing()
 */

static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
				       int scrub_ret);
static int btrfs_dev_replace_kthread(void *data);

int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info)
{}

/*
 * Initialize a new device for device replace target from a given source dev
 * and path.
 *
 * Return 0 and new device in @device_out, otherwise return < 0
 */
static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
				  const char *device_path,
				  struct btrfs_device *srcdev,
				  struct btrfs_device **device_out)
{}

/*
 * called from commit_transaction. Writes changed device replace state to
 * disk.
 */
int btrfs_run_dev_replace(struct btrfs_trans_handle *trans)
{}

static int mark_block_group_to_copy(struct btrfs_fs_info *fs_info,
				    struct btrfs_device *src_dev)
{}

bool btrfs_finish_block_group_to_copy(struct btrfs_device *srcdev,
				      struct btrfs_block_group *cache,
				      u64 physical)
{}

static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info,
		const char *tgtdev_name, u64 srcdevid, const char *srcdev_name,
		int read_src)
{}

static int btrfs_check_replace_dev_names(struct btrfs_ioctl_dev_replace_args *args)
{}

int btrfs_dev_replace_by_ioctl(struct btrfs_fs_info *fs_info,
			    struct btrfs_ioctl_dev_replace_args *args)
{}

/*
 * blocked until all in-flight bios operations are finished.
 */
static void btrfs_rm_dev_replace_blocked(struct btrfs_fs_info *fs_info)
{}

/*
 * we have removed target device, it is safe to allow new bios request.
 */
static void btrfs_rm_dev_replace_unblocked(struct btrfs_fs_info *fs_info)
{}

/*
 * When finishing the device replace, before swapping the source device with the
 * target device we must update the chunk allocation state in the target device,
 * as it is empty because replace works by directly copying the chunks and not
 * through the normal chunk allocation path.
 */
static int btrfs_set_target_alloc_state(struct btrfs_device *srcdev,
					struct btrfs_device *tgtdev)
{}

static void btrfs_dev_replace_update_device_in_mapping_tree(
						struct btrfs_fs_info *fs_info,
						struct btrfs_device *srcdev,
						struct btrfs_device *tgtdev)
{}

static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
				       int scrub_ret)
{}

/*
 * Read progress of device replace status according to the state and last
 * stored position. The value format is the same as for
 * btrfs_dev_replace::progress_1000
 */
static u64 btrfs_dev_replace_progress(struct btrfs_fs_info *fs_info)
{}

void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info,
			      struct btrfs_ioctl_dev_replace_args *args)
{}

int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info)
{}

void btrfs_dev_replace_suspend_for_unmount(struct btrfs_fs_info *fs_info)
{}

/* resume dev_replace procedure that was interrupted by unmount */
int btrfs_resume_dev_replace_async(struct btrfs_fs_info *fs_info)
{}

static int btrfs_dev_replace_kthread(void *data)
{}

int __pure btrfs_dev_replace_is_ongoing(struct btrfs_dev_replace *dev_replace)
{}

void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount)
{}

void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info)
{}