1025 lines · 25.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2014 Facebook.  All rights reserved. */ #include <linux/sched.h>#include <linux/stacktrace.h>#include "messages.h"#include "ctree.h"#include "disk-io.h"#include "locking.h"#include "delayed-ref.h"#include "ref-verify.h"#include "fs.h"#include "accessors.h" /* * Used to keep track the roots and number of refs each root has for a given * bytenr.  This just tracks the number of direct references, no shared * references. */struct root_entry {	u64 root_objectid;	u64 num_refs;	struct rb_node node;}; /* * These are meant to represent what should exist in the extent tree, these can * be used to verify the extent tree is consistent as these should all match * what the extent tree says. */struct ref_entry {	u64 root_objectid;	u64 parent;	u64 owner;	u64 offset;	u64 num_refs;	struct rb_node node;}; #define MAX_TRACE	16 /* * Whenever we add/remove a reference we record the action.  The action maps * back to the delayed ref action.  We hold the ref we are changing in the * action so we can account for the history properly, and we record the root we * were called with since it could be different from ref_root.  We also store * stack traces because that's how I roll. */struct ref_action {	int action;	u64 root;	struct ref_entry ref;	struct list_head list;	unsigned long trace[MAX_TRACE];	unsigned int trace_len;}; /* * One of these for every block we reference, it holds the roots and references * to it as well as all of the ref actions that have occurred to it.  We never * free it until we unmount the file system in order to make sure re-allocations * are happening properly. */struct block_entry {	u64 bytenr;	u64 len;	u64 num_refs;	int metadata;	int from_disk;	struct rb_root roots;	struct rb_root refs;	struct rb_node node;	struct list_head actions;}; static int block_entry_bytenr_key_cmp(const void *key, const struct rb_node *node){	const u64 *bytenr = key;	const struct block_entry *entry = rb_entry(node, struct block_entry, node); 	if (entry->bytenr < *bytenr)		return 1;	else if (entry->bytenr > *bytenr)		return -1; 	return 0;} static int block_entry_bytenr_cmp(struct rb_node *new, const struct rb_node *existing){	const struct block_entry *new_entry = rb_entry(new, struct block_entry, node); 	return block_entry_bytenr_key_cmp(&new_entry->bytenr, existing);} static struct block_entry *insert_block_entry(struct rb_root *root,					      struct block_entry *be){	struct rb_node *node; 	node = rb_find_add(&be->node, root, block_entry_bytenr_cmp);	return rb_entry_safe(node, struct block_entry, node);} static struct block_entry *lookup_block_entry(struct rb_root *root, u64 bytenr){	struct rb_node *node; 	node = rb_find(&bytenr, root, block_entry_bytenr_key_cmp);	return rb_entry_safe(node, struct block_entry, node);} static int root_entry_root_objectid_key_cmp(const void *key, const struct rb_node *node){	const u64 *objectid = key;	const struct root_entry *entry = rb_entry(node, struct root_entry, node); 	if (entry->root_objectid < *objectid)		return 1;	else if (entry->root_objectid > *objectid)		return -1; 	return 0;} static int root_entry_root_objectid_cmp(struct rb_node *new, const struct rb_node *existing){	const struct root_entry *new_entry = rb_entry(new, struct root_entry, node); 	return root_entry_root_objectid_key_cmp(&new_entry->root_objectid, existing);} static struct root_entry *insert_root_entry(struct rb_root *root,					    struct root_entry *re){	struct rb_node *node; 	node = rb_find_add(&re->node, root, root_entry_root_objectid_cmp);	return rb_entry_safe(node, struct root_entry, node);} static int comp_refs(struct ref_entry *ref1, struct ref_entry *ref2){	if (ref1->root_objectid < ref2->root_objectid)		return -1;	if (ref1->root_objectid > ref2->root_objectid)		return 1;	if (ref1->parent < ref2->parent)		return -1;	if (ref1->parent > ref2->parent)		return 1;	if (ref1->owner < ref2->owner)		return -1;	if (ref1->owner > ref2->owner)		return 1;	if (ref1->offset < ref2->offset)		return -1;	if (ref1->offset > ref2->offset)		return 1;	return 0;} static int ref_entry_cmp(struct rb_node *new, const struct rb_node *existing){	struct ref_entry *new_entry = rb_entry(new, struct ref_entry, node);	struct ref_entry *existing_entry = rb_entry(existing, struct ref_entry, node); 	return comp_refs(new_entry, existing_entry);} static struct ref_entry *insert_ref_entry(struct rb_root *root,					  struct ref_entry *ref){	struct rb_node *node; 	node = rb_find_add(&ref->node, root, ref_entry_cmp);	return rb_entry_safe(node, struct ref_entry, node);} static struct root_entry *lookup_root_entry(struct rb_root *root, u64 objectid){	struct rb_node *node; 	node = rb_find(&objectid, root, root_entry_root_objectid_key_cmp);	return rb_entry_safe(node, struct root_entry, node);} #ifdef CONFIG_STACKTRACEstatic void __save_stack_trace(struct ref_action *ra){	ra->trace_len = stack_trace_save(ra->trace, MAX_TRACE, 2);} static void __print_stack_trace(struct btrfs_fs_info *fs_info,				struct ref_action *ra){	if (ra->trace_len == 0) {		btrfs_err(fs_info, "  ref-verify: no stacktrace");		return;	}	stack_trace_print(ra->trace, ra->trace_len, 2);}#elsestatic inline void __save_stack_trace(struct ref_action *ra){} static inline void __print_stack_trace(struct btrfs_fs_info *fs_info,				       struct ref_action *ra){	btrfs_err(fs_info, "  ref-verify: no stacktrace support");}#endif static void free_block_entry(struct block_entry *be){	struct root_entry *re;	struct ref_entry *ref;	struct ref_action *ra;	struct rb_node *n; 	while ((n = rb_first(&be->roots))) {		re = rb_entry(n, struct root_entry, node);		rb_erase(&re->node, &be->roots);		kfree(re);	} 	while((n = rb_first(&be->refs))) {		ref = rb_entry(n, struct ref_entry, node);		rb_erase(&ref->node, &be->refs);		kfree(ref);	} 	while (!list_empty(&be->actions)) {		ra = list_first_entry(&be->actions, struct ref_action,				      list);		list_del(&ra->list);		kfree(ra);	}	kfree(be);} static struct block_entry *add_block_entry(struct btrfs_fs_info *fs_info,					   u64 bytenr, u64 len,					   u64 root_objectid){	struct block_entry *be = NULL, *exist;	struct root_entry *re = NULL; 	re = kzalloc_obj(struct root_entry, GFP_NOFS);	be = kzalloc_obj(struct block_entry, GFP_NOFS);	if (!be || !re) {		kfree(re);		kfree(be);		return ERR_PTR(-ENOMEM);	}	be->bytenr = bytenr;	be->len = len; 	re->root_objectid = root_objectid;	re->num_refs = 0; 	spin_lock(&fs_info->ref_verify_lock);	exist = insert_block_entry(&fs_info->block_tree, be);	if (exist) {		if (root_objectid) {			struct root_entry *exist_re; 			exist_re = insert_root_entry(&exist->roots, re);			if (exist_re)				kfree(re);		} else {			kfree(re);		}		kfree(be);		return exist;	} 	be->num_refs = 0;	be->metadata = 0;	be->from_disk = 0;	be->roots = RB_ROOT;	be->refs = RB_ROOT;	INIT_LIST_HEAD(&be->actions);	if (root_objectid)		insert_root_entry(&be->roots, re);	else		kfree(re);	return be;} static int add_tree_block(struct btrfs_fs_info *fs_info, u64 ref_root,			  u64 parent, u64 bytenr, int level){	struct block_entry *be;	struct root_entry *re;	struct ref_entry *ref = NULL, *exist; 	ref = kmalloc_obj(struct ref_entry, GFP_NOFS);	if (!ref)		return -ENOMEM; 	if (parent)		ref->root_objectid = 0;	else		ref->root_objectid = ref_root;	ref->parent = parent;	ref->owner = level;	ref->offset = 0;	ref->num_refs = 1; 	be = add_block_entry(fs_info, bytenr, fs_info->nodesize, ref_root);	if (IS_ERR(be)) {		kfree(ref);		return PTR_ERR(be);	}	be->num_refs++;	be->from_disk = 1;	be->metadata = 1; 	if (!parent) {		ASSERT(ref_root);		re = lookup_root_entry(&be->roots, ref_root);		ASSERT(re);		re->num_refs++;	}	exist = insert_ref_entry(&be->refs, ref);	if (exist) {		exist->num_refs++;		kfree(ref);	}	spin_unlock(&fs_info->ref_verify_lock); 	return 0;} static int add_shared_data_ref(struct btrfs_fs_info *fs_info,			       u64 parent, u32 num_refs, u64 bytenr,			       u64 num_bytes){	struct block_entry *be;	struct ref_entry *ref; 	ref = kzalloc_obj(struct ref_entry, GFP_NOFS);	if (!ref)		return -ENOMEM;	be = add_block_entry(fs_info, bytenr, num_bytes, 0);	if (IS_ERR(be)) {		kfree(ref);		return PTR_ERR(be);	}	be->num_refs += num_refs; 	ref->parent = parent;	ref->num_refs = num_refs;	if (insert_ref_entry(&be->refs, ref)) {		spin_unlock(&fs_info->ref_verify_lock);		btrfs_err(fs_info, "existing shared ref when reading from disk?");		kfree(ref);		return -EINVAL;	}	spin_unlock(&fs_info->ref_verify_lock);	return 0;} static int add_extent_data_ref(struct btrfs_fs_info *fs_info,			       struct extent_buffer *leaf,			       struct btrfs_extent_data_ref *dref,			       u64 bytenr, u64 num_bytes){	struct block_entry *be;	struct ref_entry *ref;	struct root_entry *re;	u64 ref_root = btrfs_extent_data_ref_root(leaf, dref);	u64 owner = btrfs_extent_data_ref_objectid(leaf, dref);	u64 offset = btrfs_extent_data_ref_offset(leaf, dref);	u32 num_refs = btrfs_extent_data_ref_count(leaf, dref); 	ref = kzalloc_obj(struct ref_entry, GFP_NOFS);	if (!ref)		return -ENOMEM;	be = add_block_entry(fs_info, bytenr, num_bytes, ref_root);	if (IS_ERR(be)) {		kfree(ref);		return PTR_ERR(be);	}	be->num_refs += num_refs; 	ref->parent = 0;	ref->owner = owner;	ref->root_objectid = ref_root;	ref->offset = offset;	ref->num_refs = num_refs;	if (insert_ref_entry(&be->refs, ref)) {		spin_unlock(&fs_info->ref_verify_lock);		btrfs_err(fs_info, "existing ref when reading from disk?");		kfree(ref);		return -EINVAL;	} 	re = lookup_root_entry(&be->roots, ref_root);	if (!re) {		spin_unlock(&fs_info->ref_verify_lock);		btrfs_err(fs_info, "missing root in new block entry?");		return -EINVAL;	}	re->num_refs += num_refs;	spin_unlock(&fs_info->ref_verify_lock);	return 0;} static int process_extent_item(struct btrfs_fs_info *fs_info,			       struct btrfs_path *path, struct btrfs_key *key,			       int slot, int *tree_block_level){	struct btrfs_extent_item *ei;	struct btrfs_extent_inline_ref *iref;	struct btrfs_extent_data_ref *dref;	struct btrfs_shared_data_ref *sref;	struct extent_buffer *leaf = path->nodes[0];	u32 item_size = btrfs_item_size(leaf, slot);	unsigned long end, ptr;	u64 offset, flags, count;	int type;	int ret = 0; 	ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);	flags = btrfs_extent_flags(leaf, ei); 	if ((key->type == BTRFS_EXTENT_ITEM_KEY) &&	    flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {		struct btrfs_tree_block_info *info; 		info = (struct btrfs_tree_block_info *)(ei + 1);		*tree_block_level = btrfs_tree_block_level(leaf, info);		iref = (struct btrfs_extent_inline_ref *)(info + 1);	} else {		if (key->type == BTRFS_METADATA_ITEM_KEY)			*tree_block_level = key->offset;		iref = (struct btrfs_extent_inline_ref *)(ei + 1);	} 	ptr = (unsigned long)iref;	end = (unsigned long)ei + item_size;	while (ptr < end) {		iref = (struct btrfs_extent_inline_ref *)ptr;		type = btrfs_extent_inline_ref_type(leaf, iref);		offset = btrfs_extent_inline_ref_offset(leaf, iref);		switch (type) {		case BTRFS_TREE_BLOCK_REF_KEY:			ret = add_tree_block(fs_info, offset, 0, key->objectid,					     *tree_block_level);			break;		case BTRFS_SHARED_BLOCK_REF_KEY:			ret = add_tree_block(fs_info, 0, offset, key->objectid,					     *tree_block_level);			break;		case BTRFS_EXTENT_DATA_REF_KEY:			dref = (struct btrfs_extent_data_ref *)(&iref->offset);			ret = add_extent_data_ref(fs_info, leaf, dref,						  key->objectid, key->offset);			break;		case BTRFS_SHARED_DATA_REF_KEY:			sref = (struct btrfs_shared_data_ref *)(iref + 1);			count = btrfs_shared_data_ref_count(leaf, sref);			ret = add_shared_data_ref(fs_info, offset, count,						  key->objectid, key->offset);			break;		case BTRFS_EXTENT_OWNER_REF_KEY:			if (!btrfs_fs_incompat(fs_info, SIMPLE_QUOTA)) {				btrfs_err(fs_info,			  "found extent owner ref without simple quotas enabled");				ret = -EINVAL;			}			break;		default:			btrfs_err(fs_info, "invalid key type in iref");			ret = -EINVAL;			break;		}		if (ret)			break;		ptr += btrfs_extent_inline_ref_size(type);	}	return ret;} static int process_leaf(struct btrfs_root *root,			struct btrfs_path *path, u64 *bytenr, u64 *num_bytes,			int *tree_block_level){	struct btrfs_fs_info *fs_info = root->fs_info;	struct extent_buffer *leaf = path->nodes[0];	struct btrfs_extent_data_ref *dref;	struct btrfs_shared_data_ref *sref;	u32 count;	int i = 0, ret = 0;	struct btrfs_key key;

Lines 1–500 of 1025.