forked from Minki/linux
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs
Pull btrfs fixes and features from Chris Mason:
"We've merged in the error handling patches from SuSE. These are
already shipping in the sles kernel, and they give btrfs the ability
to abort transactions and go readonly on errors. It involves a lot of
churn as they clarify BUG_ONs, and remove the ones we now properly
deal with.
Josef reworked the way our metadata interacts with the page cache.
page->private now points to the btrfs extent_buffer object, which
makes everything faster. He changed it so we write an whole extent
buffer at a time instead of allowing individual pages to go down,,
which will be important for the raid5/6 code (for the 3.5 merge
window ;)
Josef also made us more aggressive about dropping pages for metadata
blocks that were freed due to COW. Overall, our metadata caching is
much faster now.
We've integrated my patch for metadata bigger than the page size.
This allows metadata blocks up to 64KB in size. In practice 16K and
32K seem to work best. For workloads with lots of metadata, this cuts
down the size of the extent allocation tree dramatically and fragments
much less.
Scrub was updated to support the larger block sizes, which ended up
being a fairly large change (thanks Stefan Behrens).
We also have an assortment of fixes and updates, especially to the
balancing code (Ilya Dryomov), the back ref walker (Jan Schmidt) and
the defragging code (Liu Bo)."
Fixed up trivial conflicts in fs/btrfs/scrub.c that were just due to
removal of the second argument to k[un]map_atomic() in commit
7ac687d9e0
.
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs: (75 commits)
Btrfs: update the checks for mixed block groups with big metadata blocks
Btrfs: update to the right index of defragment
Btrfs: do not bother to defrag an extent if it is a big real extent
Btrfs: add a check to decide if we should defrag the range
Btrfs: fix recursive defragment with autodefrag option
Btrfs: fix the mismatch of page->mapping
Btrfs: fix race between direct io and autodefrag
Btrfs: fix deadlock during allocating chunks
Btrfs: show useful info in space reservation tracepoint
Btrfs: don't use crc items bigger than 4KB
Btrfs: flush out and clean up any block device pages during mount
btrfs: disallow unequal data/metadata blocksize for mixed block groups
Btrfs: enhance superblock sanity checks
Btrfs: change scrub to support big blocks
Btrfs: minor cleanup in scrub
Btrfs: introduce common define for max number of mirrors
Btrfs: fix infinite loop in btrfs_shrink_device()
Btrfs: fix memory leak in resolver code
Btrfs: allow dup for data chunks in mixed mode
Btrfs: validate target profiles only if we are going to use them
...
This commit is contained in:
commit
9613bebb22
@ -171,11 +171,11 @@ out:
|
||||
spin_unlock_irqrestore(&workers->lock, flags);
|
||||
}
|
||||
|
||||
static noinline int run_ordered_completions(struct btrfs_workers *workers,
|
||||
static noinline void run_ordered_completions(struct btrfs_workers *workers,
|
||||
struct btrfs_work *work)
|
||||
{
|
||||
if (!workers->ordered)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
set_bit(WORK_DONE_BIT, &work->flags);
|
||||
|
||||
@ -213,7 +213,6 @@ static noinline int run_ordered_completions(struct btrfs_workers *workers,
|
||||
}
|
||||
|
||||
spin_unlock(&workers->order_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void put_worker(struct btrfs_worker_thread *worker)
|
||||
@ -399,7 +398,7 @@ again:
|
||||
/*
|
||||
* this will wait for all the worker threads to shutdown
|
||||
*/
|
||||
int btrfs_stop_workers(struct btrfs_workers *workers)
|
||||
void btrfs_stop_workers(struct btrfs_workers *workers)
|
||||
{
|
||||
struct list_head *cur;
|
||||
struct btrfs_worker_thread *worker;
|
||||
@ -427,7 +426,6 @@ int btrfs_stop_workers(struct btrfs_workers *workers)
|
||||
put_worker(worker);
|
||||
}
|
||||
spin_unlock_irq(&workers->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -615,14 +613,14 @@ found:
|
||||
* it was taken from. It is intended for use with long running work functions
|
||||
* that make some progress and want to give the cpu up for others.
|
||||
*/
|
||||
int btrfs_requeue_work(struct btrfs_work *work)
|
||||
void btrfs_requeue_work(struct btrfs_work *work)
|
||||
{
|
||||
struct btrfs_worker_thread *worker = work->worker;
|
||||
unsigned long flags;
|
||||
int wake = 0;
|
||||
|
||||
if (test_and_set_bit(WORK_QUEUED_BIT, &work->flags))
|
||||
goto out;
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&worker->lock, flags);
|
||||
if (test_bit(WORK_HIGH_PRIO_BIT, &work->flags))
|
||||
@ -649,9 +647,6 @@ int btrfs_requeue_work(struct btrfs_work *work)
|
||||
if (wake)
|
||||
wake_up_process(worker->task);
|
||||
spin_unlock_irqrestore(&worker->lock, flags);
|
||||
out:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void btrfs_set_work_high_prio(struct btrfs_work *work)
|
||||
|
@ -111,9 +111,9 @@ struct btrfs_workers {
|
||||
|
||||
void btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work);
|
||||
int btrfs_start_workers(struct btrfs_workers *workers);
|
||||
int btrfs_stop_workers(struct btrfs_workers *workers);
|
||||
void btrfs_stop_workers(struct btrfs_workers *workers);
|
||||
void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max,
|
||||
struct btrfs_workers *async_starter);
|
||||
int btrfs_requeue_work(struct btrfs_work *work);
|
||||
void btrfs_requeue_work(struct btrfs_work *work);
|
||||
void btrfs_set_work_high_prio(struct btrfs_work *work);
|
||||
#endif
|
||||
|
@ -116,6 +116,7 @@ add_parent:
|
||||
* to a logical address
|
||||
*/
|
||||
static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
|
||||
int search_commit_root,
|
||||
struct __prelim_ref *ref,
|
||||
struct ulist *parents)
|
||||
{
|
||||
@ -131,6 +132,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
path->search_commit_root = !!search_commit_root;
|
||||
|
||||
root_key.objectid = ref->root_id;
|
||||
root_key.type = BTRFS_ROOT_ITEM_KEY;
|
||||
@ -188,6 +190,7 @@ out:
|
||||
* resolve all indirect backrefs from the list
|
||||
*/
|
||||
static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
|
||||
int search_commit_root,
|
||||
struct list_head *head)
|
||||
{
|
||||
int err;
|
||||
@ -212,7 +215,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
|
||||
continue;
|
||||
if (ref->count == 0)
|
||||
continue;
|
||||
err = __resolve_indirect_ref(fs_info, ref, parents);
|
||||
err = __resolve_indirect_ref(fs_info, search_commit_root,
|
||||
ref, parents);
|
||||
if (err) {
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
@ -586,6 +590,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_delayed_ref_head *head;
|
||||
int info_level = 0;
|
||||
int ret;
|
||||
int search_commit_root = (trans == BTRFS_BACKREF_SEARCH_COMMIT_ROOT);
|
||||
struct list_head prefs_delayed;
|
||||
struct list_head prefs;
|
||||
struct __prelim_ref *ref;
|
||||
@ -600,6 +605,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
path->search_commit_root = !!search_commit_root;
|
||||
|
||||
/*
|
||||
* grab both a lock on the path and a lock on the delayed ref head.
|
||||
@ -614,35 +620,39 @@ again:
|
||||
goto out;
|
||||
BUG_ON(ret == 0);
|
||||
|
||||
/*
|
||||
* look if there are updates for this ref queued and lock the head
|
||||
*/
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
spin_lock(&delayed_refs->lock);
|
||||
head = btrfs_find_delayed_ref_head(trans, bytenr);
|
||||
if (head) {
|
||||
if (!mutex_trylock(&head->mutex)) {
|
||||
atomic_inc(&head->node.refs);
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
if (trans != BTRFS_BACKREF_SEARCH_COMMIT_ROOT) {
|
||||
/*
|
||||
* look if there are updates for this ref queued and lock the
|
||||
* head
|
||||
*/
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
spin_lock(&delayed_refs->lock);
|
||||
head = btrfs_find_delayed_ref_head(trans, bytenr);
|
||||
if (head) {
|
||||
if (!mutex_trylock(&head->mutex)) {
|
||||
atomic_inc(&head->node.refs);
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
|
||||
btrfs_release_path(path);
|
||||
btrfs_release_path(path);
|
||||
|
||||
/*
|
||||
* Mutex was contended, block until it's
|
||||
* released and try again
|
||||
*/
|
||||
mutex_lock(&head->mutex);
|
||||
mutex_unlock(&head->mutex);
|
||||
btrfs_put_delayed_ref(&head->node);
|
||||
goto again;
|
||||
}
|
||||
ret = __add_delayed_refs(head, seq, &info_key, &prefs_delayed);
|
||||
if (ret) {
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
goto out;
|
||||
/*
|
||||
* Mutex was contended, block until it's
|
||||
* released and try again
|
||||
*/
|
||||
mutex_lock(&head->mutex);
|
||||
mutex_unlock(&head->mutex);
|
||||
btrfs_put_delayed_ref(&head->node);
|
||||
goto again;
|
||||
}
|
||||
ret = __add_delayed_refs(head, seq, &info_key,
|
||||
&prefs_delayed);
|
||||
if (ret) {
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
}
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
|
||||
if (path->slots[0]) {
|
||||
struct extent_buffer *leaf;
|
||||
@ -679,7 +689,7 @@ again:
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = __resolve_indirect_refs(fs_info, &prefs);
|
||||
ret = __resolve_indirect_refs(fs_info, search_commit_root, &prefs);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@ -1074,8 +1084,7 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iterate_leaf_refs(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_path *path, u64 logical,
|
||||
static int iterate_leaf_refs(struct btrfs_fs_info *fs_info, u64 logical,
|
||||
u64 orig_extent_item_objectid,
|
||||
u64 extent_item_pos, u64 root,
|
||||
iterate_extent_inodes_t *iterate, void *ctx)
|
||||
@ -1143,35 +1152,38 @@ static int iterate_leaf_refs(struct btrfs_fs_info *fs_info,
|
||||
* calls iterate() for every inode that references the extent identified by
|
||||
* the given parameters.
|
||||
* when the iterator function returns a non-zero value, iteration stops.
|
||||
* path is guaranteed to be in released state when iterate() is called.
|
||||
*/
|
||||
int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_path *path,
|
||||
u64 extent_item_objectid, u64 extent_item_pos,
|
||||
int search_commit_root,
|
||||
iterate_extent_inodes_t *iterate, void *ctx)
|
||||
{
|
||||
int ret;
|
||||
struct list_head data_refs = LIST_HEAD_INIT(data_refs);
|
||||
struct list_head shared_refs = LIST_HEAD_INIT(shared_refs);
|
||||
struct btrfs_trans_handle *trans;
|
||||
struct ulist *refs;
|
||||
struct ulist *roots;
|
||||
struct ulist *refs = NULL;
|
||||
struct ulist *roots = NULL;
|
||||
struct ulist_node *ref_node = NULL;
|
||||
struct ulist_node *root_node = NULL;
|
||||
struct seq_list seq_elem;
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
|
||||
trans = btrfs_join_transaction(fs_info->extent_root);
|
||||
if (IS_ERR(trans))
|
||||
return PTR_ERR(trans);
|
||||
struct btrfs_delayed_ref_root *delayed_refs = NULL;
|
||||
|
||||
pr_debug("resolving all inodes for extent %llu\n",
|
||||
extent_item_objectid);
|
||||
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
spin_lock(&delayed_refs->lock);
|
||||
btrfs_get_delayed_seq(delayed_refs, &seq_elem);
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
if (search_commit_root) {
|
||||
trans = BTRFS_BACKREF_SEARCH_COMMIT_ROOT;
|
||||
} else {
|
||||
trans = btrfs_join_transaction(fs_info->extent_root);
|
||||
if (IS_ERR(trans))
|
||||
return PTR_ERR(trans);
|
||||
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
spin_lock(&delayed_refs->lock);
|
||||
btrfs_get_delayed_seq(delayed_refs, &seq_elem);
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
}
|
||||
|
||||
ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid,
|
||||
extent_item_pos, seq_elem.seq,
|
||||
@ -1188,7 +1200,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
|
||||
while (!ret && (root_node = ulist_next(roots, root_node))) {
|
||||
pr_debug("root %llu references leaf %llu\n",
|
||||
root_node->val, ref_node->val);
|
||||
ret = iterate_leaf_refs(fs_info, path, ref_node->val,
|
||||
ret = iterate_leaf_refs(fs_info, ref_node->val,
|
||||
extent_item_objectid,
|
||||
extent_item_pos, root_node->val,
|
||||
iterate, ctx);
|
||||
@ -1198,8 +1210,11 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
|
||||
ulist_free(refs);
|
||||
ulist_free(roots);
|
||||
out:
|
||||
btrfs_put_delayed_seq(delayed_refs, &seq_elem);
|
||||
btrfs_end_transaction(trans, fs_info->extent_root);
|
||||
if (!search_commit_root) {
|
||||
btrfs_put_delayed_seq(delayed_refs, &seq_elem);
|
||||
btrfs_end_transaction(trans, fs_info->extent_root);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1210,6 +1225,7 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
|
||||
int ret;
|
||||
u64 extent_item_pos;
|
||||
struct btrfs_key found_key;
|
||||
int search_commit_root = path->search_commit_root;
|
||||
|
||||
ret = extent_from_logical(fs_info, logical, path,
|
||||
&found_key);
|
||||
@ -1220,8 +1236,9 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
|
||||
return ret;
|
||||
|
||||
extent_item_pos = logical - found_key.objectid;
|
||||
ret = iterate_extent_inodes(fs_info, path, found_key.objectid,
|
||||
extent_item_pos, iterate, ctx);
|
||||
ret = iterate_extent_inodes(fs_info, found_key.objectid,
|
||||
extent_item_pos, search_commit_root,
|
||||
iterate, ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1342,12 +1359,6 @@ int paths_from_inode(u64 inum, struct inode_fs_paths *ipath)
|
||||
inode_to_path, ipath);
|
||||
}
|
||||
|
||||
/*
|
||||
* allocates space to return multiple file system paths for an inode.
|
||||
* total_bytes to allocate are passed, note that space usable for actual path
|
||||
* information will be total_bytes - sizeof(struct inode_fs_paths).
|
||||
* the returned pointer must be freed with free_ipath() in the end.
|
||||
*/
|
||||
struct btrfs_data_container *init_data_container(u32 total_bytes)
|
||||
{
|
||||
struct btrfs_data_container *data;
|
||||
@ -1403,5 +1414,6 @@ struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root,
|
||||
|
||||
void free_ipath(struct inode_fs_paths *ipath)
|
||||
{
|
||||
kfree(ipath->fspath);
|
||||
kfree(ipath);
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include "ioctl.h"
|
||||
#include "ulist.h"
|
||||
|
||||
#define BTRFS_BACKREF_SEARCH_COMMIT_ROOT ((struct btrfs_trans_handle *)0)
|
||||
|
||||
struct inode_fs_paths {
|
||||
struct btrfs_path *btrfs_path;
|
||||
struct btrfs_root *fs_root;
|
||||
@ -44,9 +46,8 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
|
||||
u64 *out_root, u8 *out_level);
|
||||
|
||||
int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_path *path,
|
||||
u64 extent_item_objectid,
|
||||
u64 extent_offset,
|
||||
u64 extent_offset, int search_commit_root,
|
||||
iterate_extent_inodes_t *iterate, void *ctx);
|
||||
|
||||
int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
|
||||
|
@ -226,8 +226,8 @@ out:
|
||||
* Clear the writeback bits on all of the file
|
||||
* pages for a compressed write
|
||||
*/
|
||||
static noinline int end_compressed_writeback(struct inode *inode, u64 start,
|
||||
unsigned long ram_size)
|
||||
static noinline void end_compressed_writeback(struct inode *inode, u64 start,
|
||||
unsigned long ram_size)
|
||||
{
|
||||
unsigned long index = start >> PAGE_CACHE_SHIFT;
|
||||
unsigned long end_index = (start + ram_size - 1) >> PAGE_CACHE_SHIFT;
|
||||
@ -253,7 +253,6 @@ static noinline int end_compressed_writeback(struct inode *inode, u64 start,
|
||||
index += ret;
|
||||
}
|
||||
/* the inode may be gone now */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -392,16 +391,16 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
|
||||
*/
|
||||
atomic_inc(&cb->pending_bios);
|
||||
ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
if (!skip_sum) {
|
||||
ret = btrfs_csum_one_bio(root, inode, bio,
|
||||
start, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
|
||||
ret = btrfs_map_bio(root, WRITE, bio, 0, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
bio_put(bio);
|
||||
|
||||
@ -421,15 +420,15 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
|
||||
bio_get(bio);
|
||||
|
||||
ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
if (!skip_sum) {
|
||||
ret = btrfs_csum_one_bio(root, inode, bio, start, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
|
||||
ret = btrfs_map_bio(root, WRITE, bio, 0, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
bio_put(bio);
|
||||
return 0;
|
||||
@ -497,7 +496,7 @@ static noinline int add_ra_bio_pages(struct inode *inode,
|
||||
* sure they map to this compressed extent on disk.
|
||||
*/
|
||||
set_page_extent_mapped(page);
|
||||
lock_extent(tree, last_offset, end, GFP_NOFS);
|
||||
lock_extent(tree, last_offset, end);
|
||||
read_lock(&em_tree->lock);
|
||||
em = lookup_extent_mapping(em_tree, last_offset,
|
||||
PAGE_CACHE_SIZE);
|
||||
@ -507,7 +506,7 @@ static noinline int add_ra_bio_pages(struct inode *inode,
|
||||
(last_offset + PAGE_CACHE_SIZE > extent_map_end(em)) ||
|
||||
(em->block_start >> 9) != cb->orig_bio->bi_sector) {
|
||||
free_extent_map(em);
|
||||
unlock_extent(tree, last_offset, end, GFP_NOFS);
|
||||
unlock_extent(tree, last_offset, end);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
break;
|
||||
@ -535,7 +534,7 @@ static noinline int add_ra_bio_pages(struct inode *inode,
|
||||
nr_pages++;
|
||||
page_cache_release(page);
|
||||
} else {
|
||||
unlock_extent(tree, last_offset, end, GFP_NOFS);
|
||||
unlock_extent(tree, last_offset, end);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
break;
|
||||
@ -662,7 +661,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
|
||||
bio_get(comp_bio);
|
||||
|
||||
ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
/*
|
||||
* inc the count before we submit the bio so
|
||||
@ -675,14 +674,14 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
|
||||
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
|
||||
ret = btrfs_lookup_bio_sums(root, inode,
|
||||
comp_bio, sums);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
sums += (comp_bio->bi_size + root->sectorsize - 1) /
|
||||
root->sectorsize;
|
||||
|
||||
ret = btrfs_map_bio(root, READ, comp_bio,
|
||||
mirror_num, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
bio_put(comp_bio);
|
||||
|
||||
@ -698,15 +697,15 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
|
||||
bio_get(comp_bio);
|
||||
|
||||
ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
|
||||
ret = btrfs_lookup_bio_sums(root, inode, comp_bio, sums);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
|
||||
ret = btrfs_map_bio(root, READ, comp_bio, mirror_num, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
bio_put(comp_bio);
|
||||
return 0;
|
||||
@ -734,7 +733,7 @@ struct btrfs_compress_op *btrfs_compress_op[] = {
|
||||
&btrfs_lzo_compress,
|
||||
};
|
||||
|
||||
int __init btrfs_init_compress(void)
|
||||
void __init btrfs_init_compress(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -744,7 +743,6 @@ int __init btrfs_init_compress(void)
|
||||
atomic_set(&comp_alloc_workspace[i], 0);
|
||||
init_waitqueue_head(&comp_workspace_wait[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -19,7 +19,7 @@
|
||||
#ifndef __BTRFS_COMPRESSION_
|
||||
#define __BTRFS_COMPRESSION_
|
||||
|
||||
int btrfs_init_compress(void);
|
||||
void btrfs_init_compress(void);
|
||||
void btrfs_exit_compress(void);
|
||||
|
||||
int btrfs_compress_pages(int type, struct address_space *mapping,
|
||||
|
384
fs/btrfs/ctree.c
384
fs/btrfs/ctree.c
@ -36,7 +36,7 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct extent_buffer *dst_buf,
|
||||
struct extent_buffer *src_buf);
|
||||
static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
static void del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct btrfs_path *path, int level, int slot);
|
||||
|
||||
struct btrfs_path *btrfs_alloc_path(void)
|
||||
@ -156,10 +156,23 @@ struct extent_buffer *btrfs_root_node(struct btrfs_root *root)
|
||||
{
|
||||
struct extent_buffer *eb;
|
||||
|
||||
rcu_read_lock();
|
||||
eb = rcu_dereference(root->node);
|
||||
extent_buffer_get(eb);
|
||||
rcu_read_unlock();
|
||||
while (1) {
|
||||
rcu_read_lock();
|
||||
eb = rcu_dereference(root->node);
|
||||
|
||||
/*
|
||||
* RCU really hurts here, we could free up the root node because
|
||||
* it was cow'ed but we may not get the new root node yet so do
|
||||
* the inc_not_zero dance and if it doesn't work then
|
||||
* synchronize_rcu and try again.
|
||||
*/
|
||||
if (atomic_inc_not_zero(&eb->refs)) {
|
||||
rcu_read_unlock();
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
synchronize_rcu();
|
||||
}
|
||||
return eb;
|
||||
}
|
||||
|
||||
@ -331,8 +344,13 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
|
||||
if (btrfs_block_can_be_shared(root, buf)) {
|
||||
ret = btrfs_lookup_extent_info(trans, root, buf->start,
|
||||
buf->len, &refs, &flags);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(refs == 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (refs == 0) {
|
||||
ret = -EROFS;
|
||||
btrfs_std_error(root->fs_info, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
refs = 1;
|
||||
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID ||
|
||||
@ -351,14 +369,14 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
|
||||
root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) &&
|
||||
!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) {
|
||||
ret = btrfs_inc_ref(trans, root, buf, 1, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
if (root->root_key.objectid ==
|
||||
BTRFS_TREE_RELOC_OBJECTID) {
|
||||
ret = btrfs_dec_ref(trans, root, buf, 0, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
ret = btrfs_inc_ref(trans, root, cow, 1, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
new_flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
|
||||
} else {
|
||||
@ -368,14 +386,15 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_inc_ref(trans, root, cow, 1, 1);
|
||||
else
|
||||
ret = btrfs_inc_ref(trans, root, cow, 0, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
if (new_flags != 0) {
|
||||
ret = btrfs_set_disk_extent_flags(trans, root,
|
||||
buf->start,
|
||||
buf->len,
|
||||
new_flags, 0);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) {
|
||||
@ -384,9 +403,9 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_inc_ref(trans, root, cow, 1, 1);
|
||||
else
|
||||
ret = btrfs_inc_ref(trans, root, cow, 0, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
ret = btrfs_dec_ref(trans, root, buf, 1, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
clean_tree_block(trans, root, buf);
|
||||
*last_ref = 1;
|
||||
@ -415,7 +434,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
|
||||
{
|
||||
struct btrfs_disk_key disk_key;
|
||||
struct extent_buffer *cow;
|
||||
int level;
|
||||
int level, ret;
|
||||
int last_ref = 0;
|
||||
int unlock_orig = 0;
|
||||
u64 parent_start;
|
||||
@ -467,7 +486,11 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
|
||||
(unsigned long)btrfs_header_fsid(cow),
|
||||
BTRFS_FSID_SIZE);
|
||||
|
||||
update_ref_for_cow(trans, root, buf, cow, &last_ref);
|
||||
ret = update_ref_for_cow(trans, root, buf, cow, &last_ref);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (root->ref_cows)
|
||||
btrfs_reloc_cow_block(trans, root, buf, cow);
|
||||
@ -504,7 +527,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
|
||||
}
|
||||
if (unlock_orig)
|
||||
btrfs_tree_unlock(buf);
|
||||
free_extent_buffer(buf);
|
||||
free_extent_buffer_stale(buf);
|
||||
btrfs_mark_buffer_dirty(cow);
|
||||
*cow_ret = cow;
|
||||
return 0;
|
||||
@ -934,7 +957,12 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
|
||||
|
||||
/* promote the child to a root */
|
||||
child = read_node_slot(root, mid, 0);
|
||||
BUG_ON(!child);
|
||||
if (!child) {
|
||||
ret = -EROFS;
|
||||
btrfs_std_error(root->fs_info, ret);
|
||||
goto enospc;
|
||||
}
|
||||
|
||||
btrfs_tree_lock(child);
|
||||
btrfs_set_lock_blocking(child);
|
||||
ret = btrfs_cow_block(trans, root, child, mid, 0, &child);
|
||||
@ -959,7 +987,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
|
||||
root_sub_used(root, mid->len);
|
||||
btrfs_free_tree_block(trans, root, mid, 0, 1, 0);
|
||||
/* once for the root ptr */
|
||||
free_extent_buffer(mid);
|
||||
free_extent_buffer_stale(mid);
|
||||
return 0;
|
||||
}
|
||||
if (btrfs_header_nritems(mid) >
|
||||
@ -1010,13 +1038,10 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
|
||||
if (btrfs_header_nritems(right) == 0) {
|
||||
clean_tree_block(trans, root, right);
|
||||
btrfs_tree_unlock(right);
|
||||
wret = del_ptr(trans, root, path, level + 1, pslot +
|
||||
1);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
del_ptr(trans, root, path, level + 1, pslot + 1);
|
||||
root_sub_used(root, right->len);
|
||||
btrfs_free_tree_block(trans, root, right, 0, 1, 0);
|
||||
free_extent_buffer(right);
|
||||
free_extent_buffer_stale(right);
|
||||
right = NULL;
|
||||
} else {
|
||||
struct btrfs_disk_key right_key;
|
||||
@ -1035,7 +1060,11 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
|
||||
* otherwise we would have pulled some pointers from the
|
||||
* right
|
||||
*/
|
||||
BUG_ON(!left);
|
||||
if (!left) {
|
||||
ret = -EROFS;
|
||||
btrfs_std_error(root->fs_info, ret);
|
||||
goto enospc;
|
||||
}
|
||||
wret = balance_node_right(trans, root, mid, left);
|
||||
if (wret < 0) {
|
||||
ret = wret;
|
||||
@ -1051,12 +1080,10 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
|
||||
if (btrfs_header_nritems(mid) == 0) {
|
||||
clean_tree_block(trans, root, mid);
|
||||
btrfs_tree_unlock(mid);
|
||||
wret = del_ptr(trans, root, path, level + 1, pslot);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
del_ptr(trans, root, path, level + 1, pslot);
|
||||
root_sub_used(root, mid->len);
|
||||
btrfs_free_tree_block(trans, root, mid, 0, 1, 0);
|
||||
free_extent_buffer(mid);
|
||||
free_extent_buffer_stale(mid);
|
||||
mid = NULL;
|
||||
} else {
|
||||
/* update the parent key to reflect our changes */
|
||||
@ -1382,7 +1409,8 @@ static noinline int reada_for_balance(struct btrfs_root *root,
|
||||
* if lowest_unlock is 1, level 0 won't be unlocked
|
||||
*/
|
||||
static noinline void unlock_up(struct btrfs_path *path, int level,
|
||||
int lowest_unlock)
|
||||
int lowest_unlock, int min_write_lock_level,
|
||||
int *write_lock_level)
|
||||
{
|
||||
int i;
|
||||
int skip_level = level;
|
||||
@ -1414,6 +1442,11 @@ static noinline void unlock_up(struct btrfs_path *path, int level,
|
||||
if (i >= lowest_unlock && i > skip_level && path->locks[i]) {
|
||||
btrfs_tree_unlock_rw(t, path->locks[i]);
|
||||
path->locks[i] = 0;
|
||||
if (write_lock_level &&
|
||||
i > min_write_lock_level &&
|
||||
i <= *write_lock_level) {
|
||||
*write_lock_level = i - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1637,6 +1670,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
/* everything at write_lock_level or lower must be write locked */
|
||||
int write_lock_level = 0;
|
||||
u8 lowest_level = 0;
|
||||
int min_write_lock_level;
|
||||
|
||||
lowest_level = p->lowest_level;
|
||||
WARN_ON(lowest_level && ins_len > 0);
|
||||
@ -1664,6 +1698,8 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
if (cow && (p->keep_locks || p->lowest_level))
|
||||
write_lock_level = BTRFS_MAX_LEVEL;
|
||||
|
||||
min_write_lock_level = write_lock_level;
|
||||
|
||||
again:
|
||||
/*
|
||||
* we try very hard to do read locks on the root
|
||||
@ -1795,7 +1831,8 @@ cow_done:
|
||||
goto again;
|
||||
}
|
||||
|
||||
unlock_up(p, level, lowest_unlock);
|
||||
unlock_up(p, level, lowest_unlock,
|
||||
min_write_lock_level, &write_lock_level);
|
||||
|
||||
if (level == lowest_level) {
|
||||
if (dec)
|
||||
@ -1857,7 +1894,8 @@ cow_done:
|
||||
}
|
||||
}
|
||||
if (!p->search_for_split)
|
||||
unlock_up(p, level, lowest_unlock);
|
||||
unlock_up(p, level, lowest_unlock,
|
||||
min_write_lock_level, &write_lock_level);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
@ -1881,15 +1919,12 @@ done:
|
||||
* fixing up pointers when a given leaf/node is not in slot 0 of the
|
||||
* higher levels
|
||||
*
|
||||
* If this fails to write a tree block, it returns -1, but continues
|
||||
* fixing up the blocks in ram so the tree is consistent.
|
||||
*/
|
||||
static int fixup_low_keys(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_disk_key *key, int level)
|
||||
static void fixup_low_keys(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_disk_key *key, int level)
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
struct extent_buffer *t;
|
||||
|
||||
for (i = level; i < BTRFS_MAX_LEVEL; i++) {
|
||||
@ -1902,7 +1937,6 @@ static int fixup_low_keys(struct btrfs_trans_handle *trans,
|
||||
if (tslot != 0)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1911,9 +1945,9 @@ static int fixup_low_keys(struct btrfs_trans_handle *trans,
|
||||
* This function isn't completely safe. It's the caller's responsibility
|
||||
* that the new key won't break the order
|
||||
*/
|
||||
int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_key *new_key)
|
||||
void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_key *new_key)
|
||||
{
|
||||
struct btrfs_disk_key disk_key;
|
||||
struct extent_buffer *eb;
|
||||
@ -1923,13 +1957,11 @@ int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
|
||||
slot = path->slots[0];
|
||||
if (slot > 0) {
|
||||
btrfs_item_key(eb, &disk_key, slot - 1);
|
||||
if (comp_keys(&disk_key, new_key) >= 0)
|
||||
return -1;
|
||||
BUG_ON(comp_keys(&disk_key, new_key) >= 0);
|
||||
}
|
||||
if (slot < btrfs_header_nritems(eb) - 1) {
|
||||
btrfs_item_key(eb, &disk_key, slot + 1);
|
||||
if (comp_keys(&disk_key, new_key) <= 0)
|
||||
return -1;
|
||||
BUG_ON(comp_keys(&disk_key, new_key) <= 0);
|
||||
}
|
||||
|
||||
btrfs_cpu_key_to_disk(&disk_key, new_key);
|
||||
@ -1937,7 +1969,6 @@ int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
|
||||
btrfs_mark_buffer_dirty(eb);
|
||||
if (slot == 0)
|
||||
fixup_low_keys(trans, root, path, &disk_key, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2140,12 +2171,11 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
|
||||
*
|
||||
* slot and level indicate where you want the key to go, and
|
||||
* blocknr is the block the key points to.
|
||||
*
|
||||
* returns zero on success and < 0 on any error
|
||||
*/
|
||||
static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
*root, struct btrfs_path *path, struct btrfs_disk_key
|
||||
*key, u64 bytenr, int slot, int level)
|
||||
static void insert_ptr(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_disk_key *key, u64 bytenr,
|
||||
int slot, int level)
|
||||
{
|
||||
struct extent_buffer *lower;
|
||||
int nritems;
|
||||
@ -2155,8 +2185,7 @@ static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
lower = path->nodes[level];
|
||||
nritems = btrfs_header_nritems(lower);
|
||||
BUG_ON(slot > nritems);
|
||||
if (nritems == BTRFS_NODEPTRS_PER_BLOCK(root))
|
||||
BUG();
|
||||
BUG_ON(nritems == BTRFS_NODEPTRS_PER_BLOCK(root));
|
||||
if (slot != nritems) {
|
||||
memmove_extent_buffer(lower,
|
||||
btrfs_node_key_ptr_offset(slot + 1),
|
||||
@ -2169,7 +2198,6 @@ static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
btrfs_set_node_ptr_generation(lower, slot, trans->transid);
|
||||
btrfs_set_header_nritems(lower, nritems + 1);
|
||||
btrfs_mark_buffer_dirty(lower);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2190,7 +2218,6 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_disk_key disk_key;
|
||||
int mid;
|
||||
int ret;
|
||||
int wret;
|
||||
u32 c_nritems;
|
||||
|
||||
c = path->nodes[level];
|
||||
@ -2247,11 +2274,8 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
|
||||
btrfs_mark_buffer_dirty(c);
|
||||
btrfs_mark_buffer_dirty(split);
|
||||
|
||||
wret = insert_ptr(trans, root, path, &disk_key, split->start,
|
||||
path->slots[level + 1] + 1,
|
||||
level + 1);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
insert_ptr(trans, root, path, &disk_key, split->start,
|
||||
path->slots[level + 1] + 1, level + 1);
|
||||
|
||||
if (path->slots[level] >= mid) {
|
||||
path->slots[level] -= mid;
|
||||
@ -2320,6 +2344,7 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
|
||||
{
|
||||
struct extent_buffer *left = path->nodes[0];
|
||||
struct extent_buffer *upper = path->nodes[1];
|
||||
struct btrfs_map_token token;
|
||||
struct btrfs_disk_key disk_key;
|
||||
int slot;
|
||||
u32 i;
|
||||
@ -2331,6 +2356,8 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
|
||||
u32 data_end;
|
||||
u32 this_item_size;
|
||||
|
||||
btrfs_init_map_token(&token);
|
||||
|
||||
if (empty)
|
||||
nr = 0;
|
||||
else
|
||||
@ -2408,8 +2435,8 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
|
||||
push_space = BTRFS_LEAF_DATA_SIZE(root);
|
||||
for (i = 0; i < right_nritems; i++) {
|
||||
item = btrfs_item_nr(right, i);
|
||||
push_space -= btrfs_item_size(right, item);
|
||||
btrfs_set_item_offset(right, item, push_space);
|
||||
push_space -= btrfs_token_item_size(right, item, &token);
|
||||
btrfs_set_token_item_offset(right, item, push_space, &token);
|
||||
}
|
||||
|
||||
left_nritems -= push_items;
|
||||
@ -2537,9 +2564,11 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
|
||||
u32 old_left_nritems;
|
||||
u32 nr;
|
||||
int ret = 0;
|
||||
int wret;
|
||||
u32 this_item_size;
|
||||
u32 old_left_item_size;
|
||||
struct btrfs_map_token token;
|
||||
|
||||
btrfs_init_map_token(&token);
|
||||
|
||||
if (empty)
|
||||
nr = min(right_nritems, max_slot);
|
||||
@ -2600,9 +2629,10 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
|
||||
|
||||
item = btrfs_item_nr(left, i);
|
||||
|
||||
ioff = btrfs_item_offset(left, item);
|
||||
btrfs_set_item_offset(left, item,
|
||||
ioff - (BTRFS_LEAF_DATA_SIZE(root) - old_left_item_size));
|
||||
ioff = btrfs_token_item_offset(left, item, &token);
|
||||
btrfs_set_token_item_offset(left, item,
|
||||
ioff - (BTRFS_LEAF_DATA_SIZE(root) - old_left_item_size),
|
||||
&token);
|
||||
}
|
||||
btrfs_set_header_nritems(left, old_left_nritems + push_items);
|
||||
|
||||
@ -2632,8 +2662,9 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
|
||||
for (i = 0; i < right_nritems; i++) {
|
||||
item = btrfs_item_nr(right, i);
|
||||
|
||||
push_space = push_space - btrfs_item_size(right, item);
|
||||
btrfs_set_item_offset(right, item, push_space);
|
||||
push_space = push_space - btrfs_token_item_size(right,
|
||||
item, &token);
|
||||
btrfs_set_token_item_offset(right, item, push_space, &token);
|
||||
}
|
||||
|
||||
btrfs_mark_buffer_dirty(left);
|
||||
@ -2643,9 +2674,7 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
|
||||
clean_tree_block(trans, root, right);
|
||||
|
||||
btrfs_item_key(right, &disk_key, 0);
|
||||
wret = fixup_low_keys(trans, root, path, &disk_key, 1);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
fixup_low_keys(trans, root, path, &disk_key, 1);
|
||||
|
||||
/* then fixup the leaf pointer in the path */
|
||||
if (path->slots[0] < push_items) {
|
||||
@ -2716,7 +2745,8 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
path->nodes[1], slot - 1, &left);
|
||||
if (ret) {
|
||||
/* we hit -ENOSPC, but it isn't fatal here */
|
||||
ret = 1;
|
||||
if (ret == -ENOSPC)
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -2738,22 +2768,21 @@ out:
|
||||
/*
|
||||
* split the path's leaf in two, making sure there is at least data_size
|
||||
* available for the resulting leaf level of the path.
|
||||
*
|
||||
* returns 0 if all went well and < 0 on failure.
|
||||
*/
|
||||
static noinline int copy_for_split(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
struct extent_buffer *l,
|
||||
struct extent_buffer *right,
|
||||
int slot, int mid, int nritems)
|
||||
static noinline void copy_for_split(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
struct extent_buffer *l,
|
||||
struct extent_buffer *right,
|
||||
int slot, int mid, int nritems)
|
||||
{
|
||||
int data_copy_size;
|
||||
int rt_data_off;
|
||||
int i;
|
||||
int ret = 0;
|
||||
int wret;
|
||||
struct btrfs_disk_key disk_key;
|
||||
struct btrfs_map_token token;
|
||||
|
||||
btrfs_init_map_token(&token);
|
||||
|
||||
nritems = nritems - mid;
|
||||
btrfs_set_header_nritems(right, nritems);
|
||||
@ -2775,17 +2804,15 @@ static noinline int copy_for_split(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_item *item = btrfs_item_nr(right, i);
|
||||
u32 ioff;
|
||||
|
||||
ioff = btrfs_item_offset(right, item);
|
||||
btrfs_set_item_offset(right, item, ioff + rt_data_off);
|
||||
ioff = btrfs_token_item_offset(right, item, &token);
|
||||
btrfs_set_token_item_offset(right, item,
|
||||
ioff + rt_data_off, &token);
|
||||
}
|
||||
|
||||
btrfs_set_header_nritems(l, mid);
|
||||
ret = 0;
|
||||
btrfs_item_key(right, &disk_key, 0);
|
||||
wret = insert_ptr(trans, root, path, &disk_key, right->start,
|
||||
path->slots[1] + 1, 1);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
insert_ptr(trans, root, path, &disk_key, right->start,
|
||||
path->slots[1] + 1, 1);
|
||||
|
||||
btrfs_mark_buffer_dirty(right);
|
||||
btrfs_mark_buffer_dirty(l);
|
||||
@ -2803,8 +2830,6 @@ static noinline int copy_for_split(struct btrfs_trans_handle *trans,
|
||||
}
|
||||
|
||||
BUG_ON(path->slots[0] < 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2993,12 +3018,8 @@ again:
|
||||
if (split == 0) {
|
||||
if (mid <= slot) {
|
||||
btrfs_set_header_nritems(right, 0);
|
||||
wret = insert_ptr(trans, root, path,
|
||||
&disk_key, right->start,
|
||||
path->slots[1] + 1, 1);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
|
||||
insert_ptr(trans, root, path, &disk_key, right->start,
|
||||
path->slots[1] + 1, 1);
|
||||
btrfs_tree_unlock(path->nodes[0]);
|
||||
free_extent_buffer(path->nodes[0]);
|
||||
path->nodes[0] = right;
|
||||
@ -3006,29 +3027,21 @@ again:
|
||||
path->slots[1] += 1;
|
||||
} else {
|
||||
btrfs_set_header_nritems(right, 0);
|
||||
wret = insert_ptr(trans, root, path,
|
||||
&disk_key,
|
||||
right->start,
|
||||
insert_ptr(trans, root, path, &disk_key, right->start,
|
||||
path->slots[1], 1);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
btrfs_tree_unlock(path->nodes[0]);
|
||||
free_extent_buffer(path->nodes[0]);
|
||||
path->nodes[0] = right;
|
||||
path->slots[0] = 0;
|
||||
if (path->slots[1] == 0) {
|
||||
wret = fixup_low_keys(trans, root,
|
||||
path, &disk_key, 1);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
}
|
||||
if (path->slots[1] == 0)
|
||||
fixup_low_keys(trans, root, path,
|
||||
&disk_key, 1);
|
||||
}
|
||||
btrfs_mark_buffer_dirty(right);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = copy_for_split(trans, root, path, l, right, slot, mid, nritems);
|
||||
BUG_ON(ret);
|
||||
copy_for_split(trans, root, path, l, right, slot, mid, nritems);
|
||||
|
||||
if (split == 2) {
|
||||
BUG_ON(num_doubles != 0);
|
||||
@ -3036,7 +3049,7 @@ again:
|
||||
goto again;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
push_for_double:
|
||||
push_for_double_split(trans, root, path, data_size);
|
||||
@ -3238,11 +3251,9 @@ int btrfs_duplicate_item(struct btrfs_trans_handle *trans,
|
||||
return ret;
|
||||
|
||||
path->slots[0]++;
|
||||
ret = setup_items_for_insert(trans, root, path, new_key, &item_size,
|
||||
item_size, item_size +
|
||||
sizeof(struct btrfs_item), 1);
|
||||
BUG_ON(ret);
|
||||
|
||||
setup_items_for_insert(trans, root, path, new_key, &item_size,
|
||||
item_size, item_size +
|
||||
sizeof(struct btrfs_item), 1);
|
||||
leaf = path->nodes[0];
|
||||
memcpy_extent_buffer(leaf,
|
||||
btrfs_item_ptr_offset(leaf, path->slots[0]),
|
||||
@ -3257,10 +3268,10 @@ int btrfs_duplicate_item(struct btrfs_trans_handle *trans,
|
||||
* off the end of the item or if we shift the item to chop bytes off
|
||||
* the front.
|
||||
*/
|
||||
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
u32 new_size, int from_end)
|
||||
void btrfs_truncate_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
u32 new_size, int from_end)
|
||||
{
|
||||
int slot;
|
||||
struct extent_buffer *leaf;
|
||||
@ -3271,13 +3282,16 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
|
||||
unsigned int old_size;
|
||||
unsigned int size_diff;
|
||||
int i;
|
||||
struct btrfs_map_token token;
|
||||
|
||||
btrfs_init_map_token(&token);
|
||||
|
||||
leaf = path->nodes[0];
|
||||
slot = path->slots[0];
|
||||
|
||||
old_size = btrfs_item_size_nr(leaf, slot);
|
||||
if (old_size == new_size)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
nritems = btrfs_header_nritems(leaf);
|
||||
data_end = leaf_data_end(root, leaf);
|
||||
@ -3297,8 +3311,9 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
|
||||
u32 ioff;
|
||||
item = btrfs_item_nr(leaf, i);
|
||||
|
||||
ioff = btrfs_item_offset(leaf, item);
|
||||
btrfs_set_item_offset(leaf, item, ioff + size_diff);
|
||||
ioff = btrfs_token_item_offset(leaf, item, &token);
|
||||
btrfs_set_token_item_offset(leaf, item,
|
||||
ioff + size_diff, &token);
|
||||
}
|
||||
|
||||
/* shift the data */
|
||||
@ -3350,15 +3365,14 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
|
||||
btrfs_print_leaf(root, leaf);
|
||||
BUG();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* make the item pointed to by the path bigger, data_size is the new size.
|
||||
*/
|
||||
int btrfs_extend_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
u32 data_size)
|
||||
void btrfs_extend_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
u32 data_size)
|
||||
{
|
||||
int slot;
|
||||
struct extent_buffer *leaf;
|
||||
@ -3368,6 +3382,9 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans,
|
||||
unsigned int old_data;
|
||||
unsigned int old_size;
|
||||
int i;
|
||||
struct btrfs_map_token token;
|
||||
|
||||
btrfs_init_map_token(&token);
|
||||
|
||||
leaf = path->nodes[0];
|
||||
|
||||
@ -3397,8 +3414,9 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans,
|
||||
u32 ioff;
|
||||
item = btrfs_item_nr(leaf, i);
|
||||
|
||||
ioff = btrfs_item_offset(leaf, item);
|
||||
btrfs_set_item_offset(leaf, item, ioff - data_size);
|
||||
ioff = btrfs_token_item_offset(leaf, item, &token);
|
||||
btrfs_set_token_item_offset(leaf, item,
|
||||
ioff - data_size, &token);
|
||||
}
|
||||
|
||||
/* shift the data */
|
||||
@ -3416,7 +3434,6 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans,
|
||||
btrfs_print_leaf(root, leaf);
|
||||
BUG();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3441,6 +3458,9 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
|
||||
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) >
|
||||
@ -3506,8 +3526,9 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
|
||||
u32 ioff;
|
||||
|
||||
item = btrfs_item_nr(leaf, i);
|
||||
ioff = btrfs_item_offset(leaf, item);
|
||||
btrfs_set_item_offset(leaf, item, ioff - total_data);
|
||||
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),
|
||||
@ -3534,9 +3555,10 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
|
||||
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_item_offset(leaf, item, data_end - data_size[i]);
|
||||
btrfs_set_token_item_offset(leaf, item,
|
||||
data_end - data_size[i], &token);
|
||||
data_end -= data_size[i];
|
||||
btrfs_set_item_size(leaf, item, 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);
|
||||
@ -3544,7 +3566,7 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
|
||||
ret = 0;
|
||||
if (slot == 0) {
|
||||
btrfs_cpu_key_to_disk(&disk_key, cpu_key);
|
||||
ret = fixup_low_keys(trans, root, path, &disk_key, 1);
|
||||
fixup_low_keys(trans, root, path, &disk_key, 1);
|
||||
}
|
||||
|
||||
if (btrfs_leaf_free_space(root, leaf) < 0) {
|
||||
@ -3562,19 +3584,21 @@ out:
|
||||
* to save stack depth by doing the bulk of the work in a function
|
||||
* that doesn't call btrfs_search_slot
|
||||
*/
|
||||
int setup_items_for_insert(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_key *cpu_key, u32 *data_size,
|
||||
u32 total_data, u32 total_size, int nr)
|
||||
void setup_items_for_insert(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_key *cpu_key, u32 *data_size,
|
||||
u32 total_data, u32 total_size, int nr)
|
||||
{
|
||||
struct btrfs_item *item;
|
||||
int i;
|
||||
u32 nritems;
|
||||
unsigned int data_end;
|
||||
struct btrfs_disk_key disk_key;
|
||||
int ret;
|
||||
struct extent_buffer *leaf;
|
||||
int slot;
|
||||
struct btrfs_map_token token;
|
||||
|
||||
btrfs_init_map_token(&token);
|
||||
|
||||
leaf = path->nodes[0];
|
||||
slot = path->slots[0];
|
||||
@ -3606,8 +3630,9 @@ int setup_items_for_insert(struct btrfs_trans_handle *trans,
|
||||
u32 ioff;
|
||||
|
||||
item = btrfs_item_nr(leaf, i);
|
||||
ioff = btrfs_item_offset(leaf, item);
|
||||
btrfs_set_item_offset(leaf, item, ioff - total_data);
|
||||
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),
|
||||
@ -3626,17 +3651,17 @@ int setup_items_for_insert(struct btrfs_trans_handle *trans,
|
||||
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_item_offset(leaf, item, data_end - data_size[i]);
|
||||
btrfs_set_token_item_offset(leaf, item,
|
||||
data_end - data_size[i], &token);
|
||||
data_end -= data_size[i];
|
||||
btrfs_set_item_size(leaf, item, data_size[i]);
|
||||
btrfs_set_token_item_size(leaf, item, data_size[i], &token);
|
||||
}
|
||||
|
||||
btrfs_set_header_nritems(leaf, nritems + nr);
|
||||
|
||||
ret = 0;
|
||||
if (slot == 0) {
|
||||
btrfs_cpu_key_to_disk(&disk_key, cpu_key);
|
||||
ret = fixup_low_keys(trans, root, path, &disk_key, 1);
|
||||
fixup_low_keys(trans, root, path, &disk_key, 1);
|
||||
}
|
||||
btrfs_unlock_up_safe(path, 1);
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
@ -3645,7 +3670,6 @@ int setup_items_for_insert(struct btrfs_trans_handle *trans,
|
||||
btrfs_print_leaf(root, leaf);
|
||||
BUG();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3672,16 +3696,14 @@ int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
|
||||
if (ret == 0)
|
||||
return -EEXIST;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
slot = path->slots[0];
|
||||
BUG_ON(slot < 0);
|
||||
|
||||
ret = setup_items_for_insert(trans, root, path, cpu_key, data_size,
|
||||
setup_items_for_insert(trans, root, path, cpu_key, data_size,
|
||||
total_data, total_size, nr);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3717,13 +3739,11 @@ int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
* the tree should have been previously balanced so the deletion does not
|
||||
* empty a node.
|
||||
*/
|
||||
static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct btrfs_path *path, int level, int slot)
|
||||
static void del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct btrfs_path *path, int level, int slot)
|
||||
{
|
||||
struct extent_buffer *parent = path->nodes[level];
|
||||
u32 nritems;
|
||||
int ret = 0;
|
||||
int wret;
|
||||
|
||||
nritems = btrfs_header_nritems(parent);
|
||||
if (slot != nritems - 1) {
|
||||
@ -3743,12 +3763,9 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct btrfs_disk_key disk_key;
|
||||
|
||||
btrfs_node_key(parent, &disk_key, 0);
|
||||
wret = fixup_low_keys(trans, root, path, &disk_key, level + 1);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
fixup_low_keys(trans, root, path, &disk_key, level + 1);
|
||||
}
|
||||
btrfs_mark_buffer_dirty(parent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3761,17 +3778,13 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
* The path must have already been setup for deleting the leaf, including
|
||||
* all the proper balancing. path->nodes[1] must be locked.
|
||||
*/
|
||||
static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
struct extent_buffer *leaf)
|
||||
static noinline void btrfs_del_leaf(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
struct extent_buffer *leaf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
WARN_ON(btrfs_header_generation(leaf) != trans->transid);
|
||||
ret = del_ptr(trans, root, path, 1, path->slots[1]);
|
||||
if (ret)
|
||||
return ret;
|
||||
del_ptr(trans, root, path, 1, path->slots[1]);
|
||||
|
||||
/*
|
||||
* btrfs_free_extent is expensive, we want to make sure we
|
||||
@ -3781,8 +3794,9 @@ static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans,
|
||||
|
||||
root_sub_used(root, leaf->len);
|
||||
|
||||
extent_buffer_get(leaf);
|
||||
btrfs_free_tree_block(trans, root, leaf, 0, 1, 0);
|
||||
return 0;
|
||||
free_extent_buffer_stale(leaf);
|
||||
}
|
||||
/*
|
||||
* delete the item at the leaf level in path. If that empties
|
||||
@ -3799,6 +3813,9 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
int wret;
|
||||
int i;
|
||||
u32 nritems;
|
||||
struct btrfs_map_token token;
|
||||
|
||||
btrfs_init_map_token(&token);
|
||||
|
||||
leaf = path->nodes[0];
|
||||
last_off = btrfs_item_offset_nr(leaf, slot + nr - 1);
|
||||
@ -3820,8 +3837,9 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
u32 ioff;
|
||||
|
||||
item = btrfs_item_nr(leaf, i);
|
||||
ioff = btrfs_item_offset(leaf, item);
|
||||
btrfs_set_item_offset(leaf, item, ioff + dsize);
|
||||
ioff = btrfs_token_item_offset(leaf, item, &token);
|
||||
btrfs_set_token_item_offset(leaf, item,
|
||||
ioff + dsize, &token);
|
||||
}
|
||||
|
||||
memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot),
|
||||
@ -3839,8 +3857,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
} else {
|
||||
btrfs_set_path_blocking(path);
|
||||
clean_tree_block(trans, root, leaf);
|
||||
ret = btrfs_del_leaf(trans, root, path, leaf);
|
||||
BUG_ON(ret);
|
||||
btrfs_del_leaf(trans, root, path, leaf);
|
||||
}
|
||||
} else {
|
||||
int used = leaf_space_used(leaf, 0, nritems);
|
||||
@ -3848,10 +3865,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct btrfs_disk_key disk_key;
|
||||
|
||||
btrfs_item_key(leaf, &disk_key, 0);
|
||||
wret = fixup_low_keys(trans, root, path,
|
||||
&disk_key, 1);
|
||||
if (wret)
|
||||
ret = wret;
|
||||
fixup_low_keys(trans, root, path, &disk_key, 1);
|
||||
}
|
||||
|
||||
/* delete the leaf if it is mostly empty */
|
||||
@ -3879,9 +3893,9 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
|
||||
if (btrfs_header_nritems(leaf) == 0) {
|
||||
path->slots[1] = slot;
|
||||
ret = btrfs_del_leaf(trans, root, path, leaf);
|
||||
BUG_ON(ret);
|
||||
btrfs_del_leaf(trans, root, path, leaf);
|
||||
free_extent_buffer(leaf);
|
||||
ret = 0;
|
||||
} else {
|
||||
/* if we're still in the path, make sure
|
||||
* we're dirty. Otherwise, one of the
|
||||
@ -4059,18 +4073,18 @@ find_next_key:
|
||||
path->slots[level] = slot;
|
||||
if (level == path->lowest_level) {
|
||||
ret = 0;
|
||||
unlock_up(path, level, 1);
|
||||
unlock_up(path, level, 1, 0, NULL);
|
||||
goto out;
|
||||
}
|
||||
btrfs_set_path_blocking(path);
|
||||
cur = read_node_slot(root, cur, slot);
|
||||
BUG_ON(!cur);
|
||||
BUG_ON(!cur); /* -ENOMEM */
|
||||
|
||||
btrfs_tree_read_lock(cur);
|
||||
|
||||
path->locks[level - 1] = BTRFS_READ_LOCK;
|
||||
path->nodes[level - 1] = cur;
|
||||
unlock_up(path, level, 1);
|
||||
unlock_up(path, level, 1, 0, NULL);
|
||||
btrfs_clear_path_blocking(path, NULL, 0);
|
||||
}
|
||||
out:
|
||||
@ -4306,7 +4320,7 @@ again:
|
||||
}
|
||||
ret = 0;
|
||||
done:
|
||||
unlock_up(path, 0, 1);
|
||||
unlock_up(path, 0, 1, 0, NULL);
|
||||
path->leave_spinning = old_spinning;
|
||||
if (!old_spinning)
|
||||
btrfs_set_path_blocking(path);
|
||||
|
169
fs/btrfs/ctree.h
169
fs/btrfs/ctree.h
@ -48,6 +48,8 @@ struct btrfs_ordered_sum;
|
||||
|
||||
#define BTRFS_MAGIC "_BHRfS_M"
|
||||
|
||||
#define BTRFS_MAX_MIRRORS 2
|
||||
|
||||
#define BTRFS_MAX_LEVEL 8
|
||||
|
||||
#define BTRFS_COMPAT_EXTENT_TREE_V0
|
||||
@ -137,6 +139,12 @@ struct btrfs_ordered_sum;
|
||||
|
||||
#define BTRFS_EMPTY_SUBVOL_DIR_OBJECTID 2
|
||||
|
||||
/*
|
||||
* the max metadata block size. This limit is somewhat artificial,
|
||||
* but the memmove costs go through the roof for larger blocks.
|
||||
*/
|
||||
#define BTRFS_MAX_METADATA_BLOCKSIZE 65536
|
||||
|
||||
/*
|
||||
* we can actually store much bigger names, but lets not confuse the rest
|
||||
* of linux
|
||||
@ -461,6 +469,19 @@ struct btrfs_super_block {
|
||||
#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
|
||||
#define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS (1ULL << 2)
|
||||
#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO (1ULL << 3)
|
||||
/*
|
||||
* some patches floated around with a second compression method
|
||||
* lets save that incompat here for when they do get in
|
||||
* Note we don't actually support it, we're just reserving the
|
||||
* number
|
||||
*/
|
||||
#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZOv2 (1ULL << 4)
|
||||
|
||||
/*
|
||||
* older kernels tried to do bigger metadata blocks, but the
|
||||
* code was pretty buggy. Lets not let them try anymore.
|
||||
*/
|
||||
#define BTRFS_FEATURE_INCOMPAT_BIG_METADATA (1ULL << 5)
|
||||
|
||||
#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
|
||||
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
|
||||
@ -468,6 +489,7 @@ struct btrfs_super_block {
|
||||
(BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
|
||||
BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \
|
||||
BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \
|
||||
BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \
|
||||
BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO)
|
||||
|
||||
/*
|
||||
@ -829,6 +851,21 @@ struct btrfs_csum_item {
|
||||
*/
|
||||
#define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1ULL << 48)
|
||||
|
||||
#define BTRFS_EXTENDED_PROFILE_MASK (BTRFS_BLOCK_GROUP_PROFILE_MASK | \
|
||||
BTRFS_AVAIL_ALLOC_BIT_SINGLE)
|
||||
|
||||
static inline u64 chunk_to_extended(u64 flags)
|
||||
{
|
||||
if ((flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0)
|
||||
flags |= BTRFS_AVAIL_ALLOC_BIT_SINGLE;
|
||||
|
||||
return flags;
|
||||
}
|
||||
static inline u64 extended_to_chunk(u64 flags)
|
||||
{
|
||||
return flags & ~BTRFS_AVAIL_ALLOC_BIT_SINGLE;
|
||||
}
|
||||
|
||||
struct btrfs_block_group_item {
|
||||
__le64 used;
|
||||
__le64 chunk_objectid;
|
||||
@ -1503,6 +1540,7 @@ struct btrfs_ioctl_defrag_range_args {
|
||||
#define BTRFS_MOUNT_SKIP_BALANCE (1 << 19)
|
||||
#define BTRFS_MOUNT_CHECK_INTEGRITY (1 << 20)
|
||||
#define BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA (1 << 21)
|
||||
#define BTRFS_MOUNT_PANIC_ON_FATAL_ERROR (1 << 22)
|
||||
|
||||
#define btrfs_clear_opt(o, opt) ((o) &= ~BTRFS_MOUNT_##opt)
|
||||
#define btrfs_set_opt(o, opt) ((o) |= BTRFS_MOUNT_##opt)
|
||||
@ -1526,6 +1564,17 @@ struct btrfs_ioctl_defrag_range_args {
|
||||
|
||||
#define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31)
|
||||
|
||||
struct btrfs_map_token {
|
||||
struct extent_buffer *eb;
|
||||
char *kaddr;
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
static inline void btrfs_init_map_token (struct btrfs_map_token *token)
|
||||
{
|
||||
memset(token, 0, sizeof(*token));
|
||||
}
|
||||
|
||||
/* some macros to generate set/get funcs for the struct fields. This
|
||||
* assumes there is a lefoo_to_cpu for every type, so lets make a simple
|
||||
* one for u8:
|
||||
@ -1549,20 +1598,22 @@ struct btrfs_ioctl_defrag_range_args {
|
||||
#ifndef BTRFS_SETGET_FUNCS
|
||||
#define BTRFS_SETGET_FUNCS(name, type, member, bits) \
|
||||
u##bits btrfs_##name(struct extent_buffer *eb, type *s); \
|
||||
u##bits btrfs_token_##name(struct extent_buffer *eb, type *s, struct btrfs_map_token *token); \
|
||||
void btrfs_set_token_##name(struct extent_buffer *eb, type *s, u##bits val, struct btrfs_map_token *token);\
|
||||
void btrfs_set_##name(struct extent_buffer *eb, type *s, u##bits val);
|
||||
#endif
|
||||
|
||||
#define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits) \
|
||||
static inline u##bits btrfs_##name(struct extent_buffer *eb) \
|
||||
{ \
|
||||
type *p = page_address(eb->first_page); \
|
||||
type *p = page_address(eb->pages[0]); \
|
||||
u##bits res = le##bits##_to_cpu(p->member); \
|
||||
return res; \
|
||||
} \
|
||||
static inline void btrfs_set_##name(struct extent_buffer *eb, \
|
||||
u##bits val) \
|
||||
{ \
|
||||
type *p = page_address(eb->first_page); \
|
||||
type *p = page_address(eb->pages[0]); \
|
||||
p->member = cpu_to_le##bits(val); \
|
||||
}
|
||||
|
||||
@ -2466,8 +2517,7 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 num_bytes, u64 min_alloc_size,
|
||||
u64 empty_size, u64 hint_byte,
|
||||
u64 search_end, struct btrfs_key *ins,
|
||||
u64 data);
|
||||
struct btrfs_key *ins, u64 data);
|
||||
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct extent_buffer *buf, int full_backref, int for_cow);
|
||||
int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
@ -2484,8 +2534,8 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||
int btrfs_free_reserved_extent(struct btrfs_root *root, u64 start, u64 len);
|
||||
int btrfs_free_and_pin_reserved_extent(struct btrfs_root *root,
|
||||
u64 start, u64 len);
|
||||
int btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
|
||||
@ -2548,8 +2598,8 @@ void btrfs_block_rsv_release(struct btrfs_root *root,
|
||||
u64 num_bytes);
|
||||
int btrfs_set_block_group_ro(struct btrfs_root *root,
|
||||
struct btrfs_block_group_cache *cache);
|
||||
int btrfs_set_block_group_rw(struct btrfs_root *root,
|
||||
struct btrfs_block_group_cache *cache);
|
||||
void btrfs_set_block_group_rw(struct btrfs_root *root,
|
||||
struct btrfs_block_group_cache *cache);
|
||||
void btrfs_put_block_group_cache(struct btrfs_fs_info *info);
|
||||
u64 btrfs_account_ro_block_groups_free_space(struct btrfs_space_info *sinfo);
|
||||
int btrfs_error_unpin_extent_range(struct btrfs_root *root,
|
||||
@ -2568,9 +2618,9 @@ int btrfs_comp_cpu_keys(struct btrfs_key *k1, struct btrfs_key *k2);
|
||||
int btrfs_previous_item(struct btrfs_root *root,
|
||||
struct btrfs_path *path, u64 min_objectid,
|
||||
int type);
|
||||
int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_key *new_key);
|
||||
void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_key *new_key);
|
||||
struct extent_buffer *btrfs_root_node(struct btrfs_root *root);
|
||||
struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root);
|
||||
int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path,
|
||||
@ -2590,12 +2640,13 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
|
||||
struct extent_buffer **cow_ret, u64 new_root_objectid);
|
||||
int btrfs_block_can_be_shared(struct btrfs_root *root,
|
||||
struct extent_buffer *buf);
|
||||
int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
*root, struct btrfs_path *path, u32 data_size);
|
||||
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
u32 new_size, int from_end);
|
||||
void btrfs_extend_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
u32 data_size);
|
||||
void btrfs_truncate_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
u32 new_size, int from_end);
|
||||
int btrfs_split_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
@ -2629,10 +2680,10 @@ static inline int btrfs_del_item(struct btrfs_trans_handle *trans,
|
||||
return btrfs_del_items(trans, root, path, path->slots[0], 1);
|
||||
}
|
||||
|
||||
int setup_items_for_insert(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_key *cpu_key, u32 *data_size,
|
||||
u32 total_data, u32 total_size, int nr);
|
||||
void setup_items_for_insert(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_key *cpu_key, u32 *data_size,
|
||||
u32 total_data, u32 total_size, int nr);
|
||||
int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
*root, struct btrfs_key *key, void *data, u32 data_size);
|
||||
int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
|
||||
@ -2659,9 +2710,9 @@ static inline int btrfs_next_item(struct btrfs_root *root, struct btrfs_path *p)
|
||||
}
|
||||
int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
|
||||
int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf);
|
||||
void btrfs_drop_snapshot(struct btrfs_root *root,
|
||||
struct btrfs_block_rsv *block_rsv, int update_ref,
|
||||
int for_reloc);
|
||||
int __must_check btrfs_drop_snapshot(struct btrfs_root *root,
|
||||
struct btrfs_block_rsv *block_rsv,
|
||||
int update_ref, int for_reloc);
|
||||
int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct extent_buffer *node,
|
||||
@ -2687,24 +2738,6 @@ static inline void free_fs_info(struct btrfs_fs_info *fs_info)
|
||||
kfree(fs_info->super_for_commit);
|
||||
kfree(fs_info);
|
||||
}
|
||||
/**
|
||||
* profile_is_valid - tests whether a given profile is valid and reduced
|
||||
* @flags: profile to validate
|
||||
* @extended: if true @flags is treated as an extended profile
|
||||
*/
|
||||
static inline int profile_is_valid(u64 flags, int extended)
|
||||
{
|
||||
u64 mask = ~BTRFS_BLOCK_GROUP_PROFILE_MASK;
|
||||
|
||||
flags &= ~BTRFS_BLOCK_GROUP_TYPE_MASK;
|
||||
if (extended)
|
||||
mask &= ~BTRFS_AVAIL_ALLOC_BIT_SINGLE;
|
||||
|
||||
if (flags & mask)
|
||||
return 0;
|
||||
/* true if zero or exactly one bit set */
|
||||
return (flags & (~flags + 1)) == flags;
|
||||
}
|
||||
|
||||
/* root-item.c */
|
||||
int btrfs_find_root_ref(struct btrfs_root *tree_root,
|
||||
@ -2723,9 +2756,10 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
*root, struct btrfs_key *key, struct btrfs_root_item
|
||||
*item);
|
||||
int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
*root, struct btrfs_key *key, struct btrfs_root_item
|
||||
*item);
|
||||
int __must_check btrfs_update_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_key *key,
|
||||
struct btrfs_root_item *item);
|
||||
int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
|
||||
btrfs_root_item *item, struct btrfs_key *key);
|
||||
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
|
||||
@ -2909,7 +2943,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root);
|
||||
void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size);
|
||||
int btrfs_invalidate_inodes(struct btrfs_root *root);
|
||||
void btrfs_invalidate_inodes(struct btrfs_root *root);
|
||||
void btrfs_add_delayed_iput(struct inode *inode);
|
||||
void btrfs_run_delayed_iputs(struct btrfs_root *root);
|
||||
int btrfs_prealloc_file_range(struct inode *inode, int mode,
|
||||
@ -2961,13 +2995,41 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
|
||||
/* super.c */
|
||||
int btrfs_parse_options(struct btrfs_root *root, char *options);
|
||||
int btrfs_sync_fs(struct super_block *sb, int wait);
|
||||
void btrfs_printk(struct btrfs_fs_info *fs_info, const char *fmt, ...);
|
||||
void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function,
|
||||
unsigned int line, int errno);
|
||||
unsigned int line, int errno, const char *fmt, ...);
|
||||
|
||||
void __btrfs_abort_transaction(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, const char *function,
|
||||
unsigned int line, int errno);
|
||||
|
||||
#define btrfs_abort_transaction(trans, root, errno) \
|
||||
do { \
|
||||
__btrfs_abort_transaction(trans, root, __func__, \
|
||||
__LINE__, errno); \
|
||||
} while (0)
|
||||
|
||||
#define btrfs_std_error(fs_info, errno) \
|
||||
do { \
|
||||
if ((errno)) \
|
||||
__btrfs_std_error((fs_info), __func__, __LINE__, (errno));\
|
||||
__btrfs_std_error((fs_info), __func__, \
|
||||
__LINE__, (errno), NULL); \
|
||||
} while (0)
|
||||
|
||||
#define btrfs_error(fs_info, errno, fmt, args...) \
|
||||
do { \
|
||||
__btrfs_std_error((fs_info), __func__, __LINE__, \
|
||||
(errno), fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function,
|
||||
unsigned int line, int errno, const char *fmt, ...);
|
||||
|
||||
#define btrfs_panic(fs_info, errno, fmt, args...) \
|
||||
do { \
|
||||
struct btrfs_fs_info *_i = (fs_info); \
|
||||
__btrfs_panic(_i, __func__, __LINE__, errno, fmt, ##args); \
|
||||
BUG_ON(!(_i->mount_opt & BTRFS_MOUNT_PANIC_ON_FATAL_ERROR)); \
|
||||
} while (0)
|
||||
|
||||
/* acl.c */
|
||||
@ -3003,16 +3065,17 @@ void btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
|
||||
void btrfs_reloc_pre_snapshot(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_pending_snapshot *pending,
|
||||
u64 *bytes_to_reserve);
|
||||
void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
|
||||
int btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_pending_snapshot *pending);
|
||||
|
||||
/* scrub.c */
|
||||
int btrfs_scrub_dev(struct btrfs_root *root, u64 devid, u64 start, u64 end,
|
||||
struct btrfs_scrub_progress *progress, int readonly);
|
||||
int btrfs_scrub_pause(struct btrfs_root *root);
|
||||
int btrfs_scrub_pause_super(struct btrfs_root *root);
|
||||
int btrfs_scrub_continue(struct btrfs_root *root);
|
||||
int btrfs_scrub_continue_super(struct btrfs_root *root);
|
||||
void btrfs_scrub_pause(struct btrfs_root *root);
|
||||
void btrfs_scrub_pause_super(struct btrfs_root *root);
|
||||
void btrfs_scrub_continue(struct btrfs_root *root);
|
||||
void btrfs_scrub_continue_super(struct btrfs_root *root);
|
||||
int __btrfs_scrub_cancel(struct btrfs_fs_info *info);
|
||||
int btrfs_scrub_cancel(struct btrfs_root *root);
|
||||
int btrfs_scrub_cancel_dev(struct btrfs_root *root, struct btrfs_device *dev);
|
||||
int btrfs_scrub_cancel_devid(struct btrfs_root *root, u64 devid);
|
||||
|
@ -115,6 +115,7 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(struct inode *inode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Will return either the node or PTR_ERR(-ENOMEM) */
|
||||
static struct btrfs_delayed_node *btrfs_get_or_create_delayed_node(
|
||||
struct inode *inode)
|
||||
{
|
||||
@ -836,10 +837,8 @@ static int btrfs_batch_insert_items(struct btrfs_trans_handle *trans,
|
||||
btrfs_clear_path_blocking(path, NULL, 0);
|
||||
|
||||
/* insert the keys of the items */
|
||||
ret = setup_items_for_insert(trans, root, path, keys, data_size,
|
||||
total_data_size, total_size, nitems);
|
||||
if (ret)
|
||||
goto error;
|
||||
setup_items_for_insert(trans, root, path, keys, data_size,
|
||||
total_data_size, total_size, nitems);
|
||||
|
||||
/* insert the dir index items */
|
||||
slot = path->slots[0];
|
||||
@ -1108,16 +1107,25 @@ static int btrfs_update_delayed_inode(struct btrfs_trans_handle *trans,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called when committing the transaction. */
|
||||
/*
|
||||
* Called when committing the transaction.
|
||||
* Returns 0 on success.
|
||||
* Returns < 0 on error and returns with an aborted transaction with any
|
||||
* outstanding delayed items cleaned up.
|
||||
*/
|
||||
int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_root *curr_root = root;
|
||||
struct btrfs_delayed_root *delayed_root;
|
||||
struct btrfs_delayed_node *curr_node, *prev_node;
|
||||
struct btrfs_path *path;
|
||||
struct btrfs_block_rsv *block_rsv;
|
||||
int ret = 0;
|
||||
|
||||
if (trans->aborted)
|
||||
return -EIO;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
@ -1130,17 +1138,18 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
|
||||
|
||||
curr_node = btrfs_first_delayed_node(delayed_root);
|
||||
while (curr_node) {
|
||||
root = curr_node->root;
|
||||
ret = btrfs_insert_delayed_items(trans, path, root,
|
||||
curr_root = curr_node->root;
|
||||
ret = btrfs_insert_delayed_items(trans, path, curr_root,
|
||||
curr_node);
|
||||
if (!ret)
|
||||
ret = btrfs_delete_delayed_items(trans, path, root,
|
||||
curr_node);
|
||||
ret = btrfs_delete_delayed_items(trans, path,
|
||||
curr_root, curr_node);
|
||||
if (!ret)
|
||||
ret = btrfs_update_delayed_inode(trans, root, path,
|
||||
curr_node);
|
||||
ret = btrfs_update_delayed_inode(trans, curr_root,
|
||||
path, curr_node);
|
||||
if (ret) {
|
||||
btrfs_release_delayed_node(curr_node);
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1151,6 +1160,7 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
|
||||
|
||||
btrfs_free_path(path);
|
||||
trans->block_rsv = block_rsv;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1371,6 +1381,7 @@ void btrfs_balance_delayed_items(struct btrfs_root *root)
|
||||
btrfs_wq_run_delayed_node(delayed_root, root, 0);
|
||||
}
|
||||
|
||||
/* Will return 0 or -ENOMEM */
|
||||
int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, const char *name,
|
||||
int name_len, struct inode *dir,
|
||||
|
@ -420,7 +420,7 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
|
||||
* this does all the dirty work in terms of maintaining the correct
|
||||
* overall modification count.
|
||||
*/
|
||||
static noinline int add_delayed_ref_head(struct btrfs_fs_info *fs_info,
|
||||
static noinline void add_delayed_ref_head(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_trans_handle *trans,
|
||||
struct btrfs_delayed_ref_node *ref,
|
||||
u64 bytenr, u64 num_bytes,
|
||||
@ -487,20 +487,19 @@ static noinline int add_delayed_ref_head(struct btrfs_fs_info *fs_info,
|
||||
* we've updated the existing ref, free the newly
|
||||
* allocated ref
|
||||
*/
|
||||
kfree(ref);
|
||||
kfree(head_ref);
|
||||
} else {
|
||||
delayed_refs->num_heads++;
|
||||
delayed_refs->num_heads_ready++;
|
||||
delayed_refs->num_entries++;
|
||||
trans->delayed_ref_updates++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* helper to insert a delayed tree ref into the rbtree.
|
||||
*/
|
||||
static noinline int add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
|
||||
static noinline void add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_trans_handle *trans,
|
||||
struct btrfs_delayed_ref_node *ref,
|
||||
u64 bytenr, u64 num_bytes, u64 parent,
|
||||
@ -549,18 +548,17 @@ static noinline int add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
|
||||
* we've updated the existing ref, free the newly
|
||||
* allocated ref
|
||||
*/
|
||||
kfree(ref);
|
||||
kfree(full_ref);
|
||||
} else {
|
||||
delayed_refs->num_entries++;
|
||||
trans->delayed_ref_updates++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* helper to insert a delayed data ref into the rbtree.
|
||||
*/
|
||||
static noinline int add_delayed_data_ref(struct btrfs_fs_info *fs_info,
|
||||
static noinline void add_delayed_data_ref(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_trans_handle *trans,
|
||||
struct btrfs_delayed_ref_node *ref,
|
||||
u64 bytenr, u64 num_bytes, u64 parent,
|
||||
@ -611,12 +609,11 @@ static noinline int add_delayed_data_ref(struct btrfs_fs_info *fs_info,
|
||||
* we've updated the existing ref, free the newly
|
||||
* allocated ref
|
||||
*/
|
||||
kfree(ref);
|
||||
kfree(full_ref);
|
||||
} else {
|
||||
delayed_refs->num_entries++;
|
||||
trans->delayed_ref_updates++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -634,7 +631,6 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_delayed_tree_ref *ref;
|
||||
struct btrfs_delayed_ref_head *head_ref;
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
int ret;
|
||||
|
||||
BUG_ON(extent_op && extent_op->is_data);
|
||||
ref = kmalloc(sizeof(*ref), GFP_NOFS);
|
||||
@ -656,14 +652,12 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
|
||||
* insert both the head node and the new ref without dropping
|
||||
* the spin lock
|
||||
*/
|
||||
ret = add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
|
||||
add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
|
||||
num_bytes, action, 0);
|
||||
BUG_ON(ret);
|
||||
|
||||
ret = add_delayed_tree_ref(fs_info, trans, &ref->node, bytenr,
|
||||
add_delayed_tree_ref(fs_info, trans, &ref->node, bytenr,
|
||||
num_bytes, parent, ref_root, level, action,
|
||||
for_cow);
|
||||
BUG_ON(ret);
|
||||
if (!need_ref_seq(for_cow, ref_root) &&
|
||||
waitqueue_active(&delayed_refs->seq_wait))
|
||||
wake_up(&delayed_refs->seq_wait);
|
||||
@ -685,7 +679,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_delayed_data_ref *ref;
|
||||
struct btrfs_delayed_ref_head *head_ref;
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
int ret;
|
||||
|
||||
BUG_ON(extent_op && !extent_op->is_data);
|
||||
ref = kmalloc(sizeof(*ref), GFP_NOFS);
|
||||
@ -707,14 +700,12 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
|
||||
* insert both the head node and the new ref without dropping
|
||||
* the spin lock
|
||||
*/
|
||||
ret = add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
|
||||
add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
|
||||
num_bytes, action, 1);
|
||||
BUG_ON(ret);
|
||||
|
||||
ret = add_delayed_data_ref(fs_info, trans, &ref->node, bytenr,
|
||||
add_delayed_data_ref(fs_info, trans, &ref->node, bytenr,
|
||||
num_bytes, parent, ref_root, owner, offset,
|
||||
action, for_cow);
|
||||
BUG_ON(ret);
|
||||
if (!need_ref_seq(for_cow, ref_root) &&
|
||||
waitqueue_active(&delayed_refs->seq_wait))
|
||||
wake_up(&delayed_refs->seq_wait);
|
||||
@ -729,7 +720,6 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info,
|
||||
{
|
||||
struct btrfs_delayed_ref_head *head_ref;
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
int ret;
|
||||
|
||||
head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
|
||||
if (!head_ref)
|
||||
@ -740,10 +730,9 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info,
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
spin_lock(&delayed_refs->lock);
|
||||
|
||||
ret = add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
|
||||
add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
|
||||
num_bytes, BTRFS_UPDATE_DELAYED_HEAD,
|
||||
extent_op->is_data);
|
||||
BUG_ON(ret);
|
||||
|
||||
if (waitqueue_active(&delayed_refs->seq_wait))
|
||||
wake_up(&delayed_refs->seq_wait);
|
||||
|
@ -49,9 +49,8 @@ static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
|
||||
di = btrfs_match_dir_item_name(root, path, name, name_len);
|
||||
if (di)
|
||||
return ERR_PTR(-EEXIST);
|
||||
ret = btrfs_extend_item(trans, root, path, data_size);
|
||||
}
|
||||
if (ret < 0)
|
||||
btrfs_extend_item(trans, root, path, data_size);
|
||||
} else if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
WARN_ON(ret > 0);
|
||||
leaf = path->nodes[0];
|
||||
@ -116,6 +115,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
|
||||
* 'location' is the key to stuff into the directory item, 'type' is the
|
||||
* type of the inode we're pointing to, and 'index' is the sequence number
|
||||
* to use for the second index (if one is created).
|
||||
* Will return 0 or -ENOMEM
|
||||
*/
|
||||
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
*root, const char *name, int name_len,
|
||||
@ -383,8 +383,8 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
|
||||
start = btrfs_item_ptr_offset(leaf, path->slots[0]);
|
||||
memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
|
||||
item_len - (ptr + sub_item_len - start));
|
||||
ret = btrfs_truncate_item(trans, root, path,
|
||||
item_len - sub_item_len, 1);
|
||||
btrfs_truncate_item(trans, root, path,
|
||||
item_len - sub_item_len, 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -44,8 +44,8 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, u32 blocksize,
|
||||
int mirror_num, struct extent_buffer **eb);
|
||||
struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
|
||||
u64 bytenr, u32 blocksize);
|
||||
int clean_tree_block(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct extent_buffer *buf);
|
||||
void clean_tree_block(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct extent_buffer *buf);
|
||||
int open_ctree(struct super_block *sb,
|
||||
struct btrfs_fs_devices *fs_devices,
|
||||
char *options);
|
||||
@ -64,7 +64,7 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
|
||||
int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info);
|
||||
void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr);
|
||||
void __btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr);
|
||||
int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root);
|
||||
void btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root);
|
||||
void btrfs_mark_buffer_dirty(struct extent_buffer *buf);
|
||||
int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid);
|
||||
int btrfs_set_buffer_uptodate(struct extent_buffer *buf);
|
||||
@ -85,6 +85,10 @@ int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_fs_info *fs_info);
|
||||
int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
int btrfs_cleanup_transaction(struct btrfs_root *root);
|
||||
void btrfs_cleanup_one_transaction(struct btrfs_transaction *trans,
|
||||
struct btrfs_root *root);
|
||||
void btrfs_abort_devices(struct btrfs_root *root);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
void btrfs_init_lockdep(void);
|
||||
|
@ -193,7 +193,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child)
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
BUG_ON(ret == 0);
|
||||
BUG_ON(ret == 0); /* Key with offset of -1 found */
|
||||
if (path->slots[0] == 0) {
|
||||
ret = -ENOENT;
|
||||
goto fail;
|
||||
|
File diff suppressed because it is too large
Load Diff
1045
fs/btrfs/extent_io.c
1045
fs/btrfs/extent_io.c
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,10 @@
|
||||
#define EXTENT_BUFFER_DIRTY 2
|
||||
#define EXTENT_BUFFER_CORRUPT 3
|
||||
#define EXTENT_BUFFER_READAHEAD 4 /* this got triggered by readahead */
|
||||
#define EXTENT_BUFFER_TREE_REF 5
|
||||
#define EXTENT_BUFFER_STALE 6
|
||||
#define EXTENT_BUFFER_WRITEBACK 7
|
||||
#define EXTENT_BUFFER_IOERR 8
|
||||
|
||||
/* these are flags for extent_clear_unlock_delalloc */
|
||||
#define EXTENT_CLEAR_UNLOCK_PAGE 0x1
|
||||
@ -54,6 +58,7 @@
|
||||
#define EXTENT_PAGE_PRIVATE_FIRST_PAGE 3
|
||||
|
||||
struct extent_state;
|
||||
struct btrfs_root;
|
||||
|
||||
typedef int (extent_submit_bio_hook_t)(struct inode *inode, int rw,
|
||||
struct bio *bio, int mirror_num,
|
||||
@ -69,9 +74,7 @@ struct extent_io_ops {
|
||||
size_t size, struct bio *bio,
|
||||
unsigned long bio_flags);
|
||||
int (*readpage_io_hook)(struct page *page, u64 start, u64 end);
|
||||
int (*readpage_io_failed_hook)(struct bio *bio, struct page *page,
|
||||
u64 start, u64 end, int failed_mirror,
|
||||
struct extent_state *state);
|
||||
int (*readpage_io_failed_hook)(struct page *page, int failed_mirror);
|
||||
int (*writepage_io_failed_hook)(struct bio *bio, struct page *page,
|
||||
u64 start, u64 end,
|
||||
struct extent_state *state);
|
||||
@ -97,6 +100,7 @@ struct extent_io_tree {
|
||||
struct radix_tree_root buffer;
|
||||
struct address_space *mapping;
|
||||
u64 dirty_bytes;
|
||||
int track_uptodate;
|
||||
spinlock_t lock;
|
||||
spinlock_t buffer_lock;
|
||||
struct extent_io_ops *ops;
|
||||
@ -119,16 +123,21 @@ struct extent_state {
|
||||
struct list_head leak_list;
|
||||
};
|
||||
|
||||
#define INLINE_EXTENT_BUFFER_PAGES 16
|
||||
#define MAX_INLINE_EXTENT_BUFFER_SIZE (INLINE_EXTENT_BUFFER_PAGES * PAGE_CACHE_SIZE)
|
||||
struct extent_buffer {
|
||||
u64 start;
|
||||
unsigned long len;
|
||||
unsigned long map_start;
|
||||
unsigned long map_len;
|
||||
struct page *first_page;
|
||||
unsigned long bflags;
|
||||
struct extent_io_tree *tree;
|
||||
spinlock_t refs_lock;
|
||||
atomic_t refs;
|
||||
atomic_t io_pages;
|
||||
int failed_mirror;
|
||||
struct list_head leak_list;
|
||||
struct rcu_head rcu_head;
|
||||
atomic_t refs;
|
||||
pid_t lock_owner;
|
||||
|
||||
/* count of read lock holders on the extent buffer */
|
||||
@ -152,6 +161,9 @@ struct extent_buffer {
|
||||
* to unlock
|
||||
*/
|
||||
wait_queue_head_t read_lock_wq;
|
||||
wait_queue_head_t lock_wq;
|
||||
struct page *inline_pages[INLINE_EXTENT_BUFFER_PAGES];
|
||||
struct page **pages;
|
||||
};
|
||||
|
||||
static inline void extent_set_compress_type(unsigned long *bio_flags,
|
||||
@ -178,18 +190,17 @@ void extent_io_tree_init(struct extent_io_tree *tree,
|
||||
int try_release_extent_mapping(struct extent_map_tree *map,
|
||||
struct extent_io_tree *tree, struct page *page,
|
||||
gfp_t mask);
|
||||
int try_release_extent_buffer(struct extent_io_tree *tree, struct page *page);
|
||||
int try_release_extent_buffer(struct page *page, gfp_t mask);
|
||||
int try_release_extent_state(struct extent_map_tree *map,
|
||||
struct extent_io_tree *tree, struct page *page,
|
||||
gfp_t mask);
|
||||
int lock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask);
|
||||
int lock_extent(struct extent_io_tree *tree, u64 start, u64 end);
|
||||
int lock_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
int bits, struct extent_state **cached, gfp_t mask);
|
||||
int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask);
|
||||
int bits, struct extent_state **cached);
|
||||
int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end);
|
||||
int unlock_extent_cached(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
struct extent_state **cached, gfp_t mask);
|
||||
int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
gfp_t mask);
|
||||
int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end);
|
||||
int extent_read_full_page(struct extent_io_tree *tree, struct page *page,
|
||||
get_extent_t *get_extent, int mirror_num);
|
||||
int __init extent_io_init(void);
|
||||
@ -210,7 +221,7 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
int bits, gfp_t mask);
|
||||
int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
int bits, int exclusive_bits, u64 *failed_start,
|
||||
int bits, u64 *failed_start,
|
||||
struct extent_state **cached_state, gfp_t mask);
|
||||
int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
struct extent_state **cached_state, gfp_t mask);
|
||||
@ -240,6 +251,8 @@ int extent_writepages(struct extent_io_tree *tree,
|
||||
struct address_space *mapping,
|
||||
get_extent_t *get_extent,
|
||||
struct writeback_control *wbc);
|
||||
int btree_write_cache_pages(struct address_space *mapping,
|
||||
struct writeback_control *wbc);
|
||||
int extent_readpages(struct extent_io_tree *tree,
|
||||
struct address_space *mapping,
|
||||
struct list_head *pages, unsigned nr_pages,
|
||||
@ -251,11 +264,11 @@ int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private);
|
||||
void set_page_extent_mapped(struct page *page);
|
||||
|
||||
struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
|
||||
u64 start, unsigned long len,
|
||||
struct page *page0);
|
||||
u64 start, unsigned long len);
|
||||
struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
|
||||
u64 start, unsigned long len);
|
||||
void free_extent_buffer(struct extent_buffer *eb);
|
||||
void free_extent_buffer_stale(struct extent_buffer *eb);
|
||||
#define WAIT_NONE 0
|
||||
#define WAIT_COMPLETE 1
|
||||
#define WAIT_PAGE_LOCK 2
|
||||
@ -287,19 +300,12 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
|
||||
unsigned long src_offset, unsigned long len);
|
||||
void memset_extent_buffer(struct extent_buffer *eb, char c,
|
||||
unsigned long start, unsigned long len);
|
||||
int wait_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits);
|
||||
int clear_extent_buffer_dirty(struct extent_io_tree *tree,
|
||||
struct extent_buffer *eb);
|
||||
int set_extent_buffer_dirty(struct extent_io_tree *tree,
|
||||
struct extent_buffer *eb);
|
||||
int set_extent_buffer_uptodate(struct extent_io_tree *tree,
|
||||
struct extent_buffer *eb);
|
||||
int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
|
||||
struct extent_buffer *eb,
|
||||
struct extent_state **cached_state);
|
||||
int extent_buffer_uptodate(struct extent_io_tree *tree,
|
||||
struct extent_buffer *eb,
|
||||
struct extent_state *cached_state);
|
||||
void wait_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits);
|
||||
void clear_extent_buffer_dirty(struct extent_buffer *eb);
|
||||
int set_extent_buffer_dirty(struct extent_buffer *eb);
|
||||
int set_extent_buffer_uptodate(struct extent_buffer *eb);
|
||||
int clear_extent_buffer_uptodate(struct extent_buffer *eb);
|
||||
int extent_buffer_uptodate(struct extent_buffer *eb);
|
||||
int map_private_extent_buffer(struct extent_buffer *eb, unsigned long offset,
|
||||
unsigned long min_len, char **map,
|
||||
unsigned long *map_start,
|
||||
@ -320,4 +326,6 @@ int repair_io_failure(struct btrfs_mapping_tree *map_tree, u64 start,
|
||||
u64 length, u64 logical, struct page *page,
|
||||
int mirror_num);
|
||||
int end_extent_writepage(struct page *page, int err, u64 start, u64 end);
|
||||
int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
|
||||
int mirror_num);
|
||||
#endif
|
||||
|
@ -25,10 +25,12 @@
|
||||
#include "transaction.h"
|
||||
#include "print-tree.h"
|
||||
|
||||
#define MAX_CSUM_ITEMS(r, size) ((((BTRFS_LEAF_DATA_SIZE(r) - \
|
||||
#define __MAX_CSUM_ITEMS(r, size) ((((BTRFS_LEAF_DATA_SIZE(r) - \
|
||||
sizeof(struct btrfs_item) * 2) / \
|
||||
size) - 1))
|
||||
|
||||
#define MAX_CSUM_ITEMS(r, size) (min(__MAX_CSUM_ITEMS(r, size), PAGE_CACHE_SIZE))
|
||||
|
||||
#define MAX_ORDERED_SUM_BYTES(r) ((PAGE_SIZE - \
|
||||
sizeof(struct btrfs_ordered_sum)) / \
|
||||
sizeof(struct btrfs_sector_sum) * \
|
||||
@ -59,7 +61,7 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
|
||||
sizeof(*item));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* Can't happen */
|
||||
leaf = path->nodes[0];
|
||||
item = btrfs_item_ptr(leaf, path->slots[0],
|
||||
struct btrfs_file_extent_item);
|
||||
@ -284,6 +286,7 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
|
||||
struct btrfs_ordered_sum *sums;
|
||||
struct btrfs_sector_sum *sector_sum;
|
||||
struct btrfs_csum_item *item;
|
||||
LIST_HEAD(tmplist);
|
||||
unsigned long offset;
|
||||
int ret;
|
||||
size_t size;
|
||||
@ -358,7 +361,10 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
|
||||
MAX_ORDERED_SUM_BYTES(root));
|
||||
sums = kzalloc(btrfs_ordered_sum_size(root, size),
|
||||
GFP_NOFS);
|
||||
BUG_ON(!sums);
|
||||
if (!sums) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sector_sum = sums->sums;
|
||||
sums->bytenr = start;
|
||||
@ -380,12 +386,19 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
|
||||
offset += csum_size;
|
||||
sector_sum++;
|
||||
}
|
||||
list_add_tail(&sums->list, list);
|
||||
list_add_tail(&sums->list, &tmplist);
|
||||
}
|
||||
path->slots[0]++;
|
||||
}
|
||||
ret = 0;
|
||||
fail:
|
||||
while (ret < 0 && !list_empty(&tmplist)) {
|
||||
sums = list_entry(&tmplist, struct btrfs_ordered_sum, list);
|
||||
list_del(&sums->list);
|
||||
kfree(sums);
|
||||
}
|
||||
list_splice_tail(&tmplist, list);
|
||||
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
@ -420,7 +433,7 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
|
||||
offset = page_offset(bvec->bv_page) + bvec->bv_offset;
|
||||
|
||||
ordered = btrfs_lookup_ordered_extent(inode, offset);
|
||||
BUG_ON(!ordered);
|
||||
BUG_ON(!ordered); /* Logic error */
|
||||
sums->bytenr = ordered->start;
|
||||
|
||||
while (bio_index < bio->bi_vcnt) {
|
||||
@ -439,11 +452,11 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
|
||||
|
||||
sums = kzalloc(btrfs_ordered_sum_size(root, bytes_left),
|
||||
GFP_NOFS);
|
||||
BUG_ON(!sums);
|
||||
BUG_ON(!sums); /* -ENOMEM */
|
||||
sector_sum = sums->sums;
|
||||
sums->len = bytes_left;
|
||||
ordered = btrfs_lookup_ordered_extent(inode, offset);
|
||||
BUG_ON(!ordered);
|
||||
BUG_ON(!ordered); /* Logic error */
|
||||
sums->bytenr = ordered->start;
|
||||
}
|
||||
|
||||
@ -483,18 +496,17 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
|
||||
* This calls btrfs_truncate_item with the correct args based on the
|
||||
* overlap, and fixes up the key as required.
|
||||
*/
|
||||
static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
struct btrfs_key *key,
|
||||
u64 bytenr, u64 len)
|
||||
static noinline void truncate_one_csum(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
struct btrfs_key *key,
|
||||
u64 bytenr, u64 len)
|
||||
{
|
||||
struct extent_buffer *leaf;
|
||||
u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
|
||||
u64 csum_end;
|
||||
u64 end_byte = bytenr + len;
|
||||
u32 blocksize_bits = root->fs_info->sb->s_blocksize_bits;
|
||||
int ret;
|
||||
|
||||
leaf = path->nodes[0];
|
||||
csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size;
|
||||
@ -510,7 +522,7 @@ static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
|
||||
*/
|
||||
u32 new_size = (bytenr - key->offset) >> blocksize_bits;
|
||||
new_size *= csum_size;
|
||||
ret = btrfs_truncate_item(trans, root, path, new_size, 1);
|
||||
btrfs_truncate_item(trans, root, path, new_size, 1);
|
||||
} else if (key->offset >= bytenr && csum_end > end_byte &&
|
||||
end_byte > key->offset) {
|
||||
/*
|
||||
@ -522,15 +534,13 @@ static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
|
||||
u32 new_size = (csum_end - end_byte) >> blocksize_bits;
|
||||
new_size *= csum_size;
|
||||
|
||||
ret = btrfs_truncate_item(trans, root, path, new_size, 0);
|
||||
btrfs_truncate_item(trans, root, path, new_size, 0);
|
||||
|
||||
key->offset = end_byte;
|
||||
ret = btrfs_set_item_key_safe(trans, root, path, key);
|
||||
BUG_ON(ret);
|
||||
btrfs_set_item_key_safe(trans, root, path, key);
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -635,13 +645,14 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
|
||||
* item changed size or key
|
||||
*/
|
||||
ret = btrfs_split_item(trans, root, path, &key, offset);
|
||||
BUG_ON(ret && ret != -EAGAIN);
|
||||
if (ret && ret != -EAGAIN) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
key.offset = end_byte - 1;
|
||||
} else {
|
||||
ret = truncate_one_csum(trans, root, path,
|
||||
&key, bytenr, len);
|
||||
BUG_ON(ret);
|
||||
truncate_one_csum(trans, root, path, &key, bytenr, len);
|
||||
if (key.offset < bytenr)
|
||||
break;
|
||||
}
|
||||
@ -772,7 +783,7 @@ again:
|
||||
if (diff != csum_size)
|
||||
goto insert;
|
||||
|
||||
ret = btrfs_extend_item(trans, root, path, diff);
|
||||
btrfs_extend_item(trans, root, path, diff);
|
||||
goto csum;
|
||||
}
|
||||
|
||||
|
@ -452,7 +452,7 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
|
||||
split = alloc_extent_map();
|
||||
if (!split2)
|
||||
split2 = alloc_extent_map();
|
||||
BUG_ON(!split || !split2);
|
||||
BUG_ON(!split || !split2); /* -ENOMEM */
|
||||
|
||||
write_lock(&em_tree->lock);
|
||||
em = lookup_extent_mapping(em_tree, start, len);
|
||||
@ -494,7 +494,7 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
|
||||
split->flags = flags;
|
||||
split->compress_type = em->compress_type;
|
||||
ret = add_extent_mapping(em_tree, split);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* Logic error */
|
||||
free_extent_map(split);
|
||||
split = split2;
|
||||
split2 = NULL;
|
||||
@ -520,7 +520,7 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
|
||||
}
|
||||
|
||||
ret = add_extent_mapping(em_tree, split);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* Logic error */
|
||||
free_extent_map(split);
|
||||
split = NULL;
|
||||
}
|
||||
@ -679,7 +679,7 @@ next_slot:
|
||||
root->root_key.objectid,
|
||||
new_key.objectid,
|
||||
start - extent_offset, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
*hint_byte = disk_bytenr;
|
||||
}
|
||||
key.offset = start;
|
||||
@ -754,7 +754,7 @@ next_slot:
|
||||
root->root_key.objectid,
|
||||
key.objectid, key.offset -
|
||||
extent_offset, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
inode_sub_bytes(inode,
|
||||
extent_end - key.offset);
|
||||
*hint_byte = disk_bytenr;
|
||||
@ -770,7 +770,10 @@ next_slot:
|
||||
|
||||
ret = btrfs_del_items(trans, root, path, del_slot,
|
||||
del_nr);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
del_nr = 0;
|
||||
del_slot = 0;
|
||||
@ -782,11 +785,13 @@ next_slot:
|
||||
BUG_ON(1);
|
||||
}
|
||||
|
||||
if (del_nr > 0) {
|
||||
if (!ret && del_nr > 0) {
|
||||
ret = btrfs_del_items(trans, root, path, del_slot, del_nr);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
}
|
||||
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
@ -944,7 +949,10 @@ again:
|
||||
btrfs_release_path(path);
|
||||
goto again;
|
||||
}
|
||||
BUG_ON(ret < 0);
|
||||
if (ret < 0) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
leaf = path->nodes[0];
|
||||
fi = btrfs_item_ptr(leaf, path->slots[0] - 1,
|
||||
@ -963,7 +971,7 @@ again:
|
||||
ret = btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, 0,
|
||||
root->root_key.objectid,
|
||||
ino, orig_offset, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
|
||||
if (split == start) {
|
||||
key.offset = start;
|
||||
@ -990,7 +998,7 @@ again:
|
||||
ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
|
||||
0, root->root_key.objectid,
|
||||
ino, orig_offset, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
other_start = 0;
|
||||
other_end = start;
|
||||
@ -1007,7 +1015,7 @@ again:
|
||||
ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
|
||||
0, root->root_key.objectid,
|
||||
ino, orig_offset, 0);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
if (del_nr == 0) {
|
||||
fi = btrfs_item_ptr(leaf, path->slots[0],
|
||||
@ -1025,7 +1033,10 @@ again:
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
|
||||
ret = btrfs_del_items(trans, root, path, del_slot, del_nr);
|
||||
BUG_ON(ret);
|
||||
if (ret < 0) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
@ -1105,8 +1116,7 @@ again:
|
||||
if (start_pos < inode->i_size) {
|
||||
struct btrfs_ordered_extent *ordered;
|
||||
lock_extent_bits(&BTRFS_I(inode)->io_tree,
|
||||
start_pos, last_pos - 1, 0, &cached_state,
|
||||
GFP_NOFS);
|
||||
start_pos, last_pos - 1, 0, &cached_state);
|
||||
ordered = btrfs_lookup_first_ordered_extent(inode,
|
||||
last_pos - 1);
|
||||
if (ordered &&
|
||||
@ -1638,7 +1648,7 @@ static long btrfs_fallocate(struct file *file, int mode,
|
||||
* transaction
|
||||
*/
|
||||
lock_extent_bits(&BTRFS_I(inode)->io_tree, alloc_start,
|
||||
locked_end, 0, &cached_state, GFP_NOFS);
|
||||
locked_end, 0, &cached_state);
|
||||
ordered = btrfs_lookup_first_ordered_extent(inode,
|
||||
alloc_end - 1);
|
||||
if (ordered &&
|
||||
@ -1667,7 +1677,13 @@ static long btrfs_fallocate(struct file *file, int mode,
|
||||
|
||||
em = btrfs_get_extent(inode, NULL, 0, cur_offset,
|
||||
alloc_end - cur_offset, 0);
|
||||
BUG_ON(IS_ERR_OR_NULL(em));
|
||||
if (IS_ERR_OR_NULL(em)) {
|
||||
if (!em)
|
||||
ret = -ENOMEM;
|
||||
else
|
||||
ret = PTR_ERR(em);
|
||||
break;
|
||||
}
|
||||
last_byte = min(extent_map_end(em), alloc_end);
|
||||
actual_end = min_t(u64, extent_map_end(em), offset + len);
|
||||
last_byte = (last_byte + mask) & ~mask;
|
||||
@ -1737,7 +1753,7 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int origin)
|
||||
return -ENXIO;
|
||||
|
||||
lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0,
|
||||
&cached_state, GFP_NOFS);
|
||||
&cached_state);
|
||||
|
||||
/*
|
||||
* Delalloc is such a pain. If we have a hole and we have pending
|
||||
|
@ -230,11 +230,13 @@ int btrfs_truncate_free_space_cache(struct btrfs_root *root,
|
||||
|
||||
if (ret) {
|
||||
trans->block_rsv = rsv;
|
||||
WARN_ON(1);
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = btrfs_update_inode(trans, root, inode);
|
||||
if (ret)
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
trans->block_rsv = rsv;
|
||||
|
||||
return ret;
|
||||
@ -869,7 +871,7 @@ int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
|
||||
io_ctl_prepare_pages(&io_ctl, inode, 0);
|
||||
|
||||
lock_extent_bits(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1,
|
||||
0, &cached_state, GFP_NOFS);
|
||||
0, &cached_state);
|
||||
|
||||
node = rb_first(&ctl->free_space_offset);
|
||||
if (!node && cluster) {
|
||||
@ -1948,14 +1950,14 @@ again:
|
||||
*/
|
||||
ret = btrfs_add_free_space(block_group, old_start,
|
||||
offset - old_start);
|
||||
WARN_ON(ret);
|
||||
WARN_ON(ret); /* -ENOMEM */
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = remove_from_bitmap(ctl, info, &offset, &bytes);
|
||||
if (ret == -EAGAIN)
|
||||
goto again;
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* logic error */
|
||||
out_lock:
|
||||
spin_unlock(&ctl->tree_lock);
|
||||
out:
|
||||
@ -2346,7 +2348,7 @@ again:
|
||||
rb_erase(&entry->offset_index, &ctl->free_space_offset);
|
||||
ret = tree_insert_offset(&cluster->root, entry->offset,
|
||||
&entry->offset_index, 1);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -EEXIST; Logic error */
|
||||
|
||||
trace_btrfs_setup_cluster(block_group, cluster,
|
||||
total_found * block_group->sectorsize, 1);
|
||||
@ -2439,7 +2441,7 @@ setup_cluster_no_bitmap(struct btrfs_block_group_cache *block_group,
|
||||
ret = tree_insert_offset(&cluster->root, entry->offset,
|
||||
&entry->offset_index, 0);
|
||||
total_size += entry->bytes;
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -EEXIST; Logic error */
|
||||
} while (node && entry != last);
|
||||
|
||||
cluster->max_size = max_extent;
|
||||
@ -2830,6 +2832,7 @@ u64 btrfs_find_ino_for_alloc(struct btrfs_root *fs_root)
|
||||
int ret;
|
||||
|
||||
ret = search_bitmap(ctl, entry, &offset, &count);
|
||||
/* Logic error; Should be empty if it can't find anything */
|
||||
BUG_ON(ret);
|
||||
|
||||
ino = offset;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "ctree.h"
|
||||
#include "disk-io.h"
|
||||
#include "transaction.h"
|
||||
#include "print-tree.h"
|
||||
|
||||
static int find_name_in_backref(struct btrfs_path *path, const char *name,
|
||||
int name_len, struct btrfs_inode_ref **ref_ret)
|
||||
@ -128,13 +129,14 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
|
||||
item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
|
||||
memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
|
||||
item_size - (ptr + sub_item_len - item_start));
|
||||
ret = btrfs_truncate_item(trans, root, path,
|
||||
btrfs_truncate_item(trans, root, path,
|
||||
item_size - sub_item_len, 1);
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Will return 0, -ENOMEM, -EMLINK, or -EEXIST or anything from the CoW path */
|
||||
int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
const char *name, int name_len,
|
||||
@ -165,7 +167,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
|
||||
goto out;
|
||||
|
||||
old_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]);
|
||||
ret = btrfs_extend_item(trans, root, path, ins_len);
|
||||
btrfs_extend_item(trans, root, path, ins_len);
|
||||
ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
||||
struct btrfs_inode_ref);
|
||||
ref = (struct btrfs_inode_ref *)((unsigned long)ref + old_size);
|
||||
|
@ -178,7 +178,7 @@ static void start_caching(struct btrfs_root *root)
|
||||
|
||||
tsk = kthread_run(caching_kthread, root, "btrfs-ino-cache-%llu\n",
|
||||
root->root_key.objectid);
|
||||
BUG_ON(IS_ERR(tsk));
|
||||
BUG_ON(IS_ERR(tsk)); /* -ENOMEM */
|
||||
}
|
||||
|
||||
int btrfs_find_free_ino(struct btrfs_root *root, u64 *objectid)
|
||||
@ -271,7 +271,7 @@ void btrfs_unpin_free_ino(struct btrfs_root *root)
|
||||
break;
|
||||
|
||||
info = rb_entry(n, struct btrfs_free_space, offset_index);
|
||||
BUG_ON(info->bitmap);
|
||||
BUG_ON(info->bitmap); /* Logic error */
|
||||
|
||||
if (info->offset > root->cache_progress)
|
||||
goto free;
|
||||
@ -439,17 +439,16 @@ int btrfs_save_ino_cache(struct btrfs_root *root,
|
||||
if (ret)
|
||||
goto out;
|
||||
trace_btrfs_space_reservation(root->fs_info, "ino_cache",
|
||||
(u64)(unsigned long)trans,
|
||||
trans->bytes_reserved, 1);
|
||||
trans->transid, trans->bytes_reserved, 1);
|
||||
again:
|
||||
inode = lookup_free_ino_inode(root, path);
|
||||
if (IS_ERR(inode) && PTR_ERR(inode) != -ENOENT) {
|
||||
if (IS_ERR(inode) && (PTR_ERR(inode) != -ENOENT || retry)) {
|
||||
ret = PTR_ERR(inode);
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
if (IS_ERR(inode)) {
|
||||
BUG_ON(retry);
|
||||
BUG_ON(retry); /* Logic error */
|
||||
retry = true;
|
||||
|
||||
ret = create_free_ino_inode(root, trans, path);
|
||||
@ -460,12 +459,17 @@ again:
|
||||
|
||||
BTRFS_I(inode)->generation = 0;
|
||||
ret = btrfs_update_inode(trans, root, inode);
|
||||
WARN_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
if (i_size_read(inode) > 0) {
|
||||
ret = btrfs_truncate_free_space_cache(root, trans, path, inode);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out_put;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(&root->cache_lock);
|
||||
@ -502,8 +506,7 @@ out_put:
|
||||
iput(inode);
|
||||
out_release:
|
||||
trace_btrfs_space_reservation(root->fs_info, "ino_cache",
|
||||
(u64)(unsigned long)trans,
|
||||
trans->bytes_reserved, 0);
|
||||
trans->transid, trans->bytes_reserved, 0);
|
||||
btrfs_block_rsv_release(root, trans->block_rsv, trans->bytes_reserved);
|
||||
out:
|
||||
trans->block_rsv = rsv;
|
||||
@ -532,7 +535,7 @@ static int btrfs_find_highest_objectid(struct btrfs_root *root, u64 *objectid)
|
||||
ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
BUG_ON(ret == 0);
|
||||
BUG_ON(ret == 0); /* Corruption */
|
||||
if (path->slots[0] > 0) {
|
||||
slot = path->slots[0] - 1;
|
||||
l = path->nodes[0];
|
||||
|
457
fs/btrfs/inode.c
457
fs/btrfs/inode.c
File diff suppressed because it is too large
Load Diff
194
fs/btrfs/ioctl.c
194
fs/btrfs/ioctl.c
@ -425,22 +425,37 @@ static noinline int create_subvol(struct btrfs_root *root,
|
||||
|
||||
key.offset = (u64)-1;
|
||||
new_root = btrfs_read_fs_root_no_name(root->fs_info, &key);
|
||||
BUG_ON(IS_ERR(new_root));
|
||||
if (IS_ERR(new_root)) {
|
||||
btrfs_abort_transaction(trans, root, PTR_ERR(new_root));
|
||||
ret = PTR_ERR(new_root);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
btrfs_record_root_in_trans(trans, new_root);
|
||||
|
||||
ret = btrfs_create_subvol_root(trans, new_root, new_dirid);
|
||||
if (ret) {
|
||||
/* We potentially lose an unused inode item here */
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* insert the directory item
|
||||
*/
|
||||
ret = btrfs_set_inode_index(dir, &index);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = btrfs_insert_dir_item(trans, root,
|
||||
name, namelen, dir, &key,
|
||||
BTRFS_FT_DIR, index);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
btrfs_i_size_write(dir, dir->i_size + namelen * 2);
|
||||
ret = btrfs_update_inode(trans, root, dir);
|
||||
@ -769,6 +784,31 @@ none:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validaty check of prev em and next em:
|
||||
* 1) no prev/next em
|
||||
* 2) prev/next em is an hole/inline extent
|
||||
*/
|
||||
static int check_adjacent_extents(struct inode *inode, struct extent_map *em)
|
||||
{
|
||||
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
||||
struct extent_map *prev = NULL, *next = NULL;
|
||||
int ret = 0;
|
||||
|
||||
read_lock(&em_tree->lock);
|
||||
prev = lookup_extent_mapping(em_tree, em->start - 1, (u64)-1);
|
||||
next = lookup_extent_mapping(em_tree, em->start + em->len, (u64)-1);
|
||||
read_unlock(&em_tree->lock);
|
||||
|
||||
if ((!prev || prev->block_start >= EXTENT_MAP_LAST_BYTE) &&
|
||||
(!next || next->block_start >= EXTENT_MAP_LAST_BYTE))
|
||||
ret = 1;
|
||||
free_extent_map(prev);
|
||||
free_extent_map(next);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int should_defrag_range(struct inode *inode, u64 start, u64 len,
|
||||
int thresh, u64 *last_len, u64 *skip,
|
||||
u64 *defrag_end)
|
||||
@ -797,17 +837,25 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
|
||||
|
||||
if (!em) {
|
||||
/* get the big lock and read metadata off disk */
|
||||
lock_extent(io_tree, start, start + len - 1, GFP_NOFS);
|
||||
lock_extent(io_tree, start, start + len - 1);
|
||||
em = btrfs_get_extent(inode, NULL, 0, start, len, 0);
|
||||
unlock_extent(io_tree, start, start + len - 1, GFP_NOFS);
|
||||
unlock_extent(io_tree, start, start + len - 1);
|
||||
|
||||
if (IS_ERR(em))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this will cover holes, and inline extents */
|
||||
if (em->block_start >= EXTENT_MAP_LAST_BYTE)
|
||||
if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If we have nothing to merge with us, just skip. */
|
||||
if (check_adjacent_extents(inode, em)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* we hit a real extent, if it is big don't bother defragging it again
|
||||
@ -815,6 +863,7 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
|
||||
if ((*last_len == 0 || *last_len >= thresh) && em->len >= thresh)
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
/*
|
||||
* last_len ends up being a counter of how many bytes we've defragged.
|
||||
* every time we choose not to defrag an extent, we reset *last_len
|
||||
@ -856,6 +905,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
|
||||
u64 isize = i_size_read(inode);
|
||||
u64 page_start;
|
||||
u64 page_end;
|
||||
u64 page_cnt;
|
||||
int ret;
|
||||
int i;
|
||||
int i_done;
|
||||
@ -864,19 +914,21 @@ static int cluster_pages_for_defrag(struct inode *inode,
|
||||
struct extent_io_tree *tree;
|
||||
gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping);
|
||||
|
||||
if (isize == 0)
|
||||
return 0;
|
||||
file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
|
||||
if (!isize || start_index > file_end)
|
||||
return 0;
|
||||
|
||||
page_cnt = min_t(u64, (u64)num_pages, (u64)file_end - start_index + 1);
|
||||
|
||||
ret = btrfs_delalloc_reserve_space(inode,
|
||||
num_pages << PAGE_CACHE_SHIFT);
|
||||
page_cnt << PAGE_CACHE_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
i_done = 0;
|
||||
tree = &BTRFS_I(inode)->io_tree;
|
||||
|
||||
/* step one, lock all the pages */
|
||||
for (i = 0; i < num_pages; i++) {
|
||||
for (i = 0; i < page_cnt; i++) {
|
||||
struct page *page;
|
||||
again:
|
||||
page = find_or_create_page(inode->i_mapping,
|
||||
@ -887,10 +939,10 @@ again:
|
||||
page_start = page_offset(page);
|
||||
page_end = page_start + PAGE_CACHE_SIZE - 1;
|
||||
while (1) {
|
||||
lock_extent(tree, page_start, page_end, GFP_NOFS);
|
||||
lock_extent(tree, page_start, page_end);
|
||||
ordered = btrfs_lookup_ordered_extent(inode,
|
||||
page_start);
|
||||
unlock_extent(tree, page_start, page_end, GFP_NOFS);
|
||||
unlock_extent(tree, page_start, page_end);
|
||||
if (!ordered)
|
||||
break;
|
||||
|
||||
@ -898,6 +950,15 @@ again:
|
||||
btrfs_start_ordered_extent(inode, ordered, 1);
|
||||
btrfs_put_ordered_extent(ordered);
|
||||
lock_page(page);
|
||||
/*
|
||||
* we unlocked the page above, so we need check if
|
||||
* it was released or not.
|
||||
*/
|
||||
if (page->mapping != inode->i_mapping) {
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PageUptodate(page)) {
|
||||
@ -911,15 +972,6 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
isize = i_size_read(inode);
|
||||
file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
|
||||
if (!isize || page->index > file_end) {
|
||||
/* whoops, we blew past eof, skip this page */
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
break;
|
||||
}
|
||||
|
||||
if (page->mapping != inode->i_mapping) {
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
@ -946,19 +998,18 @@ again:
|
||||
page_end = page_offset(pages[i_done - 1]) + PAGE_CACHE_SIZE;
|
||||
|
||||
lock_extent_bits(&BTRFS_I(inode)->io_tree,
|
||||
page_start, page_end - 1, 0, &cached_state,
|
||||
GFP_NOFS);
|
||||
page_start, page_end - 1, 0, &cached_state);
|
||||
clear_extent_bit(&BTRFS_I(inode)->io_tree, page_start,
|
||||
page_end - 1, EXTENT_DIRTY | EXTENT_DELALLOC |
|
||||
EXTENT_DO_ACCOUNTING, 0, 0, &cached_state,
|
||||
GFP_NOFS);
|
||||
|
||||
if (i_done != num_pages) {
|
||||
if (i_done != page_cnt) {
|
||||
spin_lock(&BTRFS_I(inode)->lock);
|
||||
BTRFS_I(inode)->outstanding_extents++;
|
||||
spin_unlock(&BTRFS_I(inode)->lock);
|
||||
btrfs_delalloc_release_space(inode,
|
||||
(num_pages - i_done) << PAGE_CACHE_SHIFT);
|
||||
(page_cnt - i_done) << PAGE_CACHE_SHIFT);
|
||||
}
|
||||
|
||||
|
||||
@ -983,7 +1034,7 @@ out:
|
||||
unlock_page(pages[i]);
|
||||
page_cache_release(pages[i]);
|
||||
}
|
||||
btrfs_delalloc_release_space(inode, num_pages << PAGE_CACHE_SHIFT);
|
||||
btrfs_delalloc_release_space(inode, page_cnt << PAGE_CACHE_SHIFT);
|
||||
return ret;
|
||||
|
||||
}
|
||||
@ -1089,12 +1140,9 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
|
||||
if (!(inode->i_sb->s_flags & MS_ACTIVE))
|
||||
break;
|
||||
|
||||
if (!newer_than &&
|
||||
!should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,
|
||||
PAGE_CACHE_SIZE,
|
||||
extent_thresh,
|
||||
&last_len, &skip,
|
||||
&defrag_end)) {
|
||||
if (!should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,
|
||||
PAGE_CACHE_SIZE, extent_thresh,
|
||||
&last_len, &skip, &defrag_end)) {
|
||||
unsigned long next;
|
||||
/*
|
||||
* the should_defrag function tells us how much to skip
|
||||
@ -1123,17 +1171,24 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
|
||||
ra_index += max_cluster;
|
||||
}
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
ret = cluster_pages_for_defrag(inode, pages, i, cluster);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
goto out_ra;
|
||||
}
|
||||
|
||||
defrag_count += ret;
|
||||
balance_dirty_pages_ratelimited_nr(inode->i_mapping, ret);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
if (newer_than) {
|
||||
if (newer_off == (u64)-1)
|
||||
break;
|
||||
|
||||
if (ret > 0)
|
||||
i += ret;
|
||||
|
||||
newer_off = max(newer_off + 1,
|
||||
(u64)i << PAGE_CACHE_SHIFT);
|
||||
|
||||
@ -1966,7 +2021,11 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
||||
dest->root_key.objectid,
|
||||
dentry->d_name.name,
|
||||
dentry->d_name.len);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
err = ret;
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out_end_trans;
|
||||
}
|
||||
|
||||
btrfs_record_root_in_trans(trans, dest);
|
||||
|
||||
@ -1979,11 +2038,16 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
||||
ret = btrfs_insert_orphan_item(trans,
|
||||
root->fs_info->tree_root,
|
||||
dest->root_key.objectid);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
err = ret;
|
||||
goto out_end_trans;
|
||||
}
|
||||
}
|
||||
|
||||
out_end_trans:
|
||||
ret = btrfs_end_transaction(trans, root);
|
||||
BUG_ON(ret);
|
||||
if (ret && !err)
|
||||
err = ret;
|
||||
inode->i_flags |= S_DEAD;
|
||||
out_up_write:
|
||||
up_write(&root->fs_info->subvol_sem);
|
||||
@ -2326,13 +2390,13 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||
another, and lock file content */
|
||||
while (1) {
|
||||
struct btrfs_ordered_extent *ordered;
|
||||
lock_extent(&BTRFS_I(src)->io_tree, off, off+len, GFP_NOFS);
|
||||
lock_extent(&BTRFS_I(src)->io_tree, off, off+len);
|
||||
ordered = btrfs_lookup_first_ordered_extent(src, off+len);
|
||||
if (!ordered &&
|
||||
!test_range_bit(&BTRFS_I(src)->io_tree, off, off+len,
|
||||
EXTENT_DELALLOC, 0, NULL))
|
||||
break;
|
||||
unlock_extent(&BTRFS_I(src)->io_tree, off, off+len, GFP_NOFS);
|
||||
unlock_extent(&BTRFS_I(src)->io_tree, off, off+len);
|
||||
if (ordered)
|
||||
btrfs_put_ordered_extent(ordered);
|
||||
btrfs_wait_ordered_range(src, off, len);
|
||||
@ -2447,11 +2511,21 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||
new_key.offset,
|
||||
new_key.offset + datal,
|
||||
&hint_byte, 1);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root,
|
||||
ret);
|
||||
btrfs_end_transaction(trans, root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = btrfs_insert_empty_item(trans, root, path,
|
||||
&new_key, size);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root,
|
||||
ret);
|
||||
btrfs_end_transaction(trans, root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
leaf = path->nodes[0];
|
||||
slot = path->slots[0];
|
||||
@ -2478,7 +2552,15 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||
btrfs_ino(inode),
|
||||
new_key.offset - datao,
|
||||
0);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans,
|
||||
root,
|
||||
ret);
|
||||
btrfs_end_transaction(trans,
|
||||
root);
|
||||
goto out;
|
||||
|
||||
}
|
||||
}
|
||||
} else if (type == BTRFS_FILE_EXTENT_INLINE) {
|
||||
u64 skip = 0;
|
||||
@ -2503,11 +2585,21 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||
new_key.offset,
|
||||
new_key.offset + datal,
|
||||
&hint_byte, 1);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root,
|
||||
ret);
|
||||
btrfs_end_transaction(trans, root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = btrfs_insert_empty_item(trans, root, path,
|
||||
&new_key, size);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root,
|
||||
ret);
|
||||
btrfs_end_transaction(trans, root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
u32 start =
|
||||
@ -2541,8 +2633,12 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||
btrfs_i_size_write(inode, endoff);
|
||||
|
||||
ret = btrfs_update_inode(trans, root, inode);
|
||||
BUG_ON(ret);
|
||||
btrfs_end_transaction(trans, root);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
btrfs_end_transaction(trans, root);
|
||||
goto out;
|
||||
}
|
||||
ret = btrfs_end_transaction(trans, root);
|
||||
}
|
||||
next:
|
||||
btrfs_release_path(path);
|
||||
@ -2551,7 +2647,7 @@ next:
|
||||
ret = 0;
|
||||
out:
|
||||
btrfs_release_path(path);
|
||||
unlock_extent(&BTRFS_I(src)->io_tree, off, off+len, GFP_NOFS);
|
||||
unlock_extent(&BTRFS_I(src)->io_tree, off, off+len);
|
||||
out_unlock:
|
||||
mutex_unlock(&src->i_mutex);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
@ -3066,8 +3162,8 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_root *root,
|
||||
goto out;
|
||||
|
||||
extent_item_pos = loi->logical - key.objectid;
|
||||
ret = iterate_extent_inodes(root->fs_info, path, key.objectid,
|
||||
extent_item_pos, build_ino_list,
|
||||
ret = iterate_extent_inodes(root->fs_info, key.objectid,
|
||||
extent_item_pos, 0, build_ino_list,
|
||||
inodes);
|
||||
|
||||
if (ret < 0)
|
||||
|
@ -208,7 +208,7 @@ void btrfs_tree_read_unlock_blocking(struct extent_buffer *eb)
|
||||
* take a spinning write lock. This will wait for both
|
||||
* blocking readers or writers
|
||||
*/
|
||||
int btrfs_tree_lock(struct extent_buffer *eb)
|
||||
void btrfs_tree_lock(struct extent_buffer *eb)
|
||||
{
|
||||
again:
|
||||
wait_event(eb->read_lock_wq, atomic_read(&eb->blocking_readers) == 0);
|
||||
@ -230,13 +230,12 @@ again:
|
||||
atomic_inc(&eb->spinning_writers);
|
||||
atomic_inc(&eb->write_locks);
|
||||
eb->lock_owner = current->pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* drop a spinning or a blocking write lock.
|
||||
*/
|
||||
int btrfs_tree_unlock(struct extent_buffer *eb)
|
||||
void btrfs_tree_unlock(struct extent_buffer *eb)
|
||||
{
|
||||
int blockers = atomic_read(&eb->blocking_writers);
|
||||
|
||||
@ -255,7 +254,6 @@ int btrfs_tree_unlock(struct extent_buffer *eb)
|
||||
atomic_dec(&eb->spinning_writers);
|
||||
write_unlock(&eb->lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void btrfs_assert_tree_locked(struct extent_buffer *eb)
|
||||
|
@ -24,8 +24,8 @@
|
||||
#define BTRFS_WRITE_LOCK_BLOCKING 3
|
||||
#define BTRFS_READ_LOCK_BLOCKING 4
|
||||
|
||||
int btrfs_tree_lock(struct extent_buffer *eb);
|
||||
int btrfs_tree_unlock(struct extent_buffer *eb);
|
||||
void btrfs_tree_lock(struct extent_buffer *eb);
|
||||
void btrfs_tree_unlock(struct extent_buffer *eb);
|
||||
int btrfs_try_spin_lock(struct extent_buffer *eb);
|
||||
|
||||
void btrfs_tree_read_lock(struct extent_buffer *eb);
|
||||
|
@ -59,6 +59,14 @@ static struct rb_node *tree_insert(struct rb_root *root, u64 file_offset,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ordered_data_tree_panic(struct inode *inode, int errno,
|
||||
u64 offset)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
||||
btrfs_panic(fs_info, errno, "Inconsistency in ordered tree at offset "
|
||||
"%llu\n", (unsigned long long)offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* look for a given offset in the tree, and if it can't be found return the
|
||||
* first lesser offset
|
||||
@ -207,7 +215,8 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
|
||||
spin_lock(&tree->lock);
|
||||
node = tree_insert(&tree->tree, file_offset,
|
||||
&entry->rb_node);
|
||||
BUG_ON(node);
|
||||
if (node)
|
||||
ordered_data_tree_panic(inode, -EEXIST, file_offset);
|
||||
spin_unlock(&tree->lock);
|
||||
|
||||
spin_lock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
|
||||
@ -215,7 +224,6 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
|
||||
&BTRFS_I(inode)->root->fs_info->ordered_extents);
|
||||
spin_unlock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
|
||||
|
||||
BUG_ON(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -249,9 +257,9 @@ int btrfs_add_ordered_extent_compress(struct inode *inode, u64 file_offset,
|
||||
* when an ordered extent is finished. If the list covers more than one
|
||||
* ordered extent, it is split across multiples.
|
||||
*/
|
||||
int btrfs_add_ordered_sum(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry,
|
||||
struct btrfs_ordered_sum *sum)
|
||||
void btrfs_add_ordered_sum(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry,
|
||||
struct btrfs_ordered_sum *sum)
|
||||
{
|
||||
struct btrfs_ordered_inode_tree *tree;
|
||||
|
||||
@ -259,7 +267,6 @@ int btrfs_add_ordered_sum(struct inode *inode,
|
||||
spin_lock(&tree->lock);
|
||||
list_add_tail(&sum->list, &entry->list);
|
||||
spin_unlock(&tree->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -384,7 +391,7 @@ out:
|
||||
* used to drop a reference on an ordered extent. This will free
|
||||
* the extent if the last reference is dropped
|
||||
*/
|
||||
int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
|
||||
void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
|
||||
{
|
||||
struct list_head *cur;
|
||||
struct btrfs_ordered_sum *sum;
|
||||
@ -400,7 +407,6 @@ int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
|
||||
}
|
||||
kfree(entry);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -408,8 +414,8 @@ int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
|
||||
* and you must wake_up entry->wait. You must hold the tree lock
|
||||
* while you call this function.
|
||||
*/
|
||||
static int __btrfs_remove_ordered_extent(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry)
|
||||
static void __btrfs_remove_ordered_extent(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry)
|
||||
{
|
||||
struct btrfs_ordered_inode_tree *tree;
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
@ -436,35 +442,30 @@ static int __btrfs_remove_ordered_extent(struct inode *inode,
|
||||
list_del_init(&BTRFS_I(inode)->ordered_operations);
|
||||
}
|
||||
spin_unlock(&root->fs_info->ordered_extent_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* remove an ordered extent from the tree. No references are dropped
|
||||
* but any waiters are woken.
|
||||
*/
|
||||
int btrfs_remove_ordered_extent(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry)
|
||||
void btrfs_remove_ordered_extent(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry)
|
||||
{
|
||||
struct btrfs_ordered_inode_tree *tree;
|
||||
int ret;
|
||||
|
||||
tree = &BTRFS_I(inode)->ordered_tree;
|
||||
spin_lock(&tree->lock);
|
||||
ret = __btrfs_remove_ordered_extent(inode, entry);
|
||||
__btrfs_remove_ordered_extent(inode, entry);
|
||||
spin_unlock(&tree->lock);
|
||||
wake_up(&entry->wait);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for all the ordered extents in a root. This is done when balancing
|
||||
* space between drives.
|
||||
*/
|
||||
int btrfs_wait_ordered_extents(struct btrfs_root *root,
|
||||
int nocow_only, int delay_iput)
|
||||
void btrfs_wait_ordered_extents(struct btrfs_root *root,
|
||||
int nocow_only, int delay_iput)
|
||||
{
|
||||
struct list_head splice;
|
||||
struct list_head *cur;
|
||||
@ -512,7 +513,6 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root,
|
||||
spin_lock(&root->fs_info->ordered_extent_lock);
|
||||
}
|
||||
spin_unlock(&root->fs_info->ordered_extent_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -525,7 +525,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root,
|
||||
* extra check to make sure the ordered operation list really is empty
|
||||
* before we return
|
||||
*/
|
||||
int btrfs_run_ordered_operations(struct btrfs_root *root, int wait)
|
||||
void btrfs_run_ordered_operations(struct btrfs_root *root, int wait)
|
||||
{
|
||||
struct btrfs_inode *btrfs_inode;
|
||||
struct inode *inode;
|
||||
@ -573,8 +573,6 @@ again:
|
||||
|
||||
spin_unlock(&root->fs_info->ordered_extent_lock);
|
||||
mutex_unlock(&root->fs_info->ordered_operations_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -609,7 +607,7 @@ void btrfs_start_ordered_extent(struct inode *inode,
|
||||
/*
|
||||
* Used to wait on ordered extents across a large range of bytes.
|
||||
*/
|
||||
int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
|
||||
void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
|
||||
{
|
||||
u64 end;
|
||||
u64 orig_end;
|
||||
@ -664,7 +662,6 @@ again:
|
||||
schedule_timeout(1);
|
||||
goto again;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -948,9 +945,8 @@ out:
|
||||
* If trans is not null, we'll do a friendly check for a transaction that
|
||||
* is already flushing things and force the IO down ourselves.
|
||||
*/
|
||||
int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct inode *inode)
|
||||
void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct inode *inode)
|
||||
{
|
||||
u64 last_mod;
|
||||
|
||||
@ -961,7 +957,7 @@ int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
|
||||
* commit, we can safely return without doing anything
|
||||
*/
|
||||
if (last_mod < root->fs_info->last_trans_committed)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
/*
|
||||
* the transaction is already committing. Just start the IO and
|
||||
@ -969,7 +965,7 @@ int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
|
||||
*/
|
||||
if (trans && root->fs_info->running_transaction->blocked) {
|
||||
btrfs_wait_ordered_range(inode, 0, (u64)-1);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&root->fs_info->ordered_extent_lock);
|
||||
@ -978,6 +974,4 @@ int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
|
||||
&root->fs_info->ordered_operations);
|
||||
}
|
||||
spin_unlock(&root->fs_info->ordered_extent_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -138,8 +138,8 @@ btrfs_ordered_inode_tree_init(struct btrfs_ordered_inode_tree *t)
|
||||
t->last = NULL;
|
||||
}
|
||||
|
||||
int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry);
|
||||
int btrfs_remove_ordered_extent(struct inode *inode,
|
||||
void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry);
|
||||
void btrfs_remove_ordered_extent(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry);
|
||||
int btrfs_dec_test_ordered_pending(struct inode *inode,
|
||||
struct btrfs_ordered_extent **cached,
|
||||
@ -154,14 +154,14 @@ int btrfs_add_ordered_extent_dio(struct inode *inode, u64 file_offset,
|
||||
int btrfs_add_ordered_extent_compress(struct inode *inode, u64 file_offset,
|
||||
u64 start, u64 len, u64 disk_len,
|
||||
int type, int compress_type);
|
||||
int btrfs_add_ordered_sum(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry,
|
||||
struct btrfs_ordered_sum *sum);
|
||||
void btrfs_add_ordered_sum(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry,
|
||||
struct btrfs_ordered_sum *sum);
|
||||
struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode,
|
||||
u64 file_offset);
|
||||
void btrfs_start_ordered_extent(struct inode *inode,
|
||||
struct btrfs_ordered_extent *entry, int wait);
|
||||
int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len);
|
||||
void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len);
|
||||
struct btrfs_ordered_extent *
|
||||
btrfs_lookup_first_ordered_extent(struct inode * inode, u64 file_offset);
|
||||
struct btrfs_ordered_extent *btrfs_lookup_ordered_range(struct inode *inode,
|
||||
@ -170,10 +170,10 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_range(struct inode *inode,
|
||||
int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
|
||||
struct btrfs_ordered_extent *ordered);
|
||||
int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr, u32 *sum);
|
||||
int btrfs_run_ordered_operations(struct btrfs_root *root, int wait);
|
||||
int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct inode *inode);
|
||||
int btrfs_wait_ordered_extents(struct btrfs_root *root,
|
||||
int nocow_only, int delay_iput);
|
||||
void btrfs_run_ordered_operations(struct btrfs_root *root, int wait);
|
||||
void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct inode *inode);
|
||||
void btrfs_wait_ordered_extents(struct btrfs_root *root,
|
||||
int nocow_only, int delay_iput);
|
||||
#endif
|
||||
|
@ -58,7 +58,7 @@ int btrfs_del_orphan_item(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret) {
|
||||
if (ret) { /* JDM: Really? */
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
@ -54,7 +54,6 @@
|
||||
* than the 2 started one after another.
|
||||
*/
|
||||
|
||||
#define MAX_MIRRORS 2
|
||||
#define MAX_IN_FLIGHT 6
|
||||
|
||||
struct reada_extctl {
|
||||
@ -71,7 +70,7 @@ struct reada_extent {
|
||||
struct list_head extctl;
|
||||
struct kref refcnt;
|
||||
spinlock_t lock;
|
||||
struct reada_zone *zones[MAX_MIRRORS];
|
||||
struct reada_zone *zones[BTRFS_MAX_MIRRORS];
|
||||
int nzones;
|
||||
struct btrfs_device *scheduled_for;
|
||||
};
|
||||
@ -84,7 +83,8 @@ struct reada_zone {
|
||||
spinlock_t lock;
|
||||
int locked;
|
||||
struct btrfs_device *device;
|
||||
struct btrfs_device *devs[MAX_MIRRORS]; /* full list, incl self */
|
||||
struct btrfs_device *devs[BTRFS_MAX_MIRRORS]; /* full list, incl
|
||||
* self */
|
||||
int ndevs;
|
||||
struct kref refcnt;
|
||||
};
|
||||
@ -365,9 +365,9 @@ again:
|
||||
if (ret || !bbio || length < blocksize)
|
||||
goto error;
|
||||
|
||||
if (bbio->num_stripes > MAX_MIRRORS) {
|
||||
if (bbio->num_stripes > BTRFS_MAX_MIRRORS) {
|
||||
printk(KERN_ERR "btrfs readahead: more than %d copies not "
|
||||
"supported", MAX_MIRRORS);
|
||||
"supported", BTRFS_MAX_MIRRORS);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -326,6 +326,19 @@ static struct rb_node *tree_search(struct rb_root *root, u64 bytenr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void backref_tree_panic(struct rb_node *rb_node, int errno,
|
||||
u64 bytenr)
|
||||
{
|
||||
|
||||
struct btrfs_fs_info *fs_info = NULL;
|
||||
struct backref_node *bnode = rb_entry(rb_node, struct backref_node,
|
||||
rb_node);
|
||||
if (bnode->root)
|
||||
fs_info = bnode->root->fs_info;
|
||||
btrfs_panic(fs_info, errno, "Inconsistency in backref cache "
|
||||
"found at offset %llu\n", (unsigned long long)bytenr);
|
||||
}
|
||||
|
||||
/*
|
||||
* walk up backref nodes until reach node presents tree root
|
||||
*/
|
||||
@ -452,7 +465,8 @@ static void update_backref_node(struct backref_cache *cache,
|
||||
rb_erase(&node->rb_node, &cache->rb_root);
|
||||
node->bytenr = bytenr;
|
||||
rb_node = tree_insert(&cache->rb_root, node->bytenr, &node->rb_node);
|
||||
BUG_ON(rb_node);
|
||||
if (rb_node)
|
||||
backref_tree_panic(rb_node, -EEXIST, bytenr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -999,7 +1013,8 @@ next:
|
||||
if (!cowonly) {
|
||||
rb_node = tree_insert(&cache->rb_root, node->bytenr,
|
||||
&node->rb_node);
|
||||
BUG_ON(rb_node);
|
||||
if (rb_node)
|
||||
backref_tree_panic(rb_node, -EEXIST, node->bytenr);
|
||||
list_add_tail(&node->lower, &cache->leaves);
|
||||
}
|
||||
|
||||
@ -1034,7 +1049,9 @@ next:
|
||||
if (!cowonly) {
|
||||
rb_node = tree_insert(&cache->rb_root, upper->bytenr,
|
||||
&upper->rb_node);
|
||||
BUG_ON(rb_node);
|
||||
if (rb_node)
|
||||
backref_tree_panic(rb_node, -EEXIST,
|
||||
upper->bytenr);
|
||||
}
|
||||
|
||||
list_add_tail(&edge->list[UPPER], &upper->lower);
|
||||
@ -1180,7 +1197,8 @@ static int clone_backref_node(struct btrfs_trans_handle *trans,
|
||||
|
||||
rb_node = tree_insert(&cache->rb_root, new_node->bytenr,
|
||||
&new_node->rb_node);
|
||||
BUG_ON(rb_node);
|
||||
if (rb_node)
|
||||
backref_tree_panic(rb_node, -EEXIST, new_node->bytenr);
|
||||
|
||||
if (!new_node->lowest) {
|
||||
list_for_each_entry(new_edge, &new_node->lower, list[UPPER]) {
|
||||
@ -1203,14 +1221,15 @@ fail:
|
||||
/*
|
||||
* helper to add 'address of tree root -> reloc tree' mapping
|
||||
*/
|
||||
static int __add_reloc_root(struct btrfs_root *root)
|
||||
static int __must_check __add_reloc_root(struct btrfs_root *root)
|
||||
{
|
||||
struct rb_node *rb_node;
|
||||
struct mapping_node *node;
|
||||
struct reloc_control *rc = root->fs_info->reloc_ctl;
|
||||
|
||||
node = kmalloc(sizeof(*node), GFP_NOFS);
|
||||
BUG_ON(!node);
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
|
||||
node->bytenr = root->node->start;
|
||||
node->data = root;
|
||||
@ -1219,7 +1238,12 @@ static int __add_reloc_root(struct btrfs_root *root)
|
||||
rb_node = tree_insert(&rc->reloc_root_tree.rb_root,
|
||||
node->bytenr, &node->rb_node);
|
||||
spin_unlock(&rc->reloc_root_tree.lock);
|
||||
BUG_ON(rb_node);
|
||||
if (rb_node) {
|
||||
kfree(node);
|
||||
btrfs_panic(root->fs_info, -EEXIST, "Duplicate root found "
|
||||
"for start=%llu while inserting into relocation "
|
||||
"tree\n");
|
||||
}
|
||||
|
||||
list_add_tail(&root->root_list, &rc->reloc_roots);
|
||||
return 0;
|
||||
@ -1252,7 +1276,8 @@ static int __update_reloc_root(struct btrfs_root *root, int del)
|
||||
rb_node = tree_insert(&rc->reloc_root_tree.rb_root,
|
||||
node->bytenr, &node->rb_node);
|
||||
spin_unlock(&rc->reloc_root_tree.lock);
|
||||
BUG_ON(rb_node);
|
||||
if (rb_node)
|
||||
backref_tree_panic(rb_node, -EEXIST, node->bytenr);
|
||||
} else {
|
||||
list_del_init(&root->root_list);
|
||||
kfree(node);
|
||||
@ -1334,6 +1359,7 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *reloc_root;
|
||||
struct reloc_control *rc = root->fs_info->reloc_ctl;
|
||||
int clear_rsv = 0;
|
||||
int ret;
|
||||
|
||||
if (root->reloc_root) {
|
||||
reloc_root = root->reloc_root;
|
||||
@ -1353,7 +1379,8 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
|
||||
if (clear_rsv)
|
||||
trans->block_rsv = NULL;
|
||||
|
||||
__add_reloc_root(reloc_root);
|
||||
ret = __add_reloc_root(reloc_root);
|
||||
BUG_ON(ret < 0);
|
||||
root->reloc_root = reloc_root;
|
||||
return 0;
|
||||
}
|
||||
@ -1577,15 +1604,14 @@ int replace_file_extents(struct btrfs_trans_handle *trans,
|
||||
WARN_ON(!IS_ALIGNED(end, root->sectorsize));
|
||||
end--;
|
||||
ret = try_lock_extent(&BTRFS_I(inode)->io_tree,
|
||||
key.offset, end,
|
||||
GFP_NOFS);
|
||||
key.offset, end);
|
||||
if (!ret)
|
||||
continue;
|
||||
|
||||
btrfs_drop_extent_cache(inode, key.offset, end,
|
||||
1);
|
||||
unlock_extent(&BTRFS_I(inode)->io_tree,
|
||||
key.offset, end, GFP_NOFS);
|
||||
key.offset, end);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1956,9 +1982,9 @@ static int invalidate_extent_cache(struct btrfs_root *root,
|
||||
}
|
||||
|
||||
/* the lock_extent waits for readpage to complete */
|
||||
lock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
|
||||
lock_extent(&BTRFS_I(inode)->io_tree, start, end);
|
||||
btrfs_drop_extent_cache(inode, start, end, 1);
|
||||
unlock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
|
||||
unlock_extent(&BTRFS_I(inode)->io_tree, start, end);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -2246,7 +2272,8 @@ again:
|
||||
} else {
|
||||
list_del_init(&reloc_root->root_list);
|
||||
}
|
||||
btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1);
|
||||
ret = btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1);
|
||||
BUG_ON(ret < 0);
|
||||
}
|
||||
|
||||
if (found) {
|
||||
@ -2862,12 +2889,12 @@ int prealloc_file_extent_cluster(struct inode *inode,
|
||||
else
|
||||
end = cluster->end - offset;
|
||||
|
||||
lock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
|
||||
lock_extent(&BTRFS_I(inode)->io_tree, start, end);
|
||||
num_bytes = end + 1 - start;
|
||||
ret = btrfs_prealloc_file_range(inode, 0, start,
|
||||
num_bytes, num_bytes,
|
||||
end + 1, &alloc_hint);
|
||||
unlock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
|
||||
unlock_extent(&BTRFS_I(inode)->io_tree, start, end);
|
||||
if (ret)
|
||||
break;
|
||||
nr++;
|
||||
@ -2899,7 +2926,7 @@ int setup_extent_mapping(struct inode *inode, u64 start, u64 end,
|
||||
em->bdev = root->fs_info->fs_devices->latest_bdev;
|
||||
set_bit(EXTENT_FLAG_PINNED, &em->flags);
|
||||
|
||||
lock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
|
||||
lock_extent(&BTRFS_I(inode)->io_tree, start, end);
|
||||
while (1) {
|
||||
write_lock(&em_tree->lock);
|
||||
ret = add_extent_mapping(em_tree, em);
|
||||
@ -2910,7 +2937,7 @@ int setup_extent_mapping(struct inode *inode, u64 start, u64 end,
|
||||
}
|
||||
btrfs_drop_extent_cache(inode, start, end, 0);
|
||||
}
|
||||
unlock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
|
||||
unlock_extent(&BTRFS_I(inode)->io_tree, start, end);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2990,8 +3017,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
|
||||
page_start = (u64)page->index << PAGE_CACHE_SHIFT;
|
||||
page_end = page_start + PAGE_CACHE_SIZE - 1;
|
||||
|
||||
lock_extent(&BTRFS_I(inode)->io_tree,
|
||||
page_start, page_end, GFP_NOFS);
|
||||
lock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end);
|
||||
|
||||
set_page_extent_mapped(page);
|
||||
|
||||
@ -3007,7 +3033,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
|
||||
set_page_dirty(page);
|
||||
|
||||
unlock_extent(&BTRFS_I(inode)->io_tree,
|
||||
page_start, page_end, GFP_NOFS);
|
||||
page_start, page_end);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
|
||||
@ -3154,7 +3180,8 @@ static int add_tree_block(struct reloc_control *rc,
|
||||
block->key_ready = 0;
|
||||
|
||||
rb_node = tree_insert(blocks, block->bytenr, &block->rb_node);
|
||||
BUG_ON(rb_node);
|
||||
if (rb_node)
|
||||
backref_tree_panic(rb_node, -EEXIST, block->bytenr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3426,7 +3453,9 @@ static int find_data_references(struct reloc_control *rc,
|
||||
block->key_ready = 1;
|
||||
rb_node = tree_insert(blocks, block->bytenr,
|
||||
&block->rb_node);
|
||||
BUG_ON(rb_node);
|
||||
if (rb_node)
|
||||
backref_tree_panic(rb_node, -EEXIST,
|
||||
block->bytenr);
|
||||
}
|
||||
if (counted)
|
||||
added = 1;
|
||||
@ -4073,10 +4102,11 @@ out:
|
||||
static noinline_for_stack int mark_garbage_root(struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_trans_handle *trans;
|
||||
int ret;
|
||||
int ret, err;
|
||||
|
||||
trans = btrfs_start_transaction(root->fs_info->tree_root, 0);
|
||||
BUG_ON(IS_ERR(trans));
|
||||
if (IS_ERR(trans))
|
||||
return PTR_ERR(trans);
|
||||
|
||||
memset(&root->root_item.drop_progress, 0,
|
||||
sizeof(root->root_item.drop_progress));
|
||||
@ -4084,11 +4114,11 @@ static noinline_for_stack int mark_garbage_root(struct btrfs_root *root)
|
||||
btrfs_set_root_refs(&root->root_item, 0);
|
||||
ret = btrfs_update_root(trans, root->fs_info->tree_root,
|
||||
&root->root_key, &root->root_item);
|
||||
BUG_ON(ret);
|
||||
|
||||
ret = btrfs_end_transaction(trans, root->fs_info->tree_root);
|
||||
BUG_ON(ret);
|
||||
return 0;
|
||||
err = btrfs_end_transaction(trans, root->fs_info->tree_root);
|
||||
if (err)
|
||||
return err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4156,7 +4186,11 @@ int btrfs_recover_relocation(struct btrfs_root *root)
|
||||
err = ret;
|
||||
goto out;
|
||||
}
|
||||
mark_garbage_root(reloc_root);
|
||||
ret = mark_garbage_root(reloc_root);
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4202,13 +4236,19 @@ int btrfs_recover_relocation(struct btrfs_root *root)
|
||||
|
||||
fs_root = read_fs_root(root->fs_info,
|
||||
reloc_root->root_key.offset);
|
||||
BUG_ON(IS_ERR(fs_root));
|
||||
if (IS_ERR(fs_root)) {
|
||||
err = PTR_ERR(fs_root);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
__add_reloc_root(reloc_root);
|
||||
err = __add_reloc_root(reloc_root);
|
||||
BUG_ON(err < 0); /* -ENOMEM or logic error */
|
||||
fs_root->reloc_root = reloc_root;
|
||||
}
|
||||
|
||||
btrfs_commit_transaction(trans, rc->extent_root);
|
||||
err = btrfs_commit_transaction(trans, rc->extent_root);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
merge_reloc_roots(rc);
|
||||
|
||||
@ -4218,7 +4258,7 @@ int btrfs_recover_relocation(struct btrfs_root *root)
|
||||
if (IS_ERR(trans))
|
||||
err = PTR_ERR(trans);
|
||||
else
|
||||
btrfs_commit_transaction(trans, rc->extent_root);
|
||||
err = btrfs_commit_transaction(trans, rc->extent_root);
|
||||
out_free:
|
||||
kfree(rc);
|
||||
out:
|
||||
@ -4267,6 +4307,8 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
|
||||
disk_bytenr = file_pos + BTRFS_I(inode)->index_cnt;
|
||||
ret = btrfs_lookup_csums_range(root->fs_info->csum_root, disk_bytenr,
|
||||
disk_bytenr + len - 1, &list, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
while (!list_empty(&list)) {
|
||||
sums = list_entry(list.next, struct btrfs_ordered_sum, list);
|
||||
@ -4284,6 +4326,7 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
|
||||
|
||||
btrfs_add_ordered_sum(inode, ordered, sums);
|
||||
}
|
||||
out:
|
||||
btrfs_put_ordered_extent(ordered);
|
||||
return ret;
|
||||
}
|
||||
@ -4380,7 +4423,7 @@ void btrfs_reloc_pre_snapshot(struct btrfs_trans_handle *trans,
|
||||
* called after snapshot is created. migrate block reservation
|
||||
* and create reloc root for the newly created snapshot
|
||||
*/
|
||||
void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
|
||||
int btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_pending_snapshot *pending)
|
||||
{
|
||||
struct btrfs_root *root = pending->root;
|
||||
@ -4390,7 +4433,7 @@ void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
|
||||
int ret;
|
||||
|
||||
if (!root->reloc_root)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
rc = root->fs_info->reloc_ctl;
|
||||
rc->merging_rsv_size += rc->nodes_relocated;
|
||||
@ -4399,18 +4442,21 @@ void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_block_rsv_migrate(&pending->block_rsv,
|
||||
rc->block_rsv,
|
||||
rc->nodes_relocated);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
new_root = pending->snap;
|
||||
reloc_root = create_reloc_root(trans, root->reloc_root,
|
||||
new_root->root_key.objectid);
|
||||
if (IS_ERR(reloc_root))
|
||||
return PTR_ERR(reloc_root);
|
||||
|
||||
__add_reloc_root(reloc_root);
|
||||
ret = __add_reloc_root(reloc_root);
|
||||
BUG_ON(ret < 0);
|
||||
new_root->reloc_root = reloc_root;
|
||||
|
||||
if (rc->create_reloc_tree) {
|
||||
if (rc->create_reloc_tree)
|
||||
ret = clone_backref_node(trans, rc, root, reloc_root);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -93,10 +93,14 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
unsigned long ptr;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
BUG_ON(!path);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
btrfs_print_leaf(root, path->nodes[0]);
|
||||
@ -116,13 +120,10 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
*root, struct btrfs_key *key, struct btrfs_root_item
|
||||
*item)
|
||||
int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct btrfs_key *key, struct btrfs_root_item *item)
|
||||
{
|
||||
int ret;
|
||||
ret = btrfs_insert_item(trans, root, key, item, sizeof(*item));
|
||||
return ret;
|
||||
return btrfs_insert_item(trans, root, key, item, sizeof(*item));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -384,6 +385,8 @@ int btrfs_find_root_ref(struct btrfs_root *tree_root,
|
||||
*
|
||||
* For a back ref the root_id is the id of the subvol or snapshot and
|
||||
* ref_id is the id of the tree referencing it.
|
||||
*
|
||||
* Will return 0, -ENOMEM, or anything from the CoW path
|
||||
*/
|
||||
int btrfs_add_root_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *tree_root,
|
||||
@ -407,7 +410,11 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans,
|
||||
again:
|
||||
ret = btrfs_insert_empty_item(trans, tree_root, path, &key,
|
||||
sizeof(*ref) + name_len);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, tree_root, ret);
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
leaf = path->nodes[0];
|
||||
ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref);
|
||||
|
1499
fs/btrfs/scrub.c
1499
fs/btrfs/scrub.c
File diff suppressed because it is too large
Load Diff
@ -44,8 +44,9 @@
|
||||
#define BTRFS_SETGET_FUNCS(name, type, member, bits) \
|
||||
u##bits btrfs_##name(struct extent_buffer *eb, type *s); \
|
||||
void btrfs_set_##name(struct extent_buffer *eb, type *s, u##bits val); \
|
||||
u##bits btrfs_##name(struct extent_buffer *eb, \
|
||||
type *s) \
|
||||
void btrfs_set_token_##name(struct extent_buffer *eb, type *s, u##bits val, struct btrfs_map_token *token); \
|
||||
u##bits btrfs_token_##name(struct extent_buffer *eb, \
|
||||
type *s, struct btrfs_map_token *token) \
|
||||
{ \
|
||||
unsigned long part_offset = (unsigned long)s; \
|
||||
unsigned long offset = part_offset + offsetof(type, member); \
|
||||
@ -54,9 +55,18 @@ u##bits btrfs_##name(struct extent_buffer *eb, \
|
||||
char *kaddr; \
|
||||
unsigned long map_start; \
|
||||
unsigned long map_len; \
|
||||
unsigned long mem_len = sizeof(((type *)0)->member); \
|
||||
u##bits res; \
|
||||
if (token && token->kaddr && token->offset <= offset && \
|
||||
token->eb == eb && \
|
||||
(token->offset + PAGE_CACHE_SIZE >= offset + mem_len)) { \
|
||||
kaddr = token->kaddr; \
|
||||
p = (type *)(kaddr + part_offset - token->offset); \
|
||||
res = le##bits##_to_cpu(p->member); \
|
||||
return res; \
|
||||
} \
|
||||
err = map_private_extent_buffer(eb, offset, \
|
||||
sizeof(((type *)0)->member), \
|
||||
mem_len, \
|
||||
&kaddr, &map_start, &map_len); \
|
||||
if (err) { \
|
||||
__le##bits leres; \
|
||||
@ -65,10 +75,15 @@ u##bits btrfs_##name(struct extent_buffer *eb, \
|
||||
} \
|
||||
p = (type *)(kaddr + part_offset - map_start); \
|
||||
res = le##bits##_to_cpu(p->member); \
|
||||
if (token) { \
|
||||
token->kaddr = kaddr; \
|
||||
token->offset = map_start; \
|
||||
token->eb = eb; \
|
||||
} \
|
||||
return res; \
|
||||
} \
|
||||
void btrfs_set_##name(struct extent_buffer *eb, \
|
||||
type *s, u##bits val) \
|
||||
void btrfs_set_token_##name(struct extent_buffer *eb, \
|
||||
type *s, u##bits val, struct btrfs_map_token *token) \
|
||||
{ \
|
||||
unsigned long part_offset = (unsigned long)s; \
|
||||
unsigned long offset = part_offset + offsetof(type, member); \
|
||||
@ -77,8 +92,17 @@ void btrfs_set_##name(struct extent_buffer *eb, \
|
||||
char *kaddr; \
|
||||
unsigned long map_start; \
|
||||
unsigned long map_len; \
|
||||
unsigned long mem_len = sizeof(((type *)0)->member); \
|
||||
if (token && token->kaddr && token->offset <= offset && \
|
||||
token->eb == eb && \
|
||||
(token->offset + PAGE_CACHE_SIZE >= offset + mem_len)) { \
|
||||
kaddr = token->kaddr; \
|
||||
p = (type *)(kaddr + part_offset - token->offset); \
|
||||
p->member = cpu_to_le##bits(val); \
|
||||
return; \
|
||||
} \
|
||||
err = map_private_extent_buffer(eb, offset, \
|
||||
sizeof(((type *)0)->member), \
|
||||
mem_len, \
|
||||
&kaddr, &map_start, &map_len); \
|
||||
if (err) { \
|
||||
__le##bits val2; \
|
||||
@ -88,7 +112,22 @@ void btrfs_set_##name(struct extent_buffer *eb, \
|
||||
} \
|
||||
p = (type *)(kaddr + part_offset - map_start); \
|
||||
p->member = cpu_to_le##bits(val); \
|
||||
}
|
||||
if (token) { \
|
||||
token->kaddr = kaddr; \
|
||||
token->offset = map_start; \
|
||||
token->eb = eb; \
|
||||
} \
|
||||
} \
|
||||
void btrfs_set_##name(struct extent_buffer *eb, \
|
||||
type *s, u##bits val) \
|
||||
{ \
|
||||
btrfs_set_token_##name(eb, s, val, NULL); \
|
||||
} \
|
||||
u##bits btrfs_##name(struct extent_buffer *eb, \
|
||||
type *s) \
|
||||
{ \
|
||||
return btrfs_token_##name(eb, s, NULL); \
|
||||
} \
|
||||
|
||||
#include "ctree.h"
|
||||
|
||||
|
190
fs/btrfs/super.c
190
fs/btrfs/super.c
@ -76,6 +76,9 @@ static const char *btrfs_decode_error(struct btrfs_fs_info *fs_info, int errno,
|
||||
case -EROFS:
|
||||
errstr = "Readonly filesystem";
|
||||
break;
|
||||
case -EEXIST:
|
||||
errstr = "Object already exists";
|
||||
break;
|
||||
default:
|
||||
if (nbuf) {
|
||||
if (snprintf(nbuf, 16, "error %d", -errno) >= 0)
|
||||
@ -116,6 +119,8 @@ static void btrfs_handle_error(struct btrfs_fs_info *fs_info)
|
||||
if (fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
|
||||
sb->s_flags |= MS_RDONLY;
|
||||
printk(KERN_INFO "btrfs is forced readonly\n");
|
||||
__btrfs_scrub_cancel(fs_info);
|
||||
// WARN_ON(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,25 +129,132 @@ static void btrfs_handle_error(struct btrfs_fs_info *fs_info)
|
||||
* invokes the approciate error response.
|
||||
*/
|
||||
void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function,
|
||||
unsigned int line, int errno)
|
||||
unsigned int line, int errno, const char *fmt, ...)
|
||||
{
|
||||
struct super_block *sb = fs_info->sb;
|
||||
char nbuf[16];
|
||||
const char *errstr;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
/*
|
||||
* Special case: if the error is EROFS, and we're already
|
||||
* under MS_RDONLY, then it is safe here.
|
||||
*/
|
||||
if (errno == -EROFS && (sb->s_flags & MS_RDONLY))
|
||||
return;
|
||||
|
||||
errstr = btrfs_decode_error(fs_info, errno, nbuf);
|
||||
if (fmt) {
|
||||
struct va_format vaf = {
|
||||
.fmt = fmt,
|
||||
.va = &args,
|
||||
};
|
||||
|
||||
printk(KERN_CRIT "BTRFS error (device %s) in %s:%d: %s (%pV)\n",
|
||||
sb->s_id, function, line, errstr, &vaf);
|
||||
} else {
|
||||
printk(KERN_CRIT "BTRFS error (device %s) in %s:%d: %s\n",
|
||||
sb->s_id, function, line, errstr);
|
||||
}
|
||||
|
||||
/* Don't go through full error handling during mount */
|
||||
if (sb->s_flags & MS_BORN) {
|
||||
save_error_info(fs_info);
|
||||
btrfs_handle_error(fs_info);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
const char *logtypes[] = {
|
||||
"emergency",
|
||||
"alert",
|
||||
"critical",
|
||||
"error",
|
||||
"warning",
|
||||
"notice",
|
||||
"info",
|
||||
"debug",
|
||||
};
|
||||
|
||||
void btrfs_printk(struct btrfs_fs_info *fs_info, const char *fmt, ...)
|
||||
{
|
||||
struct super_block *sb = fs_info->sb;
|
||||
char lvl[4];
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
const char *type = logtypes[4];
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
if (fmt[0] == '<' && isdigit(fmt[1]) && fmt[2] == '>') {
|
||||
strncpy(lvl, fmt, 3);
|
||||
fmt += 3;
|
||||
type = logtypes[fmt[1] - '0'];
|
||||
} else
|
||||
*lvl = '\0';
|
||||
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
printk("%sBTRFS %s (device %s): %pV", lvl, type, sb->s_id, &vaf);
|
||||
}
|
||||
|
||||
/*
|
||||
* We only mark the transaction aborted and then set the file system read-only.
|
||||
* This will prevent new transactions from starting or trying to join this
|
||||
* one.
|
||||
*
|
||||
* This means that error recovery at the call site is limited to freeing
|
||||
* any local memory allocations and passing the error code up without
|
||||
* further cleanup. The transaction should complete as it normally would
|
||||
* in the call path but will return -EIO.
|
||||
*
|
||||
* We'll complete the cleanup in btrfs_end_transaction and
|
||||
* btrfs_commit_transaction.
|
||||
*/
|
||||
void __btrfs_abort_transaction(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, const char *function,
|
||||
unsigned int line, int errno)
|
||||
{
|
||||
WARN_ONCE(1, KERN_DEBUG "btrfs: Transaction aborted");
|
||||
trans->aborted = errno;
|
||||
/* Nothing used. The other threads that have joined this
|
||||
* transaction may be able to continue. */
|
||||
if (!trans->blocks_used) {
|
||||
btrfs_printk(root->fs_info, "Aborting unused transaction.\n");
|
||||
return;
|
||||
}
|
||||
trans->transaction->aborted = errno;
|
||||
__btrfs_std_error(root->fs_info, function, line, errno, NULL);
|
||||
}
|
||||
/*
|
||||
* __btrfs_panic decodes unexpected, fatal errors from the caller,
|
||||
* issues an alert, and either panics or BUGs, depending on mount options.
|
||||
*/
|
||||
void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function,
|
||||
unsigned int line, int errno, const char *fmt, ...)
|
||||
{
|
||||
char nbuf[16];
|
||||
char *s_id = "<unknown>";
|
||||
const char *errstr;
|
||||
struct va_format vaf = { .fmt = fmt };
|
||||
va_list args;
|
||||
|
||||
if (fs_info)
|
||||
s_id = fs_info->sb->s_id;
|
||||
|
||||
va_start(args, fmt);
|
||||
vaf.va = &args;
|
||||
|
||||
errstr = btrfs_decode_error(fs_info, errno, nbuf);
|
||||
printk(KERN_CRIT "BTRFS error (device %s) in %s:%d: %s\n",
|
||||
sb->s_id, function, line, errstr);
|
||||
save_error_info(fs_info);
|
||||
if (fs_info->mount_opt & BTRFS_MOUNT_PANIC_ON_FATAL_ERROR)
|
||||
panic(KERN_CRIT "BTRFS panic (device %s) in %s:%d: %pV (%s)\n",
|
||||
s_id, function, line, &vaf, errstr);
|
||||
|
||||
btrfs_handle_error(fs_info);
|
||||
printk(KERN_CRIT "BTRFS panic (device %s) in %s:%d: %pV (%s)\n",
|
||||
s_id, function, line, &vaf, errstr);
|
||||
va_end(args);
|
||||
/* Caller calls BUG() */
|
||||
}
|
||||
|
||||
static void btrfs_put_super(struct super_block *sb)
|
||||
@ -166,7 +278,7 @@ enum {
|
||||
Opt_enospc_debug, Opt_subvolrootid, Opt_defrag, Opt_inode_cache,
|
||||
Opt_no_space_cache, Opt_recovery, Opt_skip_balance,
|
||||
Opt_check_integrity, Opt_check_integrity_including_extent_data,
|
||||
Opt_check_integrity_print_mask,
|
||||
Opt_check_integrity_print_mask, Opt_fatal_errors,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
@ -206,12 +318,14 @@ static match_table_t tokens = {
|
||||
{Opt_check_integrity, "check_int"},
|
||||
{Opt_check_integrity_including_extent_data, "check_int_data"},
|
||||
{Opt_check_integrity_print_mask, "check_int_print_mask=%d"},
|
||||
{Opt_fatal_errors, "fatal_errors=%s"},
|
||||
{Opt_err, NULL},
|
||||
};
|
||||
|
||||
/*
|
||||
* Regular mount options parser. Everything that is needed only when
|
||||
* reading in a new superblock is parsed here.
|
||||
* XXX JDM: This needs to be cleaned up for remount.
|
||||
*/
|
||||
int btrfs_parse_options(struct btrfs_root *root, char *options)
|
||||
{
|
||||
@ -438,6 +552,18 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
#endif
|
||||
case Opt_fatal_errors:
|
||||
if (strcmp(args[0].from, "panic") == 0)
|
||||
btrfs_set_opt(info->mount_opt,
|
||||
PANIC_ON_FATAL_ERROR);
|
||||
else if (strcmp(args[0].from, "bug") == 0)
|
||||
btrfs_clear_opt(info->mount_opt,
|
||||
PANIC_ON_FATAL_ERROR);
|
||||
else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case Opt_err:
|
||||
printk(KERN_INFO "btrfs: unrecognized mount option "
|
||||
"'%s'\n", p);
|
||||
@ -762,6 +888,8 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
|
||||
seq_puts(seq, ",inode_cache");
|
||||
if (btrfs_test_opt(root, SKIP_BALANCE))
|
||||
seq_puts(seq, ",skip_balance");
|
||||
if (btrfs_test_opt(root, PANIC_ON_FATAL_ERROR))
|
||||
seq_puts(seq, ",fatal_errors=panic");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -995,11 +1123,20 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = btrfs_sb(sb);
|
||||
struct btrfs_root *root = fs_info->tree_root;
|
||||
unsigned old_flags = sb->s_flags;
|
||||
unsigned long old_opts = fs_info->mount_opt;
|
||||
unsigned long old_compress_type = fs_info->compress_type;
|
||||
u64 old_max_inline = fs_info->max_inline;
|
||||
u64 old_alloc_start = fs_info->alloc_start;
|
||||
int old_thread_pool_size = fs_info->thread_pool_size;
|
||||
unsigned int old_metadata_ratio = fs_info->metadata_ratio;
|
||||
int ret;
|
||||
|
||||
ret = btrfs_parse_options(root, data);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
if (ret) {
|
||||
ret = -EINVAL;
|
||||
goto restore;
|
||||
}
|
||||
|
||||
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
|
||||
return 0;
|
||||
@ -1007,26 +1144,44 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
|
||||
if (*flags & MS_RDONLY) {
|
||||
sb->s_flags |= MS_RDONLY;
|
||||
|
||||
ret = btrfs_commit_super(root);
|
||||
WARN_ON(ret);
|
||||
ret = btrfs_commit_super(root);
|
||||
if (ret)
|
||||
goto restore;
|
||||
} else {
|
||||
if (fs_info->fs_devices->rw_devices == 0)
|
||||
return -EACCES;
|
||||
ret = -EACCES;
|
||||
goto restore;
|
||||
|
||||
if (btrfs_super_log_root(fs_info->super_copy) != 0)
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto restore;
|
||||
|
||||
ret = btrfs_cleanup_fs_roots(fs_info);
|
||||
WARN_ON(ret);
|
||||
if (ret)
|
||||
goto restore;
|
||||
|
||||
/* recover relocation */
|
||||
ret = btrfs_recover_relocation(root);
|
||||
WARN_ON(ret);
|
||||
if (ret)
|
||||
goto restore;
|
||||
|
||||
sb->s_flags &= ~MS_RDONLY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
restore:
|
||||
/* We've hit an error - don't reset MS_RDONLY */
|
||||
if (sb->s_flags & MS_RDONLY)
|
||||
old_flags |= MS_RDONLY;
|
||||
sb->s_flags = old_flags;
|
||||
fs_info->mount_opt = old_opts;
|
||||
fs_info->compress_type = old_compress_type;
|
||||
fs_info->max_inline = old_max_inline;
|
||||
fs_info->alloc_start = old_alloc_start;
|
||||
fs_info->thread_pool_size = old_thread_pool_size;
|
||||
fs_info->metadata_ratio = old_metadata_ratio;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Used to sort the devices by max_avail(descending sort) */
|
||||
@ -1356,9 +1511,7 @@ static int __init init_btrfs_fs(void)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = btrfs_init_compress();
|
||||
if (err)
|
||||
goto free_sysfs;
|
||||
btrfs_init_compress();
|
||||
|
||||
err = btrfs_init_cachep();
|
||||
if (err)
|
||||
@ -1384,6 +1537,8 @@ static int __init init_btrfs_fs(void)
|
||||
if (err)
|
||||
goto unregister_ioctl;
|
||||
|
||||
btrfs_init_lockdep();
|
||||
|
||||
printk(KERN_INFO "%s loaded\n", BTRFS_BUILD_VERSION);
|
||||
return 0;
|
||||
|
||||
@ -1399,7 +1554,6 @@ free_cachep:
|
||||
btrfs_destroy_cachep();
|
||||
free_compress:
|
||||
btrfs_exit_compress();
|
||||
free_sysfs:
|
||||
btrfs_exit_sysfs();
|
||||
return err;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
#define BTRFS_ROOT_TRANS_TAG 0
|
||||
|
||||
static noinline void put_transaction(struct btrfs_transaction *transaction)
|
||||
void put_transaction(struct btrfs_transaction *transaction)
|
||||
{
|
||||
WARN_ON(atomic_read(&transaction->use_count) == 0);
|
||||
if (atomic_dec_and_test(&transaction->use_count)) {
|
||||
@ -58,6 +58,12 @@ static noinline int join_transaction(struct btrfs_root *root, int nofail)
|
||||
|
||||
spin_lock(&root->fs_info->trans_lock);
|
||||
loop:
|
||||
/* The file system has been taken offline. No new transactions. */
|
||||
if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
|
||||
spin_unlock(&root->fs_info->trans_lock);
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
if (root->fs_info->trans_no_join) {
|
||||
if (!nofail) {
|
||||
spin_unlock(&root->fs_info->trans_lock);
|
||||
@ -67,6 +73,8 @@ loop:
|
||||
|
||||
cur_trans = root->fs_info->running_transaction;
|
||||
if (cur_trans) {
|
||||
if (cur_trans->aborted)
|
||||
return cur_trans->aborted;
|
||||
atomic_inc(&cur_trans->use_count);
|
||||
atomic_inc(&cur_trans->num_writers);
|
||||
cur_trans->num_joined++;
|
||||
@ -123,6 +131,7 @@ loop:
|
||||
root->fs_info->generation++;
|
||||
cur_trans->transid = root->fs_info->generation;
|
||||
root->fs_info->running_transaction = cur_trans;
|
||||
cur_trans->aborted = 0;
|
||||
spin_unlock(&root->fs_info->trans_lock);
|
||||
|
||||
return 0;
|
||||
@ -318,6 +327,7 @@ again:
|
||||
h->use_count = 1;
|
||||
h->block_rsv = NULL;
|
||||
h->orig_rsv = NULL;
|
||||
h->aborted = 0;
|
||||
|
||||
smp_mb();
|
||||
if (cur_trans->blocked && may_wait_transaction(root, type)) {
|
||||
@ -327,8 +337,7 @@ again:
|
||||
|
||||
if (num_bytes) {
|
||||
trace_btrfs_space_reservation(root->fs_info, "transaction",
|
||||
(u64)(unsigned long)h,
|
||||
num_bytes, 1);
|
||||
h->transid, num_bytes, 1);
|
||||
h->block_rsv = &root->fs_info->trans_block_rsv;
|
||||
h->bytes_reserved = num_bytes;
|
||||
}
|
||||
@ -440,6 +449,7 @@ int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_transaction *cur_trans = trans->transaction;
|
||||
struct btrfs_block_rsv *rsv = trans->block_rsv;
|
||||
int updates;
|
||||
int err;
|
||||
|
||||
smp_mb();
|
||||
if (cur_trans->blocked || cur_trans->delayed_refs.flushing)
|
||||
@ -453,8 +463,11 @@ int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,
|
||||
|
||||
updates = trans->delayed_ref_updates;
|
||||
trans->delayed_ref_updates = 0;
|
||||
if (updates)
|
||||
btrfs_run_delayed_refs(trans, root, updates);
|
||||
if (updates) {
|
||||
err = btrfs_run_delayed_refs(trans, root, updates);
|
||||
if (err) /* Error code will also eval true */
|
||||
return err;
|
||||
}
|
||||
|
||||
trans->block_rsv = rsv;
|
||||
|
||||
@ -525,6 +538,11 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
||||
if (throttle)
|
||||
btrfs_run_delayed_iputs(root);
|
||||
|
||||
if (trans->aborted ||
|
||||
root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -690,11 +708,13 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_update_root(trans, tree_root,
|
||||
&root->root_key,
|
||||
&root->root_item);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
old_root_used = btrfs_root_used(&root->root_item);
|
||||
ret = btrfs_write_dirty_block_groups(trans, root);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (root != root->fs_info->extent_root)
|
||||
@ -705,6 +725,10 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
|
||||
|
||||
/*
|
||||
* update all the cowonly tree roots on disk
|
||||
*
|
||||
* The error handling in this function may not be obvious. Any of the
|
||||
* failures will cause the file system to go offline. We still need
|
||||
* to clean up the delayed refs.
|
||||
*/
|
||||
static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
@ -715,22 +739,30 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
|
||||
int ret;
|
||||
|
||||
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
eb = btrfs_lock_root_node(fs_info->tree_root);
|
||||
btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb);
|
||||
ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL,
|
||||
0, &eb);
|
||||
btrfs_tree_unlock(eb);
|
||||
free_extent_buffer(eb);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (!list_empty(&fs_info->dirty_cowonly_roots)) {
|
||||
next = fs_info->dirty_cowonly_roots.next;
|
||||
list_del_init(next);
|
||||
root = list_entry(next, struct btrfs_root, dirty_list);
|
||||
|
||||
update_cowonly_root(trans, root);
|
||||
ret = update_cowonly_root(trans, root);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
down_write(&fs_info->extent_commit_sem);
|
||||
@ -874,7 +906,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
||||
|
||||
new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS);
|
||||
if (!new_root_item) {
|
||||
pending->error = -ENOMEM;
|
||||
ret = pending->error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -911,21 +943,24 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
||||
* insert the directory item
|
||||
*/
|
||||
ret = btrfs_set_inode_index(parent_inode, &index);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
ret = btrfs_insert_dir_item(trans, parent_root,
|
||||
dentry->d_name.name, dentry->d_name.len,
|
||||
parent_inode, &key,
|
||||
BTRFS_FT_DIR, index);
|
||||
if (ret) {
|
||||
if (ret == -EEXIST) {
|
||||
pending->error = -EEXIST;
|
||||
dput(parent);
|
||||
goto fail;
|
||||
} else if (ret) {
|
||||
goto abort_trans_dput;
|
||||
}
|
||||
|
||||
btrfs_i_size_write(parent_inode, parent_inode->i_size +
|
||||
dentry->d_name.len * 2);
|
||||
ret = btrfs_update_inode(trans, parent_root, parent_inode);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto abort_trans_dput;
|
||||
|
||||
/*
|
||||
* pull in the delayed directory update
|
||||
@ -934,7 +969,10 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
||||
* snapshot
|
||||
*/
|
||||
ret = btrfs_run_delayed_items(trans, root);
|
||||
BUG_ON(ret);
|
||||
if (ret) { /* Transaction aborted */
|
||||
dput(parent);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
record_root_in_trans(trans, root);
|
||||
btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
|
||||
@ -949,12 +987,21 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
||||
btrfs_set_root_flags(new_root_item, root_flags);
|
||||
|
||||
old = btrfs_lock_root_node(root);
|
||||
btrfs_cow_block(trans, root, old, NULL, 0, &old);
|
||||
ret = btrfs_cow_block(trans, root, old, NULL, 0, &old);
|
||||
if (ret) {
|
||||
btrfs_tree_unlock(old);
|
||||
free_extent_buffer(old);
|
||||
goto abort_trans_dput;
|
||||
}
|
||||
|
||||
btrfs_set_lock_blocking(old);
|
||||
|
||||
btrfs_copy_root(trans, root, old, &tmp, objectid);
|
||||
ret = btrfs_copy_root(trans, root, old, &tmp, objectid);
|
||||
/* clean up in any case */
|
||||
btrfs_tree_unlock(old);
|
||||
free_extent_buffer(old);
|
||||
if (ret)
|
||||
goto abort_trans_dput;
|
||||
|
||||
/* see comments in should_cow_block() */
|
||||
root->force_cow = 1;
|
||||
@ -966,7 +1013,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_insert_root(trans, tree_root, &key, new_root_item);
|
||||
btrfs_tree_unlock(tmp);
|
||||
free_extent_buffer(tmp);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto abort_trans_dput;
|
||||
|
||||
/*
|
||||
* insert root back/forward references
|
||||
@ -975,19 +1023,32 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
||||
parent_root->root_key.objectid,
|
||||
btrfs_ino(parent_inode), index,
|
||||
dentry->d_name.name, dentry->d_name.len);
|
||||
BUG_ON(ret);
|
||||
dput(parent);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
key.offset = (u64)-1;
|
||||
pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key);
|
||||
BUG_ON(IS_ERR(pending->snap));
|
||||
if (IS_ERR(pending->snap)) {
|
||||
ret = PTR_ERR(pending->snap);
|
||||
goto abort_trans;
|
||||
}
|
||||
|
||||
btrfs_reloc_post_snapshot(trans, pending);
|
||||
ret = btrfs_reloc_post_snapshot(trans, pending);
|
||||
if (ret)
|
||||
goto abort_trans;
|
||||
ret = 0;
|
||||
fail:
|
||||
kfree(new_root_item);
|
||||
trans->block_rsv = rsv;
|
||||
btrfs_block_rsv_release(root, &pending->block_rsv, (u64)-1);
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
abort_trans_dput:
|
||||
dput(parent);
|
||||
abort_trans:
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1124,6 +1185,33 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void cleanup_transaction(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_transaction *cur_trans = trans->transaction;
|
||||
|
||||
WARN_ON(trans->use_count > 1);
|
||||
|
||||
spin_lock(&root->fs_info->trans_lock);
|
||||
list_del_init(&cur_trans->list);
|
||||
spin_unlock(&root->fs_info->trans_lock);
|
||||
|
||||
btrfs_cleanup_one_transaction(trans->transaction, root);
|
||||
|
||||
put_transaction(cur_trans);
|
||||
put_transaction(cur_trans);
|
||||
|
||||
trace_btrfs_transaction_commit(root);
|
||||
|
||||
btrfs_scrub_continue(root);
|
||||
|
||||
if (current->journal_info == trans)
|
||||
current->journal_info = NULL;
|
||||
|
||||
kmem_cache_free(btrfs_trans_handle_cachep, trans);
|
||||
}
|
||||
|
||||
/*
|
||||
* btrfs_transaction state sequence:
|
||||
* in_commit = 0, blocked = 0 (initial)
|
||||
@ -1135,10 +1223,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
unsigned long joined = 0;
|
||||
struct btrfs_transaction *cur_trans;
|
||||
struct btrfs_transaction *cur_trans = trans->transaction;
|
||||
struct btrfs_transaction *prev_trans = NULL;
|
||||
DEFINE_WAIT(wait);
|
||||
int ret;
|
||||
int ret = -EIO;
|
||||
int should_grow = 0;
|
||||
unsigned long now = get_seconds();
|
||||
int flush_on_commit = btrfs_test_opt(root, FLUSHONCOMMIT);
|
||||
@ -1148,13 +1236,18 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
btrfs_trans_release_metadata(trans, root);
|
||||
trans->block_rsv = NULL;
|
||||
|
||||
if (cur_trans->aborted)
|
||||
goto cleanup_transaction;
|
||||
|
||||
/* make a pass through all the delayed refs we have so far
|
||||
* any runnings procs may add more while we are here
|
||||
*/
|
||||
ret = btrfs_run_delayed_refs(trans, root, 0);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto cleanup_transaction;
|
||||
|
||||
cur_trans = trans->transaction;
|
||||
|
||||
/*
|
||||
* set the flushing flag so procs in this transaction have to
|
||||
* start sending their work down.
|
||||
@ -1162,19 +1255,20 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
cur_trans->delayed_refs.flushing = 1;
|
||||
|
||||
ret = btrfs_run_delayed_refs(trans, root, 0);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto cleanup_transaction;
|
||||
|
||||
spin_lock(&cur_trans->commit_lock);
|
||||
if (cur_trans->in_commit) {
|
||||
spin_unlock(&cur_trans->commit_lock);
|
||||
atomic_inc(&cur_trans->use_count);
|
||||
btrfs_end_transaction(trans, root);
|
||||
ret = btrfs_end_transaction(trans, root);
|
||||
|
||||
wait_for_commit(root, cur_trans);
|
||||
|
||||
put_transaction(cur_trans);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
trans->transaction->in_commit = 1;
|
||||
@ -1214,12 +1308,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
|
||||
if (flush_on_commit || snap_pending) {
|
||||
btrfs_start_delalloc_inodes(root, 1);
|
||||
ret = btrfs_wait_ordered_extents(root, 0, 1);
|
||||
BUG_ON(ret);
|
||||
btrfs_wait_ordered_extents(root, 0, 1);
|
||||
}
|
||||
|
||||
ret = btrfs_run_delayed_items(trans, root);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto cleanup_transaction;
|
||||
|
||||
/*
|
||||
* rename don't use btrfs_join_transaction, so, once we
|
||||
@ -1261,13 +1355,22 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
mutex_lock(&root->fs_info->reloc_mutex);
|
||||
|
||||
ret = btrfs_run_delayed_items(trans, root);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
mutex_unlock(&root->fs_info->reloc_mutex);
|
||||
goto cleanup_transaction;
|
||||
}
|
||||
|
||||
ret = create_pending_snapshots(trans, root->fs_info);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
mutex_unlock(&root->fs_info->reloc_mutex);
|
||||
goto cleanup_transaction;
|
||||
}
|
||||
|
||||
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
mutex_unlock(&root->fs_info->reloc_mutex);
|
||||
goto cleanup_transaction;
|
||||
}
|
||||
|
||||
/*
|
||||
* make sure none of the code above managed to slip in a
|
||||
@ -1294,7 +1397,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
mutex_lock(&root->fs_info->tree_log_mutex);
|
||||
|
||||
ret = commit_fs_roots(trans, root);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
mutex_unlock(&root->fs_info->tree_log_mutex);
|
||||
goto cleanup_transaction;
|
||||
}
|
||||
|
||||
/* commit_fs_roots gets rid of all the tree log roots, it is now
|
||||
* safe to free the root of tree log roots
|
||||
@ -1302,7 +1408,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
btrfs_free_log_root_tree(trans, root->fs_info);
|
||||
|
||||
ret = commit_cowonly_roots(trans, root);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
mutex_unlock(&root->fs_info->tree_log_mutex);
|
||||
goto cleanup_transaction;
|
||||
}
|
||||
|
||||
btrfs_prepare_extent_commit(trans, root);
|
||||
|
||||
@ -1336,8 +1445,18 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
wake_up(&root->fs_info->transaction_wait);
|
||||
|
||||
ret = btrfs_write_and_wait_transaction(trans, root);
|
||||
BUG_ON(ret);
|
||||
write_ctree_super(trans, root, 0);
|
||||
if (ret) {
|
||||
btrfs_error(root->fs_info, ret,
|
||||
"Error while writing out transaction.");
|
||||
mutex_unlock(&root->fs_info->tree_log_mutex);
|
||||
goto cleanup_transaction;
|
||||
}
|
||||
|
||||
ret = write_ctree_super(trans, root, 0);
|
||||
if (ret) {
|
||||
mutex_unlock(&root->fs_info->tree_log_mutex);
|
||||
goto cleanup_transaction;
|
||||
}
|
||||
|
||||
/*
|
||||
* the super is written, we can safely allow the tree-loggers
|
||||
@ -1373,6 +1492,15 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
btrfs_run_delayed_iputs(root);
|
||||
|
||||
return ret;
|
||||
|
||||
cleanup_transaction:
|
||||
btrfs_printk(root->fs_info, "Skipping commit of aborted transaction.\n");
|
||||
// WARN_ON(1);
|
||||
if (current->journal_info == trans)
|
||||
current->journal_info = NULL;
|
||||
cleanup_transaction(trans, root);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1388,6 +1516,8 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root)
|
||||
spin_unlock(&fs_info->trans_lock);
|
||||
|
||||
while (!list_empty(&list)) {
|
||||
int ret;
|
||||
|
||||
root = list_entry(list.next, struct btrfs_root, root_list);
|
||||
list_del(&root->root_list);
|
||||
|
||||
@ -1395,9 +1525,10 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root)
|
||||
|
||||
if (btrfs_header_backref_rev(root->node) <
|
||||
BTRFS_MIXED_BACKREF_REV)
|
||||
btrfs_drop_snapshot(root, NULL, 0, 0);
|
||||
ret = btrfs_drop_snapshot(root, NULL, 0, 0);
|
||||
else
|
||||
btrfs_drop_snapshot(root, NULL, 1, 0);
|
||||
ret =btrfs_drop_snapshot(root, NULL, 1, 0);
|
||||
BUG_ON(ret < 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ struct btrfs_transaction {
|
||||
wait_queue_head_t commit_wait;
|
||||
struct list_head pending_snapshots;
|
||||
struct btrfs_delayed_ref_root delayed_refs;
|
||||
int aborted;
|
||||
};
|
||||
|
||||
struct btrfs_trans_handle {
|
||||
@ -55,6 +56,7 @@ struct btrfs_trans_handle {
|
||||
struct btrfs_transaction *transaction;
|
||||
struct btrfs_block_rsv *block_rsv;
|
||||
struct btrfs_block_rsv *orig_rsv;
|
||||
int aborted;
|
||||
};
|
||||
|
||||
struct btrfs_pending_snapshot {
|
||||
@ -114,4 +116,5 @@ int btrfs_wait_marked_extents(struct btrfs_root *root,
|
||||
struct extent_io_tree *dirty_pages, int mark);
|
||||
int btrfs_transaction_blocked(struct btrfs_fs_info *info);
|
||||
int btrfs_transaction_in_commit(struct btrfs_fs_info *info);
|
||||
void put_transaction(struct btrfs_transaction *transaction);
|
||||
#endif
|
||||
|
@ -212,14 +212,13 @@ int btrfs_pin_log_trans(struct btrfs_root *root)
|
||||
* indicate we're done making changes to the log tree
|
||||
* and wake up anyone waiting to do a sync
|
||||
*/
|
||||
int btrfs_end_log_trans(struct btrfs_root *root)
|
||||
void btrfs_end_log_trans(struct btrfs_root *root)
|
||||
{
|
||||
if (atomic_dec_and_test(&root->log_writers)) {
|
||||
smp_mb();
|
||||
if (waitqueue_active(&root->log_writer_wait))
|
||||
wake_up(&root->log_writer_wait);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -378,12 +377,11 @@ insert:
|
||||
u32 found_size;
|
||||
found_size = btrfs_item_size_nr(path->nodes[0],
|
||||
path->slots[0]);
|
||||
if (found_size > item_size) {
|
||||
if (found_size > item_size)
|
||||
btrfs_truncate_item(trans, root, path, item_size, 1);
|
||||
} else if (found_size < item_size) {
|
||||
ret = btrfs_extend_item(trans, root, path,
|
||||
item_size - found_size);
|
||||
}
|
||||
else if (found_size < item_size)
|
||||
btrfs_extend_item(trans, root, path,
|
||||
item_size - found_size);
|
||||
} else if (ret) {
|
||||
return ret;
|
||||
}
|
||||
@ -1763,7 +1761,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
|
||||
BTRFS_TREE_LOG_OBJECTID);
|
||||
ret = btrfs_free_and_pin_reserved_extent(root,
|
||||
bytenr, blocksize);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM or logic errors */
|
||||
}
|
||||
free_extent_buffer(next);
|
||||
continue;
|
||||
@ -1871,20 +1869,26 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
|
||||
wret = walk_down_log_tree(trans, log, path, &level, wc);
|
||||
if (wret > 0)
|
||||
break;
|
||||
if (wret < 0)
|
||||
if (wret < 0) {
|
||||
ret = wret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wret = walk_up_log_tree(trans, log, path, &level, wc);
|
||||
if (wret > 0)
|
||||
break;
|
||||
if (wret < 0)
|
||||
if (wret < 0) {
|
||||
ret = wret;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* was the root node processed? if not, catch it here */
|
||||
if (path->nodes[orig_level]) {
|
||||
wc->process_func(log, path->nodes[orig_level], wc,
|
||||
ret = wc->process_func(log, path->nodes[orig_level], wc,
|
||||
btrfs_header_generation(path->nodes[orig_level]));
|
||||
if (ret)
|
||||
goto out;
|
||||
if (wc->free) {
|
||||
struct extent_buffer *next;
|
||||
|
||||
@ -1900,10 +1904,11 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
|
||||
BTRFS_TREE_LOG_OBJECTID);
|
||||
ret = btrfs_free_and_pin_reserved_extent(log, next->start,
|
||||
next->len);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM or logic errors */
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
for (i = 0; i <= orig_level; i++) {
|
||||
if (path->nodes[i]) {
|
||||
free_extent_buffer(path->nodes[i]);
|
||||
@ -1963,8 +1968,8 @@ static int wait_log_commit(struct btrfs_trans_handle *trans,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_writer(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
static void wait_for_writer(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
while (root->fs_info->last_trans_log_full_commit !=
|
||||
@ -1978,7 +1983,6 @@ static int wait_for_writer(struct btrfs_trans_handle *trans,
|
||||
mutex_lock(&root->log_mutex);
|
||||
finish_wait(&root->log_writer_wait, &wait);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2046,7 +2050,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
|
||||
* wait for them until later.
|
||||
*/
|
||||
ret = btrfs_write_marked_extents(log, &log->dirty_log_pages, mark);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
mutex_unlock(&root->log_mutex);
|
||||
goto out;
|
||||
}
|
||||
|
||||
btrfs_set_root_node(&log->root_item, log->node);
|
||||
|
||||
@ -2077,7 +2085,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
BUG_ON(ret != -ENOSPC);
|
||||
if (ret != -ENOSPC) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
mutex_unlock(&log_root_tree->log_mutex);
|
||||
goto out;
|
||||
}
|
||||
root->fs_info->last_trans_log_full_commit = trans->transid;
|
||||
btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
|
||||
mutex_unlock(&log_root_tree->log_mutex);
|
||||
@ -2117,7 +2129,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_write_and_wait_marked_extents(log_root_tree,
|
||||
&log_root_tree->dirty_log_pages,
|
||||
EXTENT_DIRTY | EXTENT_NEW);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
mutex_unlock(&log_root_tree->log_mutex);
|
||||
goto out_wake_log_root;
|
||||
}
|
||||
btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
|
||||
|
||||
btrfs_set_super_log_root(root->fs_info->super_for_commit,
|
||||
@ -2326,7 +2342,9 @@ out_unlock:
|
||||
if (ret == -ENOSPC) {
|
||||
root->fs_info->last_trans_log_full_commit = trans->transid;
|
||||
ret = 0;
|
||||
}
|
||||
} else if (ret < 0)
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
|
||||
btrfs_end_log_trans(root);
|
||||
|
||||
return err;
|
||||
@ -2357,7 +2375,8 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
|
||||
if (ret == -ENOSPC) {
|
||||
root->fs_info->last_trans_log_full_commit = trans->transid;
|
||||
ret = 0;
|
||||
}
|
||||
} else if (ret < 0 && ret != -ENOENT)
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
btrfs_end_log_trans(root);
|
||||
|
||||
return ret;
|
||||
@ -3169,13 +3188,20 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
|
||||
fs_info->log_root_recovering = 1;
|
||||
|
||||
trans = btrfs_start_transaction(fs_info->tree_root, 0);
|
||||
BUG_ON(IS_ERR(trans));
|
||||
if (IS_ERR(trans)) {
|
||||
ret = PTR_ERR(trans);
|
||||
goto error;
|
||||
}
|
||||
|
||||
wc.trans = trans;
|
||||
wc.pin = 1;
|
||||
|
||||
ret = walk_log_tree(trans, log_root_tree, &wc);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_error(fs_info, ret, "Failed to pin buffers while "
|
||||
"recovering log root tree.");
|
||||
goto error;
|
||||
}
|
||||
|
||||
again:
|
||||
key.objectid = BTRFS_TREE_LOG_OBJECTID;
|
||||
@ -3184,8 +3210,12 @@ again:
|
||||
|
||||
while (1) {
|
||||
ret = btrfs_search_slot(NULL, log_root_tree, &key, path, 0, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
if (ret < 0) {
|
||||
btrfs_error(fs_info, ret,
|
||||
"Couldn't find tree log root.");
|
||||
goto error;
|
||||
}
|
||||
if (ret > 0) {
|
||||
if (path->slots[0] == 0)
|
||||
break;
|
||||
@ -3199,14 +3229,24 @@ again:
|
||||
|
||||
log = btrfs_read_fs_root_no_radix(log_root_tree,
|
||||
&found_key);
|
||||
BUG_ON(IS_ERR(log));
|
||||
if (IS_ERR(log)) {
|
||||
ret = PTR_ERR(log);
|
||||
btrfs_error(fs_info, ret,
|
||||
"Couldn't read tree log root.");
|
||||
goto error;
|
||||
}
|
||||
|
||||
tmp_key.objectid = found_key.offset;
|
||||
tmp_key.type = BTRFS_ROOT_ITEM_KEY;
|
||||
tmp_key.offset = (u64)-1;
|
||||
|
||||
wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key);
|
||||
BUG_ON(IS_ERR_OR_NULL(wc.replay_dest));
|
||||
if (IS_ERR(wc.replay_dest)) {
|
||||
ret = PTR_ERR(wc.replay_dest);
|
||||
btrfs_error(fs_info, ret, "Couldn't read target root "
|
||||
"for tree log recovery.");
|
||||
goto error;
|
||||
}
|
||||
|
||||
wc.replay_dest->log_root = log;
|
||||
btrfs_record_root_in_trans(trans, wc.replay_dest);
|
||||
@ -3254,6 +3294,10 @@ again:
|
||||
|
||||
kfree(log_root_tree);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -38,7 +38,7 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
const char *name, int name_len,
|
||||
struct inode *inode, u64 dirid);
|
||||
int btrfs_end_log_trans(struct btrfs_root *root);
|
||||
void btrfs_end_log_trans(struct btrfs_root *root);
|
||||
int btrfs_pin_log_trans(struct btrfs_root *root);
|
||||
int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct inode *inode,
|
||||
|
@ -67,7 +67,7 @@ static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
|
||||
kfree(fs_devices);
|
||||
}
|
||||
|
||||
int btrfs_cleanup_fs_uuids(void)
|
||||
void btrfs_cleanup_fs_uuids(void)
|
||||
{
|
||||
struct btrfs_fs_devices *fs_devices;
|
||||
|
||||
@ -77,7 +77,6 @@ int btrfs_cleanup_fs_uuids(void)
|
||||
list_del(&fs_devices->list);
|
||||
free_fs_devices(fs_devices);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static noinline struct btrfs_device *__find_device(struct list_head *head,
|
||||
@ -130,7 +129,7 @@ static void requeue_list(struct btrfs_pending_bios *pending_bios,
|
||||
* the list if the block device is congested. This way, multiple devices
|
||||
* can make progress from a single worker thread.
|
||||
*/
|
||||
static noinline int run_scheduled_bios(struct btrfs_device *device)
|
||||
static noinline void run_scheduled_bios(struct btrfs_device *device)
|
||||
{
|
||||
struct bio *pending;
|
||||
struct backing_dev_info *bdi;
|
||||
@ -316,7 +315,6 @@ loop_lock:
|
||||
|
||||
done:
|
||||
blk_finish_plug(&plug);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pending_bios_fn(struct btrfs_work *work)
|
||||
@ -455,7 +453,7 @@ error:
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
|
||||
void btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
|
||||
{
|
||||
struct btrfs_device *device, *next;
|
||||
|
||||
@ -503,7 +501,6 @@ again:
|
||||
fs_devices->latest_trans = latest_transid;
|
||||
|
||||
mutex_unlock(&uuid_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __free_device(struct work_struct *work)
|
||||
@ -552,10 +549,10 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
|
||||
fs_devices->num_can_discard--;
|
||||
|
||||
new_device = kmalloc(sizeof(*new_device), GFP_NOFS);
|
||||
BUG_ON(!new_device);
|
||||
BUG_ON(!new_device); /* -ENOMEM */
|
||||
memcpy(new_device, device, sizeof(*new_device));
|
||||
new_device->name = kstrdup(device->name, GFP_NOFS);
|
||||
BUG_ON(device->name && !new_device->name);
|
||||
BUG_ON(device->name && !new_device->name); /* -ENOMEM */
|
||||
new_device->bdev = NULL;
|
||||
new_device->writeable = 0;
|
||||
new_device->in_fs_metadata = 0;
|
||||
@ -625,6 +622,8 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
|
||||
printk(KERN_INFO "open %s failed\n", device->name);
|
||||
goto error;
|
||||
}
|
||||
filemap_write_and_wait(bdev->bd_inode->i_mapping);
|
||||
invalidate_bdev(bdev);
|
||||
set_blocksize(bdev, 4096);
|
||||
|
||||
bh = btrfs_read_dev_super(bdev);
|
||||
@ -1039,8 +1038,10 @@ again:
|
||||
leaf = path->nodes[0];
|
||||
extent = btrfs_item_ptr(leaf, path->slots[0],
|
||||
struct btrfs_dev_extent);
|
||||
} else {
|
||||
btrfs_error(root->fs_info, ret, "Slot search failed");
|
||||
goto out;
|
||||
}
|
||||
BUG_ON(ret);
|
||||
|
||||
if (device->bytes_used > 0) {
|
||||
u64 len = btrfs_dev_extent_length(leaf, extent);
|
||||
@ -1050,7 +1051,10 @@ again:
|
||||
spin_unlock(&root->fs_info->free_chunk_lock);
|
||||
}
|
||||
ret = btrfs_del_item(trans, root, path);
|
||||
|
||||
if (ret) {
|
||||
btrfs_error(root->fs_info, ret,
|
||||
"Failed to remove dev extent item");
|
||||
}
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
@ -1078,7 +1082,8 @@ int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
|
||||
key.type = BTRFS_DEV_EXTENT_KEY;
|
||||
ret = btrfs_insert_empty_item(trans, root, path, &key,
|
||||
sizeof(*extent));
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
leaf = path->nodes[0];
|
||||
extent = btrfs_item_ptr(leaf, path->slots[0],
|
||||
@ -1093,6 +1098,7 @@ int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
|
||||
|
||||
btrfs_set_dev_extent_length(leaf, extent, num_bytes);
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
@ -1118,7 +1124,7 @@ static noinline int find_next_chunk(struct btrfs_root *root,
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
BUG_ON(ret == 0);
|
||||
BUG_ON(ret == 0); /* Corruption */
|
||||
|
||||
ret = btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY);
|
||||
if (ret) {
|
||||
@ -1162,7 +1168,7 @@ static noinline int find_next_devid(struct btrfs_root *root, u64 *objectid)
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
BUG_ON(ret == 0);
|
||||
BUG_ON(ret == 0); /* Corruption */
|
||||
|
||||
ret = btrfs_previous_item(root, path, BTRFS_DEV_ITEMS_OBJECTID,
|
||||
BTRFS_DEV_ITEM_KEY);
|
||||
@ -1350,6 +1356,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
|
||||
}
|
||||
|
||||
set_blocksize(bdev, 4096);
|
||||
invalidate_bdev(bdev);
|
||||
bh = btrfs_read_dev_super(bdev);
|
||||
if (!bh) {
|
||||
ret = -EINVAL;
|
||||
@ -1596,7 +1603,7 @@ next_slot:
|
||||
(unsigned long)btrfs_device_fsid(dev_item),
|
||||
BTRFS_UUID_SIZE);
|
||||
device = btrfs_find_device(root, devid, dev_uuid, fs_uuid);
|
||||
BUG_ON(!device);
|
||||
BUG_ON(!device); /* Logic error */
|
||||
|
||||
if (device->fs_devices->seeding) {
|
||||
btrfs_set_device_generation(leaf, dev_item,
|
||||
@ -1706,7 +1713,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
|
||||
if (seeding_dev) {
|
||||
sb->s_flags &= ~MS_RDONLY;
|
||||
ret = btrfs_prepare_sprout(root);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* -ENOMEM */
|
||||
}
|
||||
|
||||
device->fs_devices = root->fs_info->fs_devices;
|
||||
@ -1744,11 +1751,15 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
|
||||
|
||||
if (seeding_dev) {
|
||||
ret = init_first_rw_device(trans, root, device);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto error_trans;
|
||||
ret = btrfs_finish_sprout(trans, root);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto error_trans;
|
||||
} else {
|
||||
ret = btrfs_add_device(trans, root, device);
|
||||
if (ret)
|
||||
goto error_trans;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1758,17 +1769,31 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
|
||||
btrfs_clear_space_info_full(root->fs_info);
|
||||
|
||||
unlock_chunks(root);
|
||||
btrfs_commit_transaction(trans, root);
|
||||
ret = btrfs_commit_transaction(trans, root);
|
||||
|
||||
if (seeding_dev) {
|
||||
mutex_unlock(&uuid_mutex);
|
||||
up_write(&sb->s_umount);
|
||||
|
||||
if (ret) /* transaction commit */
|
||||
return ret;
|
||||
|
||||
ret = btrfs_relocate_sys_chunks(root);
|
||||
BUG_ON(ret);
|
||||
if (ret < 0)
|
||||
btrfs_error(root->fs_info, ret,
|
||||
"Failed to relocate sys chunks after "
|
||||
"device initialization. This can be fixed "
|
||||
"using the \"btrfs balance\" command.");
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_trans:
|
||||
unlock_chunks(root);
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
btrfs_end_transaction(trans, root);
|
||||
kfree(device->name);
|
||||
kfree(device);
|
||||
error:
|
||||
blkdev_put(bdev, FMODE_EXCL);
|
||||
if (seeding_dev) {
|
||||
@ -1876,10 +1901,20 @@ static int btrfs_free_chunk(struct btrfs_trans_handle *trans,
|
||||
key.type = BTRFS_CHUNK_ITEM_KEY;
|
||||
|
||||
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
|
||||
BUG_ON(ret);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
else if (ret > 0) { /* Logic error or corruption */
|
||||
btrfs_error(root->fs_info, -ENOENT,
|
||||
"Failed lookup while freeing chunk.");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = btrfs_del_item(trans, root, path);
|
||||
|
||||
if (ret < 0)
|
||||
btrfs_error(root->fs_info, ret,
|
||||
"Failed to delete chunk item.");
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
@ -2041,7 +2076,7 @@ again:
|
||||
ret = btrfs_search_slot(NULL, chunk_root, &key, path, 0, 0);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
BUG_ON(ret == 0);
|
||||
BUG_ON(ret == 0); /* Corruption */
|
||||
|
||||
ret = btrfs_previous_item(chunk_root, path, key.objectid,
|
||||
key.type);
|
||||
@ -2250,15 +2285,13 @@ static void unset_balance_control(struct btrfs_fs_info *fs_info)
|
||||
* Balance filters. Return 1 if chunk should be filtered out
|
||||
* (should not be balanced).
|
||||
*/
|
||||
static int chunk_profiles_filter(u64 chunk_profile,
|
||||
static int chunk_profiles_filter(u64 chunk_type,
|
||||
struct btrfs_balance_args *bargs)
|
||||
{
|
||||
chunk_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
|
||||
chunk_type = chunk_to_extended(chunk_type) &
|
||||
BTRFS_EXTENDED_PROFILE_MASK;
|
||||
|
||||
if (chunk_profile == 0)
|
||||
chunk_profile = BTRFS_AVAIL_ALLOC_BIT_SINGLE;
|
||||
|
||||
if (bargs->profiles & chunk_profile)
|
||||
if (bargs->profiles & chunk_type)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@ -2365,18 +2398,16 @@ static int chunk_vrange_filter(struct extent_buffer *leaf,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int chunk_soft_convert_filter(u64 chunk_profile,
|
||||
static int chunk_soft_convert_filter(u64 chunk_type,
|
||||
struct btrfs_balance_args *bargs)
|
||||
{
|
||||
if (!(bargs->flags & BTRFS_BALANCE_ARGS_CONVERT))
|
||||
return 0;
|
||||
|
||||
chunk_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
|
||||
chunk_type = chunk_to_extended(chunk_type) &
|
||||
BTRFS_EXTENDED_PROFILE_MASK;
|
||||
|
||||
if (chunk_profile == 0)
|
||||
chunk_profile = BTRFS_AVAIL_ALLOC_BIT_SINGLE;
|
||||
|
||||
if (bargs->target & chunk_profile)
|
||||
if (bargs->target == chunk_type)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@ -2602,6 +2633,30 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_profile_is_valid - see if a given profile is valid and reduced
|
||||
* @flags: profile to validate
|
||||
* @extended: if true @flags is treated as an extended profile
|
||||
*/
|
||||
static int alloc_profile_is_valid(u64 flags, int extended)
|
||||
{
|
||||
u64 mask = (extended ? BTRFS_EXTENDED_PROFILE_MASK :
|
||||
BTRFS_BLOCK_GROUP_PROFILE_MASK);
|
||||
|
||||
flags &= ~BTRFS_BLOCK_GROUP_TYPE_MASK;
|
||||
|
||||
/* 1) check that all other bits are zeroed */
|
||||
if (flags & ~mask)
|
||||
return 0;
|
||||
|
||||
/* 2) see if profile is reduced */
|
||||
if (flags == 0)
|
||||
return !extended; /* "0" is valid for usual profiles */
|
||||
|
||||
/* true if exactly one bit set */
|
||||
return (flags & (flags - 1)) == 0;
|
||||
}
|
||||
|
||||
static inline int balance_need_close(struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
/* cancel requested || normal exit path */
|
||||
@ -2630,6 +2685,7 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = bctl->fs_info;
|
||||
u64 allowed;
|
||||
int mixed = 0;
|
||||
int ret;
|
||||
|
||||
if (btrfs_fs_closing(fs_info) ||
|
||||
@ -2639,13 +2695,16 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
|
||||
goto out;
|
||||
}
|
||||
|
||||
allowed = btrfs_super_incompat_flags(fs_info->super_copy);
|
||||
if (allowed & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
|
||||
mixed = 1;
|
||||
|
||||
/*
|
||||
* In case of mixed groups both data and meta should be picked,
|
||||
* and identical options should be given for both of them.
|
||||
*/
|
||||
allowed = btrfs_super_incompat_flags(fs_info->super_copy);
|
||||
if ((allowed & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS) &&
|
||||
(bctl->flags & (BTRFS_BALANCE_DATA | BTRFS_BALANCE_METADATA))) {
|
||||
allowed = BTRFS_BALANCE_DATA | BTRFS_BALANCE_METADATA;
|
||||
if (mixed && (bctl->flags & allowed)) {
|
||||
if (!(bctl->flags & BTRFS_BALANCE_DATA) ||
|
||||
!(bctl->flags & BTRFS_BALANCE_METADATA) ||
|
||||
memcmp(&bctl->data, &bctl->meta, sizeof(bctl->data))) {
|
||||
@ -2656,14 +2715,6 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Profile changing sanity checks. Skip them if a simple
|
||||
* balance is requested.
|
||||
*/
|
||||
if (!((bctl->data.flags | bctl->sys.flags | bctl->meta.flags) &
|
||||
BTRFS_BALANCE_ARGS_CONVERT))
|
||||
goto do_balance;
|
||||
|
||||
allowed = BTRFS_AVAIL_ALLOC_BIT_SINGLE;
|
||||
if (fs_info->fs_devices->num_devices == 1)
|
||||
allowed |= BTRFS_BLOCK_GROUP_DUP;
|
||||
@ -2673,24 +2724,27 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
|
||||
allowed |= (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 |
|
||||
BTRFS_BLOCK_GROUP_RAID10);
|
||||
|
||||
if (!profile_is_valid(bctl->data.target, 1) ||
|
||||
bctl->data.target & ~allowed) {
|
||||
if ((bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
|
||||
(!alloc_profile_is_valid(bctl->data.target, 1) ||
|
||||
(bctl->data.target & ~allowed))) {
|
||||
printk(KERN_ERR "btrfs: unable to start balance with target "
|
||||
"data profile %llu\n",
|
||||
(unsigned long long)bctl->data.target);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!profile_is_valid(bctl->meta.target, 1) ||
|
||||
bctl->meta.target & ~allowed) {
|
||||
if ((bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
|
||||
(!alloc_profile_is_valid(bctl->meta.target, 1) ||
|
||||
(bctl->meta.target & ~allowed))) {
|
||||
printk(KERN_ERR "btrfs: unable to start balance with target "
|
||||
"metadata profile %llu\n",
|
||||
(unsigned long long)bctl->meta.target);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!profile_is_valid(bctl->sys.target, 1) ||
|
||||
bctl->sys.target & ~allowed) {
|
||||
if ((bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
|
||||
(!alloc_profile_is_valid(bctl->sys.target, 1) ||
|
||||
(bctl->sys.target & ~allowed))) {
|
||||
printk(KERN_ERR "btrfs: unable to start balance with target "
|
||||
"system profile %llu\n",
|
||||
(unsigned long long)bctl->sys.target);
|
||||
@ -2698,7 +2752,9 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bctl->data.target & BTRFS_BLOCK_GROUP_DUP) {
|
||||
/* allow dup'ed data chunks only in mixed mode */
|
||||
if (!mixed && (bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
|
||||
(bctl->data.target & BTRFS_BLOCK_GROUP_DUP)) {
|
||||
printk(KERN_ERR "btrfs: dup for data is not allowed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@ -2724,7 +2780,6 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
|
||||
}
|
||||
}
|
||||
|
||||
do_balance:
|
||||
ret = insert_balance_item(fs_info->tree_root, bctl);
|
||||
if (ret && ret != -EEXIST)
|
||||
goto out;
|
||||
@ -2967,7 +3022,7 @@ again:
|
||||
key.offset = (u64)-1;
|
||||
key.type = BTRFS_DEV_EXTENT_KEY;
|
||||
|
||||
while (1) {
|
||||
do {
|
||||
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
@ -3009,8 +3064,7 @@ again:
|
||||
goto done;
|
||||
if (ret == -ENOSPC)
|
||||
failed++;
|
||||
key.offset -= 1;
|
||||
}
|
||||
} while (key.offset-- > 0);
|
||||
|
||||
if (failed && !retried) {
|
||||
failed = 0;
|
||||
@ -3128,11 +3182,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
|
||||
int i;
|
||||
int j;
|
||||
|
||||
if ((type & BTRFS_BLOCK_GROUP_RAID1) &&
|
||||
(type & BTRFS_BLOCK_GROUP_DUP)) {
|
||||
WARN_ON(1);
|
||||
type &= ~BTRFS_BLOCK_GROUP_DUP;
|
||||
}
|
||||
BUG_ON(!alloc_profile_is_valid(type, 0));
|
||||
|
||||
if (list_empty(&fs_devices->alloc_list))
|
||||
return -ENOSPC;
|
||||
@ -3328,13 +3378,15 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
|
||||
write_lock(&em_tree->lock);
|
||||
ret = add_extent_mapping(em_tree, em);
|
||||
write_unlock(&em_tree->lock);
|
||||
BUG_ON(ret);
|
||||
free_extent_map(em);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ret = btrfs_make_block_group(trans, extent_root, 0, type,
|
||||
BTRFS_FIRST_CHUNK_TREE_OBJECTID,
|
||||
start, num_bytes);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < map->num_stripes; ++i) {
|
||||
struct btrfs_device *device;
|
||||
@ -3347,7 +3399,10 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
|
||||
info->chunk_root->root_key.objectid,
|
||||
BTRFS_FIRST_CHUNK_TREE_OBJECTID,
|
||||
start, dev_offset, stripe_size);
|
||||
BUG_ON(ret);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, extent_root, ret);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(devices_info);
|
||||
@ -3383,7 +3438,8 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans,
|
||||
device = map->stripes[index].dev;
|
||||
device->bytes_used += stripe_size;
|
||||
ret = btrfs_update_device(trans, device);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
index++;
|
||||
}
|
||||
|
||||
@ -3420,16 +3476,19 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans,
|
||||
key.offset = chunk_offset;
|
||||
|
||||
ret = btrfs_insert_item(trans, chunk_root, &key, chunk, item_size);
|
||||
BUG_ON(ret);
|
||||
|
||||
if (map->type & BTRFS_BLOCK_GROUP_SYSTEM) {
|
||||
if (ret == 0 && map->type & BTRFS_BLOCK_GROUP_SYSTEM) {
|
||||
/*
|
||||
* TODO: Cleanup of inserted chunk root in case of
|
||||
* failure.
|
||||
*/
|
||||
ret = btrfs_add_system_chunk(chunk_root, &key, chunk,
|
||||
item_size);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
|
||||
out_free:
|
||||
kfree(chunk);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3461,7 +3520,8 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
|
||||
|
||||
ret = __finish_chunk_alloc(trans, extent_root, map, chunk_offset,
|
||||
chunk_size, stripe_size);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3493,7 +3553,8 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
|
||||
|
||||
ret = __btrfs_alloc_chunk(trans, extent_root, &map, &chunk_size,
|
||||
&stripe_size, chunk_offset, alloc_profile);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sys_chunk_offset = chunk_offset + chunk_size;
|
||||
|
||||
@ -3504,10 +3565,12 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
|
||||
ret = __btrfs_alloc_chunk(trans, extent_root, &sys_map,
|
||||
&sys_chunk_size, &sys_stripe_size,
|
||||
sys_chunk_offset, alloc_profile);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto abort;
|
||||
|
||||
ret = btrfs_add_device(trans, fs_info->chunk_root, device);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto abort;
|
||||
|
||||
/*
|
||||
* Modifying chunk tree needs allocating new blocks from both
|
||||
@ -3517,13 +3580,20 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
|
||||
*/
|
||||
ret = __finish_chunk_alloc(trans, extent_root, map, chunk_offset,
|
||||
chunk_size, stripe_size);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto abort;
|
||||
|
||||
ret = __finish_chunk_alloc(trans, extent_root, sys_map,
|
||||
sys_chunk_offset, sys_chunk_size,
|
||||
sys_stripe_size);
|
||||
BUG_ON(ret);
|
||||
if (ret)
|
||||
goto abort;
|
||||
|
||||
return 0;
|
||||
|
||||
abort:
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
|
||||
@ -3874,7 +3944,7 @@ int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
|
||||
do_div(length, map->num_stripes);
|
||||
|
||||
buf = kzalloc(sizeof(u64) * map->num_stripes, GFP_NOFS);
|
||||
BUG_ON(!buf);
|
||||
BUG_ON(!buf); /* -ENOMEM */
|
||||
|
||||
for (i = 0; i < map->num_stripes; i++) {
|
||||
if (devid && map->stripes[i].dev->devid != devid)
|
||||
@ -3967,7 +4037,7 @@ struct async_sched {
|
||||
* This will add one bio to the pending list for a device and make sure
|
||||
* the work struct is scheduled.
|
||||
*/
|
||||
static noinline int schedule_bio(struct btrfs_root *root,
|
||||
static noinline void schedule_bio(struct btrfs_root *root,
|
||||
struct btrfs_device *device,
|
||||
int rw, struct bio *bio)
|
||||
{
|
||||
@ -3979,7 +4049,7 @@ static noinline int schedule_bio(struct btrfs_root *root,
|
||||
bio_get(bio);
|
||||
btrfsic_submit_bio(rw, bio);
|
||||
bio_put(bio);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4013,7 +4083,6 @@ static noinline int schedule_bio(struct btrfs_root *root,
|
||||
if (should_queue)
|
||||
btrfs_queue_worker(&root->fs_info->submit_workers,
|
||||
&device->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
|
||||
@ -4036,7 +4105,8 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
|
||||
|
||||
ret = btrfs_map_block(map_tree, rw, logical, &map_length, &bbio,
|
||||
mirror_num);
|
||||
BUG_ON(ret);
|
||||
if (ret) /* -ENOMEM */
|
||||
return ret;
|
||||
|
||||
total_devs = bbio->num_stripes;
|
||||
if (map_length < length) {
|
||||
@ -4055,7 +4125,7 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
|
||||
while (dev_nr < total_devs) {
|
||||
if (dev_nr < total_devs - 1) {
|
||||
bio = bio_clone(first_bio, GFP_NOFS);
|
||||
BUG_ON(!bio);
|
||||
BUG_ON(!bio); /* -ENOMEM */
|
||||
} else {
|
||||
bio = first_bio;
|
||||
}
|
||||
@ -4209,13 +4279,13 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
|
||||
write_lock(&map_tree->map_tree.lock);
|
||||
ret = add_extent_mapping(&map_tree->map_tree, em);
|
||||
write_unlock(&map_tree->map_tree.lock);
|
||||
BUG_ON(ret);
|
||||
BUG_ON(ret); /* Tree corruption */
|
||||
free_extent_map(em);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fill_device_from_item(struct extent_buffer *leaf,
|
||||
static void fill_device_from_item(struct extent_buffer *leaf,
|
||||
struct btrfs_dev_item *dev_item,
|
||||
struct btrfs_device *device)
|
||||
{
|
||||
@ -4232,8 +4302,6 @@ static int fill_device_from_item(struct extent_buffer *leaf,
|
||||
|
||||
ptr = (unsigned long)btrfs_device_uuid(dev_item);
|
||||
read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int open_seed_devices(struct btrfs_root *root, u8 *fsid)
|
||||
@ -4384,7 +4452,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
|
||||
* to silence the warning eg. on PowerPC 64.
|
||||
*/
|
||||
if (PAGE_CACHE_SIZE > BTRFS_SUPER_INFO_SIZE)
|
||||
SetPageUptodate(sb->first_page);
|
||||
SetPageUptodate(sb->pages[0]);
|
||||
|
||||
write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE);
|
||||
array_size = btrfs_super_sys_array_size(super_copy);
|
||||
|
@ -260,12 +260,12 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
|
||||
int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
|
||||
struct btrfs_fs_devices **fs_devices_ret);
|
||||
int btrfs_close_devices(struct btrfs_fs_devices *fs_devices);
|
||||
int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices);
|
||||
void btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices);
|
||||
int btrfs_add_device(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_device *device);
|
||||
int btrfs_rm_device(struct btrfs_root *root, char *device_path);
|
||||
int btrfs_cleanup_fs_uuids(void);
|
||||
void btrfs_cleanup_fs_uuids(void);
|
||||
int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len);
|
||||
int btrfs_grow_device(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_device *device, u64 new_size);
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <trace/events/gfpflags.h>
|
||||
|
||||
struct btrfs_root;
|
||||
struct btrfs_fs_info;
|
||||
@ -862,6 +863,49 @@ TRACE_EVENT(btrfs_setup_cluster,
|
||||
__entry->size, __entry->max_size, __entry->bitmap)
|
||||
);
|
||||
|
||||
struct extent_state;
|
||||
TRACE_EVENT(alloc_extent_state,
|
||||
|
||||
TP_PROTO(struct extent_state *state, gfp_t mask, unsigned long IP),
|
||||
|
||||
TP_ARGS(state, mask, IP),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(struct extent_state *, state)
|
||||
__field(gfp_t, mask)
|
||||
__field(unsigned long, ip)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->state = state,
|
||||
__entry->mask = mask,
|
||||
__entry->ip = IP
|
||||
),
|
||||
|
||||
TP_printk("state=%p; mask = %s; caller = %pF", __entry->state,
|
||||
show_gfp_flags(__entry->mask), (void *)__entry->ip)
|
||||
);
|
||||
|
||||
TRACE_EVENT(free_extent_state,
|
||||
|
||||
TP_PROTO(struct extent_state *state, unsigned long IP),
|
||||
|
||||
TP_ARGS(state, IP),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(struct extent_state *, state)
|
||||
__field(unsigned long, ip)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->state = state,
|
||||
__entry->ip = IP
|
||||
),
|
||||
|
||||
TP_printk(" state=%p; caller = %pF", __entry->state,
|
||||
(void *)__entry->ip)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_BTRFS_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
Loading…
Reference in New Issue
Block a user