key.objectid = inode->i_ino;
key.offset = offset;
btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
- datasize = btrfs_file_extent_calc_inline_size(offset + size);
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
if (ret < 0) {
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);
}
if (ret == 0) {
u32 found_size;
- u64 found_start;
+ u64 found_end;
leaf = path->nodes[0];
ei = btrfs_item_ptr(leaf, path->slots[0],
offset, inode->i_ino);
goto fail;
}
- found_start = key.offset;
found_size = btrfs_file_extent_inline_len(leaf,
btrfs_item_nr(leaf, path->slots[0]));
+ found_end = key.offset + found_size;
- if (found_size < offset + size) {
+ if (found_end < offset + size) {
btrfs_release_path(root, path);
ret = btrfs_search_slot(trans, root, &key, path,
- offset + size - found_size -
- found_start, 1);
+ offset + size - found_end, 1);
BUG_ON(ret != 0);
+
ret = btrfs_extend_item(trans, root, path,
- offset + size - found_size -
- found_start);
+ offset + size - found_end);
if (ret) {
err = ret;
goto fail;
ei = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
}
+ if (found_end < offset) {
+ ptr = btrfs_file_extent_inline_start(ei) + found_size;
+ memset_extent_buffer(leaf, 0, ptr, offset - found_end);
+ }
} else {
insert:
btrfs_release_path(root, path);
+ datasize = offset + size - key.offset;
+ datasize = btrfs_file_extent_calc_inline_size(datasize);
ret = btrfs_insert_empty_item(trans, root, path, &key,
datasize);
if (ret) {
btrfs_set_file_extent_generation(leaf, ei, trans->transid);
btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE);
}
- ptr = btrfs_file_extent_inline_start(ei) + offset;
+ ptr = btrfs_file_extent_inline_start(ei) + offset - key.offset;
cur_size = size;
i = 0;
while (size > 0) {
page = pages[i];
kaddr = kmap_atomic(page, KM_USER0);
- cur_size = min(PAGE_CACHE_SIZE - page_offset, size);
+ cur_size = min_t(size_t, PAGE_CACHE_SIZE - page_offset, size);
write_extent_buffer(leaf, kaddr + page_offset, ptr, cur_size);
kunmap_atomic(kaddr, KM_USER0);
page_offset = 0;
u64 start_pos;
u64 end_of_last_block;
u64 end_pos = pos + write_bytes;
- u32 inline_size;
+ u64 inline_size;
loff_t isize = i_size_read(inode);
-
em = alloc_extent_map(GFP_NOFS);
if (!em)
return -ENOMEM;
*/
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;
SetPageUptodate(p);
set_page_dirty(p);
}
- last_end = pages[num_pages -1]->index << PAGE_CACHE_SHIFT;
+ last_end = (u64)(pages[num_pages -1]->index) <<
+ PAGE_CACHE_SHIFT;
last_end += PAGE_CACHE_SIZE - 1;
set_extent_delalloc(em_tree, start_pos, end_of_last_block,
GFP_NOFS);
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;
+ if (isize > inline_size)
+ inline_size = min_t(u64, isize, aligned_end);
+ inline_size -= start_pos;
err = insert_inline_extent(trans, root, inode, start_pos,
- end_pos - start_pos, pages, 0,
- num_pages);
+ inline_size, pages, 0, num_pages);
BUG_ON(err);
}
if (end_pos > isize) {
*/
int btrfs_drop_extents(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode,
- u64 start, u64 end, u64 inline_end, u64 *hint_byte)
+ u64 start, u64 end, u64 inline_limit, u64 *hint_byte)
{
- int ret;
- struct btrfs_key key;
+ u64 extent_end = 0;
+ u64 search_start = start;
struct extent_buffer *leaf;
- int slot;
struct btrfs_file_extent_item *extent;
- u64 extent_end = 0;
- int keep;
- struct btrfs_file_extent_item old;
struct btrfs_path *path;
- u64 search_start = start;
+ struct btrfs_key key;
+ struct btrfs_file_extent_item old;
+ int keep;
+ int slot;
int bookend;
int found_type;
int found_extent;
int found_inline;
int recow;
+ int ret;
btrfs_drop_extent_cache(inode, start, end - 1);
struct btrfs_file_extent_item);
found_type = btrfs_file_extent_type(leaf, extent);
if (found_type == BTRFS_FILE_EXTENT_REG) {
+ extent_end =
+ btrfs_file_extent_disk_bytenr(leaf,
+ extent);
+ if (extent_end)
+ *hint_byte = extent_end;
+
extent_end = key.offset +
btrfs_file_extent_num_bytes(leaf, extent);
found_extent = 1;
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 =
BUG_ON(ret);
}
}
- if (!found_inline)
- bookend = 1;
+ bookend = 1;
+ if (found_inline && start <= key.offset &&
+ inline_limit < extent_end)
+ keep = 1;
}
/* truncate existing extent */
if (start > key.offset) {
btrfs_set_file_extent_num_bytes(leaf, extent,
new_num);
btrfs_mark_buffer_dirty(leaf);
- } else if (end > extent_end &&
- key.offset < inline_end &&
- inline_end < extent_end) {
+ } else if (key.offset < inline_limit &&
+ (end > extent_end) &&
+ (inline_limit < extent_end)) {
u32 new_size;
new_size = btrfs_file_extent_calc_inline_size(
- inline_end - key.offset);
+ inline_limit - key.offset);
btrfs_truncate_item(trans, root, path,
- new_size);
+ new_size, 1);
}
}
/* delete the entire extent */
if (!bookend)
continue;
}
+ if (bookend && found_inline && start <= key.offset &&
+ inline_limit < extent_end && key.offset <= inline_limit) {
+ u32 new_size;
+ new_size = btrfs_file_extent_calc_inline_size(
+ extent_end - inline_limit);
+ btrfs_truncate_item(trans, root, path, new_size, 0);
+ }
/* create bookend, splitting the extent in two */
if (bookend && found_extent) {
struct btrfs_key ins;
size_t count, loff_t *ppos)
{
loff_t pos;
- size_t num_written = 0;
- int err = 0;
+ loff_t start_pos;
+ ssize_t num_written = 0;
+ ssize_t err = 0;
int ret = 0;
struct inode *inode = file->f_path.dentry->d_inode;
struct btrfs_root *root = BTRFS_I(inode)->root;
pinned[1] = NULL;
if (file->f_flags & O_DIRECT)
return -EINVAL;
+
pos = *ppos;
+ start_pos = pos;
+
vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
current->backing_dev_info = inode->i_mapping->backing_dev_info;
err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
num_written += write_bytes;
balance_dirty_pages_ratelimited_nr(inode->i_mapping, num_pages);
- btrfs_btree_balance_dirty(root, 1);
+ if (num_pages < (root->leafsize >> PAGE_CACHE_SHIFT) + 1)
+ btrfs_btree_balance_dirty(root, 1);
cond_resched();
}
mutex_unlock(&inode->i_mutex);
if (pinned[1])
page_cache_release(pinned[1]);
*ppos = pos;
+
+ if (num_written > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+ err = sync_page_range(inode, inode->i_mapping,
+ start_pos, num_written);
+ if (err < 0)
+ num_written = err;
+ }
current->backing_dev_info = NULL;
return num_written ? num_written : err;
}