232 lines · 5.5 KB · C
RawDownload
/* * Copyright (C) 2008 Oracle.  All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License v2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. */ #include <linux/sched.h>#include <linux/slab.h>#include <linux/sort.h>#include "ctree.h"#include "ref-cache.h"#include "transaction.h" /* * leaf refs are used to cache the information about which extents * a given leaf has references on.  This allows us to process that leaf * in btrfs_drop_snapshot without needing to read it back from disk. */ /* * kmalloc a leaf reference struct and update the counters for the * total ref cache size */struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(struct btrfs_root *root,					    int nr_extents){	struct btrfs_leaf_ref *ref;	size_t size = btrfs_leaf_ref_size(nr_extents); 	ref = kmalloc(size, GFP_NOFS);	if (ref) {		spin_lock(&root->fs_info->ref_cache_lock);		root->fs_info->total_ref_cache_size += size;		spin_unlock(&root->fs_info->ref_cache_lock); 		memset(ref, 0, sizeof(*ref));		atomic_set(&ref->usage, 1);		INIT_LIST_HEAD(&ref->list);	}	return ref;} /* * free a leaf reference struct and update the counters for the * total ref cache size */void btrfs_free_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref){	if (!ref)		return;	WARN_ON(atomic_read(&ref->usage) == 0);	if (atomic_dec_and_test(&ref->usage)) {		size_t size = btrfs_leaf_ref_size(ref->nritems); 		BUG_ON(ref->in_tree);		kfree(ref); 		spin_lock(&root->fs_info->ref_cache_lock);		root->fs_info->total_ref_cache_size -= size;		spin_unlock(&root->fs_info->ref_cache_lock);	}} static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr,				   struct rb_node *node){	struct rb_node **p = &root->rb_node;	struct rb_node *parent = NULL;	struct btrfs_leaf_ref *entry; 	while (*p) {		parent = *p;		entry = rb_entry(parent, struct btrfs_leaf_ref, rb_node); 		if (bytenr < entry->bytenr)			p = &(*p)->rb_left;		else if (bytenr > entry->bytenr)			p = &(*p)->rb_right;		else			return parent;	} 	entry = rb_entry(node, struct btrfs_leaf_ref, rb_node);	rb_link_node(node, parent, p);	rb_insert_color(node, root);	return NULL;} static struct rb_node *tree_search(struct rb_root *root, u64 bytenr){	struct rb_node *n = root->rb_node;	struct btrfs_leaf_ref *entry; 	while (n) {		entry = rb_entry(n, struct btrfs_leaf_ref, rb_node);		WARN_ON(!entry->in_tree); 		if (bytenr < entry->bytenr)			n = n->rb_left;		else if (bytenr > entry->bytenr)			n = n->rb_right;		else			return n;	}	return NULL;} int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen,			   int shared){	struct btrfs_leaf_ref *ref = NULL;	struct btrfs_leaf_ref_tree *tree = root->ref_tree; 	if (shared)		tree = &root->fs_info->shared_ref_tree;	if (!tree)		return 0; 	spin_lock(&tree->lock);	while (!list_empty(&tree->list)) {		ref = list_entry(tree->list.next, struct btrfs_leaf_ref, list);		BUG_ON(ref->tree != tree);		if (ref->root_gen > max_root_gen)			break;		if (!xchg(&ref->in_tree, 0)) {			cond_resched_lock(&tree->lock);			continue;		} 		rb_erase(&ref->rb_node, &tree->root);		list_del_init(&ref->list); 		spin_unlock(&tree->lock);		btrfs_free_leaf_ref(root, ref);		cond_resched();		spin_lock(&tree->lock);	}	spin_unlock(&tree->lock);	return 0;} /* * find the leaf ref for a given extent.  This returns the ref struct with * a usage reference incremented */struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root,					     u64 bytenr){	struct rb_node *rb;	struct btrfs_leaf_ref *ref = NULL;	struct btrfs_leaf_ref_tree *tree = root->ref_tree;again:	if (tree) {		spin_lock(&tree->lock);		rb = tree_search(&tree->root, bytenr);		if (rb)			ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node);		if (ref)			atomic_inc(&ref->usage);		spin_unlock(&tree->lock);		if (ref)			return ref;	}	if (tree != &root->fs_info->shared_ref_tree) {		tree = &root->fs_info->shared_ref_tree;		goto again;	}	return NULL;} /* * add a fully filled in leaf ref struct * remove all the refs older than a given root generation */int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref,		       int shared){	int ret = 0;	struct rb_node *rb;	struct btrfs_leaf_ref_tree *tree = root->ref_tree; 	if (shared)		tree = &root->fs_info->shared_ref_tree; 	spin_lock(&tree->lock);	rb = tree_insert(&tree->root, ref->bytenr, &ref->rb_node);	if (rb) {		ret = -EEXIST;	} else {		atomic_inc(&ref->usage);		ref->tree = tree;		ref->in_tree = 1;		list_add_tail(&ref->list, &tree->list);	}	spin_unlock(&tree->lock);	return ret;} /* * remove a single leaf ref from the tree.  This drops the ref held by the tree * only */int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref){	struct btrfs_leaf_ref_tree *tree; 	if (!xchg(&ref->in_tree, 0))		return 0; 	tree = ref->tree;	spin_lock(&tree->lock); 	rb_erase(&ref->rb_node, &tree->root);	list_del_init(&ref->list); 	spin_unlock(&tree->lock); 	btrfs_free_leaf_ref(root, ref);	return 0;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t