btrfs-progs: free-space-cache: Enhance free space cache free space check
[platform/upstream/btrfs-progs.git] / extent_io.c
index 1c6c142..eda1fb6 100644 (file)
 #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;
 }