Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs

Pull btrfs update from Chris Mason:
 "This is a large pull, with the bulk of the updates coming from:

   - Hole punching

   - send/receive fixes

   - fsync performance

   - Disk format extension allowing more hardlinks inside a single
     directory (btrfs-progs patch required to enable the compat bit for
     this one)

  I'm cooking more unrelated RAID code, but I wanted to make sure this
  original batch makes it in.  The largest updates here are relatively
  old and have been in testing for some time."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs: (121 commits)
  btrfs: init ref_index to zero in add_inode_ref
  Btrfs: remove repeated eb->pages check in, disk-io.c/csum_dirty_buffer
  Btrfs: fix page leakage
  Btrfs: do not warn_on when we cannot alloc a page for an extent buffer
  Btrfs: don't bug on enomem in readpage
  Btrfs: cleanup pages properly when ENOMEM in compression
  Btrfs: make filesystem read-only when submitting barrier fails
  Btrfs: detect corrupted filesystem after write I/O errors
  Btrfs: make compress and nodatacow mount options mutually exclusive
  btrfs: fix message printing
  Btrfs: don't bother committing delayed inode updates when fsyncing
  btrfs: move inline function code to header file
  Btrfs: remove unnecessary IS_ERR in bio_readpage_error()
  btrfs: remove unused function btrfs_insert_some_items()
  Btrfs: don't commit instead of overcommitting
  Btrfs: confirmation of value is added before trace_btrfs_get_extent() is called
  Btrfs: be smarter about dropping things from the tree log
  Btrfs: don't lookup csums for prealloc extents
  Btrfs: cache extent state when writing out dirty metadata pages
  Btrfs: do not hold the file extent leaf locked when adding extent item
  ...
