summaryrefslogtreecommitdiff
path: root/extent_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'extent_io.c')
-rw-r--r--extent_io.c73
1 files changed, 60 insertions, 13 deletions
diff --git a/extent_io.c b/extent_io.c
index 1c6c1425..eda1fb6f 100644
--- a/extent_io.c
+++ b/extent_io.c
@@ -22,11 +22,13 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+#include <stdbool.h>
#include "kerncompat.h"
#include "extent_io.h"
#include "list.h"
#include "ctree.h"
#include "volumes.h"
+#include "utils.h"
#include "internal.h"
void extent_io_tree_init(struct extent_io_tree *tree)
@@ -35,6 +37,14 @@ void extent_io_tree_init(struct extent_io_tree *tree)
cache_tree_init(&tree->cache);
INIT_LIST_HEAD(&tree->lru);
tree->cache_size = 0;
+ tree->max_cache_size = (u64)total_memory() / 4;
+}
+
+void extent_io_tree_init_cache_max(struct extent_io_tree *tree,
+ u64 max_cache_size)
+{
+ extent_io_tree_init(tree);
+ tree->max_cache_size = max_cache_size;
}
static struct extent_state *alloc_extent_state(void)
@@ -67,16 +77,21 @@ static void free_extent_state_func(struct cache_extent *cache)
btrfs_free_extent_state(es);
}
+static void free_extent_buffer_final(struct extent_buffer *eb);
void extent_io_tree_cleanup(struct extent_io_tree *tree)
{
struct extent_buffer *eb;
while(!list_empty(&tree->lru)) {
eb = list_entry(tree->lru.next, struct extent_buffer, lru);
- fprintf(stderr, "extent buffer leak: "
- "start %llu len %u\n",
- (unsigned long long)eb->start, eb->len);
- free_extent_buffer(eb);
+ if (eb->refs) {
+ fprintf(stderr,
+ "extent buffer leak: start %llu len %u\n",
+ (unsigned long long)eb->start, eb->len);
+ free_extent_buffer_nocache(eb);
+ } else {
+ free_extent_buffer_final(eb);
+ }
}
cache_tree_free_extents(&tree->state, free_extent_state_func);
@@ -567,7 +582,21 @@ struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src)
return new;
}
-void free_extent_buffer(struct extent_buffer *eb)
+static void free_extent_buffer_final(struct extent_buffer *eb)
+{
+ struct extent_io_tree *tree = eb->tree;
+
+ BUG_ON(eb->refs);
+ BUG_ON(tree->cache_size < eb->len);
+ list_del_init(&eb->lru);
+ if (!(eb->flags & EXTENT_BUFFER_DUMMY)) {
+ remove_cache_extent(&tree->cache, &eb->cache_node);
+ tree->cache_size -= eb->len;
+ }
+ free(eb);
+}
+
+static void free_extent_buffer_internal(struct extent_buffer *eb, bool free_now)
{
if (!eb || IS_ERR(eb))
return;
@@ -575,19 +604,23 @@ void free_extent_buffer(struct extent_buffer *eb)
eb->refs--;
BUG_ON(eb->refs < 0);
if (eb->refs == 0) {
- struct extent_io_tree *tree = eb->tree;
BUG_ON(eb->flags & EXTENT_DIRTY);
- list_del_init(&eb->lru);
list_del_init(&eb->recow);
- if (!(eb->flags & EXTENT_BUFFER_DUMMY)) {
- BUG_ON(tree->cache_size < eb->len);
- remove_cache_extent(&tree->cache, &eb->cache_node);
- tree->cache_size -= eb->len;
- }
- free(eb);
+ if (eb->flags & EXTENT_BUFFER_DUMMY || free_now)
+ free_extent_buffer_final(eb);
}
}
+void free_extent_buffer(struct extent_buffer *eb)
+{
+ free_extent_buffer_internal(eb, 0);
+}
+
+void free_extent_buffer_nocache(struct extent_buffer *eb)
+{
+ free_extent_buffer_internal(eb, 1);
+}
+
struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
u64 bytenr, u32 blocksize)
{
@@ -619,6 +652,18 @@ struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree,
return eb;
}
+static void trim_extent_buffer_cache(struct extent_io_tree *tree)
+{
+ struct extent_buffer *eb, *tmp;
+
+ list_for_each_entry_safe(eb, tmp, &tree->lru, lru) {
+ if (eb->refs == 0)
+ free_extent_buffer_final(eb);
+ if (tree->cache_size <= ((tree->max_cache_size * 9) / 10))
+ break;
+ }
+}
+
struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
u64 bytenr, u32 blocksize)
{
@@ -649,6 +694,8 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
}
list_add_tail(&eb->lru, &tree->lru);
tree->cache_size += blocksize;
+ if (tree->cache_size >= tree->max_cache_size)
+ trim_extent_buffer_cache(tree);
}
return eb;
}