* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
-#define _XOPEN_SOURCE 600
-#define __USE_XOPEN2K
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+#include <stdbool.h>
#include "kerncompat.h"
#include "extent_io.h"
#include "list.h"
-
-u64 cache_soft_max = 1024 * 1024 * 256;
-u64 cache_hard_max = 1 * 1024 * 1024 * 1024;
+#include "ctree.h"
+#include "volumes.h"
+#include "utils.h"
+#include "internal.h"
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)
state = malloc(sizeof(*state));
if (!state)
return NULL;
+ state->cache_node.objectid = 0;
state->refs = 1;
state->state = 0;
- state->private = 0;
+ state->xprivate = 0;
return state;
}
-static void free_extent_state(struct extent_state *state)
+static void btrfs_free_extent_state(struct extent_state *state)
{
state->refs--;
BUG_ON(state->refs < 0);
free(state);
}
-void extent_io_tree_cleanup(struct extent_io_tree *tree)
+static void free_extent_state_func(struct cache_extent *cache)
{
struct extent_state *es;
+
+ es = container_of(cache, struct extent_state, cache_node);
+ 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;
- struct cache_extent *cache;
while(!list_empty(&tree->lru)) {
eb = list_entry(tree->lru.next, struct extent_buffer, lru);
- if (eb->refs != 1) {
- fprintf(stderr, "extent buffer leak: "
- "start %llu len %u\n",
+ if (eb->refs) {
+ fprintf(stderr,
+ "extent buffer leak: start %llu len %u\n",
(unsigned long long)eb->start, eb->len);
- eb->refs = 1;
+ free_extent_buffer_nocache(eb);
+ } else {
+ free_extent_buffer_final(eb);
}
- free_extent_buffer(eb);
- }
- while (1) {
- cache = find_first_cache_extent(&tree->state, 0);
- if (!cache)
- break;
- es = container_of(cache, struct extent_state, cache_node);
- remove_cache_extent(&tree->state, &es->cache_node);
- free_extent_state(es);
}
+
+ cache_tree_free_extents(&tree->state, free_extent_state_func);
}
static inline void update_extent_state(struct extent_state *state)
state->start = other->start;
update_extent_state(state);
remove_cache_extent(&tree->state, &other->cache_node);
- free_extent_state(other);
+ btrfs_free_extent_state(other);
}
}
other_node = next_cache_extent(&state->cache_node);
other->start = state->start;
update_extent_state(other);
remove_cache_extent(&tree->state, &state->cache_node);
- free_extent_state(state);
+ btrfs_free_extent_state(state);
}
}
return 0;
state->start = start;
state->end = end;
update_extent_state(state);
- ret = insert_existing_cache_extent(&tree->state, &state->cache_node);
+ ret = insert_cache_extent(&tree->state, &state->cache_node);
BUG_ON(ret);
merge_state(tree, state);
return 0;
update_extent_state(prealloc);
orig->start = split;
update_extent_state(orig);
- ret = insert_existing_cache_extent(&tree->state,
- &prealloc->cache_node);
+ ret = insert_cache_extent(&tree->state, &prealloc->cache_node);
BUG_ON(ret);
return 0;
}
state->state &= ~bits;
if (state->state == 0) {
remove_cache_extent(&tree->state, &state->cache_node);
- free_extent_state(state);
+ btrfs_free_extent_state(state);
} else {
merge_state(tree, state);
}
}
/*
- * set some bits on a range in the tree.
+ * clear some bits on a range in the tree.
*/
-int clear_extent_bits(struct extent_io_tree *tree, u64 start,
- u64 end, int bits, gfp_t mask)
+int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits)
{
struct extent_state *state;
struct extent_state *prealloc = NULL;
int set = 0;
again:
- prealloc = alloc_extent_state();
- if (!prealloc)
- return -ENOMEM;
+ if (!prealloc) {
+ prealloc = alloc_extent_state();
+ if (!prealloc)
+ return -ENOMEM;
+ }
/*
* this search will find the extents that end after
* our range starts
*/
- node = find_first_cache_extent(&tree->state, start);
+ node = search_cache_extent(&tree->state, start);
if (!node)
goto out;
state = container_of(node, struct extent_state, cache_node);
goto search_again;
out:
if (prealloc)
- free_extent_state(prealloc);
+ btrfs_free_extent_state(prealloc);
return set;
search_again:
/*
* set some bits on a range in the tree.
*/
-int set_extent_bits(struct extent_io_tree *tree, u64 start,
- u64 end, int bits, gfp_t mask)
+int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits)
{
struct extent_state *state;
struct extent_state *prealloc = NULL;
* this search will find the extents that end after
* our range starts
*/
- node = find_first_cache_extent(&tree->state, start);
+ node = search_cache_extent(&tree->state, start);
if (!node) {
err = insert_state(tree, prealloc, start, end, bits);
BUG_ON(err == -EEXIST);
prealloc = NULL;
out:
if (prealloc)
- free_extent_state(prealloc);
+ btrfs_free_extent_state(prealloc);
return err;
search_again:
if (start > end)
goto again;
}
-int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
- gfp_t mask)
+int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end)
{
- return set_extent_bits(tree, start, end, EXTENT_DIRTY, mask);
+ return set_extent_bits(tree, start, end, EXTENT_DIRTY);
}
-int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
- gfp_t mask)
+int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end)
{
- return clear_extent_bits(tree, start, end, EXTENT_DIRTY, mask);
+ return clear_extent_bits(tree, start, end, EXTENT_DIRTY);
}
int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
* this search will find all the extents that end after
* our range starts.
*/
- node = find_first_cache_extent(&tree->state, start);
+ node = search_cache_extent(&tree->state, start);
if (!node)
goto out;
struct cache_extent *node;
int bitset = 0;
- node = find_first_cache_extent(&tree->state, start);
+ node = search_cache_extent(&tree->state, start);
while (node && start <= end) {
state = container_of(node, struct extent_state, cache_node);
struct extent_state *state;
int ret = 0;
- node = find_first_cache_extent(&tree->state, start);
+ node = search_cache_extent(&tree->state, start);
if (!node) {
ret = -ENOENT;
goto out;
ret = -ENOENT;
goto out;
}
- state->private = private;
+ state->xprivate = private;
out:
return ret;
}
struct extent_state *state;
int ret = 0;
- node = find_first_cache_extent(&tree->state, start);
+ node = search_cache_extent(&tree->state, start);
if (!node) {
ret = -ENOENT;
goto out;
ret = -ENOENT;
goto out;
}
- *private = state->private;
+ *private = state->xprivate;
out:
return ret;
}
-static int free_some_buffers(struct extent_io_tree *tree)
-{
- u32 nrscan = 0;
- struct extent_buffer *eb;
- struct list_head *node, *next;
-
- if (tree->cache_size < cache_soft_max)
- return 0;
-
- list_for_each_safe(node, next, &tree->lru) {
- eb = list_entry(node, struct extent_buffer, lru);
- if (eb->refs == 1) {
- free_extent_buffer(eb);
- if (tree->cache_size < cache_hard_max)
- break;
- } else {
- list_move_tail(&eb->lru, &tree->lru);
- }
- if (nrscan++ > 64 && tree->cache_size < cache_hard_max)
- break;
- }
- return 0;
-}
-
static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
u64 bytenr, u32 blocksize)
{
struct extent_buffer *eb;
- int ret;
- eb = malloc(sizeof(struct extent_buffer) + blocksize);
- if (!eb) {
- BUG();
+ eb = calloc(1, sizeof(struct extent_buffer) + blocksize);
+ if (!eb)
return NULL;
- }
- memset(eb, 0, sizeof(struct extent_buffer) + blocksize);
eb->start = bytenr;
eb->len = blocksize;
- eb->refs = 2;
+ eb->refs = 1;
eb->flags = 0;
eb->tree = tree;
eb->fd = -1;
eb->dev_bytenr = (u64)-1;
eb->cache_node.start = bytenr;
eb->cache_node.size = blocksize;
+ INIT_LIST_HEAD(&eb->recow);
+
+ return eb;
+}
- free_some_buffers(tree);
- ret = insert_existing_cache_extent(&tree->cache, &eb->cache_node);
- if (ret) {
- free(eb);
+struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src)
+{
+ struct extent_buffer *new;
+
+ new = __alloc_extent_buffer(NULL, src->start, src->len);
+ if (!new)
return NULL;
+
+ copy_extent_buffer(new, src, 0, 0, src->len);
+ new->flags |= EXTENT_BUFFER_DUMMY;
+
+ return new;
+}
+
+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;
}
- list_add_tail(&eb->lru, &tree->lru);
- tree->cache_size += blocksize;
- return eb;
+ free(eb);
}
-void free_extent_buffer(struct extent_buffer *eb)
+static void free_extent_buffer_internal(struct extent_buffer *eb, bool free_now)
{
- if (!eb)
+ if (!eb || IS_ERR(eb))
return;
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);
- remove_cache_extent(&tree->cache, &eb->cache_node);
- BUG_ON(tree->cache_size < eb->len);
- tree->cache_size -= eb->len;
- free(eb);
+ list_del_init(&eb->recow);
+ 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)
{
struct extent_buffer *eb = NULL;
struct cache_extent *cache;
- cache = find_cache_extent(&tree->cache, bytenr, blocksize);
- if (cache && cache->start == bytenr && cache->size == blocksize) {
+ cache = lookup_cache_extent(&tree->cache, bytenr, blocksize);
+ if (cache && cache->start == bytenr &&
+ cache->size == blocksize) {
eb = container_of(cache, struct extent_buffer, cache_node);
list_move_tail(&eb->lru, &tree->lru);
eb->refs++;
struct extent_buffer *eb = NULL;
struct cache_extent *cache;
- cache = find_first_cache_extent(&tree->cache, start);
+ cache = search_cache_extent(&tree->cache, start);
if (cache) {
eb = container_of(cache, struct extent_buffer, cache_node);
list_move_tail(&eb->lru, &tree->lru);
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)
{
struct extent_buffer *eb;
struct cache_extent *cache;
- cache = find_cache_extent(&tree->cache, bytenr, blocksize);
- if (cache && cache->start == bytenr && cache->size == blocksize) {
+ cache = lookup_cache_extent(&tree->cache, bytenr, blocksize);
+ if (cache && cache->start == bytenr &&
+ cache->size == blocksize) {
eb = container_of(cache, struct extent_buffer, cache_node);
list_move_tail(&eb->lru, &tree->lru);
eb->refs++;
} else {
+ int ret;
+
if (cache) {
eb = container_of(cache, struct extent_buffer,
cache_node);
free_extent_buffer(eb);
}
eb = __alloc_extent_buffer(tree, bytenr, blocksize);
+ if (!eb)
+ return NULL;
+ ret = insert_cache_extent(&tree->cache, &eb->cache_node);
+ if (ret) {
+ free(eb);
+ return NULL;
+ }
+ 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;
}
-int read_extent_from_disk(struct extent_buffer *eb)
+int read_extent_from_disk(struct extent_buffer *eb,
+ unsigned long offset, unsigned long len)
{
int ret;
- ret = pread(eb->fd, eb->data, eb->len, eb->dev_bytenr);
- if (ret < 0)
+ ret = pread(eb->fd, eb->data + offset, len, eb->dev_bytenr);
+ if (ret < 0) {
+ ret = -errno;
goto out;
- if (ret != eb->len) {
+ }
+ if (ret != len) {
ret = -EIO;
goto out;
}
return ret;
}
-int set_extent_buffer_uptodate(struct extent_buffer *eb)
+int read_data_from_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
+ u64 bytes, int mirror)
{
- eb->flags |= EXTENT_UPTODATE;
- return 0;
-}
+ struct btrfs_multi_bio *multi = NULL;
+ struct btrfs_device *device;
+ u64 bytes_left = bytes;
+ u64 read_len;
+ u64 total_read = 0;
+ int ret;
+
+ while (bytes_left) {
+ read_len = bytes_left;
+ ret = btrfs_map_block(info, READ, offset, &read_len, &multi,
+ mirror, NULL);
+ if (ret) {
+ fprintf(stderr, "Couldn't map the block %Lu\n",
+ offset);
+ return -EIO;
+ }
+ device = multi->stripes[0].dev;
+
+ read_len = min(bytes_left, read_len);
+ if (device->fd <= 0) {
+ kfree(multi);
+ return -EIO;
+ }
+
+ ret = pread(device->fd, buf + total_read, read_len,
+ multi->stripes[0].physical);
+ kfree(multi);
+ if (ret < 0) {
+ fprintf(stderr, "Error reading %Lu, %d\n", offset,
+ ret);
+ return ret;
+ }
+ if (ret != read_len) {
+ fprintf(stderr, "Short read for %Lu, read %d, "
+ "read_len %Lu\n", offset, ret, read_len);
+ return -EIO;
+ }
+
+ bytes_left -= read_len;
+ offset += read_len;
+ total_read += read_len;
+ }
-int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
- struct extent_buffer *eb)
-{
- eb->flags &= ~EXTENT_UPTODATE;
return 0;
}
-int extent_buffer_uptodate(struct extent_buffer *eb)
+int write_data_to_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
+ u64 bytes, int mirror)
{
- if (!eb)
- return 0;
+ struct btrfs_multi_bio *multi = NULL;
+ struct btrfs_device *device;
+ u64 bytes_left = bytes;
+ u64 this_len;
+ u64 total_write = 0;
+ u64 *raid_map = NULL;
+ u64 dev_bytenr;
+ int dev_nr;
+ int ret = 0;
+
+ while (bytes_left > 0) {
+ this_len = bytes_left;
+ dev_nr = 0;
+
+ ret = btrfs_map_block(info, WRITE, offset, &this_len, &multi,
+ mirror, &raid_map);
+ if (ret) {
+ fprintf(stderr, "Couldn't map the block %Lu\n",
+ offset);
+ return -EIO;
+ }
+
+ if (raid_map) {
+ struct extent_buffer *eb;
+ u64 stripe_len = this_len;
+
+ this_len = min(this_len, bytes_left);
+ this_len = min(this_len, (u64)info->nodesize);
+
+ eb = malloc(sizeof(struct extent_buffer) + this_len);
+ if (!eb) {
+ fprintf(stderr, "cannot allocate memory for eb\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(eb, 0, sizeof(struct extent_buffer) + this_len);
+ eb->start = offset;
+ eb->len = this_len;
+
+ memcpy(eb->data, buf + total_write, this_len);
+ ret = write_raid56_with_parity(info, eb, multi,
+ stripe_len, raid_map);
+ BUG_ON(ret);
+
+ free(eb);
+ kfree(raid_map);
+ raid_map = NULL;
+ } else while (dev_nr < multi->num_stripes) {
+ device = multi->stripes[dev_nr].dev;
+ if (device->fd <= 0) {
+ kfree(multi);
+ return -EIO;
+ }
+
+ dev_bytenr = multi->stripes[dev_nr].physical;
+ this_len = min(this_len, bytes_left);
+ dev_nr++;
+
+ ret = pwrite(device->fd, buf + total_write, this_len, dev_bytenr);
+ if (ret != this_len) {
+ if (ret < 0) {
+ fprintf(stderr, "Error writing to "
+ "device %d\n", errno);
+ ret = errno;
+ kfree(multi);
+ return ret;
+ } else {
+ fprintf(stderr, "Short write\n");
+ kfree(multi);
+ return -EIO;
+ }
+ }
+ }
+
+ BUG_ON(bytes_left < this_len);
+
+ bytes_left -= this_len;
+ offset += this_len;
+ total_write += this_len;
- if (eb->flags & EXTENT_UPTODATE)
- return 1;
+ kfree(multi);
+ multi = NULL;
+ }
return 0;
+
+out:
+ kfree(raid_map);
+ return ret;
}
int set_extent_buffer_dirty(struct extent_buffer *eb)
struct extent_io_tree *tree = eb->tree;
if (!(eb->flags & EXTENT_DIRTY)) {
eb->flags |= EXTENT_DIRTY;
- set_extent_dirty(tree, eb->start, eb->start + eb->len - 1, 0);
+ set_extent_dirty(tree, eb->start, eb->start + eb->len - 1);
extent_buffer_get(eb);
}
return 0;
struct extent_io_tree *tree = eb->tree;
if (eb->flags & EXTENT_DIRTY) {
eb->flags &= ~EXTENT_DIRTY;
- clear_extent_dirty(tree, eb->start, eb->start + eb->len - 1, 0);
+ clear_extent_dirty(tree, eb->start, eb->start + eb->len - 1);
free_extent_buffer(eb);
}
return 0;
memcpy(dst->data + dst_offset, src->data + src_offset, len);
}
-void memcpy_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
- unsigned long src_offset, unsigned long len)
-{
- memcpy(dst->data + dst_offset, dst->data + src_offset, len);
-}
-
void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
unsigned long src_offset, unsigned long len)
{
{
memset(eb->data + start, c, len);
}
+
+int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
+ unsigned long nr)
+{
+ return le_test_bit(nr, (u8 *)eb->data + start);
+}