Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5eda7b5e authored by Chris Mason's avatar Chris Mason Committed by David Woodhouse
Browse files

Btrfs: Add the ability to find and remove dead roots after a crash.

parent 54aa1f4d
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -122,12 +122,12 @@ struct btrfs_super_block {
	u8 fsid[16];    /* FS specific uuid */
	__le64 blocknr; /* this block number */
	__le64 magic;
	__le32 blocksize;
	__le64 generation;
	__le64 root;
	__le64 total_blocks;
	__le64 blocks_used;
	__le64 root_dir_objectid;
	__le32 blocksize;
} __attribute__ ((__packed__));

/*
@@ -226,10 +226,12 @@ struct btrfs_root_item {
	struct btrfs_inode_item inode;
	__le64 root_dirid;
	__le64 blocknr;
	__le32 flags;
	__le64 block_limit;
	__le64 blocks_used;
	__le32 flags;
	__le32 refs;
	struct btrfs_disk_key drop_progress;
	u8 drop_level;
} __attribute__ ((__packed__));

#define BTRFS_FILE_EXTENT_REG 0
@@ -800,6 +802,16 @@ static inline void btrfs_set_root_refs(struct btrfs_root_item *item, u32 val)
	item->refs = cpu_to_le32(val);
}

static inline u32 btrfs_root_flags(struct btrfs_root_item *item)
{
	return le32_to_cpu(item->flags);
}

static inline void btrfs_set_root_flags(struct btrfs_root_item *item, u32 val)
{
	item->flags = cpu_to_le32(val);
}

static inline u64 btrfs_super_blocknr(struct btrfs_super_block *s)
{
	return le64_to_cpu(s->blocknr);
@@ -1076,6 +1088,7 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
		      *item);
int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
			 btrfs_root_item *item, struct btrfs_key *key);
int btrfs_find_dead_roots(struct btrfs_root *root);
/* dir-item.c */
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
			  *root, const char *name, int name_len, u64 dir,
+30 −14
Original line number Diff line number Diff line
@@ -326,7 +326,7 @@ static int find_and_setup_root(int blocksize,
	return 0;
}

struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_fs_info *fs_info,
					       struct btrfs_key *location)
{
	struct btrfs_root *root;
@@ -336,11 +336,7 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
	u64 highest_inode;
	int ret = 0;

	root = radix_tree_lookup(&fs_info->fs_roots_radix,
				 (unsigned long)location->objectid);
	if (root)
		return root;
	root = kmalloc(sizeof(*root), GFP_NOFS);
	root = kzalloc(sizeof(*root), GFP_NOFS);
	if (!root)
		return ERR_PTR(-ENOMEM);
	if (location->offset == (u64)-1) {
@@ -383,6 +379,28 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
	BUG_ON(!root->node);
insert:
	root->ref_cows = 1;
	ret = btrfs_find_highest_inode(root, &highest_inode);
	if (ret == 0) {
		root->highest_inode = highest_inode;
		root->last_inode_alloc = highest_inode;
	}
	return root;
}

struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
				      struct btrfs_key *location)
{
	struct btrfs_root *root;
	int ret;

	root = radix_tree_lookup(&fs_info->fs_roots_radix,
				 (unsigned long)location->objectid);
	if (root)
		return root;

	root = btrfs_read_fs_root_no_radix(fs_info, location);
	if (IS_ERR(root))
		return root;
	ret = radix_tree_insert(&fs_info->fs_roots_radix,
				(unsigned long)root->root_key.objectid,
				root);
@@ -391,11 +409,6 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
		kfree(root);
		return ERR_PTR(ret);
	}
	ret = btrfs_find_highest_inode(root, &highest_inode);
	if (ret == 0) {
		root->highest_inode = highest_inode;
		root->last_inode_alloc = highest_inode;
	}
	return root;
}

@@ -489,6 +502,9 @@ struct btrfs_root *open_ctree(struct super_block *sb)
	btrfs_read_block_groups(extent_root);

	fs_info->generation = btrfs_super_generation(disk_super) + 1;
	ret = btrfs_find_dead_roots(tree_root);
	if (ret)
		goto fail_tree_root;
	mutex_unlock(&fs_info->fs_mutex);
	return tree_root;

@@ -538,7 +554,7 @@ int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root
	return 0;
}

static int free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
{
	radix_tree_delete(&fs_info->fs_roots_radix,
			  (unsigned long)root->root_key.objectid);
@@ -565,7 +581,7 @@ static int del_fs_roots(struct btrfs_fs_info *fs_info)
		if (!ret)
			break;
		for (i = 0; i < ret; i++)
			free_fs_root(fs_info, gang[i]);
			btrfs_free_fs_root(fs_info, gang[i]);
	}
	return 0;
}
+3 −0
Original line number Diff line number Diff line
@@ -65,6 +65,8 @@ int btrfs_csum_data(struct btrfs_root * root, char *data, size_t len,
		    char *result);
struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
				      struct btrfs_key *location);
struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_fs_info *fs_info,
					       struct btrfs_key *location);
u64 bh_blocknr(struct buffer_head *bh);
int btrfs_insert_dev_radix(struct btrfs_root *root,
			   struct block_device *bdev,
@@ -75,4 +77,5 @@ int btrfs_map_bh_to_logical(struct btrfs_root *root, struct buffer_head *bh,
			     u64 logical);
int btrfs_releasepage(struct page *page, gfp_t flags);
void btrfs_btree_balance_dirty(struct btrfs_root *root);
int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root);
#endif
+2 −0
Original line number Diff line number Diff line
@@ -2028,6 +2028,8 @@ static int create_subvol(struct btrfs_root *root, char *name, int namelen)

	btrfs_set_root_blocknr(&root_item, bh_blocknr(subvol));
	btrfs_set_root_refs(&root_item, 1);
	memset(&root_item.drop_progress, 0, sizeof(root_item.drop_progress));
	root_item.drop_level = 0;
	brelse(subvol);
	subvol = NULL;

+66 −9
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include <linux/module.h>
#include "ctree.h"
#include "transaction.h"
#include "disk-io.h"
#include "print-tree.h"

@@ -32,7 +33,7 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,

	search_key.objectid = objectid;
	search_key.flags = (u32)-1;
	search_key.offset = (u32)-1;
	search_key.offset = (u64)-1;

	path = btrfs_alloc_path();
	BUG_ON(!path);
@@ -50,6 +51,7 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
	memcpy(item, btrfs_item_ptr(l, slot, struct btrfs_root_item),
		sizeof(*item));
	btrfs_disk_key_to_cpu(key, &l->items[slot].key);
printk("find last finds key %Lu %u %Lu slot %d search for obj %Lu\n", key->objectid, key->flags, key->offset, slot, objectid);
	ret = 0;
out:
	btrfs_release_path(root, path);
@@ -93,6 +95,67 @@ int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
	return ret;
}

int btrfs_find_dead_roots(struct btrfs_root *root)
{
	struct btrfs_root *dead_root;
	struct btrfs_item *item;
	struct btrfs_root_item *ri;
	struct btrfs_key key;
	struct btrfs_path *path;
	int ret;
	u32 nritems;
	struct btrfs_leaf *leaf;
	int slot;

	key.objectid = 0;
	key.flags = 0;
	btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
	key.offset = 0;
	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;
	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
	if (ret < 0)
		goto err;
	while(1) {
		leaf = btrfs_buffer_leaf(path->nodes[0]);
		nritems = btrfs_header_nritems(&leaf->header);
		slot = path->slots[0];
		if (slot >= nritems) {
			ret = btrfs_next_leaf(root, path);
			if (ret)
				break;
			leaf = btrfs_buffer_leaf(path->nodes[0]);
			nritems = btrfs_header_nritems(&leaf->header);
			slot = path->slots[0];
		}
		item = leaf->items + slot;
		btrfs_disk_key_to_cpu(&key, &item->key);
		if (btrfs_key_type(&key) != BTRFS_ROOT_ITEM_KEY)
			goto next;
		ri = btrfs_item_ptr(leaf, slot, struct btrfs_root_item);
		if (btrfs_root_refs(ri) != 0)
			goto next;
		dead_root = btrfs_read_fs_root_no_radix(root->fs_info, &key);
		if (IS_ERR(root)) {
			ret = PTR_ERR(root);
			goto err;
		}
printk("found dead root %Lu %u %Lu\n", key.objectid, key.flags, key.offset);
		ret = btrfs_add_dead_root(dead_root,
					  &root->fs_info->dead_roots);
		if (ret)
			goto err;
next:
		slot++;
		path->slots[0]++;
	}
	ret = 0;
err:
	btrfs_free_path(path);
	return ret;
}

int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
		   struct btrfs_key *key)
{
@@ -111,14 +174,8 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
			    path->slots[0], struct btrfs_root_item);

	refs = btrfs_root_refs(ri);
	BUG_ON(refs == 0);
	if (refs == 1) {
	BUG_ON(refs != 0);
	ret = btrfs_del_item(trans, root, path);
	} else {
		btrfs_set_root_refs(ri, refs - 1);
		WARN_ON(1);
		mark_buffer_dirty(path->nodes[0]);
	}
out:
	btrfs_release_path(root, path);
	btrfs_free_path(path);
Loading