X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=extent_io.c;h=198492699438f06a4e8bee5c7a39965a55c484d0;hb=f96ca97fb451801d899650a4d03f6a96fa8c59fb;hp=ebb35b28c44e98547adc2d61877686e8b29419fb;hpb=b7ad5a84565285ccb3d0b4eb65e1c0b30f62d7cd;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/extent_io.c b/extent_io.c index ebb35b2..1984926 100644 --- a/extent_io.c +++ b/extent_io.c @@ -16,20 +16,20 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. */ -#define _XOPEN_SOURCE 600 -#define __USE_XOPEN2K #include #include #include #include #include #include +#include #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) { @@ -37,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) @@ -46,13 +54,14 @@ 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); @@ -60,30 +69,32 @@ static void free_extent_state(struct extent_state *state) 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) @@ -116,7 +127,7 @@ static int merge_state(struct extent_io_tree *tree, 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); @@ -128,7 +139,7 @@ static int merge_state(struct extent_io_tree *tree, 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; @@ -149,7 +160,7 @@ static int insert_state(struct extent_io_tree *tree, 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; @@ -170,8 +181,7 @@ static int split_state(struct extent_io_tree *tree, struct extent_state *orig, 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; } @@ -187,7 +197,7 @@ static int clear_state_bit(struct extent_io_tree *tree, 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); } @@ -195,10 +205,9 @@ static int clear_state_bit(struct extent_io_tree *tree, } /* - * 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; @@ -208,15 +217,17 @@ int clear_extent_bits(struct extent_io_tree *tree, u64 start, 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); @@ -278,7 +289,7 @@ again: goto search_again; out: if (prealloc) - free_extent_state(prealloc); + btrfs_free_extent_state(prealloc); return set; search_again: @@ -290,8 +301,7 @@ 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; @@ -300,15 +310,17 @@ int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 last_start; u64 last_end; 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) { err = insert_state(tree, prealloc, start, end, bits); BUG_ON(err == -EEXIST); @@ -404,7 +416,7 @@ again: prealloc = NULL; out: if (prealloc) - free_extent_state(prealloc); + btrfs_free_extent_state(prealloc); return err; search_again: if (start > end) @@ -412,16 +424,14 @@ search_again: 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, @@ -435,7 +445,7 @@ 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; @@ -462,7 +472,7 @@ int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, 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); @@ -499,7 +509,7 @@ int set_state_private(struct extent_io_tree *tree, u64 start, u64 private) 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; @@ -509,7 +519,7 @@ int set_state_private(struct extent_io_tree *tree, u64 start, u64 private) ret = -ENOENT; goto out; } - state->private = private; + state->xprivate = private; out: return ret; } @@ -520,7 +530,7 @@ int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private) 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; @@ -530,95 +540,100 @@ int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private) 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, +static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *info, 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; + eb->fs_info = info; + eb->tree = &info->extent_cache; + INIT_LIST_HEAD(&eb->recow); + INIT_LIST_HEAD(&eb->lru); + + return eb; +} + +struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src) +{ + struct extent_buffer *new; - free_some_buffers(tree); - ret = insert_existing_cache_extent(&tree->cache, &eb->cache_node); - if (ret) { - free(eb); + new = __alloc_extent_buffer(src->fs_info, src->start, src->len); + if (!new) return NULL; + /* cloned eb is not linked into fs_info->extent_cache */ + new->tree = 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 && 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++; @@ -632,7 +647,7 @@ struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree, 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); @@ -641,35 +656,65 @@ struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree, return eb; } -struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, +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 btrfs_fs_info *fs_info, u64 bytenr, u32 blocksize) { struct extent_buffer *eb; + struct extent_io_tree *tree = &fs_info->extent_cache; 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); + eb = __alloc_extent_buffer(fs_info, 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; } @@ -693,27 +738,147 @@ 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; - if (eb->flags & EXTENT_UPTODATE) - return 1; + 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; + + kfree(multi); + multi = NULL; + } return 0; + +out: + kfree(raid_map); + return ret; } int set_extent_buffer_dirty(struct extent_buffer *eb) @@ -721,7 +886,7 @@ 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; @@ -732,7 +897,7 @@ int clear_extent_buffer_dirty(struct extent_buffer *eb) 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; @@ -763,12 +928,6 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src, 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) { @@ -780,3 +939,9 @@ void memset_extent_buffer(struct extent_buffer *eb, char c, { 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); +}