summaryrefslogtreecommitdiff
path: root/fs/btrfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ctree.c52
-rw-r--r--fs/btrfs/ctree.h2
-rw-r--r--fs/btrfs/dir-item.c2
-rw-r--r--fs/btrfs/extent_map.c11
-rw-r--r--fs/btrfs/extent_map.h1
-rw-r--r--fs/btrfs/file-item.c2
-rw-r--r--fs/btrfs/file.c36
-rw-r--r--fs/btrfs/inode.c67
8 files changed, 135 insertions, 38 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 516b90224a1b..eef9c92f86d5 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -1930,7 +1930,7 @@ again:
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
- u32 new_size)
+ u32 new_size, int from_end)
{
int ret = 0;
int slot;
@@ -1946,13 +1946,17 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
slot_orig = path->slots[0];
leaf = path->nodes[0];
+ slot = path->slots[0];
+
+ old_size = btrfs_item_size_nr(leaf, slot);
+ if (old_size == new_size)
+ return 0;
nritems = btrfs_header_nritems(leaf);
data_end = leaf_data_end(root, leaf);
- slot = path->slots[0];
old_data_start = btrfs_item_offset_nr(leaf, slot);
- old_size = btrfs_item_size_nr(leaf, slot); BUG_ON(old_size <= new_size);
+
size_diff = old_size - new_size;
BUG_ON(slot < 0);
@@ -1984,9 +1988,45 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
}
/* shift the data */
- memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
- data_end + size_diff, btrfs_leaf_data(leaf) +
- data_end, old_data_start + new_size - data_end);
+ if (from_end) {
+ memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
+ data_end + size_diff, btrfs_leaf_data(leaf) +
+ data_end, old_data_start + new_size - data_end);
+ } else {
+ struct btrfs_disk_key disk_key;
+ u64 offset;
+
+ btrfs_item_key(leaf, &disk_key, slot);
+
+ if (btrfs_disk_key_type(&disk_key) == BTRFS_EXTENT_DATA_KEY) {
+ unsigned long ptr;
+ struct btrfs_file_extent_item *fi;
+
+ fi = btrfs_item_ptr(leaf, slot,
+ struct btrfs_file_extent_item);
+ fi = (struct btrfs_file_extent_item *)(
+ (unsigned long)fi - size_diff);
+
+ if (btrfs_file_extent_type(leaf, fi) ==
+ BTRFS_FILE_EXTENT_INLINE) {
+ ptr = btrfs_item_ptr_offset(leaf, slot);
+ memmove_extent_buffer(leaf, ptr,
+ (unsigned long)fi,
+ offsetof(struct btrfs_file_extent_item,
+ disk_bytenr));
+ }
+ }
+
+ memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
+ data_end + size_diff, btrfs_leaf_data(leaf) +
+ data_end, old_data_start - data_end);
+
+ offset = btrfs_disk_key_offset(&disk_key);
+ btrfs_set_disk_key_offset(&disk_key, offset + size_diff);
+ btrfs_set_item_key(leaf, &disk_key, slot);
+ if (slot == 0)
+ fixup_low_keys(trans, root, path, &disk_key, 1);
+ }
item = btrfs_item_nr(leaf, slot);
btrfs_set_item_size(leaf, item, new_size);
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 70e143b13577..d82afb618bf1 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -907,7 +907,7 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
- u32 new_size);
+ u32 new_size, int from_end);
int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_key *key, struct btrfs_path *p, int
ins_len, int cow);
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 6f19de41b878..514a1dc337a8 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -249,7 +249,7 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
item_len - (ptr + sub_item_len - start));
ret = btrfs_truncate_item(trans, root, path,
- item_len - sub_item_len);
+ item_len - sub_item_len, 1);
}
return 0;
}
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 238cb1d81d56..44be9cfd30ee 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -263,7 +263,12 @@ int add_extent_mapping(struct extent_map_tree *tree,
if (prev && prev->end + 1 == em->start &&
((em->block_start == EXTENT_MAP_HOLE &&
prev->block_start == EXTENT_MAP_HOLE) ||
- (em->block_start == prev->block_end + 1))) {
+ (em->block_start == EXTENT_MAP_INLINE &&
+ prev->block_start == EXTENT_MAP_INLINE) ||
+ (em->block_start == EXTENT_MAP_DELALLOC &&
+ prev->block_start == EXTENT_MAP_DELALLOC) ||
+ (em->block_start < EXTENT_MAP_DELALLOC - 1 &&
+ em->block_start == prev->block_end + 1))) {
em->start = prev->start;
em->block_start = prev->block_start;
rb_erase(&prev->rb_node, &tree->map);
@@ -1618,13 +1623,13 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
u64 extent_offset;
u64 last_byte = i_size_read(inode);
u64 block_start;
+ u64 iosize;
sector_t sector;
struct extent_map *em;
struct block_device *bdev;
int ret;
int nr = 0;
size_t page_offset = 0;
- size_t iosize;
size_t blocksize;
loff_t i_size = i_size_read(inode);
unsigned long end_index = i_size >> PAGE_CACHE_SHIFT;
@@ -1684,7 +1689,7 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
clear_extent_dirty(tree, cur, page_end, GFP_NOFS);
break;
}
- em = get_extent(inode, page, page_offset, cur, end, 0);
+ em = get_extent(inode, page, page_offset, cur, end, 1);
if (IS_ERR(em) || !em) {
SetPageError(page);
break;
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 39d78d31b6a2..b6f6519f9ba3 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -24,7 +24,6 @@
* page->private values. Every page that is controlled by the extent
* map has page->private set to one.
*/
-
#define EXTENT_PAGE_PRIVATE 1
#define EXTENT_PAGE_PRIVATE_FIRST_PAGE 3
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 7eb9a5412e2f..614176e52851 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -291,7 +291,7 @@ int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
new_item_size = blocks * BTRFS_CRC32_SIZE;
if (new_item_size >= btrfs_item_size_nr(leaf, slot))
return 0;
- ret = btrfs_truncate_item(trans, root, path, new_item_size);
+ ret = btrfs_truncate_item(trans, root, path, new_item_size, 1);
BUG_ON(ret);
return ret;
}
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index abdd9caad94e..4e52f7ec1cbe 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -115,8 +115,20 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
goto fail;
}
if (ret == 1) {
+ struct btrfs_key found_key;
+
+ if (path->slots[0] == 0)
+ goto insert;
+
path->slots[0]--;
leaf = path->nodes[0];
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+
+ if (found_key.objectid != inode->i_ino)
+ goto insert;
+
+ if (found_key.type != BTRFS_EXTENT_DATA_KEY)
+ goto insert;
ei = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
@@ -152,6 +164,7 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
ret = btrfs_search_slot(trans, root, &key, path,
offset + size - found_end, 1);
BUG_ON(ret != 0);
+
ret = btrfs_extend_item(trans, root, path,
offset + size - found_end);
if (ret) {
@@ -292,7 +305,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
*/
inline_size = end_pos;
if (isize >= BTRFS_MAX_INLINE_DATA_SIZE(root) ||
- inline_size > 8192 ||
+ inline_size > 32768 ||
inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
u64 last_end;
@@ -312,7 +325,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
aligned_end = (pos + write_bytes + root->sectorsize - 1) &
~((u64)root->sectorsize - 1);
err = btrfs_drop_extents(trans, root, inode, start_pos,
- aligned_end, end_pos, &hint_byte);
+ aligned_end, aligned_end, &hint_byte);
if (err)
goto failed;
err = insert_inline_extent(trans, root, inode, start_pos,
@@ -456,13 +469,15 @@ next_slot:
goto next_slot;
}
- /* FIXME, there's only one inline extent allowed right now */
if (found_inline) {
u64 mask = root->sectorsize - 1;
search_start = (extent_end + mask) & ~mask;
} else
search_start = extent_end;
+ if (end <= extent_end && start >= key.offset && found_inline) {
+ *hint_byte = EXTENT_MAP_INLINE;
+ }
if (end < extent_end && end >= key.offset) {
if (found_extent) {
u64 disk_bytenr =
@@ -479,8 +494,10 @@ next_slot:
BUG_ON(ret);
}
}
- if (!found_inline)
- bookend = 1;
+ bookend = 1;
+ if (found_inline && start <= key.offset &&
+ inline_end < extent_end)
+ keep = 1;
}
/* truncate existing extent */
if (start > key.offset) {
@@ -510,7 +527,7 @@ next_slot:
new_size = btrfs_file_extent_calc_inline_size(
inline_end - key.offset);
btrfs_truncate_item(trans, root, path,
- new_size);
+ new_size, 1);
}
}
/* delete the entire extent */
@@ -551,6 +568,13 @@ next_slot:
if (!bookend)
continue;
}
+ if (bookend && found_inline && start <= key.offset &&
+ inline_end < extent_end) {
+ u32 new_size;
+ new_size = btrfs_file_extent_calc_inline_size(
+ extent_end - inline_end);
+ btrfs_truncate_item(trans, root, path, new_size, 0);
+ }
/* create bookend, splitting the extent in two */
if (bookend && found_extent) {
struct btrfs_key ins;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 5d10b64e42df..0c65141b9993 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -89,6 +89,9 @@ static int run_delalloc_range(struct inode *inode, u64 start, u64 end)
ret = btrfs_drop_extents(trans, root, inode,
start, start + num_bytes, start, &alloc_hint);
+ if (alloc_hint == EXTENT_MAP_INLINE)
+ goto out;
+
ret = btrfs_alloc_extent(trans, root, inode->i_ino, num_bytes, 0,
alloc_hint, (u64)-1, &ins, 1);
if (ret) {
@@ -558,6 +561,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
u64 item_end = 0;
int found_extent;
int del_item;
+ int extent_type = -1;
btrfs_drop_extent_cache(inode, inode->i_size, (u64)-1);
path = btrfs_alloc_path();
@@ -597,10 +601,15 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
if (found_type == BTRFS_EXTENT_DATA_KEY) {
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
- if (btrfs_file_extent_type(leaf, fi) !=
- BTRFS_FILE_EXTENT_INLINE) {
+ extent_type = btrfs_file_extent_type(leaf, fi);
+ if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
item_end +=
btrfs_file_extent_num_bytes(leaf, fi);
+ } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ struct btrfs_item *item = btrfs_item_nr(leaf,
+ path->slots[0]);
+ item_end += btrfs_file_extent_inline_len(leaf,
+ item);
}
}
if (found_type == BTRFS_CSUM_ITEM_KEY) {
@@ -608,7 +617,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
inode->i_size);
BUG_ON(ret);
}
- if (item_end < inode->i_size) {
+ if (item_end <= inode->i_size) {
if (found_type == BTRFS_DIR_ITEM_KEY) {
found_type = BTRFS_INODE_ITEM_KEY;
} else if (found_type == BTRFS_EXTENT_ITEM_KEY) {
@@ -629,9 +638,10 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
found_extent = 0;
/* FIXME, shrink the extent if the ref count is only 1 */
- if (found_type == BTRFS_EXTENT_DATA_KEY &&
- btrfs_file_extent_type(leaf, fi) !=
- BTRFS_FILE_EXTENT_INLINE) {
+ if (found_type != BTRFS_EXTENT_DATA_KEY)
+ goto delete;
+
+ if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
u64 num_dec;
extent_start = btrfs_file_extent_disk_bytenr(leaf, fi);
if (!del_item) {
@@ -659,7 +669,15 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
inode->i_blocks -= num_dec;
}
}
+ } else if (extent_type == BTRFS_FILE_EXTENT_INLINE &&
+ !del_item) {
+ u32 newsize = inode->i_size - found_key.offset;
+ newsize = btrfs_file_extent_calc_inline_size(newsize);
+ ret = btrfs_truncate_item(trans, root, path,
+ newsize, 1);
+ BUG_ON(ret);
}
+delete:
if (del_item) {
ret = btrfs_del_item(trans, root, path);
if (ret)
@@ -769,7 +787,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
u64 pos = (inode->i_size + mask) & ~mask;
u64 block_end = attr->ia_size | mask;
u64 hole_size;
- u64 alloc_hint;
+ u64 alloc_hint = 0;
if (attr->ia_size <= pos)
goto out;
@@ -786,8 +804,11 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
pos, pos + hole_size, pos,
&alloc_hint);
- err = btrfs_insert_file_extent(trans, root, inode->i_ino,
- pos, 0, 0, hole_size);
+ if (alloc_hint != EXTENT_MAP_INLINE) {
+ err = btrfs_insert_file_extent(trans, root,
+ inode->i_ino,
+ pos, 0, 0, hole_size);
+ }
btrfs_end_transaction(trans, root);
mutex_unlock(&root->fs_info->fs_mutex);
unlock_extent(em_tree, pos, block_end, GFP_NOFS);
@@ -1531,8 +1552,8 @@ again:
em->end = EXTENT_MAP_HOLE;
}
em->bdev = inode->i_sb->s_bdev;
- ret = btrfs_lookup_file_extent(NULL, root, path,
- objectid, start, 0);
+ ret = btrfs_lookup_file_extent(trans, root, path,
+ objectid, start, trans != NULL);
if (ret < 0) {
err = ret;
goto out;
@@ -1627,15 +1648,23 @@ again:
((u64)root->sectorsize -1);
map = kmap(page);
ptr = btrfs_file_extent_inline_start(item) + extent_offset;
- read_extent_buffer(leaf, map + page_offset, ptr, copy_size);
-
- if (em->start + copy_size <= em->end) {
- size = min_t(u64, em->end + 1 - em->start,
- PAGE_CACHE_SIZE - page_offset) - copy_size;
- memset(map + page_offset + copy_size, 0, size);
+ if (create == 0 && !PageUptodate(page)) {
+ read_extent_buffer(leaf, map + page_offset, ptr,
+ copy_size);
+ flush_dcache_page(page);
+ } else if (create && PageUptodate(page)) {
+ if (!trans) {
+ kunmap(page);
+ free_extent_map(em);
+ em = NULL;
+ btrfs_release_path(root, path);
+ trans = btrfs_start_transaction(root, 1);
+ goto again;
+ }
+ write_extent_buffer(leaf, map + page_offset, ptr,
+ copy_size);
+ btrfs_mark_buffer_dirty(leaf);
}
-
- flush_dcache_page(page);
kunmap(page);
set_extent_uptodate(em_tree, em->start, em->end, GFP_NOFS);
goto insert;