Skip to content

Commit

Permalink
btrfs: backref: introduce the skeleton of btrfs_backref_iter
Browse files Browse the repository at this point in the history
Due to the complex nature of btrfs extent tree, when we want to iterate
all backrefs of one extent, this involves quite a lot of work, like
searching the EXTENT_ITEM/METADATA_ITEM, iteration through inline and keyed
backrefs.

Normally this would result in a complex code, something like:

  btrfs_search_slot()
  /* Ensure we are at EXTENT_ITEM/METADATA_ITEM */
  while (1) {	/* Loop for extent tree items */
	while (ptr < end) { /* Loop for inlined items */
		/* Real work here */
	}
  next:
  	ret = btrfs_next_item()
	/* Ensure we're still at keyed item for specified bytenr */
  }

The idea of btrfs_backref_iter is to avoid such complex and hard to
read code structure, but something like the following:

  iter = btrfs_backref_iter_alloc();
  ret = btrfs_backref_iter_start(iter, bytenr);
  if (ret < 0)
	goto out;
  for (; ; ret = btrfs_backref_iter_next(iter)) {
	/* Real work here */
  }
  out:
  btrfs_backref_iter_free(iter);

This patch is just the skeleton + btrfs_backref_iter_start() code.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
Qu Wenruo authored and David Sterba committed May 25, 2020
1 parent 78d933c commit a37f232
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
110 changes: 110 additions & 0 deletions fs/btrfs/backref.c
Original file line number Diff line number Diff line change
Expand Up @@ -2295,3 +2295,113 @@ void free_ipath(struct inode_fs_paths *ipath)
kvfree(ipath->fspath);
kfree(ipath);
}

struct btrfs_backref_iter *btrfs_backref_iter_alloc(
struct btrfs_fs_info *fs_info, gfp_t gfp_flag)
{
struct btrfs_backref_iter *ret;

ret = kzalloc(sizeof(*ret), gfp_flag);
if (!ret)
return NULL;

ret->path = btrfs_alloc_path();
if (!ret) {
kfree(ret);
return NULL;
}

/* Current backref iterator only supports iteration in commit root */
ret->path->search_commit_root = 1;
ret->path->skip_locking = 1;
ret->fs_info = fs_info;

return ret;
}

int btrfs_backref_iter_start(struct btrfs_backref_iter *iter, u64 bytenr)
{
struct btrfs_fs_info *fs_info = iter->fs_info;
struct btrfs_path *path = iter->path;
struct btrfs_extent_item *ei;
struct btrfs_key key;
int ret;

key.objectid = bytenr;
key.type = BTRFS_METADATA_ITEM_KEY;
key.offset = (u64)-1;
iter->bytenr = bytenr;

ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, path, 0, 0);
if (ret < 0)
return ret;
if (ret == 0) {
ret = -EUCLEAN;
goto release;
}
if (path->slots[0] == 0) {
WARN_ON(IS_ENABLED(CONFIG_BTRFS_DEBUG));
ret = -EUCLEAN;
goto release;
}
path->slots[0]--;

btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
if ((key.type != BTRFS_EXTENT_ITEM_KEY &&
key.type != BTRFS_METADATA_ITEM_KEY) || key.objectid != bytenr) {
ret = -ENOENT;
goto release;
}
memcpy(&iter->cur_key, &key, sizeof(key));
iter->item_ptr = (u32)btrfs_item_ptr_offset(path->nodes[0],
path->slots[0]);
iter->end_ptr = (u32)(iter->item_ptr +
btrfs_item_size_nr(path->nodes[0], path->slots[0]));
ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_extent_item);

/*
* Only support iteration on tree backref yet.
*
* This is an extra precaution for non skinny-metadata, where
* EXTENT_ITEM is also used for tree blocks, that we can only use
* extent flags to determine if it's a tree block.
*/
if (btrfs_extent_flags(path->nodes[0], ei) & BTRFS_EXTENT_FLAG_DATA) {
ret = -ENOTSUPP;
goto release;
}
iter->cur_ptr = (u32)(iter->item_ptr + sizeof(*ei));

/* If there is no inline backref, go search for keyed backref */
if (iter->cur_ptr >= iter->end_ptr) {
ret = btrfs_next_item(fs_info->extent_root, path);

/* No inline nor keyed ref */
if (ret > 0) {
ret = -ENOENT;
goto release;
}
if (ret < 0)
goto release;

btrfs_item_key_to_cpu(path->nodes[0], &iter->cur_key,
path->slots[0]);
if (iter->cur_key.objectid != bytenr ||
(iter->cur_key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
iter->cur_key.type != BTRFS_TREE_BLOCK_REF_KEY)) {
ret = -ENOENT;
goto release;
}
iter->cur_ptr = (u32)btrfs_item_ptr_offset(path->nodes[0],
path->slots[0]);
iter->item_ptr = iter->cur_ptr;
iter->end_ptr = (u32)(iter->item_ptr + btrfs_item_size_nr(
path->nodes[0], path->slots[0]));
}

return 0;
release:
btrfs_backref_iter_release(iter);
return ret;
}
38 changes: 38 additions & 0 deletions fs/btrfs/backref.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,42 @@ struct prelim_ref {
u64 wanted_disk_byte;
};

/*
* Iterate backrefs of one extent.
*
* Now it only supports iteration of tree block in commit root.
*/
struct btrfs_backref_iter {
u64 bytenr;
struct btrfs_path *path;
struct btrfs_fs_info *fs_info;
struct btrfs_key cur_key;
u32 item_ptr;
u32 cur_ptr;
u32 end_ptr;
};

struct btrfs_backref_iter *btrfs_backref_iter_alloc(
struct btrfs_fs_info *fs_info, gfp_t gfp_flag);

static inline void btrfs_backref_iter_free(struct btrfs_backref_iter *iter)
{
if (!iter)
return;
btrfs_free_path(iter->path);
kfree(iter);
}

int btrfs_backref_iter_start(struct btrfs_backref_iter *iter, u64 bytenr);

static inline void btrfs_backref_iter_release(struct btrfs_backref_iter *iter)
{
iter->bytenr = 0;
iter->item_ptr = 0;
iter->cur_ptr = 0;
iter->end_ptr = 0;
btrfs_release_path(iter->path);
memset(&iter->cur_key, 0, sizeof(iter->cur_key));
}

#endif

0 comments on commit a37f232

Please sign in to comment.