mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 16:24:13 +08:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
* git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable: (23 commits) Btrfs: fix extent_buffer leak during tree log replay Btrfs: fix oops when btrfs_inherit_iflags called with a NULL dir Btrfs: fix -o nodatasum printk spelling Btrfs: check duplicate backrefs for both data and metadata Btrfs: init worker struct fields before kthread-run Btrfs: pin buffers during write_dev_supers Btrfs: avoid races between super writeout and device list updates Fix btrfs when ACLs are configured out Btrfs: fdatasync should skip metadata writeout Btrfs: remove crc32c.h and use libcrc32c directly. Btrfs: implement FS_IOC_GETFLAGS/SETFLAGS/GETVERSION Btrfs: autodetect SSD devices Btrfs: add mount -o ssd_spread to spread allocations out Btrfs: avoid allocation clusters that are too spread out Btrfs: Add mount -o nossd Btrfs: avoid IO stalls behind congested devices in a multi-device FS Btrfs: don't allow WRITE_SYNC bios to starve out regular writes Btrfs: fix metadata dirty throttling limits Btrfs: reduce mount -o ssd CPU usage Btrfs: balance btree more often ...
This commit is contained in:
commit
a525890cb6
@ -6,5 +6,5 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
|
||||
transaction.o inode.o file.o tree-defrag.o \
|
||||
extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \
|
||||
extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
|
||||
ref-cache.o export.o tree-log.o acl.o free-space-cache.o zlib.o \
|
||||
compression.o delayed-ref.o
|
||||
export.o tree-log.o acl.o free-space-cache.o zlib.o \
|
||||
compression.o delayed-ref.o relocation.o
|
||||
|
@ -351,9 +351,4 @@ int btrfs_init_acl(struct inode *inode, struct inode *dir)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_check_acl(struct inode *inode, int mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_FS_POSIX_ACL */
|
||||
|
@ -294,10 +294,10 @@ int btrfs_start_workers(struct btrfs_workers *workers, int num_workers)
|
||||
INIT_LIST_HEAD(&worker->worker_list);
|
||||
spin_lock_init(&worker->lock);
|
||||
atomic_set(&worker->num_pending, 0);
|
||||
worker->workers = workers;
|
||||
worker->task = kthread_run(worker_loop, worker,
|
||||
"btrfs-%s-%d", workers->name,
|
||||
workers->num_workers + i);
|
||||
worker->workers = workers;
|
||||
if (IS_ERR(worker->task)) {
|
||||
kfree(worker);
|
||||
ret = PTR_ERR(worker->task);
|
||||
|
@ -72,6 +72,9 @@ struct btrfs_inode {
|
||||
*/
|
||||
struct list_head ordered_operations;
|
||||
|
||||
/* node for the red-black tree that links inodes in subvolume root */
|
||||
struct rb_node rb_node;
|
||||
|
||||
/* the space_info for where this inode's data allocations are done */
|
||||
struct btrfs_space_info *space_info;
|
||||
|
||||
@ -154,5 +157,4 @@ static inline void btrfs_i_size_write(struct inode *inode, u64 size)
|
||||
BTRFS_I(inode)->disk_i_size = size;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -123,7 +123,7 @@ static int check_compressed_csum(struct inode *inode,
|
||||
u32 csum;
|
||||
u32 *cb_sum = &cb->sums;
|
||||
|
||||
if (btrfs_test_flag(inode, NODATASUM))
|
||||
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < cb->nr_pages; i++) {
|
||||
@ -670,7 +670,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
|
||||
*/
|
||||
atomic_inc(&cb->pending_bios);
|
||||
|
||||
if (!btrfs_test_flag(inode, NODATASUM)) {
|
||||
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
|
||||
btrfs_lookup_bio_sums(root, inode, comp_bio,
|
||||
sums);
|
||||
}
|
||||
@ -697,7 +697,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
|
||||
ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0);
|
||||
BUG_ON(ret);
|
||||
|
||||
if (!btrfs_test_flag(inode, NODATASUM))
|
||||
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
|
||||
btrfs_lookup_bio_sums(root, inode, comp_bio, sums);
|
||||
|
||||
ret = btrfs_map_bio(root, READ, comp_bio, mirror_num, 0);
|
||||
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Oracle. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BTRFS_CRC32C__
|
||||
#define __BTRFS_CRC32C__
|
||||
#include <linux/crc32c.h>
|
||||
|
||||
/*
|
||||
* this file used to do more for selecting the HW version of crc32c,
|
||||
* perhaps it will one day again soon.
|
||||
*/
|
||||
#define btrfs_crc32c(seed, data, length) crc32c(seed, data, length)
|
||||
#endif
|
||||
|
698
fs/btrfs/ctree.c
698
fs/btrfs/ctree.c
File diff suppressed because it is too large
Load Diff
330
fs/btrfs/ctree.h
330
fs/btrfs/ctree.h
@ -45,6 +45,8 @@ struct btrfs_ordered_sum;
|
||||
|
||||
#define BTRFS_MAX_LEVEL 8
|
||||
|
||||
#define BTRFS_COMPAT_EXTENT_TREE_V0
|
||||
|
||||
/*
|
||||
* files bigger than this get some pre-flushing when they are added
|
||||
* to the ordered operations list. That way we limit the total
|
||||
@ -267,7 +269,18 @@ static inline unsigned long btrfs_chunk_item_size(int num_stripes)
|
||||
}
|
||||
|
||||
#define BTRFS_FSID_SIZE 16
|
||||
#define BTRFS_HEADER_FLAG_WRITTEN (1 << 0)
|
||||
#define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0)
|
||||
#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1)
|
||||
#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32)
|
||||
#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33)
|
||||
|
||||
#define BTRFS_BACKREF_REV_MAX 256
|
||||
#define BTRFS_BACKREF_REV_SHIFT 56
|
||||
#define BTRFS_BACKREF_REV_MASK (((u64)BTRFS_BACKREF_REV_MAX - 1) << \
|
||||
BTRFS_BACKREF_REV_SHIFT)
|
||||
|
||||
#define BTRFS_OLD_BACKREF_REV 0
|
||||
#define BTRFS_MIXED_BACKREF_REV 1
|
||||
|
||||
/*
|
||||
* every tree block (leaf or node) starts with this header.
|
||||
@ -296,7 +309,6 @@ struct btrfs_header {
|
||||
sizeof(struct btrfs_item) - \
|
||||
sizeof(struct btrfs_file_extent_item))
|
||||
|
||||
#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32)
|
||||
|
||||
/*
|
||||
* this is a very generous portion of the super block, giving us
|
||||
@ -355,9 +367,12 @@ struct btrfs_super_block {
|
||||
* Compat flags that we support. If any incompat flags are set other than the
|
||||
* ones specified below then we will fail to mount
|
||||
*/
|
||||
#define BTRFS_FEATURE_COMPAT_SUPP 0x0
|
||||
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0x0
|
||||
#define BTRFS_FEATURE_INCOMPAT_SUPP 0x0
|
||||
#define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0)
|
||||
|
||||
#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
|
||||
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
|
||||
#define BTRFS_FEATURE_INCOMPAT_SUPP \
|
||||
BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF
|
||||
|
||||
/*
|
||||
* A leaf is full of items. offset and size tell us where to find
|
||||
@ -421,23 +436,65 @@ struct btrfs_path {
|
||||
unsigned int keep_locks:1;
|
||||
unsigned int skip_locking:1;
|
||||
unsigned int leave_spinning:1;
|
||||
unsigned int search_commit_root:1;
|
||||
};
|
||||
|
||||
/*
|
||||
* items in the extent btree are used to record the objectid of the
|
||||
* owner of the block and the number of references
|
||||
*/
|
||||
|
||||
struct btrfs_extent_item {
|
||||
__le64 refs;
|
||||
__le64 generation;
|
||||
__le64 flags;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_extent_item_v0 {
|
||||
__le32 refs;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_extent_ref {
|
||||
#define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r) >> 4) - \
|
||||
sizeof(struct btrfs_item))
|
||||
|
||||
#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0)
|
||||
#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1)
|
||||
|
||||
/* following flags only apply to tree blocks */
|
||||
|
||||
/* use full backrefs for extent pointers in the block */
|
||||
#define BTRFS_BLOCK_FLAG_FULL_BACKREF (1ULL << 8)
|
||||
|
||||
struct btrfs_tree_block_info {
|
||||
struct btrfs_disk_key key;
|
||||
u8 level;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_extent_data_ref {
|
||||
__le64 root;
|
||||
__le64 objectid;
|
||||
__le64 offset;
|
||||
__le32 count;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_shared_data_ref {
|
||||
__le32 count;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_extent_inline_ref {
|
||||
u8 type;
|
||||
u64 offset;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* old style backrefs item */
|
||||
struct btrfs_extent_ref_v0 {
|
||||
__le64 root;
|
||||
__le64 generation;
|
||||
__le64 objectid;
|
||||
__le32 num_refs;
|
||||
__le32 count;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
|
||||
/* dev extents record free space on individual devices. The owner
|
||||
* field points back to the chunk allocation mapping tree that allocated
|
||||
* the extent. The chunk tree uuid field is a way to double check the owner
|
||||
@ -695,12 +752,7 @@ struct btrfs_block_group_cache {
|
||||
struct list_head cluster_list;
|
||||
};
|
||||
|
||||
struct btrfs_leaf_ref_tree {
|
||||
struct rb_root root;
|
||||
struct list_head list;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct reloc_control;
|
||||
struct btrfs_device;
|
||||
struct btrfs_fs_devices;
|
||||
struct btrfs_fs_info {
|
||||
@ -831,18 +883,11 @@ struct btrfs_fs_info {
|
||||
struct task_struct *cleaner_kthread;
|
||||
int thread_pool_size;
|
||||
|
||||
/* tree relocation relocated fields */
|
||||
struct list_head dead_reloc_roots;
|
||||
struct btrfs_leaf_ref_tree reloc_ref_tree;
|
||||
struct btrfs_leaf_ref_tree shared_ref_tree;
|
||||
|
||||
struct kobject super_kobj;
|
||||
struct completion kobj_unregister;
|
||||
int do_barriers;
|
||||
int closing;
|
||||
int log_root_recovering;
|
||||
atomic_t throttles;
|
||||
atomic_t throttle_gen;
|
||||
|
||||
u64 total_pinned;
|
||||
|
||||
@ -861,6 +906,8 @@ struct btrfs_fs_info {
|
||||
*/
|
||||
struct list_head space_info;
|
||||
|
||||
struct reloc_control *reloc_ctl;
|
||||
|
||||
spinlock_t delalloc_lock;
|
||||
spinlock_t new_trans_lock;
|
||||
u64 delalloc_bytes;
|
||||
@ -891,7 +938,6 @@ struct btrfs_fs_info {
|
||||
* in ram representation of the tree. extent_root is used for all allocations
|
||||
* and for the extent tree extent_root root.
|
||||
*/
|
||||
struct btrfs_dirty_root;
|
||||
struct btrfs_root {
|
||||
struct extent_buffer *node;
|
||||
|
||||
@ -899,9 +945,6 @@ struct btrfs_root {
|
||||
spinlock_t node_lock;
|
||||
|
||||
struct extent_buffer *commit_root;
|
||||
struct btrfs_leaf_ref_tree *ref_tree;
|
||||
struct btrfs_leaf_ref_tree ref_tree_struct;
|
||||
struct btrfs_dirty_root *dirty_root;
|
||||
struct btrfs_root *log_root;
|
||||
struct btrfs_root *reloc_root;
|
||||
|
||||
@ -952,10 +995,15 @@ struct btrfs_root {
|
||||
/* the dirty list is only used by non-reference counted roots */
|
||||
struct list_head dirty_list;
|
||||
|
||||
struct list_head root_list;
|
||||
|
||||
spinlock_t list_lock;
|
||||
struct list_head dead_list;
|
||||
struct list_head orphan_list;
|
||||
|
||||
spinlock_t inode_lock;
|
||||
/* red-black tree that keeps track of in-memory inodes */
|
||||
struct rb_root inode_tree;
|
||||
|
||||
/*
|
||||
* right now this just gets used so that a root has its own devid
|
||||
* for stat. It may be used for more later
|
||||
@ -1017,7 +1065,16 @@ struct btrfs_root {
|
||||
* are used, and how many references there are to each block
|
||||
*/
|
||||
#define BTRFS_EXTENT_ITEM_KEY 168
|
||||
#define BTRFS_EXTENT_REF_KEY 180
|
||||
|
||||
#define BTRFS_TREE_BLOCK_REF_KEY 176
|
||||
|
||||
#define BTRFS_EXTENT_DATA_REF_KEY 178
|
||||
|
||||
#define BTRFS_EXTENT_REF_V0_KEY 180
|
||||
|
||||
#define BTRFS_SHARED_BLOCK_REF_KEY 182
|
||||
|
||||
#define BTRFS_SHARED_DATA_REF_KEY 184
|
||||
|
||||
/*
|
||||
* block groups give us hints into the extent allocation trees. Which
|
||||
@ -1043,6 +1100,8 @@ struct btrfs_root {
|
||||
#define BTRFS_MOUNT_COMPRESS (1 << 5)
|
||||
#define BTRFS_MOUNT_NOTREELOG (1 << 6)
|
||||
#define BTRFS_MOUNT_FLUSHONCOMMIT (1 << 7)
|
||||
#define BTRFS_MOUNT_SSD_SPREAD (1 << 8)
|
||||
#define BTRFS_MOUNT_NOSSD (1 << 9)
|
||||
|
||||
#define btrfs_clear_opt(o, opt) ((o) &= ~BTRFS_MOUNT_##opt)
|
||||
#define btrfs_set_opt(o, opt) ((o) |= BTRFS_MOUNT_##opt)
|
||||
@ -1056,12 +1115,14 @@ struct btrfs_root {
|
||||
#define BTRFS_INODE_READONLY (1 << 2)
|
||||
#define BTRFS_INODE_NOCOMPRESS (1 << 3)
|
||||
#define BTRFS_INODE_PREALLOC (1 << 4)
|
||||
#define btrfs_clear_flag(inode, flag) (BTRFS_I(inode)->flags &= \
|
||||
~BTRFS_INODE_##flag)
|
||||
#define btrfs_set_flag(inode, flag) (BTRFS_I(inode)->flags |= \
|
||||
BTRFS_INODE_##flag)
|
||||
#define btrfs_test_flag(inode, flag) (BTRFS_I(inode)->flags & \
|
||||
BTRFS_INODE_##flag)
|
||||
#define BTRFS_INODE_SYNC (1 << 5)
|
||||
#define BTRFS_INODE_IMMUTABLE (1 << 6)
|
||||
#define BTRFS_INODE_APPEND (1 << 7)
|
||||
#define BTRFS_INODE_NODUMP (1 << 8)
|
||||
#define BTRFS_INODE_NOATIME (1 << 9)
|
||||
#define BTRFS_INODE_DIRSYNC (1 << 10)
|
||||
|
||||
|
||||
/* 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:
|
||||
@ -1317,24 +1378,67 @@ static inline u8 *btrfs_dev_extent_chunk_tree_uuid(struct btrfs_dev_extent *dev)
|
||||
return (u8 *)((unsigned long)dev + ptr);
|
||||
}
|
||||
|
||||
/* struct btrfs_extent_ref */
|
||||
BTRFS_SETGET_FUNCS(ref_root, struct btrfs_extent_ref, root, 64);
|
||||
BTRFS_SETGET_FUNCS(ref_generation, struct btrfs_extent_ref, generation, 64);
|
||||
BTRFS_SETGET_FUNCS(ref_objectid, struct btrfs_extent_ref, objectid, 64);
|
||||
BTRFS_SETGET_FUNCS(ref_num_refs, struct btrfs_extent_ref, num_refs, 32);
|
||||
BTRFS_SETGET_FUNCS(extent_refs, struct btrfs_extent_item, refs, 64);
|
||||
BTRFS_SETGET_FUNCS(extent_generation, struct btrfs_extent_item,
|
||||
generation, 64);
|
||||
BTRFS_SETGET_FUNCS(extent_flags, struct btrfs_extent_item, flags, 64);
|
||||
|
||||
BTRFS_SETGET_STACK_FUNCS(stack_ref_root, struct btrfs_extent_ref, root, 64);
|
||||
BTRFS_SETGET_STACK_FUNCS(stack_ref_generation, struct btrfs_extent_ref,
|
||||
generation, 64);
|
||||
BTRFS_SETGET_STACK_FUNCS(stack_ref_objectid, struct btrfs_extent_ref,
|
||||
objectid, 64);
|
||||
BTRFS_SETGET_STACK_FUNCS(stack_ref_num_refs, struct btrfs_extent_ref,
|
||||
num_refs, 32);
|
||||
BTRFS_SETGET_FUNCS(extent_refs_v0, struct btrfs_extent_item_v0, refs, 32);
|
||||
|
||||
/* struct btrfs_extent_item */
|
||||
BTRFS_SETGET_FUNCS(extent_refs, struct btrfs_extent_item, refs, 32);
|
||||
BTRFS_SETGET_STACK_FUNCS(stack_extent_refs, struct btrfs_extent_item,
|
||||
refs, 32);
|
||||
|
||||
BTRFS_SETGET_FUNCS(tree_block_level, struct btrfs_tree_block_info, level, 8);
|
||||
|
||||
static inline void btrfs_tree_block_key(struct extent_buffer *eb,
|
||||
struct btrfs_tree_block_info *item,
|
||||
struct btrfs_disk_key *key)
|
||||
{
|
||||
read_eb_member(eb, item, struct btrfs_tree_block_info, key, key);
|
||||
}
|
||||
|
||||
static inline void btrfs_set_tree_block_key(struct extent_buffer *eb,
|
||||
struct btrfs_tree_block_info *item,
|
||||
struct btrfs_disk_key *key)
|
||||
{
|
||||
write_eb_member(eb, item, struct btrfs_tree_block_info, key, key);
|
||||
}
|
||||
|
||||
BTRFS_SETGET_FUNCS(extent_data_ref_root, struct btrfs_extent_data_ref,
|
||||
root, 64);
|
||||
BTRFS_SETGET_FUNCS(extent_data_ref_objectid, struct btrfs_extent_data_ref,
|
||||
objectid, 64);
|
||||
BTRFS_SETGET_FUNCS(extent_data_ref_offset, struct btrfs_extent_data_ref,
|
||||
offset, 64);
|
||||
BTRFS_SETGET_FUNCS(extent_data_ref_count, struct btrfs_extent_data_ref,
|
||||
count, 32);
|
||||
|
||||
BTRFS_SETGET_FUNCS(shared_data_ref_count, struct btrfs_shared_data_ref,
|
||||
count, 32);
|
||||
|
||||
BTRFS_SETGET_FUNCS(extent_inline_ref_type, struct btrfs_extent_inline_ref,
|
||||
type, 8);
|
||||
BTRFS_SETGET_FUNCS(extent_inline_ref_offset, struct btrfs_extent_inline_ref,
|
||||
offset, 64);
|
||||
|
||||
static inline u32 btrfs_extent_inline_ref_size(int type)
|
||||
{
|
||||
if (type == BTRFS_TREE_BLOCK_REF_KEY ||
|
||||
type == BTRFS_SHARED_BLOCK_REF_KEY)
|
||||
return sizeof(struct btrfs_extent_inline_ref);
|
||||
if (type == BTRFS_SHARED_DATA_REF_KEY)
|
||||
return sizeof(struct btrfs_shared_data_ref) +
|
||||
sizeof(struct btrfs_extent_inline_ref);
|
||||
if (type == BTRFS_EXTENT_DATA_REF_KEY)
|
||||
return sizeof(struct btrfs_extent_data_ref) +
|
||||
offsetof(struct btrfs_extent_inline_ref, offset);
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
BTRFS_SETGET_FUNCS(ref_root_v0, struct btrfs_extent_ref_v0, root, 64);
|
||||
BTRFS_SETGET_FUNCS(ref_generation_v0, struct btrfs_extent_ref_v0,
|
||||
generation, 64);
|
||||
BTRFS_SETGET_FUNCS(ref_objectid_v0, struct btrfs_extent_ref_v0, objectid, 64);
|
||||
BTRFS_SETGET_FUNCS(ref_count_v0, struct btrfs_extent_ref_v0, count, 32);
|
||||
|
||||
/* struct btrfs_node */
|
||||
BTRFS_SETGET_FUNCS(key_blockptr, struct btrfs_key_ptr, blockptr, 64);
|
||||
@ -1558,6 +1662,21 @@ static inline int btrfs_clear_header_flag(struct extent_buffer *eb, u64 flag)
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
|
||||
static inline int btrfs_header_backref_rev(struct extent_buffer *eb)
|
||||
{
|
||||
u64 flags = btrfs_header_flags(eb);
|
||||
return flags >> BTRFS_BACKREF_REV_SHIFT;
|
||||
}
|
||||
|
||||
static inline void btrfs_set_header_backref_rev(struct extent_buffer *eb,
|
||||
int rev)
|
||||
{
|
||||
u64 flags = btrfs_header_flags(eb);
|
||||
flags &= ~BTRFS_BACKREF_REV_MASK;
|
||||
flags |= (u64)rev << BTRFS_BACKREF_REV_SHIFT;
|
||||
btrfs_set_header_flags(eb, flags);
|
||||
}
|
||||
|
||||
static inline u8 *btrfs_header_fsid(struct extent_buffer *eb)
|
||||
{
|
||||
unsigned long ptr = offsetof(struct btrfs_header, fsid);
|
||||
@ -1790,39 +1909,32 @@ int btrfs_update_pinned_extents(struct btrfs_root *root,
|
||||
int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct extent_buffer *leaf);
|
||||
int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 objectid, u64 bytenr);
|
||||
struct btrfs_root *root,
|
||||
u64 objectid, u64 offset, u64 bytenr);
|
||||
int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy);
|
||||
struct btrfs_block_group_cache *btrfs_lookup_block_group(
|
||||
struct btrfs_fs_info *info,
|
||||
u64 bytenr);
|
||||
void btrfs_put_block_group(struct btrfs_block_group_cache *cache);
|
||||
u64 btrfs_find_block_group(struct btrfs_root *root,
|
||||
u64 search_start, u64 search_hint, int owner);
|
||||
struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u32 blocksize, u64 parent,
|
||||
u64 root_objectid,
|
||||
u64 ref_generation,
|
||||
int level,
|
||||
u64 hint,
|
||||
u64 empty_size);
|
||||
struct btrfs_root *root, u32 blocksize,
|
||||
u64 parent, u64 root_objectid,
|
||||
struct btrfs_disk_key *key, int level,
|
||||
u64 hint, u64 empty_size);
|
||||
struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 bytenr, u32 blocksize,
|
||||
int level);
|
||||
int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 num_bytes, u64 parent, u64 min_bytes,
|
||||
u64 root_objectid, u64 ref_generation,
|
||||
u64 owner, u64 empty_size, u64 hint_byte,
|
||||
u64 search_end, struct btrfs_key *ins, u64 data);
|
||||
int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 parent,
|
||||
u64 root_objectid, u64 ref_generation,
|
||||
u64 owner, struct btrfs_key *ins);
|
||||
int btrfs_alloc_logged_extent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 parent,
|
||||
u64 root_objectid, u64 ref_generation,
|
||||
u64 owner, struct btrfs_key *ins);
|
||||
int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 root_objectid, u64 owner,
|
||||
u64 offset, struct btrfs_key *ins);
|
||||
int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 root_objectid, u64 owner, u64 offset,
|
||||
struct btrfs_key *ins);
|
||||
int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 num_bytes, u64 min_alloc_size,
|
||||
@ -1830,18 +1942,18 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
|
||||
u64 search_end, struct btrfs_key *ins,
|
||||
u64 data);
|
||||
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct extent_buffer *orig_buf, struct extent_buffer *buf,
|
||||
u32 *nr_extents);
|
||||
int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct extent_buffer *buf, u32 nr_extents);
|
||||
int btrfs_update_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct extent_buffer *orig_buf,
|
||||
struct extent_buffer *buf, int start_slot, int nr);
|
||||
struct extent_buffer *buf, int full_backref);
|
||||
int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct extent_buffer *buf, int full_backref);
|
||||
int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 bytenr, u64 num_bytes, u64 flags,
|
||||
int is_data);
|
||||
int btrfs_free_extent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 bytenr, u64 num_bytes, u64 parent,
|
||||
u64 root_objectid, u64 ref_generation,
|
||||
u64 owner_objectid, int pin);
|
||||
u64 root_objectid, u64 owner, u64 offset);
|
||||
|
||||
int btrfs_free_reserved_extent(struct btrfs_root *root, u64 start, u64 len);
|
||||
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
@ -1849,13 +1961,8 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
|
||||
int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 bytenr, u64 num_bytes, u64 parent,
|
||||
u64 root_objectid, u64 ref_generation,
|
||||
u64 owner_objectid);
|
||||
int btrfs_update_extent_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 bytenr, u64 num_bytes,
|
||||
u64 orig_parent, u64 parent,
|
||||
u64 root_objectid, u64 ref_generation,
|
||||
u64 owner_objectid);
|
||||
u64 root_objectid, u64 owner, u64 offset);
|
||||
|
||||
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr);
|
||||
@ -1867,16 +1974,9 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
|
||||
u64 size);
|
||||
int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 group_start);
|
||||
int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start);
|
||||
int btrfs_free_reloc_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
int btrfs_drop_dead_reloc_roots(struct btrfs_root *root);
|
||||
int btrfs_reloc_tree_cache_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct extent_buffer *buf, u64 orig_start);
|
||||
int btrfs_add_dead_reloc_root(struct btrfs_root *root);
|
||||
int btrfs_cleanup_reloc_trees(struct btrfs_root *root);
|
||||
int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len);
|
||||
int btrfs_prepare_block_group_relocation(struct btrfs_root *root,
|
||||
struct btrfs_block_group_cache *group);
|
||||
|
||||
u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags);
|
||||
void btrfs_set_inode_space_info(struct btrfs_root *root, struct inode *ionde);
|
||||
void btrfs_clear_space_info_full(struct btrfs_fs_info *info);
|
||||
@ -1891,13 +1991,12 @@ void btrfs_delalloc_reserve_space(struct btrfs_root *root, struct inode *inode,
|
||||
void btrfs_delalloc_free_space(struct btrfs_root *root, struct inode *inode,
|
||||
u64 bytes);
|
||||
/* ctree.c */
|
||||
int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
|
||||
int level, int *slot);
|
||||
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_merge_path(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_key *node_keys,
|
||||
u64 *nodes, int lowest_level);
|
||||
int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct btrfs_path *path,
|
||||
struct btrfs_key *new_key);
|
||||
@ -1918,6 +2017,8 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct extent_buffer *buf,
|
||||
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,
|
||||
@ -1944,9 +2045,6 @@ void btrfs_unlock_up_safe(struct btrfs_path *p, int level);
|
||||
|
||||
int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct btrfs_path *path, int slot, int nr);
|
||||
int btrfs_del_leaf(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path, u64 bytenr);
|
||||
static inline int btrfs_del_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path)
|
||||
@ -2005,8 +2103,9 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
|
||||
btrfs_root_item *item, struct btrfs_key *key);
|
||||
int btrfs_search_root(struct btrfs_root *root, u64 search_start,
|
||||
u64 *found_objectid);
|
||||
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid,
|
||||
struct btrfs_root *latest_root);
|
||||
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
|
||||
int btrfs_set_root_node(struct btrfs_root_item *item,
|
||||
struct extent_buffer *node);
|
||||
/* dir-item.c */
|
||||
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, const char *name,
|
||||
@ -2139,7 +2238,6 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
|
||||
int btrfs_readpage(struct file *file, struct page *page);
|
||||
void btrfs_delete_inode(struct inode *inode);
|
||||
void btrfs_put_inode(struct inode *inode);
|
||||
void btrfs_read_locked_inode(struct inode *inode);
|
||||
int btrfs_write_inode(struct inode *inode, int wait);
|
||||
void btrfs_dirty_inode(struct inode *inode);
|
||||
struct inode *btrfs_alloc_inode(struct super_block *sb);
|
||||
@ -2147,12 +2245,8 @@ void btrfs_destroy_inode(struct inode *inode);
|
||||
int btrfs_init_cachep(void);
|
||||
void btrfs_destroy_cachep(void);
|
||||
long btrfs_ioctl_trans_end(struct file *file);
|
||||
struct inode *btrfs_ilookup(struct super_block *s, u64 objectid,
|
||||
struct btrfs_root *root, int wait);
|
||||
struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
|
||||
struct btrfs_root *root);
|
||||
struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
|
||||
struct btrfs_root *root, int *is_new);
|
||||
struct btrfs_root *root);
|
||||
int btrfs_commit_write(struct file *file, struct page *page,
|
||||
unsigned from, unsigned to);
|
||||
struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
|
||||
@ -2168,6 +2262,8 @@ int btrfs_cont_expand(struct inode *inode, loff_t size);
|
||||
|
||||
/* ioctl.c */
|
||||
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
void btrfs_update_iflags(struct inode *inode);
|
||||
void btrfs_inherit_iflags(struct inode *inode, struct inode *dir);
|
||||
|
||||
/* file.c */
|
||||
int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync);
|
||||
@ -2205,8 +2301,20 @@ int btrfs_parse_options(struct btrfs_root *root, char *options);
|
||||
int btrfs_sync_fs(struct super_block *sb, int wait);
|
||||
|
||||
/* acl.c */
|
||||
#ifdef CONFIG_FS_POSIX_ACL
|
||||
int btrfs_check_acl(struct inode *inode, int mask);
|
||||
#else
|
||||
#define btrfs_check_acl NULL
|
||||
#endif
|
||||
int btrfs_init_acl(struct inode *inode, struct inode *dir);
|
||||
int btrfs_acl_chmod(struct inode *inode);
|
||||
|
||||
/* relocation.c */
|
||||
int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start);
|
||||
int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
int btrfs_recover_relocation(struct btrfs_root *root);
|
||||
int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len);
|
||||
#endif
|
||||
|
@ -29,27 +29,87 @@
|
||||
* add extents in the middle of btrfs_search_slot, and it allows
|
||||
* us to buffer up frequently modified backrefs in an rb tree instead
|
||||
* of hammering updates on the extent allocation tree.
|
||||
*
|
||||
* Right now this code is only used for reference counted trees, but
|
||||
* the long term goal is to get rid of the similar code for delayed
|
||||
* extent tree modifications.
|
||||
*/
|
||||
|
||||
/*
|
||||
* entries in the rb tree are ordered by the byte number of the extent
|
||||
* and by the byte number of the parent block.
|
||||
* compare two delayed tree backrefs with same bytenr and type
|
||||
*/
|
||||
static int comp_entry(struct btrfs_delayed_ref_node *ref,
|
||||
u64 bytenr, u64 parent)
|
||||
static int comp_tree_refs(struct btrfs_delayed_tree_ref *ref2,
|
||||
struct btrfs_delayed_tree_ref *ref1)
|
||||
{
|
||||
if (bytenr < ref->bytenr)
|
||||
if (ref1->node.type == BTRFS_TREE_BLOCK_REF_KEY) {
|
||||
if (ref1->root < ref2->root)
|
||||
return -1;
|
||||
if (ref1->root > ref2->root)
|
||||
return 1;
|
||||
} else {
|
||||
if (ref1->parent < ref2->parent)
|
||||
return -1;
|
||||
if (ref1->parent > ref2->parent)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* compare two delayed data backrefs with same bytenr and type
|
||||
*/
|
||||
static int comp_data_refs(struct btrfs_delayed_data_ref *ref2,
|
||||
struct btrfs_delayed_data_ref *ref1)
|
||||
{
|
||||
if (ref1->node.type == BTRFS_EXTENT_DATA_REF_KEY) {
|
||||
if (ref1->root < ref2->root)
|
||||
return -1;
|
||||
if (ref1->root > ref2->root)
|
||||
return 1;
|
||||
if (ref1->objectid < ref2->objectid)
|
||||
return -1;
|
||||
if (ref1->objectid > ref2->objectid)
|
||||
return 1;
|
||||
if (ref1->offset < ref2->offset)
|
||||
return -1;
|
||||
if (ref1->offset > ref2->offset)
|
||||
return 1;
|
||||
} else {
|
||||
if (ref1->parent < ref2->parent)
|
||||
return -1;
|
||||
if (ref1->parent > ref2->parent)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* entries in the rb tree are ordered by the byte number of the extent,
|
||||
* type of the delayed backrefs and content of delayed backrefs.
|
||||
*/
|
||||
static int comp_entry(struct btrfs_delayed_ref_node *ref2,
|
||||
struct btrfs_delayed_ref_node *ref1)
|
||||
{
|
||||
if (ref1->bytenr < ref2->bytenr)
|
||||
return -1;
|
||||
if (bytenr > ref->bytenr)
|
||||
if (ref1->bytenr > ref2->bytenr)
|
||||
return 1;
|
||||
if (parent < ref->parent)
|
||||
if (ref1->is_head && ref2->is_head)
|
||||
return 0;
|
||||
if (ref2->is_head)
|
||||
return -1;
|
||||
if (parent > ref->parent)
|
||||
if (ref1->is_head)
|
||||
return 1;
|
||||
if (ref1->type < ref2->type)
|
||||
return -1;
|
||||
if (ref1->type > ref2->type)
|
||||
return 1;
|
||||
if (ref1->type == BTRFS_TREE_BLOCK_REF_KEY ||
|
||||
ref1->type == BTRFS_SHARED_BLOCK_REF_KEY) {
|
||||
return comp_tree_refs(btrfs_delayed_node_to_tree_ref(ref2),
|
||||
btrfs_delayed_node_to_tree_ref(ref1));
|
||||
} else if (ref1->type == BTRFS_EXTENT_DATA_REF_KEY ||
|
||||
ref1->type == BTRFS_SHARED_DATA_REF_KEY) {
|
||||
return comp_data_refs(btrfs_delayed_node_to_data_ref(ref2),
|
||||
btrfs_delayed_node_to_data_ref(ref1));
|
||||
}
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -59,20 +119,21 @@ static int comp_entry(struct btrfs_delayed_ref_node *ref,
|
||||
* inserted.
|
||||
*/
|
||||
static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root,
|
||||
u64 bytenr, u64 parent,
|
||||
struct rb_node *node)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent_node = NULL;
|
||||
struct btrfs_delayed_ref_node *entry;
|
||||
struct btrfs_delayed_ref_node *ins;
|
||||
int cmp;
|
||||
|
||||
ins = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
|
||||
while (*p) {
|
||||
parent_node = *p;
|
||||
entry = rb_entry(parent_node, struct btrfs_delayed_ref_node,
|
||||
rb_node);
|
||||
|
||||
cmp = comp_entry(entry, bytenr, parent);
|
||||
cmp = comp_entry(entry, ins);
|
||||
if (cmp < 0)
|
||||
p = &(*p)->rb_left;
|
||||
else if (cmp > 0)
|
||||
@ -81,18 +142,17 @@ static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root,
|
||||
return entry;
|
||||
}
|
||||
|
||||
entry = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
|
||||
rb_link_node(node, parent_node, p);
|
||||
rb_insert_color(node, root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* find an entry based on (bytenr,parent). This returns the delayed
|
||||
* ref if it was able to find one, or NULL if nothing was in that spot
|
||||
* find an head entry based on bytenr. This returns the delayed ref
|
||||
* head if it was able to find one, or NULL if nothing was in that spot
|
||||
*/
|
||||
static struct btrfs_delayed_ref_node *tree_search(struct rb_root *root,
|
||||
u64 bytenr, u64 parent,
|
||||
static struct btrfs_delayed_ref_node *find_ref_head(struct rb_root *root,
|
||||
u64 bytenr,
|
||||
struct btrfs_delayed_ref_node **last)
|
||||
{
|
||||
struct rb_node *n = root->rb_node;
|
||||
@ -105,7 +165,15 @@ static struct btrfs_delayed_ref_node *tree_search(struct rb_root *root,
|
||||
if (last)
|
||||
*last = entry;
|
||||
|
||||
cmp = comp_entry(entry, bytenr, parent);
|
||||
if (bytenr < entry->bytenr)
|
||||
cmp = -1;
|
||||
else if (bytenr > entry->bytenr)
|
||||
cmp = 1;
|
||||
else if (!btrfs_delayed_ref_is_head(entry))
|
||||
cmp = 1;
|
||||
else
|
||||
cmp = 0;
|
||||
|
||||
if (cmp < 0)
|
||||
n = n->rb_left;
|
||||
else if (cmp > 0)
|
||||
@ -154,7 +222,7 @@ int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans,
|
||||
node = rb_first(&delayed_refs->root);
|
||||
} else {
|
||||
ref = NULL;
|
||||
tree_search(&delayed_refs->root, start, (u64)-1, &ref);
|
||||
find_ref_head(&delayed_refs->root, start, &ref);
|
||||
if (ref) {
|
||||
struct btrfs_delayed_ref_node *tmp;
|
||||
|
||||
@ -234,7 +302,7 @@ int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr)
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
spin_lock(&delayed_refs->lock);
|
||||
|
||||
ref = tree_search(&delayed_refs->root, bytenr, (u64)-1, NULL);
|
||||
ref = find_ref_head(&delayed_refs->root, bytenr, NULL);
|
||||
if (ref) {
|
||||
prev_node = rb_prev(&ref->rb_node);
|
||||
if (!prev_node)
|
||||
@ -250,25 +318,28 @@ out:
|
||||
}
|
||||
|
||||
/*
|
||||
* helper function to lookup reference count
|
||||
* helper function to lookup reference count and flags of extent.
|
||||
*
|
||||
* the head node for delayed ref is used to store the sum of all the
|
||||
* reference count modifications queued up in the rbtree. This way you
|
||||
* can check to see what the reference count would be if all of the
|
||||
* delayed refs are processed.
|
||||
* reference count modifications queued up in the rbtree. the head
|
||||
* node may also store the extent flags to set. This way you can check
|
||||
* to see what the reference count and extent flags would be if all of
|
||||
* the delayed refs are not processed.
|
||||
*/
|
||||
int btrfs_lookup_extent_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 bytenr,
|
||||
u64 num_bytes, u32 *refs)
|
||||
int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 bytenr,
|
||||
u64 num_bytes, u64 *refs, u64 *flags)
|
||||
{
|
||||
struct btrfs_delayed_ref_node *ref;
|
||||
struct btrfs_delayed_ref_head *head;
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
struct btrfs_path *path;
|
||||
struct extent_buffer *leaf;
|
||||
struct btrfs_extent_item *ei;
|
||||
struct extent_buffer *leaf;
|
||||
struct btrfs_key key;
|
||||
u32 num_refs;
|
||||
u32 item_size;
|
||||
u64 num_refs;
|
||||
u64 extent_flags;
|
||||
int ret;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
@ -287,37 +358,60 @@ again:
|
||||
|
||||
if (ret == 0) {
|
||||
leaf = path->nodes[0];
|
||||
ei = btrfs_item_ptr(leaf, path->slots[0],
|
||||
struct btrfs_extent_item);
|
||||
num_refs = btrfs_extent_refs(leaf, ei);
|
||||
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
|
||||
if (item_size >= sizeof(*ei)) {
|
||||
ei = btrfs_item_ptr(leaf, path->slots[0],
|
||||
struct btrfs_extent_item);
|
||||
num_refs = btrfs_extent_refs(leaf, ei);
|
||||
extent_flags = btrfs_extent_flags(leaf, ei);
|
||||
} else {
|
||||
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
|
||||
struct btrfs_extent_item_v0 *ei0;
|
||||
BUG_ON(item_size != sizeof(*ei0));
|
||||
ei0 = btrfs_item_ptr(leaf, path->slots[0],
|
||||
struct btrfs_extent_item_v0);
|
||||
num_refs = btrfs_extent_refs_v0(leaf, ei0);
|
||||
/* FIXME: this isn't correct for data */
|
||||
extent_flags = BTRFS_BLOCK_FLAG_FULL_BACKREF;
|
||||
#else
|
||||
BUG();
|
||||
#endif
|
||||
}
|
||||
BUG_ON(num_refs == 0);
|
||||
} else {
|
||||
num_refs = 0;
|
||||
extent_flags = 0;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
spin_lock(&delayed_refs->lock);
|
||||
ref = tree_search(&delayed_refs->root, bytenr, (u64)-1, NULL);
|
||||
ref = find_ref_head(&delayed_refs->root, bytenr, NULL);
|
||||
if (ref) {
|
||||
head = btrfs_delayed_node_to_head(ref);
|
||||
if (mutex_trylock(&head->mutex)) {
|
||||
num_refs += ref->ref_mod;
|
||||
if (!mutex_trylock(&head->mutex)) {
|
||||
atomic_inc(&ref->refs);
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
|
||||
btrfs_release_path(root->fs_info->extent_root, path);
|
||||
|
||||
mutex_lock(&head->mutex);
|
||||
mutex_unlock(&head->mutex);
|
||||
*refs = num_refs;
|
||||
goto out;
|
||||
btrfs_put_delayed_ref(ref);
|
||||
goto again;
|
||||
}
|
||||
if (head->extent_op && head->extent_op->update_flags)
|
||||
extent_flags |= head->extent_op->flags_to_set;
|
||||
else
|
||||
BUG_ON(num_refs == 0);
|
||||
|
||||
atomic_inc(&ref->refs);
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
|
||||
btrfs_release_path(root->fs_info->extent_root, path);
|
||||
|
||||
mutex_lock(&head->mutex);
|
||||
num_refs += ref->ref_mod;
|
||||
mutex_unlock(&head->mutex);
|
||||
btrfs_put_delayed_ref(ref);
|
||||
goto again;
|
||||
} else {
|
||||
*refs = num_refs;
|
||||
}
|
||||
WARN_ON(num_refs == 0);
|
||||
if (refs)
|
||||
*refs = num_refs;
|
||||
if (flags)
|
||||
*flags = extent_flags;
|
||||
out:
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
btrfs_free_path(path);
|
||||
@ -338,16 +432,7 @@ update_existing_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_delayed_ref_node *existing,
|
||||
struct btrfs_delayed_ref_node *update)
|
||||
{
|
||||
struct btrfs_delayed_ref *existing_ref;
|
||||
struct btrfs_delayed_ref *ref;
|
||||
|
||||
existing_ref = btrfs_delayed_node_to_ref(existing);
|
||||
ref = btrfs_delayed_node_to_ref(update);
|
||||
|
||||
if (ref->pin)
|
||||
existing_ref->pin = 1;
|
||||
|
||||
if (ref->action != existing_ref->action) {
|
||||
if (update->action != existing->action) {
|
||||
/*
|
||||
* this is effectively undoing either an add or a
|
||||
* drop. We decrement the ref_mod, and if it goes
|
||||
@ -363,20 +448,13 @@ update_existing_ref(struct btrfs_trans_handle *trans,
|
||||
delayed_refs->num_entries--;
|
||||
if (trans->delayed_ref_updates)
|
||||
trans->delayed_ref_updates--;
|
||||
} else {
|
||||
WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY ||
|
||||
existing->type == BTRFS_SHARED_BLOCK_REF_KEY);
|
||||
}
|
||||
} else {
|
||||
if (existing_ref->action == BTRFS_ADD_DELAYED_REF) {
|
||||
/* if we're adding refs, make sure all the
|
||||
* details match up. The extent could
|
||||
* have been totally freed and reallocated
|
||||
* by a different owner before the delayed
|
||||
* ref entries were removed.
|
||||
*/
|
||||
existing_ref->owner_objectid = ref->owner_objectid;
|
||||
existing_ref->generation = ref->generation;
|
||||
existing_ref->root = ref->root;
|
||||
existing->num_bytes = update->num_bytes;
|
||||
}
|
||||
WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY ||
|
||||
existing->type == BTRFS_SHARED_BLOCK_REF_KEY);
|
||||
/*
|
||||
* the action on the existing ref matches
|
||||
* the action on the ref we're trying to add.
|
||||
@ -401,6 +479,7 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
|
||||
|
||||
existing_ref = btrfs_delayed_node_to_head(existing);
|
||||
ref = btrfs_delayed_node_to_head(update);
|
||||
BUG_ON(existing_ref->is_data != ref->is_data);
|
||||
|
||||
if (ref->must_insert_reserved) {
|
||||
/* if the extent was freed and then
|
||||
@ -420,6 +499,24 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
|
||||
|
||||
}
|
||||
|
||||
if (ref->extent_op) {
|
||||
if (!existing_ref->extent_op) {
|
||||
existing_ref->extent_op = ref->extent_op;
|
||||
} else {
|
||||
if (ref->extent_op->update_key) {
|
||||
memcpy(&existing_ref->extent_op->key,
|
||||
&ref->extent_op->key,
|
||||
sizeof(ref->extent_op->key));
|
||||
existing_ref->extent_op->update_key = 1;
|
||||
}
|
||||
if (ref->extent_op->update_flags) {
|
||||
existing_ref->extent_op->flags_to_set |=
|
||||
ref->extent_op->flags_to_set;
|
||||
existing_ref->extent_op->update_flags = 1;
|
||||
}
|
||||
kfree(ref->extent_op);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* update the reference mod on the head to reflect this new operation
|
||||
*/
|
||||
@ -427,19 +524,16 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
|
||||
}
|
||||
|
||||
/*
|
||||
* helper function to actually insert a delayed ref into the rbtree.
|
||||
* helper function to actually insert a head node into the rbtree.
|
||||
* this does all the dirty work in terms of maintaining the correct
|
||||
* overall modification count in the head node and properly dealing
|
||||
* with updating existing nodes as new modifications are queued.
|
||||
* overall modification count.
|
||||
*/
|
||||
static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_delayed_ref_node *ref,
|
||||
u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root,
|
||||
u64 ref_generation, u64 owner_objectid, int action,
|
||||
int pin)
|
||||
static noinline int add_delayed_ref_head(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_delayed_ref_node *ref,
|
||||
u64 bytenr, u64 num_bytes,
|
||||
int action, int is_data)
|
||||
{
|
||||
struct btrfs_delayed_ref_node *existing;
|
||||
struct btrfs_delayed_ref *full_ref;
|
||||
struct btrfs_delayed_ref_head *head_ref = NULL;
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
int count_mod = 1;
|
||||
@ -449,12 +543,10 @@ static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
* the head node stores the sum of all the mods, so dropping a ref
|
||||
* should drop the sum in the head node by one.
|
||||
*/
|
||||
if (parent == (u64)-1) {
|
||||
if (action == BTRFS_DROP_DELAYED_REF)
|
||||
count_mod = -1;
|
||||
else if (action == BTRFS_UPDATE_DELAYED_HEAD)
|
||||
count_mod = 0;
|
||||
}
|
||||
if (action == BTRFS_UPDATE_DELAYED_HEAD)
|
||||
count_mod = 0;
|
||||
else if (action == BTRFS_DROP_DELAYED_REF)
|
||||
count_mod = -1;
|
||||
|
||||
/*
|
||||
* BTRFS_ADD_DELAYED_EXTENT means that we need to update
|
||||
@ -467,57 +559,42 @@ static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
* Once we record must_insert_reserved, switch the action to
|
||||
* BTRFS_ADD_DELAYED_REF because other special casing is not required.
|
||||
*/
|
||||
if (action == BTRFS_ADD_DELAYED_EXTENT) {
|
||||
if (action == BTRFS_ADD_DELAYED_EXTENT)
|
||||
must_insert_reserved = 1;
|
||||
action = BTRFS_ADD_DELAYED_REF;
|
||||
} else {
|
||||
else
|
||||
must_insert_reserved = 0;
|
||||
}
|
||||
|
||||
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
|
||||
/* first set the basic ref node struct up */
|
||||
atomic_set(&ref->refs, 1);
|
||||
ref->bytenr = bytenr;
|
||||
ref->parent = parent;
|
||||
ref->ref_mod = count_mod;
|
||||
ref->in_tree = 1;
|
||||
ref->num_bytes = num_bytes;
|
||||
ref->ref_mod = count_mod;
|
||||
ref->type = 0;
|
||||
ref->action = 0;
|
||||
ref->is_head = 1;
|
||||
ref->in_tree = 1;
|
||||
|
||||
if (btrfs_delayed_ref_is_head(ref)) {
|
||||
head_ref = btrfs_delayed_node_to_head(ref);
|
||||
head_ref->must_insert_reserved = must_insert_reserved;
|
||||
INIT_LIST_HEAD(&head_ref->cluster);
|
||||
mutex_init(&head_ref->mutex);
|
||||
} else {
|
||||
full_ref = btrfs_delayed_node_to_ref(ref);
|
||||
full_ref->root = ref_root;
|
||||
full_ref->generation = ref_generation;
|
||||
full_ref->owner_objectid = owner_objectid;
|
||||
full_ref->pin = pin;
|
||||
full_ref->action = action;
|
||||
}
|
||||
head_ref = btrfs_delayed_node_to_head(ref);
|
||||
head_ref->must_insert_reserved = must_insert_reserved;
|
||||
head_ref->is_data = is_data;
|
||||
|
||||
existing = tree_insert(&delayed_refs->root, bytenr,
|
||||
parent, &ref->rb_node);
|
||||
INIT_LIST_HEAD(&head_ref->cluster);
|
||||
mutex_init(&head_ref->mutex);
|
||||
|
||||
existing = tree_insert(&delayed_refs->root, &ref->rb_node);
|
||||
|
||||
if (existing) {
|
||||
if (btrfs_delayed_ref_is_head(ref))
|
||||
update_existing_head_ref(existing, ref);
|
||||
else
|
||||
update_existing_ref(trans, delayed_refs, existing, ref);
|
||||
|
||||
update_existing_head_ref(existing, ref);
|
||||
/*
|
||||
* we've updated the existing ref, free the newly
|
||||
* allocated ref
|
||||
*/
|
||||
kfree(ref);
|
||||
} else {
|
||||
if (btrfs_delayed_ref_is_head(ref)) {
|
||||
delayed_refs->num_heads++;
|
||||
delayed_refs->num_heads_ready++;
|
||||
}
|
||||
delayed_refs->num_heads++;
|
||||
delayed_refs->num_heads_ready++;
|
||||
delayed_refs->num_entries++;
|
||||
trans->delayed_ref_updates++;
|
||||
}
|
||||
@ -525,37 +602,139 @@ static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
}
|
||||
|
||||
/*
|
||||
* add a delayed ref to the tree. This does all of the accounting required
|
||||
* helper to insert a delayed tree ref into the rbtree.
|
||||
*/
|
||||
static noinline int add_delayed_tree_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_delayed_ref_node *ref,
|
||||
u64 bytenr, u64 num_bytes, u64 parent,
|
||||
u64 ref_root, int level, int action)
|
||||
{
|
||||
struct btrfs_delayed_ref_node *existing;
|
||||
struct btrfs_delayed_tree_ref *full_ref;
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
|
||||
if (action == BTRFS_ADD_DELAYED_EXTENT)
|
||||
action = BTRFS_ADD_DELAYED_REF;
|
||||
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
|
||||
/* first set the basic ref node struct up */
|
||||
atomic_set(&ref->refs, 1);
|
||||
ref->bytenr = bytenr;
|
||||
ref->num_bytes = num_bytes;
|
||||
ref->ref_mod = 1;
|
||||
ref->action = action;
|
||||
ref->is_head = 0;
|
||||
ref->in_tree = 1;
|
||||
|
||||
full_ref = btrfs_delayed_node_to_tree_ref(ref);
|
||||
if (parent) {
|
||||
full_ref->parent = parent;
|
||||
ref->type = BTRFS_SHARED_BLOCK_REF_KEY;
|
||||
} else {
|
||||
full_ref->root = ref_root;
|
||||
ref->type = BTRFS_TREE_BLOCK_REF_KEY;
|
||||
}
|
||||
full_ref->level = level;
|
||||
|
||||
existing = tree_insert(&delayed_refs->root, &ref->rb_node);
|
||||
|
||||
if (existing) {
|
||||
update_existing_ref(trans, delayed_refs, existing, ref);
|
||||
/*
|
||||
* we've updated the existing ref, free the newly
|
||||
* allocated ref
|
||||
*/
|
||||
kfree(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_trans_handle *trans,
|
||||
struct btrfs_delayed_ref_node *ref,
|
||||
u64 bytenr, u64 num_bytes, u64 parent,
|
||||
u64 ref_root, u64 owner, u64 offset,
|
||||
int action)
|
||||
{
|
||||
struct btrfs_delayed_ref_node *existing;
|
||||
struct btrfs_delayed_data_ref *full_ref;
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
|
||||
if (action == BTRFS_ADD_DELAYED_EXTENT)
|
||||
action = BTRFS_ADD_DELAYED_REF;
|
||||
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
|
||||
/* first set the basic ref node struct up */
|
||||
atomic_set(&ref->refs, 1);
|
||||
ref->bytenr = bytenr;
|
||||
ref->num_bytes = num_bytes;
|
||||
ref->ref_mod = 1;
|
||||
ref->action = action;
|
||||
ref->is_head = 0;
|
||||
ref->in_tree = 1;
|
||||
|
||||
full_ref = btrfs_delayed_node_to_data_ref(ref);
|
||||
if (parent) {
|
||||
full_ref->parent = parent;
|
||||
ref->type = BTRFS_SHARED_DATA_REF_KEY;
|
||||
} else {
|
||||
full_ref->root = ref_root;
|
||||
ref->type = BTRFS_EXTENT_DATA_REF_KEY;
|
||||
}
|
||||
full_ref->objectid = owner;
|
||||
full_ref->offset = offset;
|
||||
|
||||
existing = tree_insert(&delayed_refs->root, &ref->rb_node);
|
||||
|
||||
if (existing) {
|
||||
update_existing_ref(trans, delayed_refs, existing, ref);
|
||||
/*
|
||||
* we've updated the existing ref, free the newly
|
||||
* allocated ref
|
||||
*/
|
||||
kfree(ref);
|
||||
} else {
|
||||
delayed_refs->num_entries++;
|
||||
trans->delayed_ref_updates++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* add a delayed tree ref. This does all of the accounting required
|
||||
* to make sure the delayed ref is eventually processed before this
|
||||
* transaction commits.
|
||||
*/
|
||||
int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root,
|
||||
u64 ref_generation, u64 owner_objectid, int action,
|
||||
int pin)
|
||||
int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes, u64 parent,
|
||||
u64 ref_root, int level, int action,
|
||||
struct btrfs_delayed_extent_op *extent_op)
|
||||
{
|
||||
struct btrfs_delayed_ref *ref;
|
||||
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);
|
||||
if (!ref)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* the parent = 0 case comes from cases where we don't actually
|
||||
* know the parent yet. It will get updated later via a add/drop
|
||||
* pair.
|
||||
*/
|
||||
if (parent == 0)
|
||||
parent = bytenr;
|
||||
|
||||
head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
|
||||
if (!head_ref) {
|
||||
kfree(ref);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
head_ref->extent_op = extent_op;
|
||||
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
spin_lock(&delayed_refs->lock);
|
||||
|
||||
@ -563,18 +742,88 @@ int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
* insert both the head node and the new ref without dropping
|
||||
* the spin lock
|
||||
*/
|
||||
ret = __btrfs_add_delayed_ref(trans, &head_ref->node, bytenr, num_bytes,
|
||||
(u64)-1, 0, 0, 0, action, pin);
|
||||
ret = add_delayed_ref_head(trans, &head_ref->node, bytenr, num_bytes,
|
||||
action, 0);
|
||||
BUG_ON(ret);
|
||||
|
||||
ret = __btrfs_add_delayed_ref(trans, &ref->node, bytenr, num_bytes,
|
||||
parent, ref_root, ref_generation,
|
||||
owner_objectid, action, pin);
|
||||
ret = add_delayed_tree_ref(trans, &ref->node, bytenr, num_bytes,
|
||||
parent, ref_root, level, action);
|
||||
BUG_ON(ret);
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* add a delayed data ref. it's similar to btrfs_add_delayed_tree_ref.
|
||||
*/
|
||||
int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes,
|
||||
u64 parent, u64 ref_root,
|
||||
u64 owner, u64 offset, int action,
|
||||
struct btrfs_delayed_extent_op *extent_op)
|
||||
{
|
||||
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);
|
||||
if (!ref)
|
||||
return -ENOMEM;
|
||||
|
||||
head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
|
||||
if (!head_ref) {
|
||||
kfree(ref);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
head_ref->extent_op = extent_op;
|
||||
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
spin_lock(&delayed_refs->lock);
|
||||
|
||||
/*
|
||||
* insert both the head node and the new ref without dropping
|
||||
* the spin lock
|
||||
*/
|
||||
ret = add_delayed_ref_head(trans, &head_ref->node, bytenr, num_bytes,
|
||||
action, 1);
|
||||
BUG_ON(ret);
|
||||
|
||||
ret = add_delayed_data_ref(trans, &ref->node, bytenr, num_bytes,
|
||||
parent, ref_root, owner, offset, action);
|
||||
BUG_ON(ret);
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes,
|
||||
struct btrfs_delayed_extent_op *extent_op)
|
||||
{
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
head_ref->extent_op = extent_op;
|
||||
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
spin_lock(&delayed_refs->lock);
|
||||
|
||||
ret = add_delayed_ref_head(trans, &head_ref->node, bytenr,
|
||||
num_bytes, BTRFS_UPDATE_DELAYED_HEAD,
|
||||
extent_op->is_data);
|
||||
BUG_ON(ret);
|
||||
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* this does a simple search for the head node for a given extent.
|
||||
* It must be called with the delayed ref spinlock held, and it returns
|
||||
@ -587,7 +836,7 @@ btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr)
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
|
||||
delayed_refs = &trans->transaction->delayed_refs;
|
||||
ref = tree_search(&delayed_refs->root, bytenr, (u64)-1, NULL);
|
||||
ref = find_ref_head(&delayed_refs->root, bytenr, NULL);
|
||||
if (ref)
|
||||
return btrfs_delayed_node_to_head(ref);
|
||||
return NULL;
|
||||
@ -603,6 +852,7 @@ btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr)
|
||||
*
|
||||
* It is the same as doing a ref add and delete in two separate calls.
|
||||
*/
|
||||
#if 0
|
||||
int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes, u64 orig_parent,
|
||||
u64 parent, u64 orig_ref_root, u64 ref_root,
|
||||
@ -666,3 +916,4 @@ int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
spin_unlock(&delayed_refs->lock);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -30,9 +30,6 @@ struct btrfs_delayed_ref_node {
|
||||
/* the starting bytenr of the extent */
|
||||
u64 bytenr;
|
||||
|
||||
/* the parent our backref will point to */
|
||||
u64 parent;
|
||||
|
||||
/* the size of the extent */
|
||||
u64 num_bytes;
|
||||
|
||||
@ -50,10 +47,21 @@ struct btrfs_delayed_ref_node {
|
||||
*/
|
||||
int ref_mod;
|
||||
|
||||
unsigned int action:8;
|
||||
unsigned int type:8;
|
||||
/* is this node still in the rbtree? */
|
||||
unsigned int is_head:1;
|
||||
unsigned int in_tree:1;
|
||||
};
|
||||
|
||||
struct btrfs_delayed_extent_op {
|
||||
struct btrfs_disk_key key;
|
||||
u64 flags_to_set;
|
||||
unsigned int update_key:1;
|
||||
unsigned int update_flags:1;
|
||||
unsigned int is_data:1;
|
||||
};
|
||||
|
||||
/*
|
||||
* the head refs are used to hold a lock on a given extent, which allows us
|
||||
* to make sure that only one process is running the delayed refs
|
||||
@ -71,6 +79,7 @@ struct btrfs_delayed_ref_head {
|
||||
|
||||
struct list_head cluster;
|
||||
|
||||
struct btrfs_delayed_extent_op *extent_op;
|
||||
/*
|
||||
* when a new extent is allocated, it is just reserved in memory
|
||||
* The actual extent isn't inserted into the extent allocation tree
|
||||
@ -84,27 +93,26 @@ struct btrfs_delayed_ref_head {
|
||||
* the free has happened.
|
||||
*/
|
||||
unsigned int must_insert_reserved:1;
|
||||
unsigned int is_data:1;
|
||||
};
|
||||
|
||||
struct btrfs_delayed_ref {
|
||||
struct btrfs_delayed_tree_ref {
|
||||
struct btrfs_delayed_ref_node node;
|
||||
union {
|
||||
u64 root;
|
||||
u64 parent;
|
||||
};
|
||||
int level;
|
||||
};
|
||||
|
||||
/* the root objectid our ref will point to */
|
||||
u64 root;
|
||||
|
||||
/* the generation for the backref */
|
||||
u64 generation;
|
||||
|
||||
/* owner_objectid of the backref */
|
||||
u64 owner_objectid;
|
||||
|
||||
/* operation done by this entry in the rbtree */
|
||||
u8 action;
|
||||
|
||||
/* if pin == 1, when the extent is freed it will be pinned until
|
||||
* transaction commit
|
||||
*/
|
||||
unsigned int pin:1;
|
||||
struct btrfs_delayed_data_ref {
|
||||
struct btrfs_delayed_ref_node node;
|
||||
union {
|
||||
u64 root;
|
||||
u64 parent;
|
||||
};
|
||||
u64 objectid;
|
||||
u64 offset;
|
||||
};
|
||||
|
||||
struct btrfs_delayed_ref_root {
|
||||
@ -143,17 +151,25 @@ static inline void btrfs_put_delayed_ref(struct btrfs_delayed_ref_node *ref)
|
||||
}
|
||||
}
|
||||
|
||||
int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root,
|
||||
u64 ref_generation, u64 owner_objectid, int action,
|
||||
int pin);
|
||||
int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes, u64 parent,
|
||||
u64 ref_root, int level, int action,
|
||||
struct btrfs_delayed_extent_op *extent_op);
|
||||
int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes,
|
||||
u64 parent, u64 ref_root,
|
||||
u64 owner, u64 offset, int action,
|
||||
struct btrfs_delayed_extent_op *extent_op);
|
||||
int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes,
|
||||
struct btrfs_delayed_extent_op *extent_op);
|
||||
|
||||
struct btrfs_delayed_ref_head *
|
||||
btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr);
|
||||
int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr);
|
||||
int btrfs_lookup_extent_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 bytenr,
|
||||
u64 num_bytes, u32 *refs);
|
||||
int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, u64 bytenr,
|
||||
u64 num_bytes, u64 *refs, u64 *flags);
|
||||
int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr, u64 num_bytes, u64 orig_parent,
|
||||
u64 parent, u64 orig_ref_root, u64 ref_root,
|
||||
@ -169,18 +185,24 @@ int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans,
|
||||
*/
|
||||
static int btrfs_delayed_ref_is_head(struct btrfs_delayed_ref_node *node)
|
||||
{
|
||||
return node->parent == (u64)-1;
|
||||
return node->is_head;
|
||||
}
|
||||
|
||||
/*
|
||||
* helper functions to cast a node into its container
|
||||
*/
|
||||
static inline struct btrfs_delayed_ref *
|
||||
btrfs_delayed_node_to_ref(struct btrfs_delayed_ref_node *node)
|
||||
static inline struct btrfs_delayed_tree_ref *
|
||||
btrfs_delayed_node_to_tree_ref(struct btrfs_delayed_ref_node *node)
|
||||
{
|
||||
WARN_ON(btrfs_delayed_ref_is_head(node));
|
||||
return container_of(node, struct btrfs_delayed_ref, node);
|
||||
return container_of(node, struct btrfs_delayed_tree_ref, node);
|
||||
}
|
||||
|
||||
static inline struct btrfs_delayed_data_ref *
|
||||
btrfs_delayed_node_to_data_ref(struct btrfs_delayed_ref_node *node)
|
||||
{
|
||||
WARN_ON(btrfs_delayed_ref_is_head(node));
|
||||
return container_of(node, struct btrfs_delayed_data_ref, node);
|
||||
}
|
||||
|
||||
static inline struct btrfs_delayed_ref_head *
|
||||
@ -188,6 +210,5 @@ btrfs_delayed_node_to_head(struct btrfs_delayed_ref_node *node)
|
||||
{
|
||||
WARN_ON(!btrfs_delayed_ref_is_head(node));
|
||||
return container_of(node, struct btrfs_delayed_ref_head, node);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
@ -26,8 +26,8 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/crc32c.h>
|
||||
#include "compat.h"
|
||||
#include "crc32c.h"
|
||||
#include "ctree.h"
|
||||
#include "disk-io.h"
|
||||
#include "transaction.h"
|
||||
@ -36,7 +36,6 @@
|
||||
#include "print-tree.h"
|
||||
#include "async-thread.h"
|
||||
#include "locking.h"
|
||||
#include "ref-cache.h"
|
||||
#include "tree-log.h"
|
||||
#include "free-space-cache.h"
|
||||
|
||||
@ -172,7 +171,7 @@ out:
|
||||
|
||||
u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len)
|
||||
{
|
||||
return btrfs_crc32c(seed, data, len);
|
||||
return crc32c(seed, data, len);
|
||||
}
|
||||
|
||||
void btrfs_csum_final(u32 crc, char *result)
|
||||
@ -884,7 +883,6 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
|
||||
{
|
||||
root->node = NULL;
|
||||
root->commit_root = NULL;
|
||||
root->ref_tree = NULL;
|
||||
root->sectorsize = sectorsize;
|
||||
root->nodesize = nodesize;
|
||||
root->leafsize = leafsize;
|
||||
@ -899,12 +897,14 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
|
||||
root->last_inode_alloc = 0;
|
||||
root->name = NULL;
|
||||
root->in_sysfs = 0;
|
||||
root->inode_tree.rb_node = NULL;
|
||||
|
||||
INIT_LIST_HEAD(&root->dirty_list);
|
||||
INIT_LIST_HEAD(&root->orphan_list);
|
||||
INIT_LIST_HEAD(&root->dead_list);
|
||||
INIT_LIST_HEAD(&root->root_list);
|
||||
spin_lock_init(&root->node_lock);
|
||||
spin_lock_init(&root->list_lock);
|
||||
spin_lock_init(&root->inode_lock);
|
||||
mutex_init(&root->objectid_mutex);
|
||||
mutex_init(&root->log_mutex);
|
||||
init_waitqueue_head(&root->log_writer_wait);
|
||||
@ -918,9 +918,6 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
|
||||
extent_io_tree_init(&root->dirty_log_pages,
|
||||
fs_info->btree_inode->i_mapping, GFP_NOFS);
|
||||
|
||||
btrfs_leaf_ref_tree_init(&root->ref_tree_struct);
|
||||
root->ref_tree = &root->ref_tree_struct;
|
||||
|
||||
memset(&root->root_key, 0, sizeof(root->root_key));
|
||||
memset(&root->root_item, 0, sizeof(root->root_item));
|
||||
memset(&root->defrag_progress, 0, sizeof(root->defrag_progress));
|
||||
@ -959,6 +956,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
|
||||
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
|
||||
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
|
||||
blocksize, generation);
|
||||
root->commit_root = btrfs_root_node(root);
|
||||
BUG_ON(!root->node);
|
||||
return 0;
|
||||
}
|
||||
@ -1025,20 +1023,19 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
|
||||
*/
|
||||
root->ref_cows = 0;
|
||||
|
||||
leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
|
||||
0, BTRFS_TREE_LOG_OBJECTID,
|
||||
trans->transid, 0, 0, 0);
|
||||
leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
|
||||
BTRFS_TREE_LOG_OBJECTID, NULL, 0, 0, 0);
|
||||
if (IS_ERR(leaf)) {
|
||||
kfree(root);
|
||||
return ERR_CAST(leaf);
|
||||
}
|
||||
|
||||
memset_extent_buffer(leaf, 0, 0, sizeof(struct btrfs_header));
|
||||
btrfs_set_header_bytenr(leaf, leaf->start);
|
||||
btrfs_set_header_generation(leaf, trans->transid);
|
||||
btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV);
|
||||
btrfs_set_header_owner(leaf, BTRFS_TREE_LOG_OBJECTID);
|
||||
root->node = leaf;
|
||||
btrfs_set_header_nritems(root->node, 0);
|
||||
btrfs_set_header_level(root->node, 0);
|
||||
btrfs_set_header_bytenr(root->node, root->node->start);
|
||||
btrfs_set_header_generation(root->node, trans->transid);
|
||||
btrfs_set_header_owner(root->node, BTRFS_TREE_LOG_OBJECTID);
|
||||
|
||||
write_extent_buffer(root->node, root->fs_info->fsid,
|
||||
(unsigned long)btrfs_header_fsid(root->node),
|
||||
@ -1081,8 +1078,7 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
|
||||
inode_item->nbytes = cpu_to_le64(root->leafsize);
|
||||
inode_item->mode = cpu_to_le32(S_IFDIR | 0755);
|
||||
|
||||
btrfs_set_root_bytenr(&log_root->root_item, log_root->node->start);
|
||||
btrfs_set_root_generation(&log_root->root_item, trans->transid);
|
||||
btrfs_set_root_node(&log_root->root_item, log_root->node);
|
||||
|
||||
WARN_ON(root->log_root);
|
||||
root->log_root = log_root;
|
||||
@ -1144,6 +1140,7 @@ out:
|
||||
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
|
||||
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
|
||||
blocksize, generation);
|
||||
root->commit_root = btrfs_root_node(root);
|
||||
BUG_ON(!root->node);
|
||||
insert:
|
||||
if (location->objectid != BTRFS_TREE_LOG_OBJECTID) {
|
||||
@ -1210,7 +1207,7 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
|
||||
}
|
||||
if (!(fs_info->sb->s_flags & MS_RDONLY)) {
|
||||
ret = btrfs_find_dead_roots(fs_info->tree_root,
|
||||
root->root_key.objectid, root);
|
||||
root->root_key.objectid);
|
||||
BUG_ON(ret);
|
||||
btrfs_orphan_cleanup(root);
|
||||
}
|
||||
@ -1569,8 +1566,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
atomic_set(&fs_info->async_delalloc_pages, 0);
|
||||
atomic_set(&fs_info->async_submit_draining, 0);
|
||||
atomic_set(&fs_info->nr_async_bios, 0);
|
||||
atomic_set(&fs_info->throttles, 0);
|
||||
atomic_set(&fs_info->throttle_gen, 0);
|
||||
fs_info->sb = sb;
|
||||
fs_info->max_extent = (u64)-1;
|
||||
fs_info->max_inline = 8192 * 1024;
|
||||
@ -1598,6 +1593,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
fs_info->btree_inode->i_mapping->a_ops = &btree_aops;
|
||||
fs_info->btree_inode->i_mapping->backing_dev_info = &fs_info->bdi;
|
||||
|
||||
RB_CLEAR_NODE(&BTRFS_I(fs_info->btree_inode)->rb_node);
|
||||
extent_io_tree_init(&BTRFS_I(fs_info->btree_inode)->io_tree,
|
||||
fs_info->btree_inode->i_mapping,
|
||||
GFP_NOFS);
|
||||
@ -1613,10 +1609,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
fs_info->btree_inode->i_mapping, GFP_NOFS);
|
||||
fs_info->do_barriers = 1;
|
||||
|
||||
INIT_LIST_HEAD(&fs_info->dead_reloc_roots);
|
||||
btrfs_leaf_ref_tree_init(&fs_info->reloc_ref_tree);
|
||||
btrfs_leaf_ref_tree_init(&fs_info->shared_ref_tree);
|
||||
|
||||
BTRFS_I(fs_info->btree_inode)->root = tree_root;
|
||||
memset(&BTRFS_I(fs_info->btree_inode)->location, 0,
|
||||
sizeof(struct btrfs_key));
|
||||
@ -1674,6 +1666,12 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
goto fail_iput;
|
||||
}
|
||||
|
||||
features = btrfs_super_incompat_flags(disk_super);
|
||||
if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) {
|
||||
features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
|
||||
btrfs_set_super_incompat_flags(disk_super, features);
|
||||
}
|
||||
|
||||
features = btrfs_super_compat_ro_flags(disk_super) &
|
||||
~BTRFS_FEATURE_COMPAT_RO_SUPP;
|
||||
if (!(sb->s_flags & MS_RDONLY) && features) {
|
||||
@ -1771,7 +1769,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "btrfs: failed to read the system "
|
||||
"array on %s\n", sb->s_id);
|
||||
goto fail_sys_array;
|
||||
goto fail_sb_buffer;
|
||||
}
|
||||
|
||||
blocksize = btrfs_level_size(tree_root,
|
||||
@ -1785,6 +1783,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
btrfs_super_chunk_root(disk_super),
|
||||
blocksize, generation);
|
||||
BUG_ON(!chunk_root->node);
|
||||
btrfs_set_root_node(&chunk_root->root_item, chunk_root->node);
|
||||
chunk_root->commit_root = btrfs_root_node(chunk_root);
|
||||
|
||||
read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid,
|
||||
(unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node),
|
||||
@ -1810,7 +1810,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
blocksize, generation);
|
||||
if (!tree_root->node)
|
||||
goto fail_chunk_root;
|
||||
|
||||
btrfs_set_root_node(&tree_root->root_item, tree_root->node);
|
||||
tree_root->commit_root = btrfs_root_node(tree_root);
|
||||
|
||||
ret = find_and_setup_root(tree_root, fs_info,
|
||||
BTRFS_EXTENT_TREE_OBJECTID, extent_root);
|
||||
@ -1820,14 +1821,14 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
|
||||
ret = find_and_setup_root(tree_root, fs_info,
|
||||
BTRFS_DEV_TREE_OBJECTID, dev_root);
|
||||
dev_root->track_dirty = 1;
|
||||
if (ret)
|
||||
goto fail_extent_root;
|
||||
dev_root->track_dirty = 1;
|
||||
|
||||
ret = find_and_setup_root(tree_root, fs_info,
|
||||
BTRFS_CSUM_TREE_OBJECTID, csum_root);
|
||||
if (ret)
|
||||
goto fail_extent_root;
|
||||
goto fail_dev_root;
|
||||
|
||||
csum_root->track_dirty = 1;
|
||||
|
||||
@ -1849,6 +1850,14 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
if (IS_ERR(fs_info->transaction_kthread))
|
||||
goto fail_cleaner;
|
||||
|
||||
if (!btrfs_test_opt(tree_root, SSD) &&
|
||||
!btrfs_test_opt(tree_root, NOSSD) &&
|
||||
!fs_info->fs_devices->rotating) {
|
||||
printk(KERN_INFO "Btrfs detected SSD devices, enabling SSD "
|
||||
"mode\n");
|
||||
btrfs_set_opt(fs_info->mount_opt, SSD);
|
||||
}
|
||||
|
||||
if (btrfs_super_log_root(disk_super) != 0) {
|
||||
u64 bytenr = btrfs_super_log_root(disk_super);
|
||||
|
||||
@ -1881,7 +1890,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
}
|
||||
|
||||
if (!(sb->s_flags & MS_RDONLY)) {
|
||||
ret = btrfs_cleanup_reloc_trees(tree_root);
|
||||
ret = btrfs_recover_relocation(tree_root);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
|
||||
@ -1892,6 +1901,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
fs_info->fs_root = btrfs_read_fs_root_no_name(fs_info, &location);
|
||||
if (!fs_info->fs_root)
|
||||
goto fail_trans_kthread;
|
||||
|
||||
return tree_root;
|
||||
|
||||
fail_trans_kthread:
|
||||
@ -1908,14 +1918,19 @@ fail_cleaner:
|
||||
|
||||
fail_csum_root:
|
||||
free_extent_buffer(csum_root->node);
|
||||
free_extent_buffer(csum_root->commit_root);
|
||||
fail_dev_root:
|
||||
free_extent_buffer(dev_root->node);
|
||||
free_extent_buffer(dev_root->commit_root);
|
||||
fail_extent_root:
|
||||
free_extent_buffer(extent_root->node);
|
||||
free_extent_buffer(extent_root->commit_root);
|
||||
fail_tree_root:
|
||||
free_extent_buffer(tree_root->node);
|
||||
free_extent_buffer(tree_root->commit_root);
|
||||
fail_chunk_root:
|
||||
free_extent_buffer(chunk_root->node);
|
||||
fail_sys_array:
|
||||
free_extent_buffer(dev_root->node);
|
||||
free_extent_buffer(chunk_root->commit_root);
|
||||
fail_sb_buffer:
|
||||
btrfs_stop_workers(&fs_info->fixup_workers);
|
||||
btrfs_stop_workers(&fs_info->delalloc_workers);
|
||||
@ -2005,6 +2020,17 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev)
|
||||
return latest;
|
||||
}
|
||||
|
||||
/*
|
||||
* this should be called twice, once with wait == 0 and
|
||||
* once with wait == 1. When wait == 0 is done, all the buffer heads
|
||||
* we write are pinned.
|
||||
*
|
||||
* They are released when wait == 1 is done.
|
||||
* max_mirrors must be the same for both runs, and it indicates how
|
||||
* many supers on this one device should be written.
|
||||
*
|
||||
* max_mirrors == 0 means to write them all.
|
||||
*/
|
||||
static int write_dev_supers(struct btrfs_device *device,
|
||||
struct btrfs_super_block *sb,
|
||||
int do_barriers, int wait, int max_mirrors)
|
||||
@ -2040,12 +2066,16 @@ static int write_dev_supers(struct btrfs_device *device,
|
||||
bh = __find_get_block(device->bdev, bytenr / 4096,
|
||||
BTRFS_SUPER_INFO_SIZE);
|
||||
BUG_ON(!bh);
|
||||
brelse(bh);
|
||||
wait_on_buffer(bh);
|
||||
if (buffer_uptodate(bh)) {
|
||||
brelse(bh);
|
||||
continue;
|
||||
}
|
||||
if (!buffer_uptodate(bh))
|
||||
errors++;
|
||||
|
||||
/* drop our reference */
|
||||
brelse(bh);
|
||||
|
||||
/* drop the reference from the wait == 0 run */
|
||||
brelse(bh);
|
||||
continue;
|
||||
} else {
|
||||
btrfs_set_super_bytenr(sb, bytenr);
|
||||
|
||||
@ -2056,12 +2086,18 @@ static int write_dev_supers(struct btrfs_device *device,
|
||||
BTRFS_CSUM_SIZE);
|
||||
btrfs_csum_final(crc, sb->csum);
|
||||
|
||||
/*
|
||||
* one reference for us, and we leave it for the
|
||||
* caller
|
||||
*/
|
||||
bh = __getblk(device->bdev, bytenr / 4096,
|
||||
BTRFS_SUPER_INFO_SIZE);
|
||||
memcpy(bh->b_data, sb, BTRFS_SUPER_INFO_SIZE);
|
||||
|
||||
set_buffer_uptodate(bh);
|
||||
/* one reference for submit_bh */
|
||||
get_bh(bh);
|
||||
|
||||
set_buffer_uptodate(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = btrfs_end_buffer_write_sync;
|
||||
}
|
||||
@ -2073,6 +2109,7 @@ static int write_dev_supers(struct btrfs_device *device,
|
||||
device->name);
|
||||
set_buffer_uptodate(bh);
|
||||
device->barriers = 0;
|
||||
/* one reference for submit_bh */
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
ret = submit_bh(WRITE_SYNC, bh);
|
||||
@ -2081,22 +2118,15 @@ static int write_dev_supers(struct btrfs_device *device,
|
||||
ret = submit_bh(WRITE_SYNC, bh);
|
||||
}
|
||||
|
||||
if (!ret && wait) {
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh))
|
||||
errors++;
|
||||
} else if (ret) {
|
||||
if (ret)
|
||||
errors++;
|
||||
}
|
||||
if (wait)
|
||||
brelse(bh);
|
||||
}
|
||||
return errors < i ? 0 : -1;
|
||||
}
|
||||
|
||||
int write_all_supers(struct btrfs_root *root, int max_mirrors)
|
||||
{
|
||||
struct list_head *head = &root->fs_info->fs_devices->devices;
|
||||
struct list_head *head;
|
||||
struct btrfs_device *dev;
|
||||
struct btrfs_super_block *sb;
|
||||
struct btrfs_dev_item *dev_item;
|
||||
@ -2111,6 +2141,9 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
|
||||
|
||||
sb = &root->fs_info->super_for_commit;
|
||||
dev_item = &sb->dev_item;
|
||||
|
||||
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
|
||||
head = &root->fs_info->fs_devices->devices;
|
||||
list_for_each_entry(dev, head, dev_list) {
|
||||
if (!dev->bdev) {
|
||||
total_errors++;
|
||||
@ -2154,6 +2187,7 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
|
||||
if (ret)
|
||||
total_errors++;
|
||||
}
|
||||
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
|
||||
if (total_errors > max_errors) {
|
||||
printk(KERN_ERR "btrfs: %d errors while writing supers\n",
|
||||
total_errors);
|
||||
@ -2173,6 +2207,7 @@ int write_ctree_super(struct btrfs_trans_handle *trans,
|
||||
|
||||
int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
|
||||
{
|
||||
WARN_ON(!RB_EMPTY_ROOT(&root->inode_tree));
|
||||
radix_tree_delete(&fs_info->fs_roots_radix,
|
||||
(unsigned long)root->root_key.objectid);
|
||||
if (root->anon_super.s_dev) {
|
||||
@ -2219,10 +2254,12 @@ int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info)
|
||||
ARRAY_SIZE(gang));
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
root_objectid = gang[ret - 1]->root_key.objectid + 1;
|
||||
for (i = 0; i < ret; i++) {
|
||||
root_objectid = gang[i]->root_key.objectid;
|
||||
ret = btrfs_find_dead_roots(fs_info->tree_root,
|
||||
root_objectid, gang[i]);
|
||||
root_objectid);
|
||||
BUG_ON(ret);
|
||||
btrfs_orphan_cleanup(gang[i]);
|
||||
}
|
||||
@ -2278,20 +2315,16 @@ int close_ctree(struct btrfs_root *root)
|
||||
(unsigned long long)fs_info->total_ref_cache_size);
|
||||
}
|
||||
|
||||
if (fs_info->extent_root->node)
|
||||
free_extent_buffer(fs_info->extent_root->node);
|
||||
|
||||
if (fs_info->tree_root->node)
|
||||
free_extent_buffer(fs_info->tree_root->node);
|
||||
|
||||
if (root->fs_info->chunk_root->node)
|
||||
free_extent_buffer(root->fs_info->chunk_root->node);
|
||||
|
||||
if (root->fs_info->dev_root->node)
|
||||
free_extent_buffer(root->fs_info->dev_root->node);
|
||||
|
||||
if (root->fs_info->csum_root->node)
|
||||
free_extent_buffer(root->fs_info->csum_root->node);
|
||||
free_extent_buffer(fs_info->extent_root->node);
|
||||
free_extent_buffer(fs_info->extent_root->commit_root);
|
||||
free_extent_buffer(fs_info->tree_root->node);
|
||||
free_extent_buffer(fs_info->tree_root->commit_root);
|
||||
free_extent_buffer(root->fs_info->chunk_root->node);
|
||||
free_extent_buffer(root->fs_info->chunk_root->commit_root);
|
||||
free_extent_buffer(root->fs_info->dev_root->node);
|
||||
free_extent_buffer(root->fs_info->dev_root->commit_root);
|
||||
free_extent_buffer(root->fs_info->csum_root->node);
|
||||
free_extent_buffer(root->fs_info->csum_root->commit_root);
|
||||
|
||||
btrfs_free_block_groups(root->fs_info);
|
||||
|
||||
@ -2373,17 +2406,14 @@ void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr)
|
||||
* looks as though older kernels can get into trouble with
|
||||
* this code, they end up stuck in balance_dirty_pages forever
|
||||
*/
|
||||
struct extent_io_tree *tree;
|
||||
u64 num_dirty;
|
||||
u64 start = 0;
|
||||
unsigned long thresh = 32 * 1024 * 1024;
|
||||
tree = &BTRFS_I(root->fs_info->btree_inode)->io_tree;
|
||||
|
||||
if (current->flags & PF_MEMALLOC)
|
||||
return;
|
||||
|
||||
num_dirty = count_range_bits(tree, &start, (u64)-1,
|
||||
thresh, EXTENT_DIRTY);
|
||||
num_dirty = root->fs_info->dirty_metadata_bytes;
|
||||
|
||||
if (num_dirty > thresh) {
|
||||
balance_dirty_pages_ratelimited_nr(
|
||||
root->fs_info->btree_inode->i_mapping, 1);
|
||||
|
@ -78,7 +78,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
|
||||
btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
|
||||
key.offset = 0;
|
||||
|
||||
inode = btrfs_iget(sb, &key, root, NULL);
|
||||
inode = btrfs_iget(sb, &key, root);
|
||||
if (IS_ERR(inode))
|
||||
return (void *)inode;
|
||||
|
||||
@ -192,7 +192,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child)
|
||||
btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
|
||||
key.offset = 0;
|
||||
|
||||
return d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, NULL));
|
||||
return d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root));
|
||||
}
|
||||
|
||||
const struct export_operations btrfs_export_ops = {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -476,6 +476,7 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
struct extent_state *state;
|
||||
struct extent_state *prealloc = NULL;
|
||||
struct rb_node *node;
|
||||
u64 last_end;
|
||||
int err;
|
||||
int set = 0;
|
||||
|
||||
@ -498,6 +499,7 @@ again:
|
||||
if (state->start > end)
|
||||
goto out;
|
||||
WARN_ON(state->end < start);
|
||||
last_end = state->end;
|
||||
|
||||
/*
|
||||
* | ---- desired range ---- |
|
||||
@ -524,9 +526,11 @@ again:
|
||||
if (err)
|
||||
goto out;
|
||||
if (state->end <= end) {
|
||||
start = state->end + 1;
|
||||
set |= clear_state_bit(tree, state, bits,
|
||||
wake, delete);
|
||||
if (last_end == (u64)-1)
|
||||
goto out;
|
||||
start = last_end + 1;
|
||||
} else {
|
||||
start = state->start;
|
||||
}
|
||||
@ -552,8 +556,10 @@ again:
|
||||
goto out;
|
||||
}
|
||||
|
||||
start = state->end + 1;
|
||||
set |= clear_state_bit(tree, state, bits, wake, delete);
|
||||
if (last_end == (u64)-1)
|
||||
goto out;
|
||||
start = last_end + 1;
|
||||
goto search_again;
|
||||
|
||||
out:
|
||||
@ -707,8 +713,10 @@ again:
|
||||
goto out;
|
||||
}
|
||||
set_state_bits(tree, state, bits);
|
||||
start = state->end + 1;
|
||||
merge_state(tree, state);
|
||||
if (last_end == (u64)-1)
|
||||
goto out;
|
||||
start = last_end + 1;
|
||||
goto search_again;
|
||||
}
|
||||
|
||||
@ -742,8 +750,10 @@ again:
|
||||
goto out;
|
||||
if (state->end <= end) {
|
||||
set_state_bits(tree, state, bits);
|
||||
start = state->end + 1;
|
||||
merge_state(tree, state);
|
||||
if (last_end == (u64)-1)
|
||||
goto out;
|
||||
start = last_end + 1;
|
||||
} else {
|
||||
start = state->start;
|
||||
}
|
||||
|
@ -291,16 +291,12 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
|
||||
{
|
||||
u64 extent_end = 0;
|
||||
u64 search_start = start;
|
||||
u64 leaf_start;
|
||||
u64 ram_bytes = 0;
|
||||
u64 orig_parent = 0;
|
||||
u64 disk_bytenr = 0;
|
||||
u64 orig_locked_end = locked_end;
|
||||
u8 compression;
|
||||
u8 encryption;
|
||||
u16 other_encoding = 0;
|
||||
u64 root_gen;
|
||||
u64 root_owner;
|
||||
struct extent_buffer *leaf;
|
||||
struct btrfs_file_extent_item *extent;
|
||||
struct btrfs_path *path;
|
||||
@ -340,9 +336,6 @@ next_slot:
|
||||
bookend = 0;
|
||||
found_extent = 0;
|
||||
found_inline = 0;
|
||||
leaf_start = 0;
|
||||
root_gen = 0;
|
||||
root_owner = 0;
|
||||
compression = 0;
|
||||
encryption = 0;
|
||||
extent = NULL;
|
||||
@ -417,9 +410,6 @@ next_slot:
|
||||
if (found_extent) {
|
||||
read_extent_buffer(leaf, &old, (unsigned long)extent,
|
||||
sizeof(old));
|
||||
root_gen = btrfs_header_generation(leaf);
|
||||
root_owner = btrfs_header_owner(leaf);
|
||||
leaf_start = leaf->start;
|
||||
}
|
||||
|
||||
if (end < extent_end && end >= key.offset) {
|
||||
@ -443,14 +433,14 @@ next_slot:
|
||||
}
|
||||
locked_end = extent_end;
|
||||
}
|
||||
orig_parent = path->nodes[0]->start;
|
||||
disk_bytenr = le64_to_cpu(old.disk_bytenr);
|
||||
if (disk_bytenr != 0) {
|
||||
ret = btrfs_inc_extent_ref(trans, root,
|
||||
disk_bytenr,
|
||||
le64_to_cpu(old.disk_num_bytes),
|
||||
orig_parent, root->root_key.objectid,
|
||||
trans->transid, inode->i_ino);
|
||||
le64_to_cpu(old.disk_num_bytes), 0,
|
||||
root->root_key.objectid,
|
||||
key.objectid, key.offset -
|
||||
le64_to_cpu(old.offset));
|
||||
BUG_ON(ret);
|
||||
}
|
||||
}
|
||||
@ -568,17 +558,6 @@ next_slot:
|
||||
btrfs_mark_buffer_dirty(path->nodes[0]);
|
||||
btrfs_set_lock_blocking(path->nodes[0]);
|
||||
|
||||
if (disk_bytenr != 0) {
|
||||
ret = btrfs_update_extent_ref(trans, root,
|
||||
disk_bytenr,
|
||||
le64_to_cpu(old.disk_num_bytes),
|
||||
orig_parent,
|
||||
leaf->start,
|
||||
root->root_key.objectid,
|
||||
trans->transid, ins.objectid);
|
||||
|
||||
BUG_ON(ret);
|
||||
}
|
||||
path->leave_spinning = 0;
|
||||
btrfs_release_path(root, path);
|
||||
if (disk_bytenr != 0)
|
||||
@ -594,8 +573,9 @@ next_slot:
|
||||
ret = btrfs_free_extent(trans, root,
|
||||
old_disk_bytenr,
|
||||
le64_to_cpu(old.disk_num_bytes),
|
||||
leaf_start, root_owner,
|
||||
root_gen, key.objectid, 0);
|
||||
0, root->root_key.objectid,
|
||||
key.objectid, key.offset -
|
||||
le64_to_cpu(old.offset));
|
||||
BUG_ON(ret);
|
||||
*hint_byte = old_disk_bytenr;
|
||||
}
|
||||
@ -664,12 +644,11 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
|
||||
u64 bytenr;
|
||||
u64 num_bytes;
|
||||
u64 extent_end;
|
||||
u64 extent_offset;
|
||||
u64 orig_offset;
|
||||
u64 other_start;
|
||||
u64 other_end;
|
||||
u64 split = start;
|
||||
u64 locked_end = end;
|
||||
u64 orig_parent;
|
||||
int extent_type;
|
||||
int split_end = 1;
|
||||
int ret;
|
||||
@ -703,7 +682,7 @@ again:
|
||||
|
||||
bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
|
||||
num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi);
|
||||
extent_offset = btrfs_file_extent_offset(leaf, fi);
|
||||
orig_offset = key.offset - btrfs_file_extent_offset(leaf, fi);
|
||||
|
||||
if (key.offset == start)
|
||||
split = end;
|
||||
@ -711,8 +690,6 @@ again:
|
||||
if (key.offset == start && extent_end == end) {
|
||||
int del_nr = 0;
|
||||
int del_slot = 0;
|
||||
u64 leaf_owner = btrfs_header_owner(leaf);
|
||||
u64 leaf_gen = btrfs_header_generation(leaf);
|
||||
other_start = end;
|
||||
other_end = 0;
|
||||
if (extent_mergeable(leaf, path->slots[0] + 1, inode->i_ino,
|
||||
@ -721,8 +698,8 @@ again:
|
||||
del_slot = path->slots[0] + 1;
|
||||
del_nr++;
|
||||
ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
|
||||
leaf->start, leaf_owner,
|
||||
leaf_gen, inode->i_ino, 0);
|
||||
0, root->root_key.objectid,
|
||||
inode->i_ino, orig_offset);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
other_start = 0;
|
||||
@ -733,8 +710,8 @@ again:
|
||||
del_slot = path->slots[0];
|
||||
del_nr++;
|
||||
ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
|
||||
leaf->start, leaf_owner,
|
||||
leaf_gen, inode->i_ino, 0);
|
||||
0, root->root_key.objectid,
|
||||
inode->i_ino, orig_offset);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
split_end = 0;
|
||||
@ -768,13 +745,12 @@ again:
|
||||
locked_end = extent_end;
|
||||
}
|
||||
btrfs_set_file_extent_num_bytes(leaf, fi, split - key.offset);
|
||||
extent_offset += split - key.offset;
|
||||
} else {
|
||||
BUG_ON(key.offset != start);
|
||||
btrfs_set_file_extent_offset(leaf, fi, extent_offset +
|
||||
split - key.offset);
|
||||
btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - split);
|
||||
key.offset = split;
|
||||
btrfs_set_file_extent_offset(leaf, fi, key.offset -
|
||||
orig_offset);
|
||||
btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - split);
|
||||
btrfs_set_item_key_safe(trans, root, path, &key);
|
||||
extent_end = split;
|
||||
}
|
||||
@ -793,7 +769,8 @@ again:
|
||||
struct btrfs_file_extent_item);
|
||||
key.offset = split;
|
||||
btrfs_set_item_key_safe(trans, root, path, &key);
|
||||
btrfs_set_file_extent_offset(leaf, fi, extent_offset);
|
||||
btrfs_set_file_extent_offset(leaf, fi, key.offset -
|
||||
orig_offset);
|
||||
btrfs_set_file_extent_num_bytes(leaf, fi,
|
||||
other_end - split);
|
||||
goto done;
|
||||
@ -815,10 +792,9 @@ again:
|
||||
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
|
||||
orig_parent = leaf->start;
|
||||
ret = btrfs_inc_extent_ref(trans, root, bytenr, num_bytes,
|
||||
orig_parent, root->root_key.objectid,
|
||||
trans->transid, inode->i_ino);
|
||||
ret = btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, 0,
|
||||
root->root_key.objectid,
|
||||
inode->i_ino, orig_offset);
|
||||
BUG_ON(ret);
|
||||
btrfs_release_path(root, path);
|
||||
|
||||
@ -833,20 +809,12 @@ again:
|
||||
btrfs_set_file_extent_type(leaf, fi, extent_type);
|
||||
btrfs_set_file_extent_disk_bytenr(leaf, fi, bytenr);
|
||||
btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes);
|
||||
btrfs_set_file_extent_offset(leaf, fi, extent_offset);
|
||||
btrfs_set_file_extent_offset(leaf, fi, key.offset - orig_offset);
|
||||
btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - key.offset);
|
||||
btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
|
||||
btrfs_set_file_extent_compression(leaf, fi, 0);
|
||||
btrfs_set_file_extent_encryption(leaf, fi, 0);
|
||||
btrfs_set_file_extent_other_encoding(leaf, fi, 0);
|
||||
|
||||
if (orig_parent != leaf->start) {
|
||||
ret = btrfs_update_extent_ref(trans, root, bytenr, num_bytes,
|
||||
orig_parent, leaf->start,
|
||||
root->root_key.objectid,
|
||||
trans->transid, inode->i_ino);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
done:
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
|
||||
@ -1189,6 +1157,8 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
|
||||
btrfs_wait_ordered_range(inode, 0, (u64)-1);
|
||||
root->log_batch++;
|
||||
|
||||
if (datasync && !(inode->i_state & I_DIRTY_PAGES))
|
||||
goto out;
|
||||
/*
|
||||
* ok we haven't committed the transaction yet, lets do a commit
|
||||
*/
|
||||
|
@ -579,6 +579,7 @@ out:
|
||||
* it returns -enospc
|
||||
*/
|
||||
int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_block_group_cache *block_group,
|
||||
struct btrfs_free_cluster *cluster,
|
||||
u64 offset, u64 bytes, u64 empty_size)
|
||||
@ -595,7 +596,9 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
|
||||
int ret;
|
||||
|
||||
/* for metadata, allow allocates with more holes */
|
||||
if (block_group->flags & BTRFS_BLOCK_GROUP_METADATA) {
|
||||
if (btrfs_test_opt(root, SSD_SPREAD)) {
|
||||
min_bytes = bytes + empty_size;
|
||||
} else if (block_group->flags & BTRFS_BLOCK_GROUP_METADATA) {
|
||||
/*
|
||||
* we want to do larger allocations when we are
|
||||
* flushing out the delayed refs, it helps prevent
|
||||
@ -645,14 +648,15 @@ again:
|
||||
* we haven't filled the empty size and the window is
|
||||
* very large. reset and try again
|
||||
*/
|
||||
if (next->offset - window_start > (bytes + empty_size) * 2) {
|
||||
if (next->offset - (last->offset + last->bytes) > 128 * 1024 ||
|
||||
next->offset - window_start > (bytes + empty_size) * 2) {
|
||||
entry = next;
|
||||
window_start = entry->offset;
|
||||
window_free = entry->bytes;
|
||||
last = entry;
|
||||
max_extent = 0;
|
||||
total_retries++;
|
||||
if (total_retries % 256 == 0) {
|
||||
if (total_retries % 64 == 0) {
|
||||
if (min_bytes >= (bytes + empty_size)) {
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
|
@ -31,6 +31,7 @@ void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group,
|
||||
u64 bytes);
|
||||
u64 btrfs_block_group_free_space(struct btrfs_block_group_cache *block_group);
|
||||
int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_block_group_cache *block_group,
|
||||
struct btrfs_free_cluster *cluster,
|
||||
u64 offset, u64 bytes, u64 empty_size);
|
||||
|
@ -19,9 +19,9 @@
|
||||
#ifndef __HASH__
|
||||
#define __HASH__
|
||||
|
||||
#include "crc32c.h"
|
||||
#include <linux/crc32c.h>
|
||||
static inline u64 btrfs_name_hash(const char *name, int len)
|
||||
{
|
||||
return btrfs_crc32c((u32)~1, name, len);
|
||||
return crc32c((u32)~1, name, len);
|
||||
}
|
||||
#endif
|
||||
|
159
fs/btrfs/inode.c
159
fs/btrfs/inode.c
@ -48,7 +48,6 @@
|
||||
#include "ordered-data.h"
|
||||
#include "xattr.h"
|
||||
#include "tree-log.h"
|
||||
#include "ref-cache.h"
|
||||
#include "compression.h"
|
||||
#include "locking.h"
|
||||
|
||||
@ -369,7 +368,7 @@ again:
|
||||
* inode has not been flagged as nocompress. This flag can
|
||||
* change at any time if we discover bad compression ratios.
|
||||
*/
|
||||
if (!btrfs_test_flag(inode, NOCOMPRESS) &&
|
||||
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&
|
||||
btrfs_test_opt(root, COMPRESS)) {
|
||||
WARN_ON(pages);
|
||||
pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
|
||||
@ -470,7 +469,7 @@ again:
|
||||
nr_pages_ret = 0;
|
||||
|
||||
/* flag the file so we don't compress in the future */
|
||||
btrfs_set_flag(inode, NOCOMPRESS);
|
||||
BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
|
||||
}
|
||||
if (will_compress) {
|
||||
*num_added += 1;
|
||||
@ -863,7 +862,7 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
|
||||
async_cow->locked_page = locked_page;
|
||||
async_cow->start = start;
|
||||
|
||||
if (btrfs_test_flag(inode, NOCOMPRESS))
|
||||
if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS)
|
||||
cur_end = end;
|
||||
else
|
||||
cur_end = min(end, start + 512 * 1024 - 1);
|
||||
@ -944,6 +943,7 @@ static noinline int run_delalloc_nocow(struct inode *inode,
|
||||
u64 cow_start;
|
||||
u64 cur_offset;
|
||||
u64 extent_end;
|
||||
u64 extent_offset;
|
||||
u64 disk_bytenr;
|
||||
u64 num_bytes;
|
||||
int extent_type;
|
||||
@ -1005,6 +1005,7 @@ next_slot:
|
||||
if (extent_type == BTRFS_FILE_EXTENT_REG ||
|
||||
extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
|
||||
disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
|
||||
extent_offset = btrfs_file_extent_offset(leaf, fi);
|
||||
extent_end = found_key.offset +
|
||||
btrfs_file_extent_num_bytes(leaf, fi);
|
||||
if (extent_end <= start) {
|
||||
@ -1022,9 +1023,10 @@ next_slot:
|
||||
if (btrfs_extent_readonly(root, disk_bytenr))
|
||||
goto out_check;
|
||||
if (btrfs_cross_ref_exist(trans, root, inode->i_ino,
|
||||
disk_bytenr))
|
||||
found_key.offset -
|
||||
extent_offset, disk_bytenr))
|
||||
goto out_check;
|
||||
disk_bytenr += btrfs_file_extent_offset(leaf, fi);
|
||||
disk_bytenr += extent_offset;
|
||||
disk_bytenr += cur_offset - found_key.offset;
|
||||
num_bytes = min(end + 1, extent_end) - cur_offset;
|
||||
/*
|
||||
@ -1131,10 +1133,10 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
|
||||
int ret;
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
|
||||
if (btrfs_test_flag(inode, NODATACOW))
|
||||
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW)
|
||||
ret = run_delalloc_nocow(inode, locked_page, start, end,
|
||||
page_started, 1, nr_written);
|
||||
else if (btrfs_test_flag(inode, PREALLOC))
|
||||
else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC)
|
||||
ret = run_delalloc_nocow(inode, locked_page, start, end,
|
||||
page_started, 0, nr_written);
|
||||
else if (!btrfs_test_opt(root, COMPRESS))
|
||||
@ -1288,7 +1290,7 @@ static int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
|
||||
int ret = 0;
|
||||
int skip_sum;
|
||||
|
||||
skip_sum = btrfs_test_flag(inode, NODATASUM);
|
||||
skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
|
||||
|
||||
ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
|
||||
BUG_ON(ret);
|
||||
@ -1489,9 +1491,9 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
|
||||
ins.objectid = disk_bytenr;
|
||||
ins.offset = disk_num_bytes;
|
||||
ins.type = BTRFS_EXTENT_ITEM_KEY;
|
||||
ret = btrfs_alloc_reserved_extent(trans, root, leaf->start,
|
||||
root->root_key.objectid,
|
||||
trans->transid, inode->i_ino, &ins);
|
||||
ret = btrfs_alloc_reserved_file_extent(trans, root,
|
||||
root->root_key.objectid,
|
||||
inode->i_ino, file_pos, &ins);
|
||||
BUG_ON(ret);
|
||||
btrfs_free_path(path);
|
||||
|
||||
@ -1788,7 +1790,8 @@ static int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end,
|
||||
ClearPageChecked(page);
|
||||
goto good;
|
||||
}
|
||||
if (btrfs_test_flag(inode, NODATASUM))
|
||||
|
||||
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
|
||||
return 0;
|
||||
|
||||
if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID &&
|
||||
@ -1956,23 +1959,13 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
|
||||
* crossing root thing. we store the inode number in the
|
||||
* offset of the orphan item.
|
||||
*/
|
||||
inode = btrfs_iget_locked(root->fs_info->sb,
|
||||
found_key.offset, root);
|
||||
if (!inode)
|
||||
found_key.objectid = found_key.offset;
|
||||
found_key.type = BTRFS_INODE_ITEM_KEY;
|
||||
found_key.offset = 0;
|
||||
inode = btrfs_iget(root->fs_info->sb, &found_key, root);
|
||||
if (IS_ERR(inode))
|
||||
break;
|
||||
|
||||
if (inode->i_state & I_NEW) {
|
||||
BTRFS_I(inode)->root = root;
|
||||
|
||||
/* have to set the location manually */
|
||||
BTRFS_I(inode)->location.objectid = inode->i_ino;
|
||||
BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
|
||||
BTRFS_I(inode)->location.offset = 0;
|
||||
|
||||
btrfs_read_locked_inode(inode);
|
||||
unlock_new_inode(inode);
|
||||
}
|
||||
|
||||
/*
|
||||
* add this inode to the orphan list so btrfs_orphan_del does
|
||||
* the proper thing when we hit it
|
||||
@ -2069,7 +2062,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
|
||||
/*
|
||||
* read an inode from the btree into the in-memory inode
|
||||
*/
|
||||
void btrfs_read_locked_inode(struct inode *inode)
|
||||
static void btrfs_read_locked_inode(struct inode *inode)
|
||||
{
|
||||
struct btrfs_path *path;
|
||||
struct extent_buffer *leaf;
|
||||
@ -2164,6 +2157,8 @@ void btrfs_read_locked_inode(struct inode *inode)
|
||||
init_special_inode(inode, inode->i_mode, rdev);
|
||||
break;
|
||||
}
|
||||
|
||||
btrfs_update_iflags(inode);
|
||||
return;
|
||||
|
||||
make_bad:
|
||||
@ -2599,9 +2594,8 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_file_extent_item *fi;
|
||||
u64 extent_start = 0;
|
||||
u64 extent_num_bytes = 0;
|
||||
u64 extent_offset = 0;
|
||||
u64 item_end = 0;
|
||||
u64 root_gen = 0;
|
||||
u64 root_owner = 0;
|
||||
int found_extent;
|
||||
int del_item;
|
||||
int pending_del_nr = 0;
|
||||
@ -2716,6 +2710,9 @@ search_again:
|
||||
extent_num_bytes =
|
||||
btrfs_file_extent_disk_num_bytes(leaf,
|
||||
fi);
|
||||
extent_offset = found_key.offset -
|
||||
btrfs_file_extent_offset(leaf, fi);
|
||||
|
||||
/* FIXME blocksize != 4096 */
|
||||
num_dec = btrfs_file_extent_num_bytes(leaf, fi);
|
||||
if (extent_start != 0) {
|
||||
@ -2723,8 +2720,6 @@ search_again:
|
||||
if (root->ref_cows)
|
||||
inode_sub_bytes(inode, num_dec);
|
||||
}
|
||||
root_gen = btrfs_header_generation(leaf);
|
||||
root_owner = btrfs_header_owner(leaf);
|
||||
}
|
||||
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
|
||||
/*
|
||||
@ -2768,12 +2763,12 @@ delete:
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if (found_extent) {
|
||||
if (found_extent && root->ref_cows) {
|
||||
btrfs_set_path_blocking(path);
|
||||
ret = btrfs_free_extent(trans, root, extent_start,
|
||||
extent_num_bytes,
|
||||
leaf->start, root_owner,
|
||||
root_gen, inode->i_ino, 0);
|
||||
extent_num_bytes, 0,
|
||||
btrfs_header_owner(leaf),
|
||||
inode->i_ino, extent_offset);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
next:
|
||||
@ -3105,6 +3100,45 @@ static int fixup_tree_root_location(struct btrfs_root *root,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void inode_tree_add(struct inode *inode)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
struct btrfs_inode *entry;
|
||||
struct rb_node **p = &root->inode_tree.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
||||
spin_lock(&root->inode_lock);
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
entry = rb_entry(parent, struct btrfs_inode, rb_node);
|
||||
|
||||
if (inode->i_ino < entry->vfs_inode.i_ino)
|
||||
p = &(*p)->rb_left;
|
||||
else if (inode->i_ino > entry->vfs_inode.i_ino)
|
||||
p = &(*p)->rb_right;
|
||||
else {
|
||||
WARN_ON(!(entry->vfs_inode.i_state &
|
||||
(I_WILL_FREE | I_FREEING | I_CLEAR)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
rb_link_node(&BTRFS_I(inode)->rb_node, parent, p);
|
||||
rb_insert_color(&BTRFS_I(inode)->rb_node, &root->inode_tree);
|
||||
spin_unlock(&root->inode_lock);
|
||||
}
|
||||
|
||||
static void inode_tree_del(struct inode *inode)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
|
||||
if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) {
|
||||
spin_lock(&root->inode_lock);
|
||||
rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree);
|
||||
spin_unlock(&root->inode_lock);
|
||||
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
|
||||
}
|
||||
}
|
||||
|
||||
static noinline void init_btrfs_i(struct inode *inode)
|
||||
{
|
||||
struct btrfs_inode *bi = BTRFS_I(inode);
|
||||
@ -3130,6 +3164,7 @@ static noinline void init_btrfs_i(struct inode *inode)
|
||||
inode->i_mapping, GFP_NOFS);
|
||||
INIT_LIST_HEAD(&BTRFS_I(inode)->delalloc_inodes);
|
||||
INIT_LIST_HEAD(&BTRFS_I(inode)->ordered_operations);
|
||||
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
|
||||
btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
|
||||
mutex_init(&BTRFS_I(inode)->extent_mutex);
|
||||
mutex_init(&BTRFS_I(inode)->log_mutex);
|
||||
@ -3152,26 +3187,9 @@ static int btrfs_find_actor(struct inode *inode, void *opaque)
|
||||
args->root == BTRFS_I(inode)->root;
|
||||
}
|
||||
|
||||
struct inode *btrfs_ilookup(struct super_block *s, u64 objectid,
|
||||
struct btrfs_root *root, int wait)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct btrfs_iget_args args;
|
||||
args.ino = objectid;
|
||||
args.root = root;
|
||||
|
||||
if (wait) {
|
||||
inode = ilookup5(s, objectid, btrfs_find_actor,
|
||||
(void *)&args);
|
||||
} else {
|
||||
inode = ilookup5_nowait(s, objectid, btrfs_find_actor,
|
||||
(void *)&args);
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
|
||||
struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
|
||||
struct btrfs_root *root)
|
||||
static struct inode *btrfs_iget_locked(struct super_block *s,
|
||||
u64 objectid,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct btrfs_iget_args args;
|
||||
@ -3188,24 +3206,21 @@ struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
|
||||
* Returns in *is_new if the inode was read from disk
|
||||
*/
|
||||
struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
|
||||
struct btrfs_root *root, int *is_new)
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = btrfs_iget_locked(s, location->objectid, root);
|
||||
if (!inode)
|
||||
return ERR_PTR(-EACCES);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (inode->i_state & I_NEW) {
|
||||
BTRFS_I(inode)->root = root;
|
||||
memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
|
||||
btrfs_read_locked_inode(inode);
|
||||
|
||||
inode_tree_add(inode);
|
||||
unlock_new_inode(inode);
|
||||
if (is_new)
|
||||
*is_new = 1;
|
||||
} else {
|
||||
if (is_new)
|
||||
*is_new = 0;
|
||||
}
|
||||
|
||||
return inode;
|
||||
@ -3218,7 +3233,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
|
||||
struct btrfs_root *root = bi->root;
|
||||
struct btrfs_root *sub_root = root;
|
||||
struct btrfs_key location;
|
||||
int ret, new;
|
||||
int ret;
|
||||
|
||||
if (dentry->d_name.len > BTRFS_NAME_LEN)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
@ -3236,7 +3251,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
|
||||
return ERR_PTR(ret);
|
||||
if (ret > 0)
|
||||
return ERR_PTR(-ENOENT);
|
||||
inode = btrfs_iget(dir->i_sb, &location, sub_root, &new);
|
||||
inode = btrfs_iget(dir->i_sb, &location, sub_root);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
}
|
||||
@ -3574,9 +3589,9 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
||||
btrfs_find_block_group(root, 0, alloc_hint, owner);
|
||||
if ((mode & S_IFREG)) {
|
||||
if (btrfs_test_opt(root, NODATASUM))
|
||||
btrfs_set_flag(inode, NODATASUM);
|
||||
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
|
||||
if (btrfs_test_opt(root, NODATACOW))
|
||||
btrfs_set_flag(inode, NODATACOW);
|
||||
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW;
|
||||
}
|
||||
|
||||
key[0].objectid = objectid;
|
||||
@ -3630,7 +3645,10 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
||||
location->offset = 0;
|
||||
btrfs_set_key_type(location, BTRFS_INODE_ITEM_KEY);
|
||||
|
||||
btrfs_inherit_iflags(inode, dir);
|
||||
|
||||
insert_inode_hash(inode);
|
||||
inode_tree_add(inode);
|
||||
return inode;
|
||||
fail:
|
||||
if (dir)
|
||||
@ -4683,6 +4701,7 @@ void btrfs_destroy_inode(struct inode *inode)
|
||||
btrfs_put_ordered_extent(ordered);
|
||||
}
|
||||
}
|
||||
inode_tree_del(inode);
|
||||
btrfs_drop_extent_cache(inode, 0, (u64)-1, 0);
|
||||
kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
|
||||
}
|
||||
@ -5061,7 +5080,7 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans,
|
||||
out:
|
||||
if (cur_offset > start) {
|
||||
inode->i_ctime = CURRENT_TIME;
|
||||
btrfs_set_flag(inode, PREALLOC);
|
||||
BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC;
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
|
||||
cur_offset > i_size_read(inode))
|
||||
btrfs_i_size_write(inode, cur_offset);
|
||||
@ -5182,7 +5201,7 @@ static int btrfs_set_page_dirty(struct page *page)
|
||||
|
||||
static int btrfs_permission(struct inode *inode, int mask)
|
||||
{
|
||||
if (btrfs_test_flag(inode, READONLY) && (mask & MAY_WRITE))
|
||||
if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
|
||||
return -EACCES;
|
||||
return generic_permission(inode, mask, btrfs_check_acl);
|
||||
}
|
||||
|
197
fs/btrfs/ioctl.c
197
fs/btrfs/ioctl.c
@ -50,7 +50,177 @@
|
||||
#include "volumes.h"
|
||||
#include "locking.h"
|
||||
|
||||
/* Mask out flags that are inappropriate for the given type of inode. */
|
||||
static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
|
||||
{
|
||||
if (S_ISDIR(mode))
|
||||
return flags;
|
||||
else if (S_ISREG(mode))
|
||||
return flags & ~FS_DIRSYNC_FL;
|
||||
else
|
||||
return flags & (FS_NODUMP_FL | FS_NOATIME_FL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl.
|
||||
*/
|
||||
static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
|
||||
{
|
||||
unsigned int iflags = 0;
|
||||
|
||||
if (flags & BTRFS_INODE_SYNC)
|
||||
iflags |= FS_SYNC_FL;
|
||||
if (flags & BTRFS_INODE_IMMUTABLE)
|
||||
iflags |= FS_IMMUTABLE_FL;
|
||||
if (flags & BTRFS_INODE_APPEND)
|
||||
iflags |= FS_APPEND_FL;
|
||||
if (flags & BTRFS_INODE_NODUMP)
|
||||
iflags |= FS_NODUMP_FL;
|
||||
if (flags & BTRFS_INODE_NOATIME)
|
||||
iflags |= FS_NOATIME_FL;
|
||||
if (flags & BTRFS_INODE_DIRSYNC)
|
||||
iflags |= FS_DIRSYNC_FL;
|
||||
|
||||
return iflags;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update inode->i_flags based on the btrfs internal flags.
|
||||
*/
|
||||
void btrfs_update_iflags(struct inode *inode)
|
||||
{
|
||||
struct btrfs_inode *ip = BTRFS_I(inode);
|
||||
|
||||
inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
|
||||
|
||||
if (ip->flags & BTRFS_INODE_SYNC)
|
||||
inode->i_flags |= S_SYNC;
|
||||
if (ip->flags & BTRFS_INODE_IMMUTABLE)
|
||||
inode->i_flags |= S_IMMUTABLE;
|
||||
if (ip->flags & BTRFS_INODE_APPEND)
|
||||
inode->i_flags |= S_APPEND;
|
||||
if (ip->flags & BTRFS_INODE_NOATIME)
|
||||
inode->i_flags |= S_NOATIME;
|
||||
if (ip->flags & BTRFS_INODE_DIRSYNC)
|
||||
inode->i_flags |= S_DIRSYNC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inherit flags from the parent inode.
|
||||
*
|
||||
* Unlike extN we don't have any flags we don't want to inherit currently.
|
||||
*/
|
||||
void btrfs_inherit_iflags(struct inode *inode, struct inode *dir)
|
||||
{
|
||||
unsigned int flags;
|
||||
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
flags = BTRFS_I(dir)->flags;
|
||||
|
||||
if (S_ISREG(inode->i_mode))
|
||||
flags &= ~BTRFS_INODE_DIRSYNC;
|
||||
else if (!S_ISDIR(inode->i_mode))
|
||||
flags &= (BTRFS_INODE_NODUMP | BTRFS_INODE_NOATIME);
|
||||
|
||||
BTRFS_I(inode)->flags = flags;
|
||||
btrfs_update_iflags(inode);
|
||||
}
|
||||
|
||||
static int btrfs_ioctl_getflags(struct file *file, void __user *arg)
|
||||
{
|
||||
struct btrfs_inode *ip = BTRFS_I(file->f_path.dentry->d_inode);
|
||||
unsigned int flags = btrfs_flags_to_ioctl(ip->flags);
|
||||
|
||||
if (copy_to_user(arg, &flags, sizeof(flags)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
{
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct btrfs_inode *ip = BTRFS_I(inode);
|
||||
struct btrfs_root *root = ip->root;
|
||||
struct btrfs_trans_handle *trans;
|
||||
unsigned int flags, oldflags;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&flags, arg, sizeof(flags)))
|
||||
return -EFAULT;
|
||||
|
||||
if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
|
||||
FS_NOATIME_FL | FS_NODUMP_FL | \
|
||||
FS_SYNC_FL | FS_DIRSYNC_FL))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!is_owner_or_cap(inode))
|
||||
return -EACCES;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
flags = btrfs_mask_flags(inode->i_mode, flags);
|
||||
oldflags = btrfs_flags_to_ioctl(ip->flags);
|
||||
if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
|
||||
if (!capable(CAP_LINUX_IMMUTABLE)) {
|
||||
ret = -EPERM;
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mnt_want_write(file->f_path.mnt);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
if (flags & FS_SYNC_FL)
|
||||
ip->flags |= BTRFS_INODE_SYNC;
|
||||
else
|
||||
ip->flags &= ~BTRFS_INODE_SYNC;
|
||||
if (flags & FS_IMMUTABLE_FL)
|
||||
ip->flags |= BTRFS_INODE_IMMUTABLE;
|
||||
else
|
||||
ip->flags &= ~BTRFS_INODE_IMMUTABLE;
|
||||
if (flags & FS_APPEND_FL)
|
||||
ip->flags |= BTRFS_INODE_APPEND;
|
||||
else
|
||||
ip->flags &= ~BTRFS_INODE_APPEND;
|
||||
if (flags & FS_NODUMP_FL)
|
||||
ip->flags |= BTRFS_INODE_NODUMP;
|
||||
else
|
||||
ip->flags &= ~BTRFS_INODE_NODUMP;
|
||||
if (flags & FS_NOATIME_FL)
|
||||
ip->flags |= BTRFS_INODE_NOATIME;
|
||||
else
|
||||
ip->flags &= ~BTRFS_INODE_NOATIME;
|
||||
if (flags & FS_DIRSYNC_FL)
|
||||
ip->flags |= BTRFS_INODE_DIRSYNC;
|
||||
else
|
||||
ip->flags &= ~BTRFS_INODE_DIRSYNC;
|
||||
|
||||
|
||||
trans = btrfs_join_transaction(root, 1);
|
||||
BUG_ON(!trans);
|
||||
|
||||
ret = btrfs_update_inode(trans, root, inode);
|
||||
BUG_ON(ret);
|
||||
|
||||
btrfs_update_iflags(inode);
|
||||
inode->i_ctime = CURRENT_TIME;
|
||||
btrfs_end_transaction(trans, root);
|
||||
|
||||
mnt_drop_write(file->f_path.mnt);
|
||||
out_unlock:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btrfs_ioctl_getversion(struct file *file, int __user *arg)
|
||||
{
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
|
||||
return put_user(inode->i_generation, arg);
|
||||
}
|
||||
|
||||
static noinline int create_subvol(struct btrfs_root *root,
|
||||
struct dentry *dentry,
|
||||
@ -82,22 +252,25 @@ static noinline int create_subvol(struct btrfs_root *root,
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
|
||||
objectid, trans->transid, 0, 0, 0);
|
||||
leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
|
||||
0, objectid, NULL, 0, 0, 0);
|
||||
if (IS_ERR(leaf)) {
|
||||
ret = PTR_ERR(leaf);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
btrfs_set_header_nritems(leaf, 0);
|
||||
btrfs_set_header_level(leaf, 0);
|
||||
memset_extent_buffer(leaf, 0, 0, sizeof(struct btrfs_header));
|
||||
btrfs_set_header_bytenr(leaf, leaf->start);
|
||||
btrfs_set_header_generation(leaf, trans->transid);
|
||||
btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV);
|
||||
btrfs_set_header_owner(leaf, objectid);
|
||||
|
||||
write_extent_buffer(leaf, root->fs_info->fsid,
|
||||
(unsigned long)btrfs_header_fsid(leaf),
|
||||
BTRFS_FSID_SIZE);
|
||||
write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid,
|
||||
(unsigned long)btrfs_header_chunk_tree_uuid(leaf),
|
||||
BTRFS_UUID_SIZE);
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
|
||||
inode_item = &root_item.inode;
|
||||
@ -125,7 +298,7 @@ static noinline int create_subvol(struct btrfs_root *root,
|
||||
btrfs_set_root_dirid(&root_item, new_dirid);
|
||||
|
||||
key.objectid = objectid;
|
||||
key.offset = 1;
|
||||
key.offset = 0;
|
||||
btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
|
||||
ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
|
||||
&root_item);
|
||||
@ -911,10 +1084,10 @@ static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||
if (disko) {
|
||||
inode_add_bytes(inode, datal);
|
||||
ret = btrfs_inc_extent_ref(trans, root,
|
||||
disko, diskl, leaf->start,
|
||||
root->root_key.objectid,
|
||||
trans->transid,
|
||||
inode->i_ino);
|
||||
disko, diskl, 0,
|
||||
root->root_key.objectid,
|
||||
inode->i_ino,
|
||||
new_key.offset - datao);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
} else if (type == BTRFS_FILE_EXTENT_INLINE) {
|
||||
@ -1074,6 +1247,12 @@ long btrfs_ioctl(struct file *file, unsigned int
|
||||
void __user *argp = (void __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case FS_IOC_GETFLAGS:
|
||||
return btrfs_ioctl_getflags(file, argp);
|
||||
case FS_IOC_SETFLAGS:
|
||||
return btrfs_ioctl_setflags(file, argp);
|
||||
case FS_IOC_GETVERSION:
|
||||
return btrfs_ioctl_getversion(file, argp);
|
||||
case BTRFS_IOC_SNAP_CREATE:
|
||||
return btrfs_ioctl_snap_create(file, argp, 0);
|
||||
case BTRFS_IOC_SUBVOL_CREATE:
|
||||
|
@ -45,22 +45,132 @@ static void print_dev_item(struct extent_buffer *eb,
|
||||
(unsigned long long)btrfs_device_total_bytes(eb, dev_item),
|
||||
(unsigned long long)btrfs_device_bytes_used(eb, dev_item));
|
||||
}
|
||||
static void print_extent_data_ref(struct extent_buffer *eb,
|
||||
struct btrfs_extent_data_ref *ref)
|
||||
{
|
||||
printk(KERN_INFO "\t\textent data backref root %llu "
|
||||
"objectid %llu offset %llu count %u\n",
|
||||
(unsigned long long)btrfs_extent_data_ref_root(eb, ref),
|
||||
(unsigned long long)btrfs_extent_data_ref_objectid(eb, ref),
|
||||
(unsigned long long)btrfs_extent_data_ref_offset(eb, ref),
|
||||
btrfs_extent_data_ref_count(eb, ref));
|
||||
}
|
||||
|
||||
static void print_extent_item(struct extent_buffer *eb, int slot)
|
||||
{
|
||||
struct btrfs_extent_item *ei;
|
||||
struct btrfs_extent_inline_ref *iref;
|
||||
struct btrfs_extent_data_ref *dref;
|
||||
struct btrfs_shared_data_ref *sref;
|
||||
struct btrfs_disk_key key;
|
||||
unsigned long end;
|
||||
unsigned long ptr;
|
||||
int type;
|
||||
u32 item_size = btrfs_item_size_nr(eb, slot);
|
||||
u64 flags;
|
||||
u64 offset;
|
||||
|
||||
if (item_size < sizeof(*ei)) {
|
||||
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
|
||||
struct btrfs_extent_item_v0 *ei0;
|
||||
BUG_ON(item_size != sizeof(*ei0));
|
||||
ei0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_item_v0);
|
||||
printk(KERN_INFO "\t\textent refs %u\n",
|
||||
btrfs_extent_refs_v0(eb, ei0));
|
||||
return;
|
||||
#else
|
||||
BUG();
|
||||
#endif
|
||||
}
|
||||
|
||||
ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
|
||||
flags = btrfs_extent_flags(eb, ei);
|
||||
|
||||
printk(KERN_INFO "\t\textent refs %llu gen %llu flags %llu\n",
|
||||
(unsigned long long)btrfs_extent_refs(eb, ei),
|
||||
(unsigned long long)btrfs_extent_generation(eb, ei),
|
||||
(unsigned long long)flags);
|
||||
|
||||
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
|
||||
struct btrfs_tree_block_info *info;
|
||||
info = (struct btrfs_tree_block_info *)(ei + 1);
|
||||
btrfs_tree_block_key(eb, info, &key);
|
||||
printk(KERN_INFO "\t\ttree block key (%llu %x %llu) "
|
||||
"level %d\n",
|
||||
(unsigned long long)btrfs_disk_key_objectid(&key),
|
||||
key.type,
|
||||
(unsigned long long)btrfs_disk_key_offset(&key),
|
||||
btrfs_tree_block_level(eb, info));
|
||||
iref = (struct btrfs_extent_inline_ref *)(info + 1);
|
||||
} else {
|
||||
iref = (struct btrfs_extent_inline_ref *)(ei + 1);
|
||||
}
|
||||
|
||||
ptr = (unsigned long)iref;
|
||||
end = (unsigned long)ei + item_size;
|
||||
while (ptr < end) {
|
||||
iref = (struct btrfs_extent_inline_ref *)ptr;
|
||||
type = btrfs_extent_inline_ref_type(eb, iref);
|
||||
offset = btrfs_extent_inline_ref_offset(eb, iref);
|
||||
switch (type) {
|
||||
case BTRFS_TREE_BLOCK_REF_KEY:
|
||||
printk(KERN_INFO "\t\ttree block backref "
|
||||
"root %llu\n", (unsigned long long)offset);
|
||||
break;
|
||||
case BTRFS_SHARED_BLOCK_REF_KEY:
|
||||
printk(KERN_INFO "\t\tshared block backref "
|
||||
"parent %llu\n", (unsigned long long)offset);
|
||||
break;
|
||||
case BTRFS_EXTENT_DATA_REF_KEY:
|
||||
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
|
||||
print_extent_data_ref(eb, dref);
|
||||
break;
|
||||
case BTRFS_SHARED_DATA_REF_KEY:
|
||||
sref = (struct btrfs_shared_data_ref *)(iref + 1);
|
||||
printk(KERN_INFO "\t\tshared data backref "
|
||||
"parent %llu count %u\n",
|
||||
(unsigned long long)offset,
|
||||
btrfs_shared_data_ref_count(eb, sref));
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
ptr += btrfs_extent_inline_ref_size(type);
|
||||
}
|
||||
WARN_ON(ptr > end);
|
||||
}
|
||||
|
||||
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
|
||||
static void print_extent_ref_v0(struct extent_buffer *eb, int slot)
|
||||
{
|
||||
struct btrfs_extent_ref_v0 *ref0;
|
||||
|
||||
ref0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_ref_v0);
|
||||
printk("\t\textent back ref root %llu gen %llu "
|
||||
"owner %llu num_refs %lu\n",
|
||||
(unsigned long long)btrfs_ref_root_v0(eb, ref0),
|
||||
(unsigned long long)btrfs_ref_generation_v0(eb, ref0),
|
||||
(unsigned long long)btrfs_ref_objectid_v0(eb, ref0),
|
||||
(unsigned long)btrfs_ref_count_v0(eb, ref0));
|
||||
}
|
||||
#endif
|
||||
|
||||
void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
|
||||
{
|
||||
int i;
|
||||
u32 type;
|
||||
u32 nr = btrfs_header_nritems(l);
|
||||
struct btrfs_item *item;
|
||||
struct btrfs_extent_item *ei;
|
||||
struct btrfs_root_item *ri;
|
||||
struct btrfs_dir_item *di;
|
||||
struct btrfs_inode_item *ii;
|
||||
struct btrfs_block_group_item *bi;
|
||||
struct btrfs_file_extent_item *fi;
|
||||
struct btrfs_extent_data_ref *dref;
|
||||
struct btrfs_shared_data_ref *sref;
|
||||
struct btrfs_dev_extent *dev_extent;
|
||||
struct btrfs_key key;
|
||||
struct btrfs_key found_key;
|
||||
struct btrfs_extent_ref *ref;
|
||||
struct btrfs_dev_extent *dev_extent;
|
||||
u32 type;
|
||||
|
||||
printk(KERN_INFO "leaf %llu total ptrs %d free space %d\n",
|
||||
(unsigned long long)btrfs_header_bytenr(l), nr,
|
||||
@ -100,20 +210,25 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
|
||||
btrfs_disk_root_refs(l, ri));
|
||||
break;
|
||||
case BTRFS_EXTENT_ITEM_KEY:
|
||||
ei = btrfs_item_ptr(l, i, struct btrfs_extent_item);
|
||||
printk(KERN_INFO "\t\textent data refs %u\n",
|
||||
btrfs_extent_refs(l, ei));
|
||||
print_extent_item(l, i);
|
||||
break;
|
||||
case BTRFS_EXTENT_REF_KEY:
|
||||
ref = btrfs_item_ptr(l, i, struct btrfs_extent_ref);
|
||||
printk(KERN_INFO "\t\textent back ref root %llu "
|
||||
"gen %llu owner %llu num_refs %lu\n",
|
||||
(unsigned long long)btrfs_ref_root(l, ref),
|
||||
(unsigned long long)btrfs_ref_generation(l, ref),
|
||||
(unsigned long long)btrfs_ref_objectid(l, ref),
|
||||
(unsigned long)btrfs_ref_num_refs(l, ref));
|
||||
case BTRFS_TREE_BLOCK_REF_KEY:
|
||||
printk(KERN_INFO "\t\ttree block backref\n");
|
||||
break;
|
||||
case BTRFS_SHARED_BLOCK_REF_KEY:
|
||||
printk(KERN_INFO "\t\tshared block backref\n");
|
||||
break;
|
||||
case BTRFS_EXTENT_DATA_REF_KEY:
|
||||
dref = btrfs_item_ptr(l, i,
|
||||
struct btrfs_extent_data_ref);
|
||||
print_extent_data_ref(l, dref);
|
||||
break;
|
||||
case BTRFS_SHARED_DATA_REF_KEY:
|
||||
sref = btrfs_item_ptr(l, i,
|
||||
struct btrfs_shared_data_ref);
|
||||
printk(KERN_INFO "\t\tshared data backref count %u\n",
|
||||
btrfs_shared_data_ref_count(l, sref));
|
||||
break;
|
||||
|
||||
case BTRFS_EXTENT_DATA_KEY:
|
||||
fi = btrfs_item_ptr(l, i,
|
||||
struct btrfs_file_extent_item);
|
||||
@ -139,6 +254,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
|
||||
(unsigned long long)
|
||||
btrfs_file_extent_ram_bytes(l, fi));
|
||||
break;
|
||||
case BTRFS_EXTENT_REF_V0_KEY:
|
||||
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
|
||||
print_extent_ref_v0(l, i);
|
||||
#else
|
||||
BUG();
|
||||
#endif
|
||||
case BTRFS_BLOCK_GROUP_ITEM_KEY:
|
||||
bi = btrfs_item_ptr(l, i,
|
||||
struct btrfs_block_group_item);
|
||||
|
3711
fs/btrfs/relocation.c
Normal file
3711
fs/btrfs/relocation.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -111,6 +111,15 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_set_root_node(struct btrfs_root_item *item,
|
||||
struct extent_buffer *node)
|
||||
{
|
||||
btrfs_set_root_bytenr(item, node->start);
|
||||
btrfs_set_root_level(item, btrfs_header_level(node));
|
||||
btrfs_set_root_generation(item, btrfs_header_generation(node));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* copy the data in 'item' into the btree
|
||||
*/
|
||||
@ -164,8 +173,7 @@ int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
* offset lower than the latest root. They need to be queued for deletion to
|
||||
* finish what was happening when we crashed.
|
||||
*/
|
||||
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid,
|
||||
struct btrfs_root *latest)
|
||||
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid)
|
||||
{
|
||||
struct btrfs_root *dead_root;
|
||||
struct btrfs_item *item;
|
||||
@ -227,10 +235,7 @@ again:
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (objectid == BTRFS_TREE_RELOC_OBJECTID)
|
||||
ret = btrfs_add_dead_reloc_root(dead_root);
|
||||
else
|
||||
ret = btrfs_add_dead_root(dead_root, latest);
|
||||
ret = btrfs_add_dead_root(dead_root);
|
||||
if (ret)
|
||||
goto err;
|
||||
goto again;
|
||||
|
@ -52,7 +52,6 @@
|
||||
#include "export.h"
|
||||
#include "compression.h"
|
||||
|
||||
|
||||
static struct super_operations btrfs_super_ops;
|
||||
|
||||
static void btrfs_put_super(struct super_block *sb)
|
||||
@ -67,8 +66,8 @@ static void btrfs_put_super(struct super_block *sb)
|
||||
enum {
|
||||
Opt_degraded, Opt_subvol, Opt_device, Opt_nodatasum, Opt_nodatacow,
|
||||
Opt_max_extent, Opt_max_inline, Opt_alloc_start, Opt_nobarrier,
|
||||
Opt_ssd, Opt_thread_pool, Opt_noacl, Opt_compress, Opt_notreelog,
|
||||
Opt_ratio, Opt_flushoncommit, Opt_err,
|
||||
Opt_ssd, Opt_nossd, Opt_ssd_spread, Opt_thread_pool, Opt_noacl,
|
||||
Opt_compress, Opt_notreelog, Opt_ratio, Opt_flushoncommit, Opt_err,
|
||||
};
|
||||
|
||||
static match_table_t tokens = {
|
||||
@ -84,6 +83,8 @@ static match_table_t tokens = {
|
||||
{Opt_thread_pool, "thread_pool=%d"},
|
||||
{Opt_compress, "compress"},
|
||||
{Opt_ssd, "ssd"},
|
||||
{Opt_ssd_spread, "ssd_spread"},
|
||||
{Opt_nossd, "nossd"},
|
||||
{Opt_noacl, "noacl"},
|
||||
{Opt_notreelog, "notreelog"},
|
||||
{Opt_flushoncommit, "flushoncommit"},
|
||||
@ -158,7 +159,7 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
|
||||
*/
|
||||
break;
|
||||
case Opt_nodatasum:
|
||||
printk(KERN_INFO "btrfs: setting nodatacsum\n");
|
||||
printk(KERN_INFO "btrfs: setting nodatasum\n");
|
||||
btrfs_set_opt(info->mount_opt, NODATASUM);
|
||||
break;
|
||||
case Opt_nodatacow:
|
||||
@ -174,6 +175,19 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
|
||||
printk(KERN_INFO "btrfs: use ssd allocation scheme\n");
|
||||
btrfs_set_opt(info->mount_opt, SSD);
|
||||
break;
|
||||
case Opt_ssd_spread:
|
||||
printk(KERN_INFO "btrfs: use spread ssd "
|
||||
"allocation scheme\n");
|
||||
btrfs_set_opt(info->mount_opt, SSD);
|
||||
btrfs_set_opt(info->mount_opt, SSD_SPREAD);
|
||||
break;
|
||||
case Opt_nossd:
|
||||
printk(KERN_INFO "btrfs: not using ssd allocation "
|
||||
"scheme\n");
|
||||
btrfs_set_opt(info->mount_opt, NOSSD);
|
||||
btrfs_clear_opt(info->mount_opt, SSD);
|
||||
btrfs_clear_opt(info->mount_opt, SSD_SPREAD);
|
||||
break;
|
||||
case Opt_nobarrier:
|
||||
printk(KERN_INFO "btrfs: turning off barriers\n");
|
||||
btrfs_set_opt(info->mount_opt, NOBARRIER);
|
||||
@ -322,7 +336,7 @@ static int btrfs_fill_super(struct super_block *sb,
|
||||
struct dentry *root_dentry;
|
||||
struct btrfs_super_block *disk_super;
|
||||
struct btrfs_root *tree_root;
|
||||
struct btrfs_inode *bi;
|
||||
struct btrfs_key key;
|
||||
int err;
|
||||
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
@ -341,23 +355,15 @@ static int btrfs_fill_super(struct super_block *sb,
|
||||
}
|
||||
sb->s_fs_info = tree_root;
|
||||
disk_super = &tree_root->fs_info->super_copy;
|
||||
inode = btrfs_iget_locked(sb, BTRFS_FIRST_FREE_OBJECTID,
|
||||
tree_root->fs_info->fs_root);
|
||||
bi = BTRFS_I(inode);
|
||||
bi->location.objectid = inode->i_ino;
|
||||
bi->location.offset = 0;
|
||||
bi->root = tree_root->fs_info->fs_root;
|
||||
|
||||
btrfs_set_key_type(&bi->location, BTRFS_INODE_ITEM_KEY);
|
||||
|
||||
if (!inode) {
|
||||
err = -ENOMEM;
|
||||
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
|
||||
key.type = BTRFS_INODE_ITEM_KEY;
|
||||
key.offset = 0;
|
||||
inode = btrfs_iget(sb, &key, tree_root->fs_info->fs_root);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto fail_close;
|
||||
}
|
||||
if (inode->i_state & I_NEW) {
|
||||
btrfs_read_locked_inode(inode);
|
||||
unlock_new_inode(inode);
|
||||
}
|
||||
|
||||
root_dentry = d_alloc_root(inode);
|
||||
if (!root_dentry) {
|
||||
@ -433,7 +439,11 @@ static int btrfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
|
||||
seq_printf(seq, ",thread_pool=%d", info->thread_pool_size);
|
||||
if (btrfs_test_opt(root, COMPRESS))
|
||||
seq_puts(seq, ",compress");
|
||||
if (btrfs_test_opt(root, SSD))
|
||||
if (btrfs_test_opt(root, NOSSD))
|
||||
seq_puts(seq, ",nossd");
|
||||
if (btrfs_test_opt(root, SSD_SPREAD))
|
||||
seq_puts(seq, ",ssd_spread");
|
||||
else if (btrfs_test_opt(root, SSD))
|
||||
seq_puts(seq, ",ssd");
|
||||
if (btrfs_test_opt(root, NOTREELOG))
|
||||
seq_puts(seq, ",notreelog");
|
||||
@ -584,7 +594,8 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
|
||||
if (btrfs_super_log_root(&root->fs_info->super_copy) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = btrfs_cleanup_reloc_trees(root);
|
||||
/* recover relocation */
|
||||
ret = btrfs_recover_relocation(root);
|
||||
WARN_ON(ret);
|
||||
|
||||
ret = btrfs_cleanup_fs_roots(root->fs_info);
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "disk-io.h"
|
||||
#include "transaction.h"
|
||||
#include "locking.h"
|
||||
#include "ref-cache.h"
|
||||
#include "tree-log.h"
|
||||
|
||||
#define BTRFS_ROOT_TRANS_TAG 0
|
||||
@ -94,48 +93,40 @@ static noinline int join_transaction(struct btrfs_root *root)
|
||||
* to make sure the old root from before we joined the transaction is deleted
|
||||
* when the transaction commits
|
||||
*/
|
||||
noinline int btrfs_record_root_in_trans(struct btrfs_root *root)
|
||||
static noinline int record_root_in_trans(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_dirty_root *dirty;
|
||||
u64 running_trans_id = root->fs_info->running_transaction->transid;
|
||||
if (root->ref_cows && root->last_trans < running_trans_id) {
|
||||
if (root->ref_cows && root->last_trans < trans->transid) {
|
||||
WARN_ON(root == root->fs_info->extent_root);
|
||||
if (root->root_item.refs != 0) {
|
||||
radix_tree_tag_set(&root->fs_info->fs_roots_radix,
|
||||
(unsigned long)root->root_key.objectid,
|
||||
BTRFS_ROOT_TRANS_TAG);
|
||||
WARN_ON(root->root_item.refs == 0);
|
||||
WARN_ON(root->commit_root != root->node);
|
||||
|
||||
dirty = kmalloc(sizeof(*dirty), GFP_NOFS);
|
||||
BUG_ON(!dirty);
|
||||
dirty->root = kmalloc(sizeof(*dirty->root), GFP_NOFS);
|
||||
BUG_ON(!dirty->root);
|
||||
dirty->latest_root = root;
|
||||
INIT_LIST_HEAD(&dirty->list);
|
||||
|
||||
root->commit_root = btrfs_root_node(root);
|
||||
|
||||
memcpy(dirty->root, root, sizeof(*root));
|
||||
spin_lock_init(&dirty->root->node_lock);
|
||||
spin_lock_init(&dirty->root->list_lock);
|
||||
mutex_init(&dirty->root->objectid_mutex);
|
||||
mutex_init(&dirty->root->log_mutex);
|
||||
INIT_LIST_HEAD(&dirty->root->dead_list);
|
||||
dirty->root->node = root->commit_root;
|
||||
dirty->root->commit_root = NULL;
|
||||
|
||||
spin_lock(&root->list_lock);
|
||||
list_add(&dirty->root->dead_list, &root->dead_list);
|
||||
spin_unlock(&root->list_lock);
|
||||
|
||||
root->dirty_root = dirty;
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
}
|
||||
root->last_trans = running_trans_id;
|
||||
radix_tree_tag_set(&root->fs_info->fs_roots_radix,
|
||||
(unsigned long)root->root_key.objectid,
|
||||
BTRFS_ROOT_TRANS_TAG);
|
||||
root->last_trans = trans->transid;
|
||||
btrfs_init_reloc_root(trans, root);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
if (!root->ref_cows)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&root->fs_info->trans_mutex);
|
||||
if (root->last_trans == trans->transid) {
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
record_root_in_trans(trans, root);
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wait for commit against the current transaction to become unblocked
|
||||
* when this is done, it is safe to start a new transaction, but the current
|
||||
* transaction might not be fully on disk.
|
||||
@ -181,7 +172,6 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
|
||||
ret = join_transaction(root);
|
||||
BUG_ON(ret);
|
||||
|
||||
btrfs_record_root_in_trans(root);
|
||||
h->transid = root->fs_info->running_transaction->transid;
|
||||
h->transaction = root->fs_info->running_transaction;
|
||||
h->blocks_reserved = num_blocks;
|
||||
@ -192,6 +182,7 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
|
||||
h->delayed_ref_updates = 0;
|
||||
|
||||
root->fs_info->running_transaction->use_count++;
|
||||
record_root_in_trans(h, root);
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
return h;
|
||||
}
|
||||
@ -233,6 +224,7 @@ static noinline int wait_for_commit(struct btrfs_root *root,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* rate limit against the drop_snapshot code. This helps to slow down new
|
||||
* operations if the drop_snapshot code isn't able to keep up.
|
||||
@ -273,6 +265,7 @@ harder:
|
||||
goto harder;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void btrfs_throttle(struct btrfs_root *root)
|
||||
{
|
||||
@ -280,7 +273,6 @@ void btrfs_throttle(struct btrfs_root *root)
|
||||
if (!root->fs_info->open_ioctl_trans)
|
||||
wait_current_trans(root);
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
throttle_on_drops(root);
|
||||
}
|
||||
|
||||
static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
||||
@ -323,9 +315,6 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
||||
memset(trans, 0, sizeof(*trans));
|
||||
kmem_cache_free(btrfs_trans_handle_cachep, trans);
|
||||
|
||||
if (throttle)
|
||||
throttle_on_drops(root);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -462,12 +451,8 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
|
||||
old_root_bytenr = btrfs_root_bytenr(&root->root_item);
|
||||
if (old_root_bytenr == root->node->start)
|
||||
break;
|
||||
btrfs_set_root_bytenr(&root->root_item,
|
||||
root->node->start);
|
||||
btrfs_set_root_level(&root->root_item,
|
||||
btrfs_header_level(root->node));
|
||||
btrfs_set_root_generation(&root->root_item, trans->transid);
|
||||
|
||||
btrfs_set_root_node(&root->root_item, root->node);
|
||||
ret = btrfs_update_root(trans, tree_root,
|
||||
&root->root_key,
|
||||
&root->root_item);
|
||||
@ -477,14 +462,16 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
free_extent_buffer(root->commit_root);
|
||||
root->commit_root = btrfs_root_node(root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* update all the cowonly tree roots on disk
|
||||
*/
|
||||
int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct list_head *next;
|
||||
@ -520,118 +507,54 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
|
||||
* a dirty root struct and adds it into the list of dead roots that need to
|
||||
* be deleted
|
||||
*/
|
||||
int btrfs_add_dead_root(struct btrfs_root *root, struct btrfs_root *latest)
|
||||
int btrfs_add_dead_root(struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_dirty_root *dirty;
|
||||
|
||||
dirty = kmalloc(sizeof(*dirty), GFP_NOFS);
|
||||
if (!dirty)
|
||||
return -ENOMEM;
|
||||
dirty->root = root;
|
||||
dirty->latest_root = latest;
|
||||
|
||||
mutex_lock(&root->fs_info->trans_mutex);
|
||||
list_add(&dirty->list, &latest->fs_info->dead_roots);
|
||||
list_add(&root->root_list, &root->fs_info->dead_roots);
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* at transaction commit time we need to schedule the old roots for
|
||||
* deletion via btrfs_drop_snapshot. This runs through all the
|
||||
* reference counted roots that were modified in the current
|
||||
* transaction and puts them into the drop list
|
||||
* update all the cowonly tree roots on disk
|
||||
*/
|
||||
static noinline int add_dirty_roots(struct btrfs_trans_handle *trans,
|
||||
struct radix_tree_root *radix,
|
||||
struct list_head *list)
|
||||
static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_dirty_root *dirty;
|
||||
struct btrfs_root *gang[8];
|
||||
struct btrfs_root *root;
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
int i;
|
||||
int ret;
|
||||
int err = 0;
|
||||
u32 refs;
|
||||
|
||||
while (1) {
|
||||
ret = radix_tree_gang_lookup_tag(radix, (void **)gang, 0,
|
||||
ret = radix_tree_gang_lookup_tag(&fs_info->fs_roots_radix,
|
||||
(void **)gang, 0,
|
||||
ARRAY_SIZE(gang),
|
||||
BTRFS_ROOT_TRANS_TAG);
|
||||
if (ret == 0)
|
||||
break;
|
||||
for (i = 0; i < ret; i++) {
|
||||
root = gang[i];
|
||||
radix_tree_tag_clear(radix,
|
||||
(unsigned long)root->root_key.objectid,
|
||||
BTRFS_ROOT_TRANS_TAG);
|
||||
|
||||
BUG_ON(!root->ref_tree);
|
||||
dirty = root->dirty_root;
|
||||
radix_tree_tag_clear(&fs_info->fs_roots_radix,
|
||||
(unsigned long)root->root_key.objectid,
|
||||
BTRFS_ROOT_TRANS_TAG);
|
||||
|
||||
btrfs_free_log(trans, root);
|
||||
btrfs_free_reloc_root(trans, root);
|
||||
btrfs_update_reloc_root(trans, root);
|
||||
|
||||
if (root->commit_root == root->node) {
|
||||
WARN_ON(root->node->start !=
|
||||
btrfs_root_bytenr(&root->root_item));
|
||||
|
||||
free_extent_buffer(root->commit_root);
|
||||
root->commit_root = NULL;
|
||||
root->dirty_root = NULL;
|
||||
|
||||
spin_lock(&root->list_lock);
|
||||
list_del_init(&dirty->root->dead_list);
|
||||
spin_unlock(&root->list_lock);
|
||||
|
||||
kfree(dirty->root);
|
||||
kfree(dirty);
|
||||
|
||||
/* make sure to update the root on disk
|
||||
* so we get any updates to the block used
|
||||
* counts
|
||||
*/
|
||||
err = btrfs_update_root(trans,
|
||||
root->fs_info->tree_root,
|
||||
&root->root_key,
|
||||
&root->root_item);
|
||||
if (root->commit_root == root->node)
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(&root->root_item.drop_progress, 0,
|
||||
sizeof(struct btrfs_disk_key));
|
||||
root->root_item.drop_level = 0;
|
||||
root->commit_root = NULL;
|
||||
root->dirty_root = NULL;
|
||||
root->root_key.offset = root->fs_info->generation;
|
||||
btrfs_set_root_bytenr(&root->root_item,
|
||||
root->node->start);
|
||||
btrfs_set_root_level(&root->root_item,
|
||||
btrfs_header_level(root->node));
|
||||
btrfs_set_root_generation(&root->root_item,
|
||||
root->root_key.offset);
|
||||
free_extent_buffer(root->commit_root);
|
||||
root->commit_root = btrfs_root_node(root);
|
||||
|
||||
err = btrfs_insert_root(trans, root->fs_info->tree_root,
|
||||
btrfs_set_root_node(&root->root_item, root->node);
|
||||
err = btrfs_update_root(trans, fs_info->tree_root,
|
||||
&root->root_key,
|
||||
&root->root_item);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
refs = btrfs_root_refs(&dirty->root->root_item);
|
||||
btrfs_set_root_refs(&dirty->root->root_item, refs - 1);
|
||||
err = btrfs_update_root(trans, root->fs_info->tree_root,
|
||||
&dirty->root->root_key,
|
||||
&dirty->root->root_item);
|
||||
|
||||
BUG_ON(err);
|
||||
if (refs == 1) {
|
||||
list_add(&dirty->list, list);
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
free_extent_buffer(dirty->root->node);
|
||||
kfree(dirty->root);
|
||||
kfree(dirty);
|
||||
}
|
||||
}
|
||||
}
|
||||
return err;
|
||||
@ -688,12 +611,8 @@ static noinline int wait_transaction_pre_flush(struct btrfs_fs_info *info)
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
mutex_unlock(&info->trans_mutex);
|
||||
|
||||
atomic_dec(&info->throttles);
|
||||
wake_up(&info->transaction_throttle);
|
||||
|
||||
schedule();
|
||||
|
||||
atomic_inc(&info->throttles);
|
||||
mutex_lock(&info->trans_mutex);
|
||||
finish_wait(&info->transaction_wait, &wait);
|
||||
}
|
||||
@ -705,111 +624,61 @@ static noinline int wait_transaction_pre_flush(struct btrfs_fs_info *info)
|
||||
* Given a list of roots that need to be deleted, call btrfs_drop_snapshot on
|
||||
* all of them
|
||||
*/
|
||||
static noinline int drop_dirty_roots(struct btrfs_root *tree_root,
|
||||
struct list_head *list)
|
||||
int btrfs_drop_dead_root(struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_dirty_root *dirty;
|
||||
struct btrfs_trans_handle *trans;
|
||||
struct btrfs_root *tree_root = root->fs_info->tree_root;
|
||||
unsigned long nr;
|
||||
u64 num_bytes;
|
||||
u64 bytes_used;
|
||||
u64 max_useless;
|
||||
int ret = 0;
|
||||
int err;
|
||||
int ret;
|
||||
|
||||
while (!list_empty(list)) {
|
||||
struct btrfs_root *root;
|
||||
while (1) {
|
||||
/*
|
||||
* we don't want to jump in and create a bunch of
|
||||
* delayed refs if the transaction is starting to close
|
||||
*/
|
||||
wait_transaction_pre_flush(tree_root->fs_info);
|
||||
trans = btrfs_start_transaction(tree_root, 1);
|
||||
|
||||
dirty = list_entry(list->prev, struct btrfs_dirty_root, list);
|
||||
list_del_init(&dirty->list);
|
||||
|
||||
num_bytes = btrfs_root_used(&dirty->root->root_item);
|
||||
root = dirty->latest_root;
|
||||
atomic_inc(&root->fs_info->throttles);
|
||||
|
||||
while (1) {
|
||||
/*
|
||||
* we don't want to jump in and create a bunch of
|
||||
* delayed refs if the transaction is starting to close
|
||||
*/
|
||||
wait_transaction_pre_flush(tree_root->fs_info);
|
||||
trans = btrfs_start_transaction(tree_root, 1);
|
||||
|
||||
/*
|
||||
* we've joined a transaction, make sure it isn't
|
||||
* closing right now
|
||||
*/
|
||||
if (trans->transaction->delayed_refs.flushing) {
|
||||
btrfs_end_transaction(trans, tree_root);
|
||||
continue;
|
||||
}
|
||||
|
||||
mutex_lock(&root->fs_info->drop_mutex);
|
||||
ret = btrfs_drop_snapshot(trans, dirty->root);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
mutex_unlock(&root->fs_info->drop_mutex);
|
||||
|
||||
err = btrfs_update_root(trans,
|
||||
tree_root,
|
||||
&dirty->root->root_key,
|
||||
&dirty->root->root_item);
|
||||
if (err)
|
||||
ret = err;
|
||||
nr = trans->blocks_used;
|
||||
ret = btrfs_end_transaction(trans, tree_root);
|
||||
BUG_ON(ret);
|
||||
|
||||
btrfs_btree_balance_dirty(tree_root, nr);
|
||||
cond_resched();
|
||||
}
|
||||
BUG_ON(ret);
|
||||
atomic_dec(&root->fs_info->throttles);
|
||||
wake_up(&root->fs_info->transaction_throttle);
|
||||
|
||||
num_bytes -= btrfs_root_used(&dirty->root->root_item);
|
||||
bytes_used = btrfs_root_used(&root->root_item);
|
||||
if (num_bytes) {
|
||||
mutex_lock(&root->fs_info->trans_mutex);
|
||||
btrfs_record_root_in_trans(root);
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
btrfs_set_root_used(&root->root_item,
|
||||
bytes_used - num_bytes);
|
||||
/*
|
||||
* we've joined a transaction, make sure it isn't
|
||||
* closing right now
|
||||
*/
|
||||
if (trans->transaction->delayed_refs.flushing) {
|
||||
btrfs_end_transaction(trans, tree_root);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = btrfs_del_root(trans, tree_root, &dirty->root->root_key);
|
||||
if (ret) {
|
||||
BUG();
|
||||
ret = btrfs_drop_snapshot(trans, root);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&root->fs_info->drop_mutex);
|
||||
|
||||
spin_lock(&root->list_lock);
|
||||
list_del_init(&dirty->root->dead_list);
|
||||
if (!list_empty(&root->dead_list)) {
|
||||
struct btrfs_root *oldest;
|
||||
oldest = list_entry(root->dead_list.prev,
|
||||
struct btrfs_root, dead_list);
|
||||
max_useless = oldest->root_key.offset - 1;
|
||||
} else {
|
||||
max_useless = root->root_key.offset - 1;
|
||||
}
|
||||
spin_unlock(&root->list_lock);
|
||||
ret = btrfs_update_root(trans, tree_root,
|
||||
&root->root_key,
|
||||
&root->root_item);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
nr = trans->blocks_used;
|
||||
ret = btrfs_end_transaction(trans, tree_root);
|
||||
BUG_ON(ret);
|
||||
|
||||
ret = btrfs_remove_leaf_refs(root, max_useless, 0);
|
||||
BUG_ON(ret);
|
||||
|
||||
free_extent_buffer(dirty->root->node);
|
||||
kfree(dirty->root);
|
||||
kfree(dirty);
|
||||
|
||||
btrfs_btree_balance_dirty(tree_root, nr);
|
||||
cond_resched();
|
||||
}
|
||||
BUG_ON(ret);
|
||||
|
||||
ret = btrfs_del_root(trans, tree_root, &root->root_key);
|
||||
BUG_ON(ret);
|
||||
|
||||
nr = trans->blocks_used;
|
||||
ret = btrfs_end_transaction(trans, tree_root);
|
||||
BUG_ON(ret);
|
||||
|
||||
free_extent_buffer(root->node);
|
||||
free_extent_buffer(root->commit_root);
|
||||
kfree(root);
|
||||
|
||||
btrfs_btree_balance_dirty(tree_root, nr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -839,24 +708,23 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
btrfs_record_root_in_trans(root);
|
||||
record_root_in_trans(trans, root);
|
||||
btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
|
||||
memcpy(new_root_item, &root->root_item, sizeof(*new_root_item));
|
||||
|
||||
key.objectid = objectid;
|
||||
key.offset = trans->transid;
|
||||
key.offset = 0;
|
||||
btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
|
||||
|
||||
old = btrfs_lock_root_node(root);
|
||||
btrfs_cow_block(trans, root, old, NULL, 0, &old);
|
||||
btrfs_set_lock_blocking(old);
|
||||
|
||||
btrfs_copy_root(trans, root, old, &tmp, objectid);
|
||||
btrfs_tree_unlock(old);
|
||||
free_extent_buffer(old);
|
||||
|
||||
btrfs_set_root_bytenr(new_root_item, tmp->start);
|
||||
btrfs_set_root_level(new_root_item, btrfs_header_level(tmp));
|
||||
btrfs_set_root_generation(new_root_item, trans->transid);
|
||||
btrfs_set_root_node(new_root_item, tmp);
|
||||
ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
|
||||
new_root_item);
|
||||
btrfs_tree_unlock(tmp);
|
||||
@ -964,6 +832,24 @@ static noinline int finish_pending_snapshots(struct btrfs_trans_handle *trans,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_super_roots(struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_root_item *root_item;
|
||||
struct btrfs_super_block *super;
|
||||
|
||||
super = &root->fs_info->super_copy;
|
||||
|
||||
root_item = &root->fs_info->chunk_root->root_item;
|
||||
super->chunk_root = root_item->bytenr;
|
||||
super->chunk_root_generation = root_item->generation;
|
||||
super->chunk_root_level = root_item->level;
|
||||
|
||||
root_item = &root->fs_info->tree_root->root_item;
|
||||
super->root = root_item->bytenr;
|
||||
super->generation = root_item->generation;
|
||||
super->root_level = root_item->level;
|
||||
}
|
||||
|
||||
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
@ -971,8 +857,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
unsigned long timeout = 1;
|
||||
struct btrfs_transaction *cur_trans;
|
||||
struct btrfs_transaction *prev_trans = NULL;
|
||||
struct btrfs_root *chunk_root = root->fs_info->chunk_root;
|
||||
struct list_head dirty_fs_roots;
|
||||
struct extent_io_tree *pinned_copy;
|
||||
DEFINE_WAIT(wait);
|
||||
int ret;
|
||||
@ -999,7 +883,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
BUG_ON(ret);
|
||||
|
||||
mutex_lock(&root->fs_info->trans_mutex);
|
||||
INIT_LIST_HEAD(&dirty_fs_roots);
|
||||
if (cur_trans->in_commit) {
|
||||
cur_trans->use_count++;
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
@ -1105,41 +988,36 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
* with the tree-log code.
|
||||
*/
|
||||
mutex_lock(&root->fs_info->tree_log_mutex);
|
||||
/*
|
||||
* keep tree reloc code from adding new reloc trees
|
||||
*/
|
||||
mutex_lock(&root->fs_info->tree_reloc_mutex);
|
||||
|
||||
|
||||
ret = add_dirty_roots(trans, &root->fs_info->fs_roots_radix,
|
||||
&dirty_fs_roots);
|
||||
ret = commit_fs_roots(trans, root);
|
||||
BUG_ON(ret);
|
||||
|
||||
/* add_dirty_roots gets rid of all the tree log roots, it is now
|
||||
/* commit_fs_roots gets rid of all the tree log roots, it is now
|
||||
* safe to free the root of tree log roots
|
||||
*/
|
||||
btrfs_free_log_root_tree(trans, root->fs_info);
|
||||
|
||||
ret = btrfs_commit_tree_roots(trans, root);
|
||||
ret = commit_cowonly_roots(trans, root);
|
||||
BUG_ON(ret);
|
||||
|
||||
cur_trans = root->fs_info->running_transaction;
|
||||
spin_lock(&root->fs_info->new_trans_lock);
|
||||
root->fs_info->running_transaction = NULL;
|
||||
spin_unlock(&root->fs_info->new_trans_lock);
|
||||
btrfs_set_super_generation(&root->fs_info->super_copy,
|
||||
cur_trans->transid);
|
||||
btrfs_set_super_root(&root->fs_info->super_copy,
|
||||
root->fs_info->tree_root->node->start);
|
||||
btrfs_set_super_root_level(&root->fs_info->super_copy,
|
||||
btrfs_header_level(root->fs_info->tree_root->node));
|
||||
|
||||
btrfs_set_super_chunk_root(&root->fs_info->super_copy,
|
||||
chunk_root->node->start);
|
||||
btrfs_set_super_chunk_root_level(&root->fs_info->super_copy,
|
||||
btrfs_header_level(chunk_root->node));
|
||||
btrfs_set_super_chunk_root_generation(&root->fs_info->super_copy,
|
||||
btrfs_header_generation(chunk_root->node));
|
||||
btrfs_set_root_node(&root->fs_info->tree_root->root_item,
|
||||
root->fs_info->tree_root->node);
|
||||
free_extent_buffer(root->fs_info->tree_root->commit_root);
|
||||
root->fs_info->tree_root->commit_root =
|
||||
btrfs_root_node(root->fs_info->tree_root);
|
||||
|
||||
btrfs_set_root_node(&root->fs_info->chunk_root->root_item,
|
||||
root->fs_info->chunk_root->node);
|
||||
free_extent_buffer(root->fs_info->chunk_root->commit_root);
|
||||
root->fs_info->chunk_root->commit_root =
|
||||
btrfs_root_node(root->fs_info->chunk_root);
|
||||
|
||||
update_super_roots(root);
|
||||
|
||||
if (!root->fs_info->log_root_recovering) {
|
||||
btrfs_set_super_log_root(&root->fs_info->super_copy, 0);
|
||||
@ -1153,7 +1031,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
|
||||
trans->transaction->blocked = 0;
|
||||
|
||||
wake_up(&root->fs_info->transaction_throttle);
|
||||
wake_up(&root->fs_info->transaction_wait);
|
||||
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
@ -1170,9 +1047,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
btrfs_finish_extent_commit(trans, root, pinned_copy);
|
||||
kfree(pinned_copy);
|
||||
|
||||
btrfs_drop_dead_reloc_roots(root);
|
||||
mutex_unlock(&root->fs_info->tree_reloc_mutex);
|
||||
|
||||
/* do the directory inserts of any pending snapshot creations */
|
||||
finish_pending_snapshots(trans, root->fs_info);
|
||||
|
||||
@ -1186,16 +1060,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
put_transaction(cur_trans);
|
||||
put_transaction(cur_trans);
|
||||
|
||||
list_splice_init(&dirty_fs_roots, &root->fs_info->dead_roots);
|
||||
if (root->fs_info->closing)
|
||||
list_splice_init(&root->fs_info->dead_roots, &dirty_fs_roots);
|
||||
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
|
||||
kmem_cache_free(btrfs_trans_handle_cachep, trans);
|
||||
|
||||
if (root->fs_info->closing)
|
||||
drop_dirty_roots(root->fs_info->tree_root, &dirty_fs_roots);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1204,16 +1071,17 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
*/
|
||||
int btrfs_clean_old_snapshots(struct btrfs_root *root)
|
||||
{
|
||||
struct list_head dirty_roots;
|
||||
INIT_LIST_HEAD(&dirty_roots);
|
||||
again:
|
||||
mutex_lock(&root->fs_info->trans_mutex);
|
||||
list_splice_init(&root->fs_info->dead_roots, &dirty_roots);
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
LIST_HEAD(list);
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
|
||||
if (!list_empty(&dirty_roots)) {
|
||||
drop_dirty_roots(root, &dirty_roots);
|
||||
goto again;
|
||||
mutex_lock(&fs_info->trans_mutex);
|
||||
list_splice_init(&fs_info->dead_roots, &list);
|
||||
mutex_unlock(&fs_info->trans_mutex);
|
||||
|
||||
while (!list_empty(&list)) {
|
||||
root = list_entry(list.next, struct btrfs_root, root_list);
|
||||
list_del_init(&root->root_list);
|
||||
btrfs_drop_dead_root(root);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -62,12 +62,6 @@ struct btrfs_pending_snapshot {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct btrfs_dirty_root {
|
||||
struct list_head list;
|
||||
struct btrfs_root *root;
|
||||
struct btrfs_root *latest_root;
|
||||
};
|
||||
|
||||
static inline void btrfs_set_trans_block_group(struct btrfs_trans_handle *trans,
|
||||
struct inode *inode)
|
||||
{
|
||||
@ -100,7 +94,8 @@ int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans,
|
||||
int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
|
||||
int btrfs_add_dead_root(struct btrfs_root *root, struct btrfs_root *latest);
|
||||
int btrfs_add_dead_root(struct btrfs_root *root);
|
||||
int btrfs_drop_dead_root(struct btrfs_root *root);
|
||||
int btrfs_defrag_root(struct btrfs_root *root, int cacheonly);
|
||||
int btrfs_clean_old_snapshots(struct btrfs_root *root);
|
||||
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
@ -108,7 +103,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||
int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
void btrfs_throttle(struct btrfs_root *root);
|
||||
int btrfs_record_root_in_trans(struct btrfs_root *root);
|
||||
int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
int btrfs_write_and_wait_marked_extents(struct btrfs_root *root,
|
||||
struct extent_io_tree *dirty_pages);
|
||||
#endif
|
||||
|
@ -430,18 +430,16 @@ no_copy:
|
||||
static noinline struct inode *read_one_inode(struct btrfs_root *root,
|
||||
u64 objectid)
|
||||
{
|
||||
struct btrfs_key key;
|
||||
struct inode *inode;
|
||||
inode = btrfs_iget_locked(root->fs_info->sb, objectid, root);
|
||||
if (inode->i_state & I_NEW) {
|
||||
BTRFS_I(inode)->root = root;
|
||||
BTRFS_I(inode)->location.objectid = objectid;
|
||||
BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
|
||||
BTRFS_I(inode)->location.offset = 0;
|
||||
btrfs_read_locked_inode(inode);
|
||||
unlock_new_inode(inode);
|
||||
|
||||
}
|
||||
if (is_bad_inode(inode)) {
|
||||
key.objectid = objectid;
|
||||
key.type = BTRFS_INODE_ITEM_KEY;
|
||||
key.offset = 0;
|
||||
inode = btrfs_iget(root->fs_info->sb, &key, root);
|
||||
if (IS_ERR(inode)) {
|
||||
inode = NULL;
|
||||
} else if (is_bad_inode(inode)) {
|
||||
iput(inode);
|
||||
inode = NULL;
|
||||
}
|
||||
@ -541,6 +539,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
|
||||
|
||||
if (found_type == BTRFS_FILE_EXTENT_REG ||
|
||||
found_type == BTRFS_FILE_EXTENT_PREALLOC) {
|
||||
u64 offset;
|
||||
unsigned long dest_offset;
|
||||
struct btrfs_key ins;
|
||||
|
||||
@ -555,6 +554,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
|
||||
ins.objectid = btrfs_file_extent_disk_bytenr(eb, item);
|
||||
ins.offset = btrfs_file_extent_disk_num_bytes(eb, item);
|
||||
ins.type = BTRFS_EXTENT_ITEM_KEY;
|
||||
offset = key->offset - btrfs_file_extent_offset(eb, item);
|
||||
|
||||
if (ins.objectid > 0) {
|
||||
u64 csum_start;
|
||||
@ -569,19 +569,16 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
|
||||
if (ret == 0) {
|
||||
ret = btrfs_inc_extent_ref(trans, root,
|
||||
ins.objectid, ins.offset,
|
||||
path->nodes[0]->start,
|
||||
root->root_key.objectid,
|
||||
trans->transid, key->objectid);
|
||||
0, root->root_key.objectid,
|
||||
key->objectid, offset);
|
||||
} else {
|
||||
/*
|
||||
* insert the extent pointer in the extent
|
||||
* allocation tree
|
||||
*/
|
||||
ret = btrfs_alloc_logged_extent(trans, root,
|
||||
path->nodes[0]->start,
|
||||
root->root_key.objectid,
|
||||
trans->transid, key->objectid,
|
||||
&ins);
|
||||
ret = btrfs_alloc_logged_file_extent(trans,
|
||||
root, root->root_key.objectid,
|
||||
key->objectid, offset, &ins);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
btrfs_release_path(root, path);
|
||||
@ -1706,9 +1703,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
|
||||
btrfs_wait_tree_block_writeback(next);
|
||||
btrfs_tree_unlock(next);
|
||||
|
||||
ret = btrfs_drop_leaf_ref(trans, root, next);
|
||||
BUG_ON(ret);
|
||||
|
||||
WARN_ON(root_owner !=
|
||||
BTRFS_TREE_LOG_OBJECTID);
|
||||
ret = btrfs_free_reserved_extent(root,
|
||||
@ -1753,10 +1747,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
|
||||
btrfs_wait_tree_block_writeback(next);
|
||||
btrfs_tree_unlock(next);
|
||||
|
||||
if (*level == 0) {
|
||||
ret = btrfs_drop_leaf_ref(trans, root, next);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
|
||||
ret = btrfs_free_reserved_extent(root, bytenr, blocksize);
|
||||
BUG_ON(ret);
|
||||
@ -1811,12 +1801,6 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
|
||||
btrfs_wait_tree_block_writeback(next);
|
||||
btrfs_tree_unlock(next);
|
||||
|
||||
if (*level == 0) {
|
||||
ret = btrfs_drop_leaf_ref(trans, root,
|
||||
next);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
|
||||
WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
|
||||
ret = btrfs_free_reserved_extent(root,
|
||||
path->nodes[*level]->start,
|
||||
@ -1884,11 +1868,6 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
|
||||
btrfs_wait_tree_block_writeback(next);
|
||||
btrfs_tree_unlock(next);
|
||||
|
||||
if (orig_level == 0) {
|
||||
ret = btrfs_drop_leaf_ref(trans, log,
|
||||
next);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
WARN_ON(log->root_key.objectid !=
|
||||
BTRFS_TREE_LOG_OBJECTID);
|
||||
ret = btrfs_free_reserved_extent(log, next->start,
|
||||
@ -2027,9 +2006,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_write_and_wait_marked_extents(log, &log->dirty_log_pages);
|
||||
BUG_ON(ret);
|
||||
|
||||
btrfs_set_root_bytenr(&log->root_item, log->node->start);
|
||||
btrfs_set_root_generation(&log->root_item, trans->transid);
|
||||
btrfs_set_root_level(&log->root_item, btrfs_header_level(log->node));
|
||||
btrfs_set_root_node(&log->root_item, log->node);
|
||||
|
||||
root->log_batch = 0;
|
||||
root->log_transid++;
|
||||
@ -2581,7 +2558,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
|
||||
ins_keys, ins_sizes, nr);
|
||||
BUG_ON(ret);
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
for (i = 0; i < nr; i++, dst_path->slots[0]++) {
|
||||
dst_offset = btrfs_item_ptr_offset(dst_path->nodes[0],
|
||||
dst_path->slots[0]);
|
||||
|
||||
@ -2617,36 +2594,31 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
|
||||
found_type = btrfs_file_extent_type(src, extent);
|
||||
if (found_type == BTRFS_FILE_EXTENT_REG ||
|
||||
found_type == BTRFS_FILE_EXTENT_PREALLOC) {
|
||||
u64 ds = btrfs_file_extent_disk_bytenr(src,
|
||||
extent);
|
||||
u64 dl = btrfs_file_extent_disk_num_bytes(src,
|
||||
extent);
|
||||
u64 cs = btrfs_file_extent_offset(src, extent);
|
||||
u64 cl = btrfs_file_extent_num_bytes(src,
|
||||
extent);;
|
||||
u64 ds, dl, cs, cl;
|
||||
ds = btrfs_file_extent_disk_bytenr(src,
|
||||
extent);
|
||||
/* ds == 0 is a hole */
|
||||
if (ds == 0)
|
||||
continue;
|
||||
|
||||
dl = btrfs_file_extent_disk_num_bytes(src,
|
||||
extent);
|
||||
cs = btrfs_file_extent_offset(src, extent);
|
||||
cl = btrfs_file_extent_num_bytes(src,
|
||||
extent);;
|
||||
if (btrfs_file_extent_compression(src,
|
||||
extent)) {
|
||||
cs = 0;
|
||||
cl = dl;
|
||||
}
|
||||
/* ds == 0 is a hole */
|
||||
if (ds != 0) {
|
||||
ret = btrfs_inc_extent_ref(trans, log,
|
||||
ds, dl,
|
||||
dst_path->nodes[0]->start,
|
||||
BTRFS_TREE_LOG_OBJECTID,
|
||||
trans->transid,
|
||||
ins_keys[i].objectid);
|
||||
BUG_ON(ret);
|
||||
ret = btrfs_lookup_csums_range(
|
||||
log->fs_info->csum_root,
|
||||
ds + cs, ds + cs + cl - 1,
|
||||
&ordered_sums);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
|
||||
ret = btrfs_lookup_csums_range(
|
||||
log->fs_info->csum_root,
|
||||
ds + cs, ds + cs + cl - 1,
|
||||
&ordered_sums);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
}
|
||||
dst_path->slots[0]++;
|
||||
}
|
||||
|
||||
btrfs_mark_buffer_dirty(dst_path->nodes[0]);
|
||||
@ -3029,9 +3001,7 @@ again:
|
||||
BUG_ON(!wc.replay_dest);
|
||||
|
||||
wc.replay_dest->log_root = log;
|
||||
mutex_lock(&fs_info->trans_mutex);
|
||||
btrfs_record_root_in_trans(wc.replay_dest);
|
||||
mutex_unlock(&fs_info->trans_mutex);
|
||||
btrfs_record_root_in_trans(trans, wc.replay_dest);
|
||||
ret = walk_log_tree(trans, log, &wc);
|
||||
BUG_ON(ret);
|
||||
|
||||
@ -3049,6 +3019,7 @@ again:
|
||||
key.offset = found_key.offset - 1;
|
||||
wc.replay_dest->log_root = NULL;
|
||||
free_extent_buffer(log->node);
|
||||
free_extent_buffer(log->commit_root);
|
||||
kfree(log);
|
||||
|
||||
if (found_key.offset == 0)
|
||||
|
@ -161,8 +161,10 @@ static noinline int run_scheduled_bios(struct btrfs_device *device)
|
||||
int again = 0;
|
||||
unsigned long num_run;
|
||||
unsigned long num_sync_run;
|
||||
unsigned long batch_run = 0;
|
||||
unsigned long limit;
|
||||
unsigned long last_waited = 0;
|
||||
int force_reg = 0;
|
||||
|
||||
bdi = blk_get_backing_dev_info(device->bdev);
|
||||
fs_info = device->dev_root->fs_info;
|
||||
@ -176,19 +178,22 @@ static noinline int run_scheduled_bios(struct btrfs_device *device)
|
||||
|
||||
loop:
|
||||
spin_lock(&device->io_lock);
|
||||
num_run = 0;
|
||||
|
||||
loop_lock:
|
||||
num_run = 0;
|
||||
|
||||
/* take all the bios off the list at once and process them
|
||||
* later on (without the lock held). But, remember the
|
||||
* tail and other pointers so the bios can be properly reinserted
|
||||
* into the list if we hit congestion
|
||||
*/
|
||||
if (device->pending_sync_bios.head)
|
||||
if (!force_reg && device->pending_sync_bios.head) {
|
||||
pending_bios = &device->pending_sync_bios;
|
||||
else
|
||||
force_reg = 1;
|
||||
} else {
|
||||
pending_bios = &device->pending_bios;
|
||||
force_reg = 0;
|
||||
}
|
||||
|
||||
pending = pending_bios->head;
|
||||
tail = pending_bios->tail;
|
||||
@ -228,10 +233,14 @@ loop_lock:
|
||||
while (pending) {
|
||||
|
||||
rmb();
|
||||
if (pending_bios != &device->pending_sync_bios &&
|
||||
device->pending_sync_bios.head &&
|
||||
num_run > 16) {
|
||||
cond_resched();
|
||||
/* we want to work on both lists, but do more bios on the
|
||||
* sync list than the regular list
|
||||
*/
|
||||
if ((num_run > 32 &&
|
||||
pending_bios != &device->pending_sync_bios &&
|
||||
device->pending_sync_bios.head) ||
|
||||
(num_run > 64 && pending_bios == &device->pending_sync_bios &&
|
||||
device->pending_bios.head)) {
|
||||
spin_lock(&device->io_lock);
|
||||
requeue_list(pending_bios, pending, tail);
|
||||
goto loop_lock;
|
||||
@ -249,6 +258,8 @@ loop_lock:
|
||||
BUG_ON(atomic_read(&cur->bi_cnt) == 0);
|
||||
submit_bio(cur->bi_rw, cur);
|
||||
num_run++;
|
||||
batch_run++;
|
||||
|
||||
if (bio_sync(cur))
|
||||
num_sync_run++;
|
||||
|
||||
@ -265,7 +276,7 @@ loop_lock:
|
||||
* is now congested. Back off and let other work structs
|
||||
* run instead
|
||||
*/
|
||||
if (pending && bdi_write_congested(bdi) && num_run > 16 &&
|
||||
if (pending && bdi_write_congested(bdi) && batch_run > 32 &&
|
||||
fs_info->fs_devices->open_devices > 1) {
|
||||
struct io_context *ioc;
|
||||
|
||||
@ -366,6 +377,7 @@ static noinline int device_list_add(const char *path,
|
||||
memcpy(fs_devices->fsid, disk_super->fsid, BTRFS_FSID_SIZE);
|
||||
fs_devices->latest_devid = devid;
|
||||
fs_devices->latest_trans = found_transid;
|
||||
mutex_init(&fs_devices->device_list_mutex);
|
||||
device = NULL;
|
||||
} else {
|
||||
device = __find_device(&fs_devices->devices, devid,
|
||||
@ -392,7 +404,11 @@ static noinline int device_list_add(const char *path,
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_LIST_HEAD(&device->dev_alloc_list);
|
||||
|
||||
mutex_lock(&fs_devices->device_list_mutex);
|
||||
list_add(&device->dev_list, &fs_devices->devices);
|
||||
mutex_unlock(&fs_devices->device_list_mutex);
|
||||
|
||||
device->fs_devices = fs_devices;
|
||||
fs_devices->num_devices++;
|
||||
}
|
||||
@ -418,10 +434,12 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
|
||||
INIT_LIST_HEAD(&fs_devices->devices);
|
||||
INIT_LIST_HEAD(&fs_devices->alloc_list);
|
||||
INIT_LIST_HEAD(&fs_devices->list);
|
||||
mutex_init(&fs_devices->device_list_mutex);
|
||||
fs_devices->latest_devid = orig->latest_devid;
|
||||
fs_devices->latest_trans = orig->latest_trans;
|
||||
memcpy(fs_devices->fsid, orig->fsid, sizeof(fs_devices->fsid));
|
||||
|
||||
mutex_lock(&orig->device_list_mutex);
|
||||
list_for_each_entry(orig_dev, &orig->devices, dev_list) {
|
||||
device = kzalloc(sizeof(*device), GFP_NOFS);
|
||||
if (!device)
|
||||
@ -443,8 +461,10 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
|
||||
device->fs_devices = fs_devices;
|
||||
fs_devices->num_devices++;
|
||||
}
|
||||
mutex_unlock(&orig->device_list_mutex);
|
||||
return fs_devices;
|
||||
error:
|
||||
mutex_unlock(&orig->device_list_mutex);
|
||||
free_fs_devices(fs_devices);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
@ -455,6 +475,7 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
|
||||
|
||||
mutex_lock(&uuid_mutex);
|
||||
again:
|
||||
mutex_lock(&fs_devices->device_list_mutex);
|
||||
list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
|
||||
if (device->in_fs_metadata)
|
||||
continue;
|
||||
@ -474,6 +495,7 @@ again:
|
||||
kfree(device->name);
|
||||
kfree(device);
|
||||
}
|
||||
mutex_unlock(&fs_devices->device_list_mutex);
|
||||
|
||||
if (fs_devices->seed) {
|
||||
fs_devices = fs_devices->seed;
|
||||
@ -594,6 +616,9 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
|
||||
device->in_fs_metadata = 0;
|
||||
device->mode = flags;
|
||||
|
||||
if (!blk_queue_nonrot(bdev_get_queue(bdev)))
|
||||
fs_devices->rotating = 1;
|
||||
|
||||
fs_devices->open_devices++;
|
||||
if (device->writeable) {
|
||||
fs_devices->rw_devices++;
|
||||
@ -1121,12 +1146,14 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
|
||||
|
||||
device = NULL;
|
||||
devices = &root->fs_info->fs_devices->devices;
|
||||
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
|
||||
list_for_each_entry(tmp, devices, dev_list) {
|
||||
if (tmp->in_fs_metadata && !tmp->bdev) {
|
||||
device = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
|
||||
bdev = NULL;
|
||||
bh = NULL;
|
||||
disk_super = NULL;
|
||||
@ -1181,7 +1208,16 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
|
||||
goto error_brelse;
|
||||
|
||||
device->in_fs_metadata = 0;
|
||||
|
||||
/*
|
||||
* the device list mutex makes sure that we don't change
|
||||
* the device list while someone else is writing out all
|
||||
* the device supers.
|
||||
*/
|
||||
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
|
||||
list_del_init(&device->dev_list);
|
||||
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
|
||||
|
||||
device->fs_devices->num_devices--;
|
||||
|
||||
next_device = list_entry(root->fs_info->fs_devices->devices.next,
|
||||
@ -1275,6 +1311,7 @@ static int btrfs_prepare_sprout(struct btrfs_trans_handle *trans,
|
||||
seed_devices->opened = 1;
|
||||
INIT_LIST_HEAD(&seed_devices->devices);
|
||||
INIT_LIST_HEAD(&seed_devices->alloc_list);
|
||||
mutex_init(&seed_devices->device_list_mutex);
|
||||
list_splice_init(&fs_devices->devices, &seed_devices->devices);
|
||||
list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list);
|
||||
list_for_each_entry(device, &seed_devices->devices, dev_list) {
|
||||
@ -1400,6 +1437,10 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
|
||||
mutex_lock(&root->fs_info->volume_mutex);
|
||||
|
||||
devices = &root->fs_info->fs_devices->devices;
|
||||
/*
|
||||
* we have the volume lock, so we don't need the extra
|
||||
* device list mutex while reading the list here.
|
||||
*/
|
||||
list_for_each_entry(device, devices, dev_list) {
|
||||
if (device->bdev == bdev) {
|
||||
ret = -EEXIST;
|
||||
@ -1454,6 +1495,12 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
|
||||
}
|
||||
|
||||
device->fs_devices = root->fs_info->fs_devices;
|
||||
|
||||
/*
|
||||
* we don't want write_supers to jump in here with our device
|
||||
* half setup
|
||||
*/
|
||||
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
|
||||
list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
|
||||
list_add(&device->dev_alloc_list,
|
||||
&root->fs_info->fs_devices->alloc_list);
|
||||
@ -1462,6 +1509,9 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
|
||||
root->fs_info->fs_devices->rw_devices++;
|
||||
root->fs_info->fs_devices->total_rw_bytes += device->total_bytes;
|
||||
|
||||
if (!blk_queue_nonrot(bdev_get_queue(bdev)))
|
||||
root->fs_info->fs_devices->rotating = 1;
|
||||
|
||||
total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
|
||||
btrfs_set_super_total_bytes(&root->fs_info->super_copy,
|
||||
total_bytes + device->total_bytes);
|
||||
@ -1469,6 +1519,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
|
||||
total_bytes = btrfs_super_num_devices(&root->fs_info->super_copy);
|
||||
btrfs_set_super_num_devices(&root->fs_info->super_copy,
|
||||
total_bytes + 1);
|
||||
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
|
||||
|
||||
if (seeding_dev) {
|
||||
ret = init_first_rw_device(trans, root, device);
|
||||
@ -1671,8 +1722,6 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "btrfs relocating chunk %llu\n",
|
||||
(unsigned long long)chunk_offset);
|
||||
root = root->fs_info->chunk_root;
|
||||
extent_root = root->fs_info->extent_root;
|
||||
em_tree = &root->fs_info->mapping_tree.map_tree;
|
||||
|
@ -96,7 +96,12 @@ struct btrfs_fs_devices {
|
||||
u64 rw_devices;
|
||||
u64 total_rw_bytes;
|
||||
struct block_device *latest_bdev;
|
||||
/* all of the devices in the FS */
|
||||
|
||||
/* all of the devices in the FS, protected by a mutex
|
||||
* so we can safely walk it to write out the supers without
|
||||
* worrying about add/remove by the multi-device code
|
||||
*/
|
||||
struct mutex device_list_mutex;
|
||||
struct list_head devices;
|
||||
|
||||
/* devices not currently being allocated */
|
||||
@ -107,6 +112,11 @@ struct btrfs_fs_devices {
|
||||
int seeding;
|
||||
|
||||
int opened;
|
||||
|
||||
/* set when we find or add a device that doesn't have the
|
||||
* nonrot flag set
|
||||
*/
|
||||
int rotating;
|
||||
};
|
||||
|
||||
struct btrfs_bio_stripe {
|
||||
|
Loading…
Reference in New Issue
Block a user