You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
+240
-59
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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__, \
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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
File diff suppressed because it is too large
Load Diff
+95
-35
@@ -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
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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) {
|
||||||
|
|||||||
@@ -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
@@ -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
Reference in New Issue
Block a user