@@ -31,7 +31,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
uuid-tree.o props.o free-space-tree.o tree-checker.o space-info.o \
block-rsv.o delalloc-space.o block-group.o discard.o reflink.o \
- subpage.o tree-mod-log.o
+ subpage.o tree-mod-log.o write-intent.o
btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
@@ -989,6 +989,7 @@ struct btrfs_fs_info {
struct workqueue_struct *scrub_wr_completion_workers;
struct workqueue_struct *scrub_parity_workers;
struct btrfs_subpage_info *subpage_info;
+ struct write_intent_ctrl *wi_ctrl;
struct btrfs_discard_ctl discard_ctl;
@@ -3699,6 +3699,12 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
ret);
goto fail_block_groups;
}
+ ret = btrfs_write_intent_init(fs_info);
+ if (ret) {
+ btrfs_err(fs_info, "failed to init write-intent bitmaps: %d",
+ ret);
+ goto fail_block_groups;
+ }
ret = btrfs_recover_balance(fs_info);
if (ret) {
btrfs_err(fs_info, "failed to recover balance: %d", ret);
@@ -4639,6 +4645,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
ret = btrfs_commit_super(fs_info);
if (ret)
btrfs_err(fs_info, "commit super ret %d", ret);
+ btrfs_write_intent_free(fs_info);
}
if (BTRFS_FS_ERROR(fs_info))
new file mode 100644
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "ctree.h"
+#include "volumes.h"
+#include "write-intent.h"
+
+/*
+ * Return 0 if a valid write intent bitmap can be found.
+ * Return 1 if no valid write intent bitmap can be found.
+ * Return <0 for other fatal errors.
+ */
+static int write_intent_load(struct btrfs_device *device, struct page *dst)
+{
+ struct btrfs_fs_info *fs_info = device->fs_info;
+ struct write_intent_super *wis;
+ struct bio *bio;
+ int ret;
+
+ bio = bio_alloc(device->bdev, 1, REQ_OP_READ | REQ_SYNC | REQ_META,
+ GFP_NOFS);
+ /* It's backed by fs_bioset. */
+ ASSERT(bio);
+ bio->bi_iter.bi_sector = BTRFS_DEVICE_RANGE_RESERVED >> SECTOR_SHIFT;
+ __bio_add_page(bio, dst, WRITE_INTENT_BITMAPS_SIZE, 0);
+ ret = submit_bio_wait(bio);
+ if (ret < 0)
+ return ret;
+
+ wis = page_address(dst);
+ if (wi_super_magic(wis) != WRITE_INTENT_SUPER_MAGIC)
+ return 1;
+
+ /* Stale bitmaps, doesn't belong to our fs. */
+ if (memcmp(wis->fsid, device->fs_devices->fsid, BTRFS_FSID_SIZE))
+ return 1;
+
+ /* Above checks pass, but still csum mismatch, a big problem. */
+ if (btrfs_super_csum_type(fs_info->super_copy) !=
+ wi_super_csum_type(wis)) {
+ btrfs_err(fs_info,
+ "csum type mismatch, write intent bitmap has %u fs has %u",
+ wi_super_csum_type(wis),
+ btrfs_super_csum_type(fs_info->super_copy));
+ return -EUCLEAN;
+ }
+
+ if (wi_super_flags(wis) & ~WRITE_INTENT_FLAGS_SUPPORTED) {
+ btrfs_err(fs_info, "unsupported flags 0x%llx",
+ wi_super_flags(wis) & ~WRITE_INTENT_FLAGS_SUPPORTED);
+ return -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static void write_intent_init(struct btrfs_fs_info *fs_info)
+{
+ struct write_intent_ctrl *ctrl = fs_info->wi_ctrl;
+ struct write_intent_super *wis;
+
+ ASSERT(ctrl);
+ ASSERT(ctrl->page);
+
+ /* Always start event count from 1. */
+ atomic64_set(&ctrl->event, 1);
+ ctrl->blocksize = WRITE_INTENT_BLOCKSIZE;
+ memzero_page(ctrl->page, 0, WRITE_INTENT_BITMAPS_SIZE);
+
+ wis = page_address(ctrl->page);
+ memcpy(wis->fsid, fs_info->fs_devices->fsid, BTRFS_FSID_SIZE);
+ wi_set_super_magic(wis, WRITE_INTENT_SUPER_MAGIC);
+ wi_set_super_csum_type(wis, btrfs_super_csum_type(fs_info->super_copy));
+ wi_set_super_events(wis, 1);
+ wi_set_super_flags(wis, WRITE_INTENT_FLAGS_SUPPORTED);
+ wi_set_super_size(wis, WRITE_INTENT_BITMAPS_SIZE);
+ wi_set_super_blocksize(wis, ctrl->blocksize);
+ wi_set_super_nr_entries(wis, 0);
+ btrfs_info(fs_info, "creating new write intent bitmaps");
+}
+
+int btrfs_write_intent_init(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_device *highest_dev = NULL;
+ struct btrfs_device *dev;
+ struct write_intent_super *wis;
+ u64 highest_event = 0;
+ int ret;
+
+ ASSERT(fs_info->wi_ctrl == NULL);
+ if (!btrfs_fs_compat_ro(fs_info, WRITE_INTENT_BITMAP))
+ return 0;
+
+ fs_info->wi_ctrl = kzalloc(sizeof(*fs_info->wi_ctrl), GFP_NOFS);
+ if (!fs_info->wi_ctrl)
+ return -ENOMEM;
+
+ fs_info->wi_ctrl->page = alloc_page(GFP_NOFS);
+ if (!fs_info->wi_ctrl->page) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ /*
+ * Go through every writeable device to find the highest event.
+ *
+ * Only the write-intent with highest event number makes sense.
+ * If during bitmap writeback we lost power, some dev may have old
+ * bitmap which is already stale.
+ */
+ list_for_each_entry(dev, &fs_info->fs_devices->devices, dev_list) {
+ u64 cur_event;
+
+ if (!dev->bdev)
+ continue;
+
+ ret = write_intent_load(dev, fs_info->wi_ctrl->page);
+ if (ret > 0)
+ continue;
+ if (ret < 0) {
+ btrfs_err(fs_info,
+ "failed to load write intent from devid %llu: %d",
+ dev->devid, ret);
+ goto cleanup;
+ }
+ wis = page_address(fs_info->wi_ctrl->page);
+ cur_event = wi_super_events(wis);
+ if (cur_event > highest_event) {
+ highest_dev = dev;
+ highest_event = cur_event;
+ }
+ }
+
+ /* Load the bitmap with lowest event as our bitmap. */
+ if (highest_dev) {
+ ret = write_intent_load(highest_dev, fs_info->wi_ctrl->page);
+ if (ret < 0) {
+ btrfs_err(fs_info,
+ "failed to load write intent from devid %llu: %d",
+ dev->devid, ret);
+ goto cleanup;
+ }
+ wis = page_address(fs_info->wi_ctrl->page);
+ atomic64_set(&fs_info->wi_ctrl->event, wi_super_events(wis));
+ fs_info->wi_ctrl->blocksize = wi_super_blocksize(wis);
+ btrfs_info(fs_info,
+ "loaded write intent bitmaps, event count %llu",
+ atomic64_read(&fs_info->wi_ctrl->event));
+ return 0;
+ }
+
+ /* No valid bitmap found, create a new one. */
+ write_intent_init(fs_info);
+ return 0;
+cleanup:
+ if (fs_info->wi_ctrl) {
+ if (fs_info->wi_ctrl->page)
+ __free_page(fs_info->wi_ctrl->page);
+ kfree(fs_info->wi_ctrl);
+ fs_info->wi_ctrl = NULL;
+ }
+ return ret;
+}
+
+void btrfs_write_intent_free(struct btrfs_fs_info *fs_info)
+{
+ struct write_intent_ctrl *ctrl = fs_info->wi_ctrl;
+
+ if (!ctrl)
+ return;
+ ASSERT(ctrl->page);
+ __free_page(ctrl->page);
+ kfree(ctrl);
+ fs_info->wi_ctrl = NULL;
+}
@@ -106,6 +106,18 @@ struct write_intent_entry {
/* The number of bits we can have in one entry. */
#define WRITE_INTENT_BITS_PER_ENTRY (64)
+/* In-memory write-intent control structure. */
+struct write_intent_ctrl {
+ /* For the write_intent super and entries. */
+ struct page *page;
+
+ /* Cached event counter.*/
+ atomic64_t event;
+
+ /* Cached blocksize from write intent super. */
+ u32 blocksize;
+};
+
/*
* ON-DISK FORMAT
* ==============
@@ -196,4 +208,7 @@ static inline void wie_set_bitmap(struct write_intent_entry *entry,
#endif
}
+int btrfs_write_intent_init(struct btrfs_fs_info *fs_info);
+void btrfs_write_intent_free(struct btrfs_fs_info *fs_info);
+
#endif
This patch will introduce btrfs_fs_info::wi_ctrl, which will have a non-highmem page for the write intent bitmaps. Please note that, if we can't find a valid bitmaps, the newly create one will only be in memory for now, the bitmaps writeback functionality will be introduced in the next commit. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/Makefile | 2 +- fs/btrfs/ctree.h | 1 + fs/btrfs/disk-io.c | 7 ++ fs/btrfs/write-intent.c | 174 ++++++++++++++++++++++++++++++++++++++++ fs/btrfs/write-intent.h | 15 ++++ 5 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 fs/btrfs/write-intent.c