This commit is contained in:
Linus Torvalds
2012-10-10 10:49:20 +09:00
39 changed files with 3578 additions and 1623 deletions
+240 -59
View File
@@ -16,6 +16,7 @@
* Boston, MA 021110-1307, USA. * Boston, MA 021110-1307, USA.
*/ */
#include <linux/vmalloc.h>
#include "ctree.h" #include "ctree.h"
#include "disk-io.h" #include "disk-io.h"
#include "backref.h" #include "backref.h"
@@ -231,7 +232,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
} }
if (!ret) { if (!ret) {
ret = ulist_add(parents, eb->start, ret = ulist_add(parents, eb->start,
(unsigned long)eie, GFP_NOFS); (uintptr_t)eie, GFP_NOFS);
if (ret < 0) if (ret < 0)
break; break;
if (!extent_item_pos) { if (!extent_item_pos) {
@@ -363,8 +364,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
ULIST_ITER_INIT(&uiter); ULIST_ITER_INIT(&uiter);
node = ulist_next(parents, &uiter); node = ulist_next(parents, &uiter);
ref->parent = node ? node->val : 0; ref->parent = node ? node->val : 0;
ref->inode_list = ref->inode_list = node ?
node ? (struct extent_inode_elem *)node->aux : 0; (struct extent_inode_elem *)(uintptr_t)node->aux : 0;
/* additional parents require new refs being added here */ /* additional parents require new refs being added here */
while ((node = ulist_next(parents, &uiter))) { while ((node = ulist_next(parents, &uiter))) {
@@ -375,8 +376,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
} }
memcpy(new_ref, ref, sizeof(*ref)); memcpy(new_ref, ref, sizeof(*ref));
new_ref->parent = node->val; new_ref->parent = node->val;
new_ref->inode_list = new_ref->inode_list = (struct extent_inode_elem *)
(struct extent_inode_elem *)node->aux; (uintptr_t)node->aux;
list_add(&new_ref->list, &ref->list); list_add(&new_ref->list, &ref->list);
} }
ulist_reinit(parents); ulist_reinit(parents);
@@ -914,8 +915,8 @@ again:
free_extent_buffer(eb); free_extent_buffer(eb);
} }
ret = ulist_add_merge(refs, ref->parent, ret = ulist_add_merge(refs, ref->parent,
(unsigned long)ref->inode_list, (uintptr_t)ref->inode_list,
(unsigned long *)&eie, GFP_NOFS); (u64 *)&eie, GFP_NOFS);
if (!ret && extent_item_pos) { if (!ret && extent_item_pos) {
/* /*
* we've recorded that parent, so we must extend * we've recorded that parent, so we must extend
@@ -959,7 +960,7 @@ static void free_leaf_list(struct ulist *blocks)
while ((node = ulist_next(blocks, &uiter))) { while ((node = ulist_next(blocks, &uiter))) {
if (!node->aux) if (!node->aux)
continue; continue;
eie = (struct extent_inode_elem *)node->aux; eie = (struct extent_inode_elem *)(uintptr_t)node->aux;
for (; eie; eie = eie_next) { for (; eie; eie = eie_next) {
eie_next = eie->next; eie_next = eie->next;
kfree(eie); kfree(eie);
@@ -1108,26 +1109,80 @@ static int inode_ref_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
found_key); found_key);
} }
/* int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
* this iterates to turn a btrfs_inode_ref into a full filesystem path. elements u64 start_off, struct btrfs_path *path,
* of the path are separated by '/' and the path is guaranteed to be struct btrfs_inode_extref **ret_extref,
* 0-terminated. the path is only given within the current file system. u64 *found_off)
* Therefore, it never starts with a '/'. the caller is responsible to provide {
* "size" bytes in "dest". the dest buffer will be filled backwards. finally, int ret, slot;
* the start point of the resulting string is returned. this pointer is within struct btrfs_key key;
* dest, normally. struct btrfs_key found_key;
* in case the path buffer would overflow, the pointer is decremented further struct btrfs_inode_extref *extref;
* as if output was written to the buffer, though no more output is actually struct extent_buffer *leaf;
* generated. that way, the caller can determine how much space would be unsigned long ptr;
* required for the path to fit into the buffer. in that case, the returned
* value will be smaller than dest. callers must check this! key.objectid = inode_objectid;
*/ btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY);
char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, key.offset = start_off;
struct btrfs_inode_ref *iref,
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0)
return ret;
while (1) {
leaf = path->nodes[0];
slot = path->slots[0];
if (slot >= btrfs_header_nritems(leaf)) {
/*
* If the item at offset is not found,
* btrfs_search_slot will point us to the slot
* where it should be inserted. In our case
* that will be the slot directly before the
* next INODE_REF_KEY_V2 item. In the case
* that we're pointing to the last slot in a
* leaf, we must move one leaf over.
*/
ret = btrfs_next_leaf(root, path);
if (ret) {
if (ret >= 1)
ret = -ENOENT;
break;
}
continue;
}
btrfs_item_key_to_cpu(leaf, &found_key, slot);
/*
* Check that we're still looking at an extended ref key for
* this particular objectid. If we have different
* objectid or type then there are no more to be found
* in the tree and we can exit.
*/
ret = -ENOENT;
if (found_key.objectid != inode_objectid)
break;
if (btrfs_key_type(&found_key) != BTRFS_INODE_EXTREF_KEY)
break;
ret = 0;
ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
extref = (struct btrfs_inode_extref *)ptr;
*ret_extref = extref;
if (found_off)
*found_off = found_key.offset;
break;
}
return ret;
}
static char *ref_to_path(struct btrfs_root *fs_root,
struct btrfs_path *path,
u32 name_len, unsigned long name_off,
struct extent_buffer *eb_in, u64 parent, struct extent_buffer *eb_in, u64 parent,
char *dest, u32 size) char *dest, u32 size)
{ {
u32 len;
int slot; int slot;
u64 next_inum; u64 next_inum;
int ret; int ret;
@@ -1135,17 +1190,17 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
struct extent_buffer *eb = eb_in; struct extent_buffer *eb = eb_in;
struct btrfs_key found_key; struct btrfs_key found_key;
int leave_spinning = path->leave_spinning; int leave_spinning = path->leave_spinning;
struct btrfs_inode_ref *iref;
if (bytes_left >= 0) if (bytes_left >= 0)
dest[bytes_left] = '\0'; dest[bytes_left] = '\0';
path->leave_spinning = 1; path->leave_spinning = 1;
while (1) { while (1) {
len = btrfs_inode_ref_name_len(eb, iref); bytes_left -= name_len;
bytes_left -= len;
if (bytes_left >= 0) if (bytes_left >= 0)
read_extent_buffer(eb, dest + bytes_left, read_extent_buffer(eb, dest + bytes_left,
(unsigned long)(iref + 1), len); name_off, name_len);
if (eb != eb_in) { if (eb != eb_in) {
btrfs_tree_read_unlock_blocking(eb); btrfs_tree_read_unlock_blocking(eb);
free_extent_buffer(eb); free_extent_buffer(eb);
@@ -1155,6 +1210,7 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
ret = -ENOENT; ret = -ENOENT;
if (ret) if (ret)
break; break;
next_inum = found_key.offset; next_inum = found_key.offset;
/* regular exit ahead */ /* regular exit ahead */
@@ -1170,8 +1226,11 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
} }
btrfs_release_path(path); btrfs_release_path(path);
iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref);
name_len = btrfs_inode_ref_name_len(eb, iref);
name_off = (unsigned long)(iref + 1);
parent = next_inum; parent = next_inum;
--bytes_left; --bytes_left;
if (bytes_left >= 0) if (bytes_left >= 0)
@@ -1187,13 +1246,40 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
return dest + bytes_left; return dest + bytes_left;
} }
/*
* this iterates to turn a btrfs_inode_ref into a full filesystem path. elements
* of the path are separated by '/' and the path is guaranteed to be
* 0-terminated. the path is only given within the current file system.
* Therefore, it never starts with a '/'. the caller is responsible to provide
* "size" bytes in "dest". the dest buffer will be filled backwards. finally,
* the start point of the resulting string is returned. this pointer is within
* dest, normally.
* in case the path buffer would overflow, the pointer is decremented further
* as if output was written to the buffer, though no more output is actually
* generated. that way, the caller can determine how much space would be
* required for the path to fit into the buffer. in that case, the returned
* value will be smaller than dest. callers must check this!
*/
char *btrfs_iref_to_path(struct btrfs_root *fs_root,
struct btrfs_path *path,
struct btrfs_inode_ref *iref,
struct extent_buffer *eb_in, u64 parent,
char *dest, u32 size)
{
return ref_to_path(fs_root, path,
btrfs_inode_ref_name_len(eb_in, iref),
(unsigned long)(iref + 1),
eb_in, parent, dest, size);
}
/* /*
* this makes the path point to (logical EXTENT_ITEM *) * this makes the path point to (logical EXTENT_ITEM *)
* returns BTRFS_EXTENT_FLAG_DATA for data, BTRFS_EXTENT_FLAG_TREE_BLOCK for * returns BTRFS_EXTENT_FLAG_DATA for data, BTRFS_EXTENT_FLAG_TREE_BLOCK for
* tree blocks and <0 on error. * tree blocks and <0 on error.
*/ */
int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
struct btrfs_path *path, struct btrfs_key *found_key) struct btrfs_path *path, struct btrfs_key *found_key,
u64 *flags_ret)
{ {
int ret; int ret;
u64 flags; u64 flags;
@@ -1237,10 +1323,17 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
(unsigned long long)found_key->objectid, (unsigned long long)found_key->objectid,
(unsigned long long)found_key->offset, (unsigned long long)found_key->offset,
(unsigned long long)flags, item_size); (unsigned long long)flags, item_size);
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)
return BTRFS_EXTENT_FLAG_TREE_BLOCK; WARN_ON(!flags_ret);
if (flags & BTRFS_EXTENT_FLAG_DATA) if (flags_ret) {
return BTRFS_EXTENT_FLAG_DATA; if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)
*flags_ret = BTRFS_EXTENT_FLAG_TREE_BLOCK;
else if (flags & BTRFS_EXTENT_FLAG_DATA)
*flags_ret = BTRFS_EXTENT_FLAG_DATA;
else
BUG_ON(1);
return 0;
}
return -EIO; return -EIO;
} }
@@ -1404,12 +1497,13 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
ULIST_ITER_INIT(&root_uiter); ULIST_ITER_INIT(&root_uiter);
while (!ret && (root_node = ulist_next(roots, &root_uiter))) { while (!ret && (root_node = ulist_next(roots, &root_uiter))) {
pr_debug("root %llu references leaf %llu, data list " pr_debug("root %llu references leaf %llu, data list "
"%#lx\n", root_node->val, ref_node->val, "%#llx\n", root_node->val, ref_node->val,
ref_node->aux); (long long)ref_node->aux);
ret = iterate_leaf_refs( ret = iterate_leaf_refs((struct extent_inode_elem *)
(struct extent_inode_elem *)ref_node->aux, (uintptr_t)ref_node->aux,
root_node->val, extent_item_objectid, root_node->val,
iterate, ctx); extent_item_objectid,
iterate, ctx);
} }
ulist_free(roots); ulist_free(roots);
roots = NULL; roots = NULL;
@@ -1432,15 +1526,15 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
{ {
int ret; int ret;
u64 extent_item_pos; u64 extent_item_pos;
u64 flags = 0;
struct btrfs_key found_key; struct btrfs_key found_key;
int search_commit_root = path->search_commit_root; int search_commit_root = path->search_commit_root;
ret = extent_from_logical(fs_info, logical, path, ret = extent_from_logical(fs_info, logical, path, &found_key, &flags);
&found_key);
btrfs_release_path(path); btrfs_release_path(path);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK) if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)
return -EINVAL; return -EINVAL;
extent_item_pos = logical - found_key.objectid; extent_item_pos = logical - found_key.objectid;
@@ -1451,9 +1545,12 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
return ret; return ret;
} }
static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, typedef int (iterate_irefs_t)(u64 parent, u32 name_len, unsigned long name_off,
struct btrfs_path *path, struct extent_buffer *eb, void *ctx);
iterate_irefs_t *iterate, void *ctx)
static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root,
struct btrfs_path *path,
iterate_irefs_t *iterate, void *ctx)
{ {
int ret = 0; int ret = 0;
int slot; int slot;
@@ -1470,7 +1567,7 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
while (!ret) { while (!ret) {
path->leave_spinning = 1; path->leave_spinning = 1;
ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path, ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path,
&found_key); &found_key);
if (ret < 0) if (ret < 0)
break; break;
if (ret) { if (ret) {
@@ -1498,7 +1595,8 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
"tree %llu\n", cur, "tree %llu\n", cur,
(unsigned long long)found_key.objectid, (unsigned long long)found_key.objectid,
(unsigned long long)fs_root->objectid); (unsigned long long)fs_root->objectid);
ret = iterate(parent, iref, eb, ctx); ret = iterate(parent, name_len,
(unsigned long)(iref + 1), eb, ctx);
if (ret) if (ret)
break; break;
len = sizeof(*iref) + name_len; len = sizeof(*iref) + name_len;
@@ -1513,12 +1611,98 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
return ret; return ret;
} }
static int iterate_inode_extrefs(u64 inum, struct btrfs_root *fs_root,
struct btrfs_path *path,
iterate_irefs_t *iterate, void *ctx)
{
int ret;
int slot;
u64 offset = 0;
u64 parent;
int found = 0;
struct extent_buffer *eb;
struct btrfs_inode_extref *extref;
struct extent_buffer *leaf;
u32 item_size;
u32 cur_offset;
unsigned long ptr;
while (1) {
ret = btrfs_find_one_extref(fs_root, inum, offset, path, &extref,
&offset);
if (ret < 0)
break;
if (ret) {
ret = found ? 0 : -ENOENT;
break;
}
++found;
slot = path->slots[0];
eb = path->nodes[0];
/* make sure we can use eb after releasing the path */
atomic_inc(&eb->refs);
btrfs_tree_read_lock(eb);
btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
btrfs_release_path(path);
leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
cur_offset = 0;
while (cur_offset < item_size) {
u32 name_len;
extref = (struct btrfs_inode_extref *)(ptr + cur_offset);
parent = btrfs_inode_extref_parent(eb, extref);
name_len = btrfs_inode_extref_name_len(eb, extref);
ret = iterate(parent, name_len,
(unsigned long)&extref->name, eb, ctx);
if (ret)
break;
cur_offset += btrfs_inode_extref_name_len(leaf, extref);
cur_offset += sizeof(*extref);
}
btrfs_tree_read_unlock_blocking(eb);
free_extent_buffer(eb);
offset++;
}
btrfs_release_path(path);
return ret;
}
static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
struct btrfs_path *path, iterate_irefs_t *iterate,
void *ctx)
{
int ret;
int found_refs = 0;
ret = iterate_inode_refs(inum, fs_root, path, iterate, ctx);
if (!ret)
++found_refs;
else if (ret != -ENOENT)
return ret;
ret = iterate_inode_extrefs(inum, fs_root, path, iterate, ctx);
if (ret == -ENOENT && found_refs)
return 0;
return ret;
}
/* /*
* returns 0 if the path could be dumped (probably truncated) * returns 0 if the path could be dumped (probably truncated)
* returns <0 in case of an error * returns <0 in case of an error
*/ */
static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref, static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off,
struct extent_buffer *eb, void *ctx) struct extent_buffer *eb, void *ctx)
{ {
struct inode_fs_paths *ipath = ctx; struct inode_fs_paths *ipath = ctx;
char *fspath; char *fspath;
@@ -1531,20 +1715,17 @@ static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref,
ipath->fspath->bytes_left - s_ptr : 0; ipath->fspath->bytes_left - s_ptr : 0;
fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr; fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr;
fspath = btrfs_iref_to_path(ipath->fs_root, ipath->btrfs_path, iref, eb, fspath = ref_to_path(ipath->fs_root, ipath->btrfs_path, name_len,
inum, fspath_min, bytes_left); name_off, eb, inum, fspath_min,
bytes_left);
if (IS_ERR(fspath)) if (IS_ERR(fspath))
return PTR_ERR(fspath); return PTR_ERR(fspath);
if (fspath > fspath_min) { if (fspath > fspath_min) {
pr_debug("path resolved: %s\n", fspath);
ipath->fspath->val[i] = (u64)(unsigned long)fspath; ipath->fspath->val[i] = (u64)(unsigned long)fspath;
++ipath->fspath->elem_cnt; ++ipath->fspath->elem_cnt;
ipath->fspath->bytes_left = fspath - fspath_min; ipath->fspath->bytes_left = fspath - fspath_min;
} else { } else {
pr_debug("missed path, not enough space. missing bytes: %lu, "
"constructed so far: %s\n",
(unsigned long)(fspath_min - fspath), fspath_min);
++ipath->fspath->elem_missed; ++ipath->fspath->elem_missed;
ipath->fspath->bytes_missing += fspath_min - fspath; ipath->fspath->bytes_missing += fspath_min - fspath;
ipath->fspath->bytes_left = 0; ipath->fspath->bytes_left = 0;
@@ -1566,7 +1747,7 @@ static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref,
int paths_from_inode(u64 inum, struct inode_fs_paths *ipath) int paths_from_inode(u64 inum, struct inode_fs_paths *ipath)
{ {
return iterate_irefs(inum, ipath->fs_root, ipath->btrfs_path, return iterate_irefs(inum, ipath->fs_root, ipath->btrfs_path,
inode_to_path, ipath); inode_to_path, ipath);
} }
struct btrfs_data_container *init_data_container(u32 total_bytes) struct btrfs_data_container *init_data_container(u32 total_bytes)
@@ -1575,7 +1756,7 @@ struct btrfs_data_container *init_data_container(u32 total_bytes)
size_t alloc_bytes; size_t alloc_bytes;
alloc_bytes = max_t(size_t, total_bytes, sizeof(*data)); alloc_bytes = max_t(size_t, total_bytes, sizeof(*data));
data = kmalloc(alloc_bytes, GFP_NOFS); data = vmalloc(alloc_bytes);
if (!data) if (!data)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@@ -1626,6 +1807,6 @@ void free_ipath(struct inode_fs_paths *ipath)
{ {
if (!ipath) if (!ipath)
return; return;
kfree(ipath->fspath); vfree(ipath->fspath);
kfree(ipath); kfree(ipath);
} }
+7 -3
View File
@@ -33,14 +33,13 @@ struct inode_fs_paths {
typedef int (iterate_extent_inodes_t)(u64 inum, u64 offset, u64 root, typedef int (iterate_extent_inodes_t)(u64 inum, u64 offset, u64 root,
void *ctx); void *ctx);
typedef int (iterate_irefs_t)(u64 parent, struct btrfs_inode_ref *iref,
struct extent_buffer *eb, void *ctx);
int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root, int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
struct btrfs_path *path); struct btrfs_path *path);
int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
struct btrfs_path *path, struct btrfs_key *found_key); struct btrfs_path *path, struct btrfs_key *found_key,
u64 *flags);
int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
struct btrfs_extent_item *ei, u32 item_size, struct btrfs_extent_item *ei, u32 item_size,
@@ -69,4 +68,9 @@ struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root,
struct btrfs_path *path); struct btrfs_path *path);
void free_ipath(struct inode_fs_paths *ipath); void free_ipath(struct inode_fs_paths *ipath);
int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
u64 start_off, struct btrfs_path *path,
struct btrfs_inode_extref **ret_extref,
u64 *found_off);
#endif #endif
+7 -8
View File
@@ -38,6 +38,7 @@
#define BTRFS_INODE_DELALLOC_META_RESERVED 4 #define BTRFS_INODE_DELALLOC_META_RESERVED 4
#define BTRFS_INODE_HAS_ORPHAN_ITEM 5 #define BTRFS_INODE_HAS_ORPHAN_ITEM 5
#define BTRFS_INODE_HAS_ASYNC_EXTENT 6 #define BTRFS_INODE_HAS_ASYNC_EXTENT 6
#define BTRFS_INODE_NEEDS_FULL_SYNC 7
/* in memory btrfs inode */ /* in memory btrfs inode */
struct btrfs_inode { struct btrfs_inode {
@@ -143,6 +144,9 @@ struct btrfs_inode {
/* flags field from the on disk inode */ /* flags field from the on disk inode */
u32 flags; u32 flags;
/* a local copy of root's last_log_commit */
unsigned long last_log_commit;
/* /*
* Counters to keep track of the number of extent item's we may use due * Counters to keep track of the number of extent item's we may use due
* to delalloc and such. outstanding_extents is the number of extent * to delalloc and such. outstanding_extents is the number of extent
@@ -202,15 +206,10 @@ static inline bool btrfs_is_free_space_inode(struct inode *inode)
static inline int btrfs_inode_in_log(struct inode *inode, u64 generation) static inline int btrfs_inode_in_log(struct inode *inode, u64 generation)
{ {
struct btrfs_root *root = BTRFS_I(inode)->root;
int ret = 0;
mutex_lock(&root->log_mutex);
if (BTRFS_I(inode)->logged_trans == generation && if (BTRFS_I(inode)->logged_trans == generation &&
BTRFS_I(inode)->last_sub_trans <= root->last_log_commit) BTRFS_I(inode)->last_sub_trans <= BTRFS_I(inode)->last_log_commit)
ret = 1; return 1;
mutex_unlock(&root->log_mutex); return 0;
return ret;
} }
#endif #endif
+14 -2
View File
@@ -37,8 +37,9 @@
* the file system was mounted, (i.e., they have been * the file system was mounted, (i.e., they have been
* referenced by the super block) or they have been * referenced by the super block) or they have been
* written since then and the write completion callback * written since then and the write completion callback
* was called and a FLUSH request to the device where * was called and no write error was indicated and a
* these blocks are located was received and completed. * FLUSH request to the device where these blocks are
* located was received and completed.
* 2b. All referenced blocks need to have a generation * 2b. All referenced blocks need to have a generation
* number which is equal to the parent's number. * number which is equal to the parent's number.
* *
@@ -2601,6 +2602,17 @@ static int btrfsic_check_all_ref_blocks(struct btrfsic_state *state,
(unsigned long long)l->block_ref_to->dev_bytenr, (unsigned long long)l->block_ref_to->dev_bytenr,
l->block_ref_to->mirror_num); l->block_ref_to->mirror_num);
ret = -1; ret = -1;
} else if (l->block_ref_to->iodone_w_error) {
printk(KERN_INFO "btrfs: attempt to write superblock"
" which references block %c @%llu (%s/%llu/%d)"
" which has write error!\n",
btrfsic_get_block_type(state, l->block_ref_to),
(unsigned long long)
l->block_ref_to->logical_bytenr,
l->block_ref_to->dev_state->name,
(unsigned long long)l->block_ref_to->dev_bytenr,
l->block_ref_to->mirror_num);
ret = -1;
} else if (l->parent_generation != } else if (l->parent_generation !=
l->block_ref_to->generation && l->block_ref_to->generation &&
BTRFSIC_GENERATION_UNKNOWN != BTRFSIC_GENERATION_UNKNOWN !=
+10 -3
View File
@@ -577,6 +577,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
u64 em_start; u64 em_start;
struct extent_map *em; struct extent_map *em;
int ret = -ENOMEM; int ret = -ENOMEM;
int faili = 0;
u32 *sums; u32 *sums;
tree = &BTRFS_I(inode)->io_tree; tree = &BTRFS_I(inode)->io_tree;
@@ -626,9 +627,13 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
for (pg_index = 0; pg_index < nr_pages; pg_index++) { for (pg_index = 0; pg_index < nr_pages; pg_index++) {
cb->compressed_pages[pg_index] = alloc_page(GFP_NOFS | cb->compressed_pages[pg_index] = alloc_page(GFP_NOFS |
__GFP_HIGHMEM); __GFP_HIGHMEM);
if (!cb->compressed_pages[pg_index]) if (!cb->compressed_pages[pg_index]) {
faili = pg_index - 1;
ret = -ENOMEM;
goto fail2; goto fail2;
}
} }
faili = nr_pages - 1;
cb->nr_pages = nr_pages; cb->nr_pages = nr_pages;
add_ra_bio_pages(inode, em_start + em_len, cb); add_ra_bio_pages(inode, em_start + em_len, cb);
@@ -713,8 +718,10 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
return 0; return 0;
fail2: fail2:
for (pg_index = 0; pg_index < nr_pages; pg_index++) while (faili >= 0) {
free_page((unsigned long)cb->compressed_pages[pg_index]); __free_page(cb->compressed_pages[faili]);
faili--;
}
kfree(cb->compressed_pages); kfree(cb->compressed_pages);
fail1: fail1:
+4 -144
View File
@@ -4401,149 +4401,6 @@ void btrfs_extend_item(struct btrfs_trans_handle *trans,
} }
} }
/*
* Given a key and some data, insert items into the tree.
* This does all the path init required, making room in the tree if needed.
* Returns the number of keys that were inserted.
*/
int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_key *cpu_key, u32 *data_size,
int nr)
{
struct extent_buffer *leaf;
struct btrfs_item *item;
int ret = 0;
int slot;
int i;
u32 nritems;
u32 total_data = 0;
u32 total_size = 0;
unsigned int data_end;
struct btrfs_disk_key disk_key;
struct btrfs_key found_key;
struct btrfs_map_token token;
btrfs_init_map_token(&token);
for (i = 0; i < nr; i++) {
if (total_size + data_size[i] + sizeof(struct btrfs_item) >
BTRFS_LEAF_DATA_SIZE(root)) {
break;
nr = i;
}
total_data += data_size[i];
total_size += data_size[i] + sizeof(struct btrfs_item);
}
BUG_ON(nr == 0);
ret = btrfs_search_slot(trans, root, cpu_key, path, total_size, 1);
if (ret == 0)
return -EEXIST;
if (ret < 0)
goto out;
leaf = path->nodes[0];
nritems = btrfs_header_nritems(leaf);
data_end = leaf_data_end(root, leaf);
if (btrfs_leaf_free_space(root, leaf) < total_size) {
for (i = nr; i >= 0; i--) {
total_data -= data_size[i];
total_size -= data_size[i] + sizeof(struct btrfs_item);
if (total_size < btrfs_leaf_free_space(root, leaf))
break;
}
nr = i;
}
slot = path->slots[0];
BUG_ON(slot < 0);
if (slot != nritems) {
unsigned int old_data = btrfs_item_end_nr(leaf, slot);
item = btrfs_item_nr(leaf, slot);
btrfs_item_key_to_cpu(leaf, &found_key, slot);
/* figure out how many keys we can insert in here */
total_data = data_size[0];
for (i = 1; i < nr; i++) {
if (btrfs_comp_cpu_keys(&found_key, cpu_key + i) <= 0)
break;
total_data += data_size[i];
}
nr = i;
if (old_data < data_end) {
btrfs_print_leaf(root, leaf);
printk(KERN_CRIT "slot %d old_data %d data_end %d\n",
slot, old_data, data_end);
BUG_ON(1);
}
/*
* item0..itemN ... dataN.offset..dataN.size .. data0.size
*/
/* first correct the data pointers */
for (i = slot; i < nritems; i++) {
u32 ioff;
item = btrfs_item_nr(leaf, i);
ioff = btrfs_token_item_offset(leaf, item, &token);
btrfs_set_token_item_offset(leaf, item,
ioff - total_data, &token);
}
/* shift the items */
memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + nr),
btrfs_item_nr_offset(slot),
(nritems - slot) * sizeof(struct btrfs_item));
/* shift the data */
memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
data_end - total_data, btrfs_leaf_data(leaf) +
data_end, old_data - data_end);
data_end = old_data;
} else {
/*
* this sucks but it has to be done, if we are inserting at
* the end of the leaf only insert 1 of the items, since we
* have no way of knowing whats on the next leaf and we'd have
* to drop our current locks to figure it out
*/
nr = 1;
}
/* setup the item for the new data */
for (i = 0; i < nr; i++) {
btrfs_cpu_key_to_disk(&disk_key, cpu_key + i);
btrfs_set_item_key(leaf, &disk_key, slot + i);
item = btrfs_item_nr(leaf, slot + i);
btrfs_set_token_item_offset(leaf, item,
data_end - data_size[i], &token);
data_end -= data_size[i];
btrfs_set_token_item_size(leaf, item, data_size[i], &token);
}
btrfs_set_header_nritems(leaf, nritems + nr);
btrfs_mark_buffer_dirty(leaf);
ret = 0;
if (slot == 0) {
btrfs_cpu_key_to_disk(&disk_key, cpu_key);
fixup_low_keys(trans, root, path, &disk_key, 1);
}
if (btrfs_leaf_free_space(root, leaf) < 0) {
btrfs_print_leaf(root, leaf);
BUG();
}
out:
if (!ret)
ret = nr;
return ret;
}
/* /*
* this is a helper for btrfs_insert_empty_items, the main goal here is * this is a helper for btrfs_insert_empty_items, the main goal here is
* to save stack depth by doing the bulk of the work in a function * to save stack depth by doing the bulk of the work in a function
@@ -5073,6 +4930,7 @@ static void tree_move_down(struct btrfs_root *root,
struct btrfs_path *path, struct btrfs_path *path,
int *level, int root_level) int *level, int root_level)
{ {
BUG_ON(*level == 0);
path->nodes[*level - 1] = read_node_slot(root, path->nodes[*level], path->nodes[*level - 1] = read_node_slot(root, path->nodes[*level],
path->slots[*level]); path->slots[*level]);
path->slots[*level - 1] = 0; path->slots[*level - 1] = 0;
@@ -5089,7 +4947,7 @@ static int tree_move_next_or_upnext(struct btrfs_root *root,
path->slots[*level]++; path->slots[*level]++;
while (path->slots[*level] == nritems) { while (path->slots[*level] >= nritems) {
if (*level == root_level) if (*level == root_level)
return -1; return -1;
@@ -5433,9 +5291,11 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
goto out; goto out;
advance_right = ADVANCE; advance_right = ADVANCE;
} else { } else {
WARN_ON(!extent_buffer_uptodate(left_path->nodes[0]));
ret = tree_compare_item(left_root, left_path, ret = tree_compare_item(left_root, left_path,
right_path, tmp_buf); right_path, tmp_buf);
if (ret) { if (ret) {
WARN_ON(!extent_buffer_uptodate(left_path->nodes[0]));
ret = changed_cb(left_root, right_root, ret = changed_cb(left_root, right_root,
left_path, right_path, left_path, right_path,
&left_key, &left_key,
+90 -19
View File
@@ -154,6 +154,13 @@ struct btrfs_ordered_sum;
*/ */
#define BTRFS_NAME_LEN 255 #define BTRFS_NAME_LEN 255
/*
* Theoretical limit is larger, but we keep this down to a sane
* value. That should limit greatly the possibility of collisions on
* inode ref items.
*/
#define BTRFS_LINK_MAX 65535U
/* 32 bytes in various csum fields */ /* 32 bytes in various csum fields */
#define BTRFS_CSUM_SIZE 32 #define BTRFS_CSUM_SIZE 32
@@ -489,6 +496,8 @@ struct btrfs_super_block {
*/ */
#define BTRFS_FEATURE_INCOMPAT_BIG_METADATA (1ULL << 5) #define BTRFS_FEATURE_INCOMPAT_BIG_METADATA (1ULL << 5)
#define BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF (1ULL << 6)
#define BTRFS_FEATURE_COMPAT_SUPP 0ULL #define BTRFS_FEATURE_COMPAT_SUPP 0ULL
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL #define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
#define BTRFS_FEATURE_INCOMPAT_SUPP \ #define BTRFS_FEATURE_INCOMPAT_SUPP \
@@ -496,7 +505,8 @@ struct btrfs_super_block {
BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \ BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \
BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \ BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \
BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \ BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \
BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO) BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \
BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
/* /*
* A leaf is full of items. offset and size tell us where to find * A leaf is full of items. offset and size tell us where to find
@@ -643,6 +653,14 @@ struct btrfs_inode_ref {
/* name goes here */ /* name goes here */
} __attribute__ ((__packed__)); } __attribute__ ((__packed__));
struct btrfs_inode_extref {
__le64 parent_objectid;
__le64 index;
__le16 name_len;
__u8 name[0];
/* name goes here */
} __attribute__ ((__packed__));
struct btrfs_timespec { struct btrfs_timespec {
__le64 sec; __le64 sec;
__le32 nsec; __le32 nsec;
@@ -1028,12 +1046,22 @@ struct btrfs_space_info {
wait_queue_head_t wait; wait_queue_head_t wait;
}; };
#define BTRFS_BLOCK_RSV_GLOBAL 1
#define BTRFS_BLOCK_RSV_DELALLOC 2
#define BTRFS_BLOCK_RSV_TRANS 3
#define BTRFS_BLOCK_RSV_CHUNK 4
#define BTRFS_BLOCK_RSV_DELOPS 5
#define BTRFS_BLOCK_RSV_EMPTY 6
#define BTRFS_BLOCK_RSV_TEMP 7
struct btrfs_block_rsv { struct btrfs_block_rsv {
u64 size; u64 size;
u64 reserved; u64 reserved;
struct btrfs_space_info *space_info; struct btrfs_space_info *space_info;
spinlock_t lock; spinlock_t lock;
unsigned int full; unsigned short full;
unsigned short type;
unsigned short failfast;
}; };
/* /*
@@ -1127,6 +1155,9 @@ struct btrfs_block_group_cache {
* Today it will only have one thing on it, but that may change * Today it will only have one thing on it, but that may change
*/ */
struct list_head cluster_list; struct list_head cluster_list;
/* For delayed block group creation */
struct list_head new_bg_list;
}; };
/* delayed seq elem */ /* delayed seq elem */
@@ -1240,7 +1271,6 @@ struct btrfs_fs_info {
struct mutex reloc_mutex; struct mutex reloc_mutex;
struct list_head trans_list; struct list_head trans_list;
struct list_head hashers;
struct list_head dead_roots; struct list_head dead_roots;
struct list_head caching_block_groups; struct list_head caching_block_groups;
@@ -1366,9 +1396,6 @@ struct btrfs_fs_info {
struct rb_root defrag_inodes; struct rb_root defrag_inodes;
atomic_t defrag_running; atomic_t defrag_running;
spinlock_t ref_cache_lock;
u64 total_ref_cache_size;
/* /*
* these three are in extended format (availability of single * these three are in extended format (availability of single
* chunks is denoted by BTRFS_AVAIL_ALLOC_BIT_SINGLE bit, other * chunks is denoted by BTRFS_AVAIL_ALLOC_BIT_SINGLE bit, other
@@ -1441,6 +1468,8 @@ struct btrfs_fs_info {
/* next backup root to be overwritten */ /* next backup root to be overwritten */
int backup_root_index; int backup_root_index;
int num_tolerated_disk_barrier_failures;
}; };
/* /*
@@ -1481,9 +1510,9 @@ struct btrfs_root {
wait_queue_head_t log_commit_wait[2]; wait_queue_head_t log_commit_wait[2];
atomic_t log_writers; atomic_t log_writers;
atomic_t log_commit[2]; atomic_t log_commit[2];
atomic_t log_batch;
unsigned long log_transid; unsigned long log_transid;
unsigned long last_log_commit; unsigned long last_log_commit;
unsigned long log_batch;
pid_t log_start_pid; pid_t log_start_pid;
bool log_multiple_pids; bool log_multiple_pids;
@@ -1592,6 +1621,7 @@ struct btrfs_ioctl_defrag_range_args {
*/ */
#define BTRFS_INODE_ITEM_KEY 1 #define BTRFS_INODE_ITEM_KEY 1
#define BTRFS_INODE_REF_KEY 12 #define BTRFS_INODE_REF_KEY 12
#define BTRFS_INODE_EXTREF_KEY 13
#define BTRFS_XATTR_ITEM_KEY 24 #define BTRFS_XATTR_ITEM_KEY 24
#define BTRFS_ORPHAN_ITEM_KEY 48 #define BTRFS_ORPHAN_ITEM_KEY 48
/* reserve 2-15 close to the inode for later flexibility */ /* reserve 2-15 close to the inode for later flexibility */
@@ -1978,6 +2008,13 @@ BTRFS_SETGET_STACK_FUNCS(block_group_flags,
BTRFS_SETGET_FUNCS(inode_ref_name_len, struct btrfs_inode_ref, name_len, 16); BTRFS_SETGET_FUNCS(inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
BTRFS_SETGET_FUNCS(inode_ref_index, struct btrfs_inode_ref, index, 64); BTRFS_SETGET_FUNCS(inode_ref_index, struct btrfs_inode_ref, index, 64);
/* struct btrfs_inode_extref */
BTRFS_SETGET_FUNCS(inode_extref_parent, struct btrfs_inode_extref,
parent_objectid, 64);
BTRFS_SETGET_FUNCS(inode_extref_name_len, struct btrfs_inode_extref,
name_len, 16);
BTRFS_SETGET_FUNCS(inode_extref_index, struct btrfs_inode_extref, index, 64);
/* struct btrfs_inode_item */ /* struct btrfs_inode_item */
BTRFS_SETGET_FUNCS(inode_generation, struct btrfs_inode_item, generation, 64); BTRFS_SETGET_FUNCS(inode_generation, struct btrfs_inode_item, generation, 64);
BTRFS_SETGET_FUNCS(inode_sequence, struct btrfs_inode_item, sequence, 64); BTRFS_SETGET_FUNCS(inode_sequence, struct btrfs_inode_item, sequence, 64);
@@ -2858,6 +2895,8 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
u64 size); u64 size);
int btrfs_remove_block_group(struct btrfs_trans_handle *trans, int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 group_start); struct btrfs_root *root, u64 group_start);
void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags); u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags);
u64 btrfs_get_alloc_profile(struct btrfs_root *root, int data); u64 btrfs_get_alloc_profile(struct btrfs_root *root, int data);
void btrfs_clear_space_info_full(struct btrfs_fs_info *info); void btrfs_clear_space_info_full(struct btrfs_fs_info *info);
@@ -2874,8 +2913,9 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes);
void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes); void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes);
int btrfs_delalloc_reserve_space(struct inode *inode, u64 num_bytes); int btrfs_delalloc_reserve_space(struct inode *inode, u64 num_bytes);
void btrfs_delalloc_release_space(struct inode *inode, u64 num_bytes); void btrfs_delalloc_release_space(struct inode *inode, u64 num_bytes);
void btrfs_init_block_rsv(struct btrfs_block_rsv *rsv); void btrfs_init_block_rsv(struct btrfs_block_rsv *rsv, unsigned short type);
struct btrfs_block_rsv *btrfs_alloc_block_rsv(struct btrfs_root *root); struct btrfs_block_rsv *btrfs_alloc_block_rsv(struct btrfs_root *root,
unsigned short type);
void btrfs_free_block_rsv(struct btrfs_root *root, void btrfs_free_block_rsv(struct btrfs_root *root,
struct btrfs_block_rsv *rsv); struct btrfs_block_rsv *rsv);
int btrfs_block_rsv_add(struct btrfs_root *root, int btrfs_block_rsv_add(struct btrfs_root *root,
@@ -3172,12 +3212,12 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
const char *name, int name_len, const char *name, int name_len,
u64 inode_objectid, u64 ref_objectid, u64 *index); u64 inode_objectid, u64 ref_objectid, u64 *index);
struct btrfs_inode_ref * int btrfs_get_inode_ref_index(struct btrfs_trans_handle *trans,
btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_path *path, const char *name, int name_len,
const char *name, int name_len, u64 inode_objectid, u64 ref_objectid, int mod,
u64 inode_objectid, u64 ref_objectid, int mod); u64 *ret_index);
int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans, int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path, u64 objectid); struct btrfs_path *path, u64 objectid);
@@ -3185,6 +3225,19 @@ int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_path *path, *root, struct btrfs_path *path,
struct btrfs_key *location, int mod); struct btrfs_key *location, int mod);
struct btrfs_inode_extref *
btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
const char *name, int name_len,
u64 inode_objectid, u64 ref_objectid, int ins_len,
int cow);
int btrfs_find_name_in_ext_backref(struct btrfs_path *path,
u64 ref_objectid, const char *name,
int name_len,
struct btrfs_inode_extref **extref_ret);
/* file-item.c */ /* file-item.c */
int btrfs_del_csums(struct btrfs_trans_handle *trans, int btrfs_del_csums(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr, u64 len); struct btrfs_root *root, u64 bytenr, u64 len);
@@ -3249,6 +3302,8 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct inode *dir, u64 objectid, struct inode *dir, u64 objectid,
const char *name, int name_len); const char *name, int name_len);
int btrfs_truncate_page(struct inode *inode, loff_t from, loff_t len,
int front);
int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct inode *inode, u64 new_size, struct inode *inode, u64 new_size,
@@ -3308,16 +3363,27 @@ void btrfs_inherit_iflags(struct inode *inode, struct inode *dir);
int btrfs_defrag_file(struct inode *inode, struct file *file, int btrfs_defrag_file(struct inode *inode, struct file *file,
struct btrfs_ioctl_defrag_range_args *range, struct btrfs_ioctl_defrag_range_args *range,
u64 newer_than, unsigned long max_pages); u64 newer_than, unsigned long max_pages);
void btrfs_get_block_group_info(struct list_head *groups_list,
struct btrfs_ioctl_space_info *space);
/* file.c */ /* file.c */
int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans, int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
struct inode *inode); struct inode *inode);
int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info); int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info);
int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync); int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync);
int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end, void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
int skip_pinned); int skip_pinned);
int btrfs_replace_extent_cache(struct inode *inode, struct extent_map *replace,
u64 start, u64 end, int skip_pinned,
int modified);
extern const struct file_operations btrfs_file_operations; extern const struct file_operations btrfs_file_operations;
int btrfs_drop_extents(struct btrfs_trans_handle *trans, struct inode *inode, int __btrfs_drop_extents(struct btrfs_trans_handle *trans,
u64 start, u64 end, u64 *hint_byte, int drop_cache); struct btrfs_root *root, struct inode *inode,
struct btrfs_path *path, u64 start, u64 end,
u64 *drop_end, int drop_cache);
int btrfs_drop_extents(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode, u64 start,
u64 end, int drop_cache);
int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
struct inode *inode, u64 start, u64 end); struct inode *inode, u64 start, u64 end);
int btrfs_release_file(struct inode *inode, struct file *file); int btrfs_release_file(struct inode *inode, struct file *file);
@@ -3378,6 +3444,11 @@ static inline void __btrfs_set_fs_incompat(struct btrfs_fs_info *fs_info,
} }
} }
/*
* Call btrfs_abort_transaction as early as possible when an error condition is
* detected, that way the exact line number is reported.
*/
#define btrfs_abort_transaction(trans, root, errno) \ #define btrfs_abort_transaction(trans, root, errno) \
do { \ do { \
__btrfs_abort_transaction(trans, root, __func__, \ __btrfs_abort_transaction(trans, root, __func__, \
+3 -3
View File
@@ -29,7 +29,7 @@ static struct kmem_cache *delayed_node_cache;
int __init btrfs_delayed_inode_init(void) int __init btrfs_delayed_inode_init(void)
{ {
delayed_node_cache = kmem_cache_create("delayed_node", delayed_node_cache = kmem_cache_create("btrfs_delayed_node",
sizeof(struct btrfs_delayed_node), sizeof(struct btrfs_delayed_node),
0, 0,
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD,
@@ -650,7 +650,7 @@ static int btrfs_delayed_inode_reserve_metadata(
* we're accounted for. * we're accounted for.
*/ */
if (!src_rsv || (!trans->bytes_reserved && if (!src_rsv || (!trans->bytes_reserved &&
src_rsv != &root->fs_info->delalloc_block_rsv)) { src_rsv->type != BTRFS_BLOCK_RSV_DELALLOC)) {
ret = btrfs_block_rsv_add_noflush(root, dst_rsv, num_bytes); ret = btrfs_block_rsv_add_noflush(root, dst_rsv, num_bytes);
/* /*
* Since we're under a transaction reserve_metadata_bytes could * Since we're under a transaction reserve_metadata_bytes could
@@ -668,7 +668,7 @@ static int btrfs_delayed_inode_reserve_metadata(
num_bytes, 1); num_bytes, 1);
} }
return ret; return ret;
} else if (src_rsv == &root->fs_info->delalloc_block_rsv) { } else if (src_rsv->type == BTRFS_BLOCK_RSV_DELALLOC) {
spin_lock(&BTRFS_I(inode)->lock); spin_lock(&BTRFS_I(inode)->lock);
if (test_and_clear_bit(BTRFS_INODE_DELALLOC_META_RESERVED, if (test_and_clear_bit(BTRFS_INODE_DELALLOC_META_RESERVED,
&BTRFS_I(inode)->runtime_flags)) { &BTRFS_I(inode)->runtime_flags)) {
+136 -94
View File
@@ -46,6 +46,10 @@
#include "check-integrity.h" #include "check-integrity.h"
#include "rcu-string.h" #include "rcu-string.h"
#ifdef CONFIG_X86
#include <asm/cpufeature.h>
#endif
static struct extent_io_ops btree_extent_io_ops; static struct extent_io_ops btree_extent_io_ops;
static void end_workqueue_fn(struct btrfs_work *work); static void end_workqueue_fn(struct btrfs_work *work);
static void free_fs_root(struct btrfs_root *root); static void free_fs_root(struct btrfs_root *root);
@@ -217,26 +221,16 @@ static struct extent_map *btree_get_extent(struct inode *inode,
write_lock(&em_tree->lock); write_lock(&em_tree->lock);
ret = add_extent_mapping(em_tree, em); ret = add_extent_mapping(em_tree, em);
if (ret == -EEXIST) { if (ret == -EEXIST) {
u64 failed_start = em->start;
u64 failed_len = em->len;
free_extent_map(em); free_extent_map(em);
em = lookup_extent_mapping(em_tree, start, len); em = lookup_extent_mapping(em_tree, start, len);
if (em) { if (!em)
ret = 0; em = ERR_PTR(-EIO);
} else {
em = lookup_extent_mapping(em_tree, failed_start,
failed_len);
ret = -EIO;
}
} else if (ret) { } else if (ret) {
free_extent_map(em); free_extent_map(em);
em = NULL; em = ERR_PTR(ret);
} }
write_unlock(&em_tree->lock); write_unlock(&em_tree->lock);
if (ret)
em = ERR_PTR(ret);
out: out:
return em; return em;
} }
@@ -439,10 +433,6 @@ static int csum_dirty_buffer(struct btrfs_root *root, struct page *page)
WARN_ON(1); WARN_ON(1);
return 0; return 0;
} }
if (eb->pages[0] != page) {
WARN_ON(1);
return 0;
}
if (!PageUptodate(page)) { if (!PageUptodate(page)) {
WARN_ON(1); WARN_ON(1);
return 0; return 0;
@@ -869,10 +859,22 @@ static int __btree_submit_bio_done(struct inode *inode, int rw, struct bio *bio,
return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio, mirror_num, 1); return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio, mirror_num, 1);
} }
static int check_async_write(struct inode *inode, unsigned long bio_flags)
{
if (bio_flags & EXTENT_BIO_TREE_LOG)
return 0;
#ifdef CONFIG_X86
if (cpu_has_xmm4_2)
return 0;
#endif
return 1;
}
static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio, static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
int mirror_num, unsigned long bio_flags, int mirror_num, unsigned long bio_flags,
u64 bio_offset) u64 bio_offset)
{ {
int async = check_async_write(inode, bio_flags);
int ret; int ret;
if (!(rw & REQ_WRITE)) { if (!(rw & REQ_WRITE)) {
@@ -887,6 +889,12 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
return ret; return ret;
return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio, return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio,
mirror_num, 0); mirror_num, 0);
} else if (!async) {
ret = btree_csum_one_bio(bio);
if (ret)
return ret;
return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio,
mirror_num, 0);
} }
/* /*
@@ -1168,8 +1176,8 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
atomic_set(&root->log_commit[0], 0); atomic_set(&root->log_commit[0], 0);
atomic_set(&root->log_commit[1], 0); atomic_set(&root->log_commit[1], 0);
atomic_set(&root->log_writers, 0); atomic_set(&root->log_writers, 0);
atomic_set(&root->log_batch, 0);
atomic_set(&root->orphan_inodes, 0); atomic_set(&root->orphan_inodes, 0);
root->log_batch = 0;
root->log_transid = 0; root->log_transid = 0;
root->last_log_commit = 0; root->last_log_commit = 0;
extent_io_tree_init(&root->dirty_log_pages, extent_io_tree_init(&root->dirty_log_pages,
@@ -1667,9 +1675,10 @@ static int transaction_kthread(void *arg)
spin_unlock(&root->fs_info->trans_lock); spin_unlock(&root->fs_info->trans_lock);
/* If the file system is aborted, this will always fail. */ /* If the file system is aborted, this will always fail. */
trans = btrfs_join_transaction(root); trans = btrfs_attach_transaction(root);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
cannot_commit = true; if (PTR_ERR(trans) != -ENOENT)
cannot_commit = true;
goto sleep; goto sleep;
} }
if (transid == trans->transid) { if (transid == trans->transid) {
@@ -1994,13 +2003,11 @@ int open_ctree(struct super_block *sb,
INIT_LIST_HEAD(&fs_info->trans_list); INIT_LIST_HEAD(&fs_info->trans_list);
INIT_LIST_HEAD(&fs_info->dead_roots); INIT_LIST_HEAD(&fs_info->dead_roots);
INIT_LIST_HEAD(&fs_info->delayed_iputs); INIT_LIST_HEAD(&fs_info->delayed_iputs);
INIT_LIST_HEAD(&fs_info->hashers);
INIT_LIST_HEAD(&fs_info->delalloc_inodes); INIT_LIST_HEAD(&fs_info->delalloc_inodes);
INIT_LIST_HEAD(&fs_info->ordered_operations); INIT_LIST_HEAD(&fs_info->ordered_operations);
INIT_LIST_HEAD(&fs_info->caching_block_groups); INIT_LIST_HEAD(&fs_info->caching_block_groups);
spin_lock_init(&fs_info->delalloc_lock); spin_lock_init(&fs_info->delalloc_lock);
spin_lock_init(&fs_info->trans_lock); spin_lock_init(&fs_info->trans_lock);
spin_lock_init(&fs_info->ref_cache_lock);
spin_lock_init(&fs_info->fs_roots_radix_lock); spin_lock_init(&fs_info->fs_roots_radix_lock);
spin_lock_init(&fs_info->delayed_iput_lock); spin_lock_init(&fs_info->delayed_iput_lock);
spin_lock_init(&fs_info->defrag_inodes_lock); spin_lock_init(&fs_info->defrag_inodes_lock);
@@ -2014,12 +2021,15 @@ int open_ctree(struct super_block *sb,
INIT_LIST_HEAD(&fs_info->space_info); INIT_LIST_HEAD(&fs_info->space_info);
INIT_LIST_HEAD(&fs_info->tree_mod_seq_list); INIT_LIST_HEAD(&fs_info->tree_mod_seq_list);
btrfs_mapping_init(&fs_info->mapping_tree); btrfs_mapping_init(&fs_info->mapping_tree);
btrfs_init_block_rsv(&fs_info->global_block_rsv); btrfs_init_block_rsv(&fs_info->global_block_rsv,
btrfs_init_block_rsv(&fs_info->delalloc_block_rsv); BTRFS_BLOCK_RSV_GLOBAL);
btrfs_init_block_rsv(&fs_info->trans_block_rsv); btrfs_init_block_rsv(&fs_info->delalloc_block_rsv,
btrfs_init_block_rsv(&fs_info->chunk_block_rsv); BTRFS_BLOCK_RSV_DELALLOC);
btrfs_init_block_rsv(&fs_info->empty_block_rsv); btrfs_init_block_rsv(&fs_info->trans_block_rsv, BTRFS_BLOCK_RSV_TRANS);
btrfs_init_block_rsv(&fs_info->delayed_block_rsv); btrfs_init_block_rsv(&fs_info->chunk_block_rsv, BTRFS_BLOCK_RSV_CHUNK);
btrfs_init_block_rsv(&fs_info->empty_block_rsv, BTRFS_BLOCK_RSV_EMPTY);
btrfs_init_block_rsv(&fs_info->delayed_block_rsv,
BTRFS_BLOCK_RSV_DELOPS);
atomic_set(&fs_info->nr_async_submits, 0); atomic_set(&fs_info->nr_async_submits, 0);
atomic_set(&fs_info->async_delalloc_pages, 0); atomic_set(&fs_info->async_delalloc_pages, 0);
atomic_set(&fs_info->async_submit_draining, 0); atomic_set(&fs_info->async_submit_draining, 0);
@@ -2491,6 +2501,8 @@ retry_root_backup:
printk(KERN_ERR "Failed to read block groups: %d\n", ret); printk(KERN_ERR "Failed to read block groups: %d\n", ret);
goto fail_block_groups; goto fail_block_groups;
} }
fs_info->num_tolerated_disk_barrier_failures =
btrfs_calc_num_tolerated_disk_barrier_failures(fs_info);
fs_info->cleaner_kthread = kthread_run(cleaner_kthread, tree_root, fs_info->cleaner_kthread = kthread_run(cleaner_kthread, tree_root,
"btrfs-cleaner"); "btrfs-cleaner");
@@ -2874,12 +2886,10 @@ static int write_dev_flush(struct btrfs_device *device, int wait)
printk_in_rcu("btrfs: disabling barriers on dev %s\n", printk_in_rcu("btrfs: disabling barriers on dev %s\n",
rcu_str_deref(device->name)); rcu_str_deref(device->name));
device->nobarriers = 1; device->nobarriers = 1;
} } else if (!bio_flagged(bio, BIO_UPTODATE)) {
if (!bio_flagged(bio, BIO_UPTODATE)) {
ret = -EIO; ret = -EIO;
if (!bio_flagged(bio, BIO_EOPNOTSUPP)) btrfs_dev_stat_inc_and_print(device,
btrfs_dev_stat_inc_and_print(device, BTRFS_DEV_STAT_FLUSH_ERRS);
BTRFS_DEV_STAT_FLUSH_ERRS);
} }
/* drop the reference from the wait == 0 run */ /* drop the reference from the wait == 0 run */
@@ -2918,14 +2928,15 @@ static int barrier_all_devices(struct btrfs_fs_info *info)
{ {
struct list_head *head; struct list_head *head;
struct btrfs_device *dev; struct btrfs_device *dev;
int errors = 0; int errors_send = 0;
int errors_wait = 0;
int ret; int ret;
/* send down all the barriers */ /* send down all the barriers */
head = &info->fs_devices->devices; head = &info->fs_devices->devices;
list_for_each_entry_rcu(dev, head, dev_list) { list_for_each_entry_rcu(dev, head, dev_list) {
if (!dev->bdev) { if (!dev->bdev) {
errors++; errors_send++;
continue; continue;
} }
if (!dev->in_fs_metadata || !dev->writeable) if (!dev->in_fs_metadata || !dev->writeable)
@@ -2933,13 +2944,13 @@ static int barrier_all_devices(struct btrfs_fs_info *info)
ret = write_dev_flush(dev, 0); ret = write_dev_flush(dev, 0);
if (ret) if (ret)
errors++; errors_send++;
} }
/* wait for all the barriers */ /* wait for all the barriers */
list_for_each_entry_rcu(dev, head, dev_list) { list_for_each_entry_rcu(dev, head, dev_list) {
if (!dev->bdev) { if (!dev->bdev) {
errors++; errors_wait++;
continue; continue;
} }
if (!dev->in_fs_metadata || !dev->writeable) if (!dev->in_fs_metadata || !dev->writeable)
@@ -2947,13 +2958,87 @@ static int barrier_all_devices(struct btrfs_fs_info *info)
ret = write_dev_flush(dev, 1); ret = write_dev_flush(dev, 1);
if (ret) if (ret)
errors++; errors_wait++;
} }
if (errors) if (errors_send > info->num_tolerated_disk_barrier_failures ||
errors_wait > info->num_tolerated_disk_barrier_failures)
return -EIO; return -EIO;
return 0; return 0;
} }
int btrfs_calc_num_tolerated_disk_barrier_failures(
struct btrfs_fs_info *fs_info)
{
struct btrfs_ioctl_space_info space;
struct btrfs_space_info *sinfo;
u64 types[] = {BTRFS_BLOCK_GROUP_DATA,
BTRFS_BLOCK_GROUP_SYSTEM,
BTRFS_BLOCK_GROUP_METADATA,
BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA};
int num_types = 4;
int i;
int c;
int num_tolerated_disk_barrier_failures =
(int)fs_info->fs_devices->num_devices;
for (i = 0; i < num_types; i++) {
struct btrfs_space_info *tmp;
sinfo = NULL;
rcu_read_lock();
list_for_each_entry_rcu(tmp, &fs_info->space_info, list) {
if (tmp->flags == types[i]) {
sinfo = tmp;
break;
}
}
rcu_read_unlock();
if (!sinfo)
continue;
down_read(&sinfo->groups_sem);
for (c = 0; c < BTRFS_NR_RAID_TYPES; c++) {
if (!list_empty(&sinfo->block_groups[c])) {
u64 flags;
btrfs_get_block_group_info(
&sinfo->block_groups[c], &space);
if (space.total_bytes == 0 ||
space.used_bytes == 0)
continue;
flags = space.flags;
/*
* return
* 0: if dup, single or RAID0 is configured for
* any of metadata, system or data, else
* 1: if RAID5 is configured, or if RAID1 or
* RAID10 is configured and only two mirrors
* are used, else
* 2: if RAID6 is configured, else
* num_mirrors - 1: if RAID1 or RAID10 is
* configured and more than
* 2 mirrors are used.
*/
if (num_tolerated_disk_barrier_failures > 0 &&
((flags & (BTRFS_BLOCK_GROUP_DUP |
BTRFS_BLOCK_GROUP_RAID0)) ||
((flags & BTRFS_BLOCK_GROUP_PROFILE_MASK)
== 0)))
num_tolerated_disk_barrier_failures = 0;
else if (num_tolerated_disk_barrier_failures > 1
&&
(flags & (BTRFS_BLOCK_GROUP_RAID1 |
BTRFS_BLOCK_GROUP_RAID10)))
num_tolerated_disk_barrier_failures = 1;
}
}
up_read(&sinfo->groups_sem);
}
return num_tolerated_disk_barrier_failures;
}
int write_all_supers(struct btrfs_root *root, int max_mirrors) int write_all_supers(struct btrfs_root *root, int max_mirrors)
{ {
struct list_head *head; struct list_head *head;
@@ -2976,8 +3061,16 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
mutex_lock(&root->fs_info->fs_devices->device_list_mutex); mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
head = &root->fs_info->fs_devices->devices; head = &root->fs_info->fs_devices->devices;
if (do_barriers) if (do_barriers) {
barrier_all_devices(root->fs_info); ret = barrier_all_devices(root->fs_info);
if (ret) {
mutex_unlock(
&root->fs_info->fs_devices->device_list_mutex);
btrfs_error(root->fs_info, ret,
"errors while submitting device barriers.");
return ret;
}
}
list_for_each_entry_rcu(dev, head, dev_list) { list_for_each_entry_rcu(dev, head, dev_list) {
if (!dev->bdev) { if (!dev->bdev) {
@@ -3211,10 +3304,6 @@ int close_ctree(struct btrfs_root *root)
printk(KERN_INFO "btrfs: at unmount delalloc count %llu\n", printk(KERN_INFO "btrfs: at unmount delalloc count %llu\n",
(unsigned long long)fs_info->delalloc_bytes); (unsigned long long)fs_info->delalloc_bytes);
} }
if (fs_info->total_ref_cache_size) {
printk(KERN_INFO "btrfs: at umount reference cache size %llu\n",
(unsigned long long)fs_info->total_ref_cache_size);
}
free_extent_buffer(fs_info->extent_root->node); free_extent_buffer(fs_info->extent_root->node);
free_extent_buffer(fs_info->extent_root->commit_root); free_extent_buffer(fs_info->extent_root->commit_root);
@@ -3360,52 +3449,6 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid)
return btree_read_extent_buffer_pages(root, buf, 0, parent_transid); return btree_read_extent_buffer_pages(root, buf, 0, parent_transid);
} }
int btree_lock_page_hook(struct page *page, void *data,
void (*flush_fn)(void *))
{
struct inode *inode = page->mapping->host;
struct btrfs_root *root = BTRFS_I(inode)->root;
struct extent_buffer *eb;
/*
* We culled this eb but the page is still hanging out on the mapping,
* carry on.
*/
if (!PagePrivate(page))
goto out;
eb = (struct extent_buffer *)page->private;
if (!eb) {
WARN_ON(1);
goto out;
}
if (page != eb->pages[0])
goto out;
if (!btrfs_try_tree_write_lock(eb)) {
flush_fn(data);
btrfs_tree_lock(eb);
}
btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
spin_lock(&root->fs_info->delalloc_lock);
if (root->fs_info->dirty_metadata_bytes >= eb->len)
root->fs_info->dirty_metadata_bytes -= eb->len;
else
WARN_ON(1);
spin_unlock(&root->fs_info->delalloc_lock);
}
btrfs_tree_unlock(eb);
out:
if (!trylock_page(page)) {
flush_fn(data);
lock_page(page);
}
return 0;
}
static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info, static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
int read_only) int read_only)
{ {
@@ -3608,7 +3651,7 @@ static int btrfs_destroy_marked_extents(struct btrfs_root *root,
while (1) { while (1) {
ret = find_first_extent_bit(dirty_pages, start, &start, &end, ret = find_first_extent_bit(dirty_pages, start, &start, &end,
mark); mark, NULL);
if (ret) if (ret)
break; break;
@@ -3663,7 +3706,7 @@ static int btrfs_destroy_pinned_extent(struct btrfs_root *root,
again: again:
while (1) { while (1) {
ret = find_first_extent_bit(unpin, 0, &start, &end, ret = find_first_extent_bit(unpin, 0, &start, &end,
EXTENT_DIRTY); EXTENT_DIRTY, NULL);
if (ret) if (ret)
break; break;
@@ -3800,7 +3843,6 @@ int btrfs_cleanup_transaction(struct btrfs_root *root)
} }
static struct extent_io_ops btree_extent_io_ops = { static struct extent_io_ops btree_extent_io_ops = {
.write_cache_pages_lock_hook = btree_lock_page_hook,
.readpage_end_io_hook = btree_readpage_end_io_hook, .readpage_end_io_hook = btree_readpage_end_io_hook,
.readpage_io_failed_hook = btree_io_failed_hook, .readpage_io_failed_hook = btree_io_failed_hook,
.submit_bio_hook = btree_submit_bio_hook, .submit_bio_hook = btree_submit_bio_hook,
+2
View File
@@ -95,6 +95,8 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
u64 objectid); u64 objectid);
int btree_lock_page_hook(struct page *page, void *data, int btree_lock_page_hook(struct page *page, void *data,
void (*flush_fn)(void *)); void (*flush_fn)(void *));
int btrfs_calc_num_tolerated_disk_barrier_failures(
struct btrfs_fs_info *fs_info);
#ifdef CONFIG_DEBUG_LOCK_ALLOC #ifdef CONFIG_DEBUG_LOCK_ALLOC
void btrfs_init_lockdep(void); void btrfs_init_lockdep(void);
+187 -189
View File
File diff suppressed because it is too large Load Diff
+95 -35
View File
@@ -45,6 +45,7 @@ struct extent_page_data {
struct bio *bio; struct bio *bio;
struct extent_io_tree *tree; struct extent_io_tree *tree;
get_extent_t *get_extent; get_extent_t *get_extent;
unsigned long bio_flags;
/* tells writepage not to lock the state bits for this range /* tells writepage not to lock the state bits for this range
* it still does the unlocking * it still does the unlocking
@@ -64,13 +65,13 @@ tree_fs_info(struct extent_io_tree *tree)
int __init extent_io_init(void) int __init extent_io_init(void)
{ {
extent_state_cache = kmem_cache_create("extent_state", extent_state_cache = kmem_cache_create("btrfs_extent_state",
sizeof(struct extent_state), 0, sizeof(struct extent_state), 0,
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL); SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
if (!extent_state_cache) if (!extent_state_cache)
return -ENOMEM; return -ENOMEM;
extent_buffer_cache = kmem_cache_create("extent_buffers", extent_buffer_cache = kmem_cache_create("btrfs_extent_buffer",
sizeof(struct extent_buffer), 0, sizeof(struct extent_buffer), 0,
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL); SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
if (!extent_buffer_cache) if (!extent_buffer_cache)
@@ -942,6 +943,7 @@ int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits,
* @end: the end offset in bytes (inclusive) * @end: the end offset in bytes (inclusive)
* @bits: the bits to set in this range * @bits: the bits to set in this range
* @clear_bits: the bits to clear in this range * @clear_bits: the bits to clear in this range
* @cached_state: state that we're going to cache
* @mask: the allocation mask * @mask: the allocation mask
* *
* This will go through and set bits for the given range. If any states exist * This will go through and set bits for the given range. If any states exist
@@ -951,7 +953,8 @@ int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits,
* boundary bits like LOCK. * boundary bits like LOCK.
*/ */
int convert_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int convert_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
int bits, int clear_bits, gfp_t mask) int bits, int clear_bits,
struct extent_state **cached_state, gfp_t mask)
{ {
struct extent_state *state; struct extent_state *state;
struct extent_state *prealloc = NULL; struct extent_state *prealloc = NULL;
@@ -968,6 +971,15 @@ again:
} }
spin_lock(&tree->lock); spin_lock(&tree->lock);
if (cached_state && *cached_state) {
state = *cached_state;
if (state->start <= start && state->end > start &&
state->tree) {
node = &state->rb_node;
goto hit_next;
}
}
/* /*
* this search will find all the extents that end after * this search will find all the extents that end after
* our range starts. * our range starts.
@@ -998,6 +1010,7 @@ hit_next:
*/ */
if (state->start == start && state->end <= end) { if (state->start == start && state->end <= end) {
set_state_bits(tree, state, &bits); set_state_bits(tree, state, &bits);
cache_state(state, cached_state);
state = clear_state_bit(tree, state, &clear_bits, 0); state = clear_state_bit(tree, state, &clear_bits, 0);
if (last_end == (u64)-1) if (last_end == (u64)-1)
goto out; goto out;
@@ -1038,6 +1051,7 @@ hit_next:
goto out; goto out;
if (state->end <= end) { if (state->end <= end) {
set_state_bits(tree, state, &bits); set_state_bits(tree, state, &bits);
cache_state(state, cached_state);
state = clear_state_bit(tree, state, &clear_bits, 0); state = clear_state_bit(tree, state, &clear_bits, 0);
if (last_end == (u64)-1) if (last_end == (u64)-1)
goto out; goto out;
@@ -1076,6 +1090,7 @@ hit_next:
&bits); &bits);
if (err) if (err)
extent_io_tree_panic(tree, err); extent_io_tree_panic(tree, err);
cache_state(prealloc, cached_state);
prealloc = NULL; prealloc = NULL;
start = this_end + 1; start = this_end + 1;
goto search_again; goto search_again;
@@ -1098,6 +1113,7 @@ hit_next:
extent_io_tree_panic(tree, err); extent_io_tree_panic(tree, err);
set_state_bits(tree, prealloc, &bits); set_state_bits(tree, prealloc, &bits);
cache_state(prealloc, cached_state);
clear_state_bit(tree, prealloc, &clear_bits, 0); clear_state_bit(tree, prealloc, &clear_bits, 0);
prealloc = NULL; prealloc = NULL;
goto out; goto out;
@@ -1150,6 +1166,14 @@ int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end,
NULL, cached_state, mask); NULL, cached_state, mask);
} }
int set_extent_defrag(struct extent_io_tree *tree, u64 start, u64 end,
struct extent_state **cached_state, gfp_t mask)
{
return set_extent_bit(tree, start, end,
EXTENT_DELALLOC | EXTENT_UPTODATE | EXTENT_DEFRAG,
NULL, cached_state, mask);
}
int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask) gfp_t mask)
{ {
@@ -1294,18 +1318,42 @@ out:
* If nothing was found, 1 is returned. If found something, return 0. * If nothing was found, 1 is returned. If found something, return 0.
*/ */
int find_first_extent_bit(struct extent_io_tree *tree, u64 start, int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
u64 *start_ret, u64 *end_ret, int bits) u64 *start_ret, u64 *end_ret, int bits,
struct extent_state **cached_state)
{ {
struct extent_state *state; struct extent_state *state;
struct rb_node *n;
int ret = 1; int ret = 1;
spin_lock(&tree->lock); spin_lock(&tree->lock);
if (cached_state && *cached_state) {
state = *cached_state;
if (state->end == start - 1 && state->tree) {
n = rb_next(&state->rb_node);
while (n) {
state = rb_entry(n, struct extent_state,
rb_node);
if (state->state & bits)
goto got_it;
n = rb_next(n);
}
free_extent_state(*cached_state);
*cached_state = NULL;
goto out;
}
free_extent_state(*cached_state);
*cached_state = NULL;
}
state = find_first_extent_bit_state(tree, start, bits); state = find_first_extent_bit_state(tree, start, bits);
got_it:
if (state) { if (state) {
cache_state(state, cached_state);
*start_ret = state->start; *start_ret = state->start;
*end_ret = state->end; *end_ret = state->end;
ret = 0; ret = 0;
} }
out:
spin_unlock(&tree->lock); spin_unlock(&tree->lock);
return ret; return ret;
} }
@@ -2068,7 +2116,7 @@ static int bio_readpage_error(struct bio *failed_bio, struct page *page,
} }
read_unlock(&em_tree->lock); read_unlock(&em_tree->lock);
if (!em || IS_ERR(em)) { if (!em) {
kfree(failrec); kfree(failrec);
return -EIO; return -EIO;
} }
@@ -2304,8 +2352,8 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
struct extent_state *cached = NULL; struct extent_state *cached = NULL;
struct extent_state *state; struct extent_state *state;
pr_debug("end_bio_extent_readpage: bi_vcnt=%d, idx=%d, err=%d, " pr_debug("end_bio_extent_readpage: bi_sector=%llu, err=%d, "
"mirror=%ld\n", bio->bi_vcnt, bio->bi_idx, err, "mirror=%ld\n", (u64)bio->bi_sector, err,
(long int)bio->bi_bdev); (long int)bio->bi_bdev);
tree = &BTRFS_I(page->mapping->host)->io_tree; tree = &BTRFS_I(page->mapping->host)->io_tree;
@@ -2709,12 +2757,15 @@ static int __extent_read_full_page(struct extent_io_tree *tree,
end_bio_extent_readpage, mirror_num, end_bio_extent_readpage, mirror_num,
*bio_flags, *bio_flags,
this_bio_flag); this_bio_flag);
BUG_ON(ret == -ENOMEM); if (!ret) {
nr++; nr++;
*bio_flags = this_bio_flag; *bio_flags = this_bio_flag;
}
} }
if (ret) if (ret) {
SetPageError(page); SetPageError(page);
unlock_extent(tree, cur, cur + iosize - 1);
}
cur = cur + iosize; cur = cur + iosize;
pg_offset += iosize; pg_offset += iosize;
} }
@@ -3161,12 +3212,16 @@ static int write_one_eb(struct extent_buffer *eb,
struct block_device *bdev = fs_info->fs_devices->latest_bdev; struct block_device *bdev = fs_info->fs_devices->latest_bdev;
u64 offset = eb->start; u64 offset = eb->start;
unsigned long i, num_pages; unsigned long i, num_pages;
unsigned long bio_flags = 0;
int rw = (epd->sync_io ? WRITE_SYNC : WRITE); int rw = (epd->sync_io ? WRITE_SYNC : WRITE);
int ret = 0; int ret = 0;
clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags); clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
num_pages = num_extent_pages(eb->start, eb->len); num_pages = num_extent_pages(eb->start, eb->len);
atomic_set(&eb->io_pages, num_pages); atomic_set(&eb->io_pages, num_pages);
if (btrfs_header_owner(eb) == BTRFS_TREE_LOG_OBJECTID)
bio_flags = EXTENT_BIO_TREE_LOG;
for (i = 0; i < num_pages; i++) { for (i = 0; i < num_pages; i++) {
struct page *p = extent_buffer_page(eb, i); struct page *p = extent_buffer_page(eb, i);
@@ -3175,7 +3230,8 @@ static int write_one_eb(struct extent_buffer *eb,
ret = submit_extent_page(rw, eb->tree, p, offset >> 9, ret = submit_extent_page(rw, eb->tree, p, offset >> 9,
PAGE_CACHE_SIZE, 0, bdev, &epd->bio, PAGE_CACHE_SIZE, 0, bdev, &epd->bio,
-1, end_bio_extent_buffer_writepage, -1, end_bio_extent_buffer_writepage,
0, 0, 0); 0, epd->bio_flags, bio_flags);
epd->bio_flags = bio_flags;
if (ret) { if (ret) {
set_bit(EXTENT_BUFFER_IOERR, &eb->bflags); set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
SetPageError(p); SetPageError(p);
@@ -3210,6 +3266,7 @@ int btree_write_cache_pages(struct address_space *mapping,
.tree = tree, .tree = tree,
.extent_locked = 0, .extent_locked = 0,
.sync_io = wbc->sync_mode == WB_SYNC_ALL, .sync_io = wbc->sync_mode == WB_SYNC_ALL,
.bio_flags = 0,
}; };
int ret = 0; int ret = 0;
int done = 0; int done = 0;
@@ -3254,20 +3311,35 @@ retry:
break; break;
} }
spin_lock(&mapping->private_lock);
if (!PagePrivate(page)) {
spin_unlock(&mapping->private_lock);
continue;
}
eb = (struct extent_buffer *)page->private; eb = (struct extent_buffer *)page->private;
/*
* Shouldn't happen and normally this would be a BUG_ON
* but no sense in crashing the users box for something
* we can survive anyway.
*/
if (!eb) { if (!eb) {
spin_unlock(&mapping->private_lock);
WARN_ON(1); WARN_ON(1);
continue; continue;
} }
if (eb == prev_eb) if (eb == prev_eb) {
continue; spin_unlock(&mapping->private_lock);
if (!atomic_inc_not_zero(&eb->refs)) {
WARN_ON(1);
continue; continue;
} }
ret = atomic_inc_not_zero(&eb->refs);
spin_unlock(&mapping->private_lock);
if (!ret)
continue;
prev_eb = eb; prev_eb = eb;
ret = lock_extent_buffer_for_io(eb, fs_info, &epd); ret = lock_extent_buffer_for_io(eb, fs_info, &epd);
if (!ret) { if (!ret) {
@@ -3457,7 +3529,7 @@ static void flush_epd_write_bio(struct extent_page_data *epd)
if (epd->sync_io) if (epd->sync_io)
rw = WRITE_SYNC; rw = WRITE_SYNC;
ret = submit_one_bio(rw, epd->bio, 0, 0); ret = submit_one_bio(rw, epd->bio, 0, epd->bio_flags);
BUG_ON(ret < 0); /* -ENOMEM */ BUG_ON(ret < 0); /* -ENOMEM */
epd->bio = NULL; epd->bio = NULL;
} }
@@ -3480,6 +3552,7 @@ int extent_write_full_page(struct extent_io_tree *tree, struct page *page,
.get_extent = get_extent, .get_extent = get_extent,
.extent_locked = 0, .extent_locked = 0,
.sync_io = wbc->sync_mode == WB_SYNC_ALL, .sync_io = wbc->sync_mode == WB_SYNC_ALL,
.bio_flags = 0,
}; };
ret = __extent_writepage(page, wbc, &epd); ret = __extent_writepage(page, wbc, &epd);
@@ -3504,6 +3577,7 @@ int extent_write_locked_range(struct extent_io_tree *tree, struct inode *inode,
.get_extent = get_extent, .get_extent = get_extent,
.extent_locked = 1, .extent_locked = 1,
.sync_io = mode == WB_SYNC_ALL, .sync_io = mode == WB_SYNC_ALL,
.bio_flags = 0,
}; };
struct writeback_control wbc_writepages = { struct writeback_control wbc_writepages = {
.sync_mode = mode, .sync_mode = mode,
@@ -3543,6 +3617,7 @@ int extent_writepages(struct extent_io_tree *tree,
.get_extent = get_extent, .get_extent = get_extent,
.extent_locked = 0, .extent_locked = 0,
.sync_io = wbc->sync_mode == WB_SYNC_ALL, .sync_io = wbc->sync_mode == WB_SYNC_ALL,
.bio_flags = 0,
}; };
ret = extent_write_cache_pages(tree, mapping, wbc, ret = extent_write_cache_pages(tree, mapping, wbc,
@@ -3920,18 +3995,6 @@ out:
return ret; return ret;
} }
inline struct page *extent_buffer_page(struct extent_buffer *eb,
unsigned long i)
{
return eb->pages[i];
}
inline unsigned long num_extent_pages(u64 start, u64 len)
{
return ((start + len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) -
(start >> PAGE_CACHE_SHIFT);
}
static void __free_extent_buffer(struct extent_buffer *eb) static void __free_extent_buffer(struct extent_buffer *eb)
{ {
#if LEAK_DEBUG #if LEAK_DEBUG
@@ -4047,7 +4110,7 @@ struct extent_buffer *alloc_dummy_extent_buffer(u64 start, unsigned long len)
return eb; return eb;
err: err:
for (i--; i > 0; i--) for (i--; i >= 0; i--)
__free_page(eb->pages[i]); __free_page(eb->pages[i]);
__free_extent_buffer(eb); __free_extent_buffer(eb);
return NULL; return NULL;
@@ -4192,10 +4255,8 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
for (i = 0; i < num_pages; i++, index++) { for (i = 0; i < num_pages; i++, index++) {
p = find_or_create_page(mapping, index, GFP_NOFS); p = find_or_create_page(mapping, index, GFP_NOFS);
if (!p) { if (!p)
WARN_ON(1);
goto free_eb; goto free_eb;
}
spin_lock(&mapping->private_lock); spin_lock(&mapping->private_lock);
if (PagePrivate(p)) { if (PagePrivate(p)) {
@@ -4338,7 +4399,6 @@ static int release_extent_buffer(struct extent_buffer *eb, gfp_t mask)
/* Should be safe to release our pages at this point */ /* Should be safe to release our pages at this point */
btrfs_release_extent_buffer_page(eb, 0); btrfs_release_extent_buffer_page(eb, 0);
call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu); call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu);
return 1; return 1;
} }
+19 -4
View File
@@ -27,6 +27,7 @@
* type for this bio * type for this bio
*/ */
#define EXTENT_BIO_COMPRESSED 1 #define EXTENT_BIO_COMPRESSED 1
#define EXTENT_BIO_TREE_LOG 2
#define EXTENT_BIO_FLAG_SHIFT 16 #define EXTENT_BIO_FLAG_SHIFT 16
/* these are bit numbers for test/set bit */ /* these are bit numbers for test/set bit */
@@ -232,11 +233,15 @@ int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask); gfp_t mask);
int convert_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int convert_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
int bits, int clear_bits, gfp_t mask); int bits, int clear_bits,
struct extent_state **cached_state, gfp_t mask);
int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end, int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end,
struct extent_state **cached_state, gfp_t mask); struct extent_state **cached_state, gfp_t mask);
int set_extent_defrag(struct extent_io_tree *tree, u64 start, u64 end,
struct extent_state **cached_state, gfp_t mask);
int find_first_extent_bit(struct extent_io_tree *tree, u64 start, int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
u64 *start_ret, u64 *end_ret, int bits); u64 *start_ret, u64 *end_ret, int bits,
struct extent_state **cached_state);
struct extent_state *find_first_extent_bit_state(struct extent_io_tree *tree, struct extent_state *find_first_extent_bit_state(struct extent_io_tree *tree,
u64 start, int bits); u64 start, int bits);
int extent_invalidatepage(struct extent_io_tree *tree, int extent_invalidatepage(struct extent_io_tree *tree,
@@ -277,8 +282,18 @@ void free_extent_buffer_stale(struct extent_buffer *eb);
int read_extent_buffer_pages(struct extent_io_tree *tree, int read_extent_buffer_pages(struct extent_io_tree *tree,
struct extent_buffer *eb, u64 start, int wait, struct extent_buffer *eb, u64 start, int wait,
get_extent_t *get_extent, int mirror_num); get_extent_t *get_extent, int mirror_num);
unsigned long num_extent_pages(u64 start, u64 len);
struct page *extent_buffer_page(struct extent_buffer *eb, unsigned long i); static inline unsigned long num_extent_pages(u64 start, u64 len)
{
return ((start + len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) -
(start >> PAGE_CACHE_SHIFT);
}
static inline struct page *extent_buffer_page(struct extent_buffer *eb,
unsigned long i)
{
return eb->pages[i];
}
static inline void extent_buffer_get(struct extent_buffer *eb) static inline void extent_buffer_get(struct extent_buffer *eb)
{ {
+53 -2
View File
@@ -11,7 +11,7 @@ static struct kmem_cache *extent_map_cache;
int __init extent_map_init(void) int __init extent_map_init(void)
{ {
extent_map_cache = kmem_cache_create("extent_map", extent_map_cache = kmem_cache_create("btrfs_extent_map",
sizeof(struct extent_map), 0, sizeof(struct extent_map), 0,
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL); SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
if (!extent_map_cache) if (!extent_map_cache)
@@ -35,6 +35,7 @@ void extent_map_exit(void)
void extent_map_tree_init(struct extent_map_tree *tree) void extent_map_tree_init(struct extent_map_tree *tree)
{ {
tree->map = RB_ROOT; tree->map = RB_ROOT;
INIT_LIST_HEAD(&tree->modified_extents);
rwlock_init(&tree->lock); rwlock_init(&tree->lock);
} }
@@ -54,7 +55,9 @@ struct extent_map *alloc_extent_map(void)
em->in_tree = 0; em->in_tree = 0;
em->flags = 0; em->flags = 0;
em->compress_type = BTRFS_COMPRESS_NONE; em->compress_type = BTRFS_COMPRESS_NONE;
em->generation = 0;
atomic_set(&em->refs, 1); atomic_set(&em->refs, 1);
INIT_LIST_HEAD(&em->list);
return em; return em;
} }
@@ -72,6 +75,7 @@ void free_extent_map(struct extent_map *em)
WARN_ON(atomic_read(&em->refs) == 0); WARN_ON(atomic_read(&em->refs) == 0);
if (atomic_dec_and_test(&em->refs)) { if (atomic_dec_and_test(&em->refs)) {
WARN_ON(em->in_tree); WARN_ON(em->in_tree);
WARN_ON(!list_empty(&em->list));
kmem_cache_free(extent_map_cache, em); kmem_cache_free(extent_map_cache, em);
} }
} }
@@ -198,6 +202,14 @@ static void try_merge_map(struct extent_map_tree *tree, struct extent_map *em)
em->block_len += merge->block_len; em->block_len += merge->block_len;
em->block_start = merge->block_start; em->block_start = merge->block_start;
merge->in_tree = 0; merge->in_tree = 0;
if (merge->generation > em->generation) {
em->mod_start = em->start;
em->mod_len = em->len;
em->generation = merge->generation;
list_move(&em->list, &tree->modified_extents);
}
list_del_init(&merge->list);
rb_erase(&merge->rb_node, &tree->map); rb_erase(&merge->rb_node, &tree->map);
free_extent_map(merge); free_extent_map(merge);
} }
@@ -211,14 +223,34 @@ static void try_merge_map(struct extent_map_tree *tree, struct extent_map *em)
em->block_len += merge->len; em->block_len += merge->len;
rb_erase(&merge->rb_node, &tree->map); rb_erase(&merge->rb_node, &tree->map);
merge->in_tree = 0; merge->in_tree = 0;
if (merge->generation > em->generation) {
em->mod_len = em->len;
em->generation = merge->generation;
list_move(&em->list, &tree->modified_extents);
}
list_del_init(&merge->list);
free_extent_map(merge); free_extent_map(merge);
} }
} }
int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len) /**
* unpint_extent_cache - unpin an extent from the cache
* @tree: tree to unpin the extent in
* @start: logical offset in the file
* @len: length of the extent
* @gen: generation that this extent has been modified in
* @prealloc: if this is set we need to clear the prealloc flag
*
* Called after an extent has been written to disk properly. Set the generation
* to the generation that actually added the file item to the inode so we know
* we need to sync this extent when we call fsync().
*/
int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len,
u64 gen)
{ {
int ret = 0; int ret = 0;
struct extent_map *em; struct extent_map *em;
bool prealloc = false;
write_lock(&tree->lock); write_lock(&tree->lock);
em = lookup_extent_mapping(tree, start, len); em = lookup_extent_mapping(tree, start, len);
@@ -228,10 +260,24 @@ int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len)
if (!em) if (!em)
goto out; goto out;
list_move(&em->list, &tree->modified_extents);
em->generation = gen;
clear_bit(EXTENT_FLAG_PINNED, &em->flags); clear_bit(EXTENT_FLAG_PINNED, &em->flags);
em->mod_start = em->start;
em->mod_len = em->len;
if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) {
prealloc = true;
clear_bit(EXTENT_FLAG_PREALLOC, &em->flags);
}
try_merge_map(tree, em); try_merge_map(tree, em);
if (prealloc) {
em->mod_start = em->start;
em->mod_len = em->len;
}
free_extent_map(em); free_extent_map(em);
out: out:
write_unlock(&tree->lock); write_unlock(&tree->lock);
@@ -269,6 +315,9 @@ int add_extent_mapping(struct extent_map_tree *tree,
} }
atomic_inc(&em->refs); atomic_inc(&em->refs);
em->mod_start = em->start;
em->mod_len = em->len;
try_merge_map(tree, em); try_merge_map(tree, em);
out: out:
return ret; return ret;
@@ -358,6 +407,8 @@ int remove_extent_mapping(struct extent_map_tree *tree, struct extent_map *em)
WARN_ON(test_bit(EXTENT_FLAG_PINNED, &em->flags)); WARN_ON(test_bit(EXTENT_FLAG_PINNED, &em->flags));
rb_erase(&em->rb_node, &tree->map); rb_erase(&em->rb_node, &tree->map);
if (!test_bit(EXTENT_FLAG_LOGGING, &em->flags))
list_del_init(&em->list);
em->in_tree = 0; em->in_tree = 0;
return ret; return ret;
} }
+7 -1
View File
@@ -13,6 +13,7 @@
#define EXTENT_FLAG_COMPRESSED 1 #define EXTENT_FLAG_COMPRESSED 1
#define EXTENT_FLAG_VACANCY 2 /* no file extent item found */ #define EXTENT_FLAG_VACANCY 2 /* no file extent item found */
#define EXTENT_FLAG_PREALLOC 3 /* pre-allocated extent */ #define EXTENT_FLAG_PREALLOC 3 /* pre-allocated extent */
#define EXTENT_FLAG_LOGGING 4 /* Logging this extent */
struct extent_map { struct extent_map {
struct rb_node rb_node; struct rb_node rb_node;
@@ -20,18 +21,23 @@ struct extent_map {
/* all of these are in bytes */ /* all of these are in bytes */
u64 start; u64 start;
u64 len; u64 len;
u64 mod_start;
u64 mod_len;
u64 orig_start; u64 orig_start;
u64 block_start; u64 block_start;
u64 block_len; u64 block_len;
u64 generation;
unsigned long flags; unsigned long flags;
struct block_device *bdev; struct block_device *bdev;
atomic_t refs; atomic_t refs;
unsigned int in_tree; unsigned int in_tree;
unsigned int compress_type; unsigned int compress_type;
struct list_head list;
}; };
struct extent_map_tree { struct extent_map_tree {
struct rb_root map; struct rb_root map;
struct list_head modified_extents;
rwlock_t lock; rwlock_t lock;
}; };
@@ -60,7 +66,7 @@ struct extent_map *alloc_extent_map(void);
void free_extent_map(struct extent_map *em); void free_extent_map(struct extent_map *em);
int __init extent_map_init(void); int __init extent_map_init(void);
void extent_map_exit(void); void extent_map_exit(void);
int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len); int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len, u64 gen);
struct extent_map *search_extent_mapping(struct extent_map_tree *tree, struct extent_map *search_extent_mapping(struct extent_map_tree *tree,
u64 start, u64 len); u64 start, u64 len);
#endif #endif
+3 -2
View File
@@ -25,11 +25,12 @@
#include "transaction.h" #include "transaction.h"
#include "print-tree.h" #include "print-tree.h"
#define __MAX_CSUM_ITEMS(r, size) ((((BTRFS_LEAF_DATA_SIZE(r) - \ #define __MAX_CSUM_ITEMS(r, size) ((unsigned long)(((BTRFS_LEAF_DATA_SIZE(r) - \
sizeof(struct btrfs_item) * 2) / \ sizeof(struct btrfs_item) * 2) / \
size) - 1)) size) - 1))
#define MAX_CSUM_ITEMS(r, size) (min(__MAX_CSUM_ITEMS(r, size), PAGE_CACHE_SIZE)) #define MAX_CSUM_ITEMS(r, size) (min_t(u32, __MAX_CSUM_ITEMS(r, size), \
PAGE_CACHE_SIZE))
#define MAX_ORDERED_SUM_BYTES(r) ((PAGE_SIZE - \ #define MAX_ORDERED_SUM_BYTES(r) ((PAGE_SIZE - \
sizeof(struct btrfs_ordered_sum)) / \ sizeof(struct btrfs_ordered_sum)) / \
+408 -39
View File
File diff suppressed because it is too large Load Diff
+3 -7
View File
@@ -966,7 +966,7 @@ int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
block_group->key.offset)) { block_group->key.offset)) {
ret = find_first_extent_bit(unpin, start, ret = find_first_extent_bit(unpin, start,
&extent_start, &extent_end, &extent_start, &extent_end,
EXTENT_DIRTY); EXTENT_DIRTY, NULL);
if (ret) { if (ret) {
ret = 0; ret = 0;
break; break;
@@ -1454,9 +1454,7 @@ static int search_bitmap(struct btrfs_free_space_ctl *ctl,
max_t(u64, *offset, bitmap_info->offset)); max_t(u64, *offset, bitmap_info->offset));
bits = bytes_to_bits(*bytes, ctl->unit); bits = bytes_to_bits(*bytes, ctl->unit);
for (i = find_next_bit(bitmap_info->bitmap, BITS_PER_BITMAP, i); for_each_set_bit_from(i, bitmap_info->bitmap, BITS_PER_BITMAP) {
i < BITS_PER_BITMAP;
i = find_next_bit(bitmap_info->bitmap, BITS_PER_BITMAP, i + 1)) {
next_zero = find_next_zero_bit(bitmap_info->bitmap, next_zero = find_next_zero_bit(bitmap_info->bitmap,
BITS_PER_BITMAP, i); BITS_PER_BITMAP, i);
if ((next_zero - i) >= bits) { if ((next_zero - i) >= bits) {
@@ -2307,9 +2305,7 @@ static int btrfs_bitmap_cluster(struct btrfs_block_group_cache *block_group,
again: again:
found_bits = 0; found_bits = 0;
for (i = find_next_bit(entry->bitmap, BITS_PER_BITMAP, i); for_each_set_bit_from(i, entry->bitmap, BITS_PER_BITMAP) {
i < BITS_PER_BITMAP;
i = find_next_bit(entry->bitmap, BITS_PER_BITMAP, i + 1)) {
next_zero = find_next_zero_bit(entry->bitmap, next_zero = find_next_zero_bit(entry->bitmap,
BITS_PER_BITMAP, i); BITS_PER_BITMAP, i);
if (next_zero - i >= min_bits) { if (next_zero - i >= min_bits) {
+10
View File
@@ -24,4 +24,14 @@ static inline u64 btrfs_name_hash(const char *name, int len)
{ {
return crc32c((u32)~1, name, len); return crc32c((u32)~1, name, len);
} }
/*
* Figure the key offset of an extended inode ref
*/
static inline u64 btrfs_extref_hash(u64 parent_objectid, const char *name,
int len)
{
return (u64) crc32c(parent_objectid, name, len);
}
#endif #endif
+275 -12
View File
@@ -18,6 +18,7 @@
#include "ctree.h" #include "ctree.h"
#include "disk-io.h" #include "disk-io.h"
#include "hash.h"
#include "transaction.h" #include "transaction.h"
#include "print-tree.h" #include "print-tree.h"
@@ -50,18 +51,57 @@ static int find_name_in_backref(struct btrfs_path *path, const char *name,
return 0; return 0;
} }
struct btrfs_inode_ref * int btrfs_find_name_in_ext_backref(struct btrfs_path *path, u64 ref_objectid,
btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans, const char *name, int name_len,
struct btrfs_root *root, struct btrfs_inode_extref **extref_ret)
struct btrfs_path *path,
const char *name, int name_len,
u64 inode_objectid, u64 ref_objectid, int mod)
{ {
struct extent_buffer *leaf;
struct btrfs_inode_extref *extref;
unsigned long ptr;
unsigned long name_ptr;
u32 item_size;
u32 cur_offset = 0;
int ref_name_len;
leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
/*
* Search all extended backrefs in this item. We're only
* looking through any collisions so most of the time this is
* just going to compare against one buffer. If all is well,
* we'll return success and the inode ref object.
*/
while (cur_offset < item_size) {
extref = (struct btrfs_inode_extref *) (ptr + cur_offset);
name_ptr = (unsigned long)(&extref->name);
ref_name_len = btrfs_inode_extref_name_len(leaf, extref);
if (ref_name_len == name_len &&
btrfs_inode_extref_parent(leaf, extref) == ref_objectid &&
(memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)) {
if (extref_ret)
*extref_ret = extref;
return 1;
}
cur_offset += ref_name_len + sizeof(*extref);
}
return 0;
}
static struct btrfs_inode_ref *
btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
const char *name, int name_len,
u64 inode_objectid, u64 ref_objectid, int ins_len,
int cow)
{
int ret;
struct btrfs_key key; struct btrfs_key key;
struct btrfs_inode_ref *ref; struct btrfs_inode_ref *ref;
int ins_len = mod < 0 ? -1 : 0;
int cow = mod != 0;
int ret;
key.objectid = inode_objectid; key.objectid = inode_objectid;
key.type = BTRFS_INODE_REF_KEY; key.type = BTRFS_INODE_REF_KEY;
@@ -77,10 +117,147 @@ btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans,
return ref; return ref;
} }
int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, /* Returns NULL if no extref found */
struct btrfs_inode_extref *
btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
const char *name, int name_len,
u64 inode_objectid, u64 ref_objectid, int ins_len,
int cow)
{
int ret;
struct btrfs_key key;
struct btrfs_inode_extref *extref;
key.objectid = inode_objectid;
key.type = BTRFS_INODE_EXTREF_KEY;
key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
if (ret < 0)
return ERR_PTR(ret);
if (ret > 0)
return NULL;
if (!btrfs_find_name_in_ext_backref(path, ref_objectid, name, name_len, &extref))
return NULL;
return extref;
}
int btrfs_get_inode_ref_index(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
const char *name, int name_len,
u64 inode_objectid, u64 ref_objectid, int mod,
u64 *ret_index)
{
struct btrfs_inode_ref *ref;
struct btrfs_inode_extref *extref;
int ins_len = mod < 0 ? -1 : 0;
int cow = mod != 0;
ref = btrfs_lookup_inode_ref(trans, root, path, name, name_len,
inode_objectid, ref_objectid, ins_len,
cow);
if (IS_ERR(ref))
return PTR_ERR(ref);
if (ref != NULL) {
*ret_index = btrfs_inode_ref_index(path->nodes[0], ref);
return 0;
}
btrfs_release_path(path);
extref = btrfs_lookup_inode_extref(trans, root, path, name,
name_len, inode_objectid,
ref_objectid, ins_len, cow);
if (IS_ERR(extref))
return PTR_ERR(extref);
if (extref) {
*ret_index = btrfs_inode_extref_index(path->nodes[0], extref);
return 0;
}
return -ENOENT;
}
int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
const char *name, int name_len, const char *name, int name_len,
u64 inode_objectid, u64 ref_objectid, u64 *index) u64 inode_objectid, u64 ref_objectid, u64 *index)
{
struct btrfs_path *path;
struct btrfs_key key;
struct btrfs_inode_extref *extref;
struct extent_buffer *leaf;
int ret;
int del_len = name_len + sizeof(*extref);
unsigned long ptr;
unsigned long item_start;
u32 item_size;
key.objectid = inode_objectid;
btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY);
key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
path->leave_spinning = 1;
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret > 0)
ret = -ENOENT;
if (ret < 0)
goto out;
/*
* Sanity check - did we find the right item for this name?
* This should always succeed so error here will make the FS
* readonly.
*/
if (!btrfs_find_name_in_ext_backref(path, ref_objectid,
name, name_len, &extref)) {
btrfs_std_error(root->fs_info, -ENOENT);
ret = -EROFS;
goto out;
}
leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
if (index)
*index = btrfs_inode_extref_index(leaf, extref);
if (del_len == item_size) {
/*
* Common case only one ref in the item, remove the
* whole item.
*/
ret = btrfs_del_item(trans, root, path);
goto out;
}
ptr = (unsigned long)extref;
item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
memmove_extent_buffer(leaf, ptr, ptr + del_len,
item_size - (ptr + del_len - item_start));
btrfs_truncate_item(trans, root, path, item_size - del_len, 1);
out:
btrfs_free_path(path);
return ret;
}
int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
const char *name, int name_len,
u64 inode_objectid, u64 ref_objectid, u64 *index)
{ {
struct btrfs_path *path; struct btrfs_path *path;
struct btrfs_key key; struct btrfs_key key;
@@ -91,6 +268,7 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
u32 item_size; u32 item_size;
u32 sub_item_len; u32 sub_item_len;
int ret; int ret;
int search_ext_refs = 0;
int del_len = name_len + sizeof(*ref); int del_len = name_len + sizeof(*ref);
key.objectid = inode_objectid; key.objectid = inode_objectid;
@@ -106,12 +284,14 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
ret = btrfs_search_slot(trans, root, &key, path, -1, 1); ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret > 0) { if (ret > 0) {
ret = -ENOENT; ret = -ENOENT;
search_ext_refs = 1;
goto out; goto out;
} else if (ret < 0) { } else if (ret < 0) {
goto out; goto out;
} }
if (!find_name_in_backref(path, name, name_len, &ref)) { if (!find_name_in_backref(path, name, name_len, &ref)) {
ret = -ENOENT; ret = -ENOENT;
search_ext_refs = 1;
goto out; goto out;
} }
leaf = path->nodes[0]; leaf = path->nodes[0];
@@ -129,8 +309,78 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
item_start = btrfs_item_ptr_offset(leaf, path->slots[0]); item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
memmove_extent_buffer(leaf, ptr, ptr + sub_item_len, memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
item_size - (ptr + sub_item_len - item_start)); item_size - (ptr + sub_item_len - item_start));
btrfs_truncate_item(trans, root, path, btrfs_truncate_item(trans, root, path, item_size - sub_item_len, 1);
item_size - sub_item_len, 1); out:
btrfs_free_path(path);
if (search_ext_refs) {
/*
* No refs were found, or we could not find the
* name in our ref array. Find and remove the extended
* inode ref then.
*/
return btrfs_del_inode_extref(trans, root, name, name_len,
inode_objectid, ref_objectid, index);
}
return ret;
}
/*
* btrfs_insert_inode_extref() - Inserts an extended inode ref into a tree.
*
* The caller must have checked against BTRFS_LINK_MAX already.
*/
static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
const char *name, int name_len,
u64 inode_objectid, u64 ref_objectid, u64 index)
{
struct btrfs_inode_extref *extref;
int ret;
int ins_len = name_len + sizeof(*extref);
unsigned long ptr;
struct btrfs_path *path;
struct btrfs_key key;
struct extent_buffer *leaf;
struct btrfs_item *item;
key.objectid = inode_objectid;
key.type = BTRFS_INODE_EXTREF_KEY;
key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
path->leave_spinning = 1;
ret = btrfs_insert_empty_item(trans, root, path, &key,
ins_len);
if (ret == -EEXIST) {
if (btrfs_find_name_in_ext_backref(path, ref_objectid,
name, name_len, NULL))
goto out;
btrfs_extend_item(trans, root, path, ins_len);
ret = 0;
}
if (ret < 0)
goto out;
leaf = path->nodes[0];
item = btrfs_item_nr(leaf, path->slots[0]);
ptr = (unsigned long)btrfs_item_ptr(leaf, path->slots[0], char);
ptr += btrfs_item_size(leaf, item) - ins_len;
extref = (struct btrfs_inode_extref *)ptr;
btrfs_set_inode_extref_name_len(path->nodes[0], extref, name_len);
btrfs_set_inode_extref_index(path->nodes[0], extref, index);
btrfs_set_inode_extref_parent(path->nodes[0], extref, ref_objectid);
ptr = (unsigned long)&extref->name;
write_extent_buffer(path->nodes[0], name, ptr, name_len);
btrfs_mark_buffer_dirty(path->nodes[0]);
out: out:
btrfs_free_path(path); btrfs_free_path(path);
return ret; return ret;
@@ -191,6 +441,19 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
out: out:
btrfs_free_path(path); btrfs_free_path(path);
if (ret == -EMLINK) {
struct btrfs_super_block *disk_super = root->fs_info->super_copy;
/* We ran out of space in the ref array. Need to
* add an extended ref. */
if (btrfs_super_incompat_flags(disk_super)
& BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
ret = btrfs_insert_inode_extref(trans, root, name,
name_len,
inode_objectid,
ref_objectid, index);
}
return ret; return ret;
} }

Some files were not shown because too many files have changed in this diff Show